Home | History | Annotate | Download | only in qt
      1 /*
      2     Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
      3     Copyright (C) 2007 Staikos Computing Services Inc.  <info (at) staikos.net>
      4     Copyright (C) 2008 Holger Hans Peter Freyther
      5 
      6     This library is free software; you can redistribute it and/or
      7     modify it under the terms of the GNU Library General Public
      8     License as published by the Free Software Foundation; either
      9     version 2 of the License, or (at your option) any later version.
     10 
     11     This library is distributed in the hope that it will be useful,
     12     but WITHOUT ANY WARRANTY; without even the implied warranty of
     13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14     Library General Public License for more details.
     15 
     16     You should have received a copy of the GNU Library General Public License
     17     along with this library; see the file COPYING.LIB.  If not, write to
     18     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19     Boston, MA 02110-1301, USA.
     20 */
     21 #include "config.h"
     22 #include "QNetworkReplyHandler.h"
     23 
     24 #include "HTTPParsers.h"
     25 #include "MIMETypeRegistry.h"
     26 #include "ResourceHandle.h"
     27 #include "ResourceHandleClient.h"
     28 #include "ResourceHandleInternal.h"
     29 #include "ResourceResponse.h"
     30 #include "ResourceRequest.h"
     31 #include <QDateTime>
     32 #include <QFile>
     33 #include <QNetworkReply>
     34 #include <QNetworkCookie>
     35 #include <qwebframe.h>
     36 #include <qwebpage.h>
     37 
     38 #include <QDebug>
     39 #include <QCoreApplication>
     40 
     41 // What type of connection should be used for the signals of the
     42 // QNetworkReply? This depends on if Qt has a bugfix for this or not.
     43 // It is fixed in Qt 4.6.1. See https://bugs.webkit.org/show_bug.cgi?id=32113
     44 #if QT_VERSION > QT_VERSION_CHECK(4, 6, 0)
     45 #define SIGNAL_CONN Qt::DirectConnection
     46 #else
     47 #define SIGNAL_CONN Qt::QueuedConnection
     48 #endif
     49 
     50 
     51 namespace WebCore {
     52 
     53 // Take a deep copy of the FormDataElement
     54 FormDataIODevice::FormDataIODevice(FormData* data)
     55     : m_formElements(data ? data->elements() : Vector<FormDataElement>())
     56     , m_currentFile(0)
     57     , m_currentDelta(0)
     58 {
     59     setOpenMode(FormDataIODevice::ReadOnly);
     60 }
     61 
     62 FormDataIODevice::~FormDataIODevice()
     63 {
     64     delete m_currentFile;
     65 }
     66 
     67 void FormDataIODevice::moveToNextElement()
     68 {
     69     if (m_currentFile)
     70         m_currentFile->close();
     71     m_currentDelta = 0;
     72 
     73     m_formElements.remove(0);
     74 
     75     if (m_formElements.isEmpty() || m_formElements[0].m_type == FormDataElement::data)
     76         return;
     77 
     78     if (!m_currentFile)
     79         m_currentFile = new QFile;
     80 
     81     m_currentFile->setFileName(m_formElements[0].m_filename);
     82     m_currentFile->open(QFile::ReadOnly);
     83 }
     84 
     85 // m_formElements[0] is the current item. If the destination buffer is
     86 // big enough we are going to read from more than one FormDataElement
     87 qint64 FormDataIODevice::readData(char* destination, qint64 size)
     88 {
     89     if (m_formElements.isEmpty())
     90         return -1;
     91 
     92     qint64 copied = 0;
     93     while (copied < size && !m_formElements.isEmpty()) {
     94         const FormDataElement& element = m_formElements[0];
     95         const qint64 available = size-copied;
     96 
     97         if (element.m_type == FormDataElement::data) {
     98             const qint64 toCopy = qMin<qint64>(available, element.m_data.size() - m_currentDelta);
     99             memcpy(destination+copied, element.m_data.data()+m_currentDelta, toCopy);
    100             m_currentDelta += toCopy;
    101             copied += toCopy;
    102 
    103             if (m_currentDelta == element.m_data.size())
    104                 moveToNextElement();
    105         } else {
    106             const QByteArray data = m_currentFile->read(available);
    107             memcpy(destination+copied, data.constData(), data.size());
    108             copied += data.size();
    109 
    110             if (m_currentFile->atEnd() || !m_currentFile->isOpen())
    111                 moveToNextElement();
    112         }
    113     }
    114 
    115     return copied;
    116 }
    117 
    118 qint64 FormDataIODevice::writeData(const char*, qint64)
    119 {
    120     return -1;
    121 }
    122 
    123 bool FormDataIODevice::isSequential() const
    124 {
    125     return true;
    126 }
    127 
    128 QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle* handle, LoadMode loadMode)
    129     : QObject(0)
    130     , m_reply(0)
    131     , m_resourceHandle(handle)
    132     , m_redirected(false)
    133     , m_responseSent(false)
    134     , m_responseDataSent(false)
    135     , m_loadMode(loadMode)
    136     , m_shouldStart(true)
    137     , m_shouldFinish(false)
    138     , m_shouldSendResponse(false)
    139     , m_shouldForwardData(false)
    140 {
    141     const ResourceRequest &r = m_resourceHandle->request();
    142 
    143     if (r.httpMethod() == "GET")
    144         m_method = QNetworkAccessManager::GetOperation;
    145     else if (r.httpMethod() == "HEAD")
    146         m_method = QNetworkAccessManager::HeadOperation;
    147     else if (r.httpMethod() == "POST")
    148         m_method = QNetworkAccessManager::PostOperation;
    149     else if (r.httpMethod() == "PUT")
    150         m_method = QNetworkAccessManager::PutOperation;
    151 #if QT_VERSION >= 0x040600
    152     else if (r.httpMethod() == "DELETE")
    153         m_method = QNetworkAccessManager::DeleteOperation;
    154 #endif
    155     else
    156         m_method = QNetworkAccessManager::UnknownOperation;
    157 
    158     m_request = r.toNetworkRequest(m_resourceHandle->getInternal()->m_frame);
    159 
    160     if (m_loadMode == LoadNormal)
    161         start();
    162 }
    163 
    164 void QNetworkReplyHandler::setLoadMode(LoadMode mode)
    165 {
    166     // https://bugs.webkit.org/show_bug.cgi?id=26556
    167     // We cannot call sendQueuedItems() from here, because the signal that
    168     // caused us to get into deferred mode, might not be processed yet.
    169     switch (mode) {
    170     case LoadNormal:
    171         m_loadMode = LoadResuming;
    172         emit processQueuedItems();
    173         break;
    174     case LoadDeferred:
    175         m_loadMode = LoadDeferred;
    176         break;
    177     case LoadResuming:
    178         Q_ASSERT(0); // should never happen
    179         break;
    180     };
    181 }
    182 
    183 void QNetworkReplyHandler::abort()
    184 {
    185     m_resourceHandle = 0;
    186     if (m_reply) {
    187         QNetworkReply* reply = release();
    188         reply->abort();
    189         reply->deleteLater();
    190     }
    191     deleteLater();
    192 }
    193 
    194 QNetworkReply* QNetworkReplyHandler::release()
    195 {
    196     QNetworkReply* reply = m_reply;
    197     if (m_reply) {
    198         disconnect(m_reply, 0, this, 0);
    199         // We have queued connections to the QNetworkReply. Make sure any
    200         // posted meta call events that were the result of a signal emission
    201         // don't reach the slots in our instance.
    202         QCoreApplication::removePostedEvents(this, QEvent::MetaCall);
    203         m_reply->setParent(0);
    204         m_reply = 0;
    205     }
    206     return reply;
    207 }
    208 
    209 static bool ignoreHttpError(QNetworkReply* reply, bool receivedData)
    210 {
    211     int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    212 
    213     if (httpStatusCode == 401 || httpStatusCode == 407)
    214         return true;
    215 
    216     if (receivedData && (httpStatusCode >= 400 && httpStatusCode < 600))
    217         return true;
    218 
    219     return false;
    220 }
    221 
    222 void QNetworkReplyHandler::finish()
    223 {
    224     m_shouldFinish = (m_loadMode != LoadNormal);
    225     if (m_shouldFinish)
    226         return;
    227 
    228     sendResponseIfNeeded();
    229 
    230     if (!m_resourceHandle)
    231         return;
    232     ResourceHandleClient* client = m_resourceHandle->client();
    233     if (!client) {
    234         m_reply->deleteLater();
    235         m_reply = 0;
    236         return;
    237     }
    238 
    239     QNetworkReply* oldReply = m_reply;
    240 
    241     if (m_redirected) {
    242         resetState();
    243         start();
    244     } else if (!m_reply->error() || ignoreHttpError(m_reply, m_responseDataSent)) {
    245         client->didFinishLoading(m_resourceHandle);
    246     } else {
    247         QUrl url = m_reply->url();
    248         int httpStatusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    249 
    250         if (httpStatusCode) {
    251             ResourceError error("HTTP", httpStatusCode, url.toString(), m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString());
    252             client->didFail(m_resourceHandle, error);
    253         } else {
    254             ResourceError error("QtNetwork", m_reply->error(), url.toString(), m_reply->errorString());
    255             client->didFail(m_resourceHandle, error);
    256         }
    257     }
    258 
    259     oldReply->deleteLater();
    260     if (oldReply == m_reply)
    261         m_reply = 0;
    262 }
    263 
    264 void QNetworkReplyHandler::sendResponseIfNeeded()
    265 {
    266     m_shouldSendResponse = (m_loadMode != LoadNormal);
    267     if (m_shouldSendResponse)
    268         return;
    269 
    270     if (m_reply->error() && !ignoreHttpError(m_reply, m_responseDataSent))
    271         return;
    272 
    273     if (m_responseSent || !m_resourceHandle)
    274         return;
    275     m_responseSent = true;
    276 
    277     ResourceHandleClient* client = m_resourceHandle->client();
    278     if (!client)
    279         return;
    280 
    281     WebCore::String contentType = m_reply->header(QNetworkRequest::ContentTypeHeader).toString();
    282     WebCore::String encoding = extractCharsetFromMediaType(contentType);
    283     WebCore::String mimeType = extractMIMETypeFromMediaType(contentType);
    284 
    285     if (mimeType.isEmpty()) {
    286         // let's try to guess from the extension
    287         QString extension = m_reply->url().path();
    288         int index = extension.lastIndexOf(QLatin1Char('.'));
    289         if (index > 0) {
    290             extension = extension.mid(index + 1);
    291             mimeType = MIMETypeRegistry::getMIMETypeForExtension(extension);
    292         }
    293     }
    294 
    295     KURL url(m_reply->url());
    296     ResourceResponse response(url, mimeType,
    297                               m_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(),
    298                               encoding, String());
    299 
    300     if (url.isLocalFile()) {
    301         client->didReceiveResponse(m_resourceHandle, response);
    302         return;
    303     }
    304 
    305     // The status code is equal to 0 for protocols not in the HTTP family.
    306     int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    307 
    308     if (url.protocolInHTTPFamily()) {
    309         String suggestedFilename = filenameFromHTTPContentDisposition(QString::fromAscii(m_reply->rawHeader("Content-Disposition")));
    310 
    311         if (!suggestedFilename.isEmpty())
    312             response.setSuggestedFilename(suggestedFilename);
    313         else
    314             response.setSuggestedFilename(url.lastPathComponent());
    315 
    316         response.setHTTPStatusCode(statusCode);
    317         response.setHTTPStatusText(m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray().constData());
    318 
    319         // Add remaining headers.
    320 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
    321         foreach (const QNetworkReply::RawHeaderPair& pair, m_reply->rawHeaderPairs()) {
    322             response.setHTTPHeaderField(QString::fromAscii(pair.first), QString::fromAscii(pair.second));
    323         }
    324 #else
    325         foreach (const QByteArray& headerName, m_reply->rawHeaderList()) {
    326             response.setHTTPHeaderField(QString::fromAscii(headerName), QString::fromAscii(m_reply->rawHeader(headerName)));
    327         }
    328 #endif
    329     }
    330 
    331     QUrl redirection = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
    332     if (redirection.isValid()) {
    333         m_redirected = true;
    334 
    335         QUrl newUrl = m_reply->url().resolved(redirection);
    336         ResourceRequest newRequest = m_resourceHandle->request();
    337         newRequest.setURL(newUrl);
    338 
    339         if (((statusCode >= 301 && statusCode <= 303) || statusCode == 307) && m_method == QNetworkAccessManager::PostOperation) {
    340             m_method = QNetworkAccessManager::GetOperation;
    341             newRequest.setHTTPMethod("GET");
    342         }
    343 
    344         // Should not set Referer after a redirect from a secure resource to non-secure one.
    345         if (!newRequest.url().protocolIs("https") && protocolIs(newRequest.httpReferrer(), "https"))
    346             newRequest.clearHTTPReferrer();
    347 
    348         client->willSendRequest(m_resourceHandle, newRequest, response);
    349         if (!m_resourceHandle) // network error did cancel the request
    350             return;
    351 
    352         m_request = newRequest.toNetworkRequest(m_resourceHandle->getInternal()->m_frame);
    353         return;
    354     }
    355 
    356     client->didReceiveResponse(m_resourceHandle, response);
    357 }
    358 
    359 void QNetworkReplyHandler::forwardData()
    360 {
    361     m_shouldForwardData = (m_loadMode != LoadNormal);
    362     if (m_shouldForwardData)
    363         return;
    364 
    365     sendResponseIfNeeded();
    366 
    367     // don't emit the "Document has moved here" type of HTML
    368     if (m_redirected)
    369         return;
    370 
    371     if (!m_resourceHandle)
    372         return;
    373 
    374     QByteArray data = m_reply->read(m_reply->bytesAvailable());
    375 
    376     ResourceHandleClient* client = m_resourceHandle->client();
    377     if (!client)
    378         return;
    379 
    380     if (!data.isEmpty()) {
    381         m_responseDataSent = true;
    382         client->didReceiveData(m_resourceHandle, data.constData(), data.length(), data.length() /*FixMe*/);
    383     }
    384 }
    385 
    386 void QNetworkReplyHandler::uploadProgress(qint64 bytesSent, qint64 bytesTotal)
    387 {
    388     if (!m_resourceHandle)
    389         return;
    390 
    391     ResourceHandleClient* client = m_resourceHandle->client();
    392     if (!client)
    393         return;
    394 
    395     client->didSendData(m_resourceHandle, bytesSent, bytesTotal);
    396 }
    397 
    398 void QNetworkReplyHandler::start()
    399 {
    400     m_shouldStart = false;
    401 
    402     ResourceHandleInternal* d = m_resourceHandle->getInternal();
    403 
    404     QNetworkAccessManager* manager = d->m_frame->page()->networkAccessManager();
    405 
    406     const QUrl url = m_request.url();
    407     const QString scheme = url.scheme();
    408     // Post requests on files and data don't really make sense, but for
    409     // fast/forms/form-post-urlencoded.html and for fast/forms/button-state-restore.html
    410     // we still need to retrieve the file/data, which means we map it to a Get instead.
    411     if (m_method == QNetworkAccessManager::PostOperation
    412         && (!url.toLocalFile().isEmpty() || url.scheme() == QLatin1String("data")))
    413         m_method = QNetworkAccessManager::GetOperation;
    414 
    415     switch (m_method) {
    416         case QNetworkAccessManager::GetOperation:
    417             m_reply = manager->get(m_request);
    418             break;
    419         case QNetworkAccessManager::PostOperation: {
    420             FormDataIODevice* postDevice = new FormDataIODevice(d->m_request.httpBody());
    421             m_reply = manager->post(m_request, postDevice);
    422             postDevice->setParent(m_reply);
    423             break;
    424         }
    425         case QNetworkAccessManager::HeadOperation:
    426             m_reply = manager->head(m_request);
    427             break;
    428         case QNetworkAccessManager::PutOperation: {
    429             FormDataIODevice* putDevice = new FormDataIODevice(d->m_request.httpBody());
    430             m_reply = manager->put(m_request, putDevice);
    431             putDevice->setParent(m_reply);
    432             break;
    433         }
    434 #if QT_VERSION >= 0x040600
    435         case QNetworkAccessManager::DeleteOperation: {
    436             m_reply = manager->deleteResource(m_request);
    437             break;
    438         }
    439 #endif
    440         case QNetworkAccessManager::UnknownOperation: {
    441             m_reply = 0;
    442             ResourceHandleClient* client = m_resourceHandle->client();
    443             if (client) {
    444                 ResourceError error(url.host(), 400 /*bad request*/,
    445                                     url.toString(),
    446                                     QCoreApplication::translate("QWebPage", "Bad HTTP request"));
    447                 client->didFail(m_resourceHandle, error);
    448             }
    449             return;
    450         }
    451     }
    452 
    453     m_reply->setParent(this);
    454 
    455     connect(m_reply, SIGNAL(finished()),
    456             this, SLOT(finish()), SIGNAL_CONN);
    457 
    458     // For http(s) we know that the headers are complete upon metaDataChanged() emission, so we
    459     // can send the response as early as possible
    460     if (scheme == QLatin1String("http") || scheme == QLatin1String("https"))
    461         connect(m_reply, SIGNAL(metaDataChanged()),
    462                 this, SLOT(sendResponseIfNeeded()), SIGNAL_CONN);
    463 
    464     connect(m_reply, SIGNAL(readyRead()),
    465             this, SLOT(forwardData()), SIGNAL_CONN);
    466 
    467     if (m_resourceHandle->request().reportUploadProgress()) {
    468         connect(m_reply, SIGNAL(uploadProgress(qint64, qint64)),
    469                 this, SLOT(uploadProgress(qint64, qint64)), SIGNAL_CONN);
    470     }
    471 
    472     // Make this a direct function call once we require 4.6.1+.
    473     connect(this, SIGNAL(processQueuedItems()),
    474             this, SLOT(sendQueuedItems()), SIGNAL_CONN);
    475 }
    476 
    477 void QNetworkReplyHandler::resetState()
    478 {
    479     m_redirected = false;
    480     m_responseSent = false;
    481     m_responseDataSent = false;
    482     m_shouldStart = true;
    483     m_shouldFinish = false;
    484     m_shouldSendResponse = false;
    485     m_shouldForwardData = false;
    486 }
    487 
    488 void QNetworkReplyHandler::sendQueuedItems()
    489 {
    490     if (m_loadMode != LoadResuming)
    491         return;
    492     m_loadMode = LoadNormal;
    493 
    494     if (m_shouldStart)
    495         start();
    496 
    497     if (m_shouldSendResponse)
    498         sendResponseIfNeeded();
    499 
    500     if (m_shouldForwardData)
    501         forwardData();
    502 
    503     if (m_shouldFinish)
    504         finish();
    505 }
    506 
    507 }
    508 
    509 #include "moc_QNetworkReplyHandler.cpp"
    510