Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to intercept AJAX-Requests within QtWebKit?

I want to intercept, inspect and (if needed) reject AJAX-Requests based on the Fingerprint of the SSL-Certificate. I use the QNetworkAccessManager::createRequest(...) function to issue requests. Everything works fine when I use QWebFrame::load(...). Even the content which is loaded within the request (like .css or .js files) emit signals. Unfortunately no AJAX-Requests emits any Signals. I know that the Signals are connected to the very same slots (for "normal" as well as AJAX-Requests) within MyNetworkAccessManager::createRequest(...) function.

QNetworkReply *reply = QNetworkAccessManager::createRequest(op, req, outgoingData);
connect(reply, SIGNAL(readyRead()), this, SLOT(handleStarted()));
connect(reply, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(handleSslErrors(const QList<QSslError> &)));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleNetworkError()));

Why are AJAX Requests so different? Where can I access them?

like image 355
Senči Avatar asked Jun 11 '14 18:06

Senči


1 Answers

From what I can tell, AJAX requests do emit the finished signal on QNetworkAccessManager. You need connect to the instance of QNetworkAccessManager on your QWebPage instance:

QWebPage *page = ui->webView->page();
QNetworkAccessManager *nam = page->networkAccessManager();

connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));

QFile file;
file.setFileName(":/js/jquery-2.1.1.min.js"); // jQuery is loaded as a resource
file.open(QIODevice::ReadOnly);
QString jQuery = file.readAll();
file.close();

ui->webView->load(QUrl("about:blank"));
QWebFrame *frame = m_page->mainFrame();


frame->evaluateJavaScript(jQuery); // load jQuery

// check that jQuery is loaded
frame->evaluateJavaScript("$(document).ready(function() { alert('jQuery loaded!'); });");

// do an AJAX GET
frame->evaluateJavaScript("$.ajax({"
                          "url: 'http://www.json-generator.com/api/json/get/cqkXBAEoQy?indent=2',"
                          "method: 'GET',"
                          "dataType: 'json'"
                      "}).done(function (data) {"
                          "for (var i = 0; i < data.length; i++) {"
                              "alert(data[i].name);"
                          "}"
                      "}).error(function (data) { alert('AJAX error'); });");

Then you can monitor replies in the replyFinished slot like so:

void MainWindow::replyFinished(QNetworkReply *reply)
{
    QByteArray bytes = reply->readAll();
    QString str = QString::fromUtf8(bytes.data(), bytes.size());
    QString replyUrl = reply->url().toString();
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    qDebug() << statusCode;
    qDebug() << replyUrl;
    qDebug() << str;
}

I have noticed that jQuery AJAX's done promise doesn't seem to execute when you do anything with the QNetworkReply, but you can see that the request actually finishes in the debug console.

See my GitHub repository to try out the above code: https://github.com/pcmantinker/QtWebkitAJAX

As far as blocking connections based on SSL certificates, you'd have to subclass QNetworkAccessManager and override QNetworkAccessManager::createRequest. Something like this could work:

QNetworkReply *CustomQNetworkAccessManager::createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData)
{
    QNetworkRequest req(request);
    QNetworkReply *reply = QNetworkAccessManager::createRequest(op, req, outgoingData);
    QSslConfiguration *sslConfig = reply->sslConfiguration();
    QList<QSslCertificate> sslCaCerts = sslConfig->caCertificates();
    // do something with sslCaCerts

    return reply;
}
like image 96
Cameron Tinker Avatar answered Sep 20 '22 03:09

Cameron Tinker