QNetworkReplyHandler.cpp 16.8 KB
Newer Older
1
/*
2
    Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3
    Copyright (C) 2007 Staikos Computing Services Inc.  <info@staikos.net>
4
    Copyright (C) 2008 Holger Hans Peter Freyther
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "QNetworkReplyHandler.h"

#include "HTTPParsers.h"
#include "MIMETypeRegistry.h"
#include "ResourceHandle.h"
#include "ResourceHandleClient.h"
#include "ResourceHandleInternal.h"
#include "ResourceResponse.h"
#include "ResourceRequest.h"
31
#include <QDateTime>
32
#include <QFile>
33 34 35 36
#include <QNetworkReply>
#include <QNetworkCookie>
#include <qwebframe.h>
#include <qwebpage.h>
37
#include <wtf/text/CString.h>
38

39
#include <QDebug>
40
#include <QCoreApplication>
41

42 43 44 45 46 47 48 49 50 51
// What type of connection should be used for the signals of the
// QNetworkReply? This depends on if Qt has a bugfix for this or not.
// It is fixed in Qt 4.6.1. See https://bugs.webkit.org/show_bug.cgi?id=32113
#if QT_VERSION > QT_VERSION_CHECK(4, 6, 0)
#define SIGNAL_CONN Qt::DirectConnection
#else
#define SIGNAL_CONN Qt::QueuedConnection
#endif


52 53
namespace WebCore {

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
// Take a deep copy of the FormDataElement
FormDataIODevice::FormDataIODevice(FormData* data)
    : m_formElements(data ? data->elements() : Vector<FormDataElement>())
    , m_currentFile(0)
    , m_currentDelta(0)
{
    setOpenMode(FormDataIODevice::ReadOnly);
}

FormDataIODevice::~FormDataIODevice()
{
    delete m_currentFile;
}

void FormDataIODevice::moveToNextElement()
{
    if (m_currentFile)
        m_currentFile->close();
    m_currentDelta = 0;

    m_formElements.remove(0);

    if (m_formElements.isEmpty() || m_formElements[0].m_type == FormDataElement::data)
        return;

    if (!m_currentFile)
        m_currentFile = new QFile;

    m_currentFile->setFileName(m_formElements[0].m_filename);
    m_currentFile->open(QFile::ReadOnly);
}

// m_formElements[0] is the current item. If the destination buffer is
// big enough we are going to read from more than one FormDataElement
qint64 FormDataIODevice::readData(char* destination, qint64 size)
{
    if (m_formElements.isEmpty())
        return -1;

    qint64 copied = 0;
    while (copied < size && !m_formElements.isEmpty()) {
        const FormDataElement& element = m_formElements[0];
        const qint64 available = size-copied;

        if (element.m_type == FormDataElement::data) {
            const qint64 toCopy = qMin<qint64>(available, element.m_data.size() - m_currentDelta);
            memcpy(destination+copied, element.m_data.data()+m_currentDelta, toCopy); 
            m_currentDelta += toCopy;
            copied += toCopy;

            if (m_currentDelta == element.m_data.size())
                moveToNextElement();
        } else {
            const QByteArray data = m_currentFile->read(available);
            memcpy(destination+copied, data.constData(), data.size());
            copied += data.size();

            if (m_currentFile->atEnd() || !m_currentFile->isOpen())
                moveToNextElement();
        }
    }

    return copied;
}

qint64 FormDataIODevice::writeData(const char*, qint64)
{
    return -1;
}

bool FormDataIODevice::isSequential() const
{
    return true;
}

129
QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle* handle, LoadMode loadMode)
130
    : QObject(0)
131
    , m_reply(0)
132
    , m_resourceHandle(handle)
133 134
    , m_redirected(false)
    , m_responseSent(false)
135
    , m_responseDataSent(false)
136 137 138 139 140
    , m_loadMode(loadMode)
    , m_shouldStart(true)
    , m_shouldFinish(false)
    , m_shouldSendResponse(false)
    , m_shouldForwardData(false)
141 142 143 144 145 146 147 148 149 150 151
{
    const ResourceRequest &r = m_resourceHandle->request();

    if (r.httpMethod() == "GET")
        m_method = QNetworkAccessManager::GetOperation;
    else if (r.httpMethod() == "HEAD")
        m_method = QNetworkAccessManager::HeadOperation;
    else if (r.httpMethod() == "POST")
        m_method = QNetworkAccessManager::PostOperation;
    else if (r.httpMethod() == "PUT")
        m_method = QNetworkAccessManager::PutOperation;
152 153 154
#if QT_VERSION >= 0x040600
    else if (r.httpMethod() == "DELETE")
        m_method = QNetworkAccessManager::DeleteOperation;
155 156 157 158
#endif
#if QT_VERSION >= 0x040700
    else if (r.httpMethod() == "OPTIONS")
        m_method = QNetworkAccessManager::CustomOperation;
159
#endif
160 161 162
    else
        m_method = QNetworkAccessManager::UnknownOperation;

163
    m_request = r.toNetworkRequest(m_resourceHandle->getInternal()->m_frame);
164

165 166 167 168 169 170
    if (m_loadMode == LoadNormal)
        start();
}

void QNetworkReplyHandler::setLoadMode(LoadMode mode)
{
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
    // https://bugs.webkit.org/show_bug.cgi?id=26556
    // We cannot call sendQueuedItems() from here, because the signal that 
    // caused us to get into deferred mode, might not be processed yet.
    switch (mode) {
    case LoadNormal:
        m_loadMode = LoadResuming;
        emit processQueuedItems();
        break;
    case LoadDeferred:
        m_loadMode = LoadDeferred;
        break;
    case LoadResuming:
        Q_ASSERT(0); // should never happen
        break;
    };
186 187 188 189
}

void QNetworkReplyHandler::abort()
{
190
    m_resourceHandle = 0;
191
    if (m_reply) {
192 193
        QNetworkReply* reply = release();
        reply->abort();
194
        reply->deleteLater();
195
    }
196
    deleteLater();
197 198
}

199
QNetworkReply* QNetworkReplyHandler::release()
200
{
201
    QNetworkReply* reply = m_reply;
202 203
    if (m_reply) {
        disconnect(m_reply, 0, this, 0);
204 205 206 207
        // We have queued connections to the QNetworkReply. Make sure any
        // posted meta call events that were the result of a signal emission
        // don't reach the slots in our instance.
        QCoreApplication::removePostedEvents(this, QEvent::MetaCall);
208
        m_reply->setParent(0);
209 210 211 212 213
        m_reply = 0;
    }
    return reply;
}

214 215 216 217 218 219 220
static bool ignoreHttpError(QNetworkReply* reply, bool receivedData)
{
    int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

    if (httpStatusCode == 401 || httpStatusCode == 407)
        return true;

221
    if (receivedData && (httpStatusCode >= 400 && httpStatusCode < 600))
222 223 224 225 226
        return true;

    return false;
}

227 228
void QNetworkReplyHandler::finish()
{
229 230
    m_shouldFinish = (m_loadMode != LoadNormal);
    if (m_shouldFinish)
231 232
        return;

233
    sendResponseIfNeeded();
234

235 236
    if (!m_resourceHandle)
        return;
237
    ResourceHandleClient* client = m_resourceHandle->client();
238 239 240
    if (!client) {
        m_reply->deleteLater();
        m_reply = 0;
241
        return;
242
    }
243

244
    QNetworkReply* oldReply = m_reply;
245

246
    if (m_redirected) {
247
        resetState();
248
        start();
249 250 251 252
    } else if (!m_reply->error() || ignoreHttpError(m_reply, m_responseDataSent)) {
        client->didFinishLoading(m_resourceHandle);
    } else {
        QUrl url = m_reply->url();
253
        int httpStatusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
254

255 256
        if (httpStatusCode) {
            ResourceError error("HTTP", httpStatusCode, url.toString(), m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString());
257 258 259 260 261
            client->didFail(m_resourceHandle, error);
        } else {
            ResourceError error("QtNetwork", m_reply->error(), url.toString(), m_reply->errorString());
            client->didFail(m_resourceHandle, error);
        }
262
    }
263

264 265 266
    oldReply->deleteLater();
    if (oldReply == m_reply)
        m_reply = 0;
267 268 269 270
}

void QNetworkReplyHandler::sendResponseIfNeeded()
{
271 272
    m_shouldSendResponse = (m_loadMode != LoadNormal);
    if (m_shouldSendResponse)
273 274
        return;

275
    if (m_reply->error() && !ignoreHttpError(m_reply, m_responseDataSent))
276 277
        return;

278
    if (m_responseSent || !m_resourceHandle)
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
        return;
    m_responseSent = true;

    ResourceHandleClient* client = m_resourceHandle->client();
    if (!client)
        return;

    WebCore::String contentType = m_reply->header(QNetworkRequest::ContentTypeHeader).toString();
    WebCore::String encoding = extractCharsetFromMediaType(contentType);
    WebCore::String mimeType = extractMIMETypeFromMediaType(contentType);

    if (mimeType.isEmpty()) {
        // let's try to guess from the extension
        QString extension = m_reply->url().path();
        int index = extension.lastIndexOf(QLatin1Char('.'));
        if (index > 0) {
            extension = extension.mid(index + 1);
            mimeType = MIMETypeRegistry::getMIMETypeForExtension(extension);
        }
    }

300
    KURL url(m_reply->url());
301 302
    ResourceResponse response(url, mimeType,
                              m_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(),
303
                              encoding, String());
304

305 306 307 308 309 310
    if (url.isLocalFile()) {
        client->didReceiveResponse(m_resourceHandle, response);
        return;
    }

    // The status code is equal to 0 for protocols not in the HTTP family.
311
    int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
312 313 314 315 316 317 318 319 320

    if (url.protocolInHTTPFamily()) {
        String suggestedFilename = filenameFromHTTPContentDisposition(QString::fromAscii(m_reply->rawHeader("Content-Disposition")));

        if (!suggestedFilename.isEmpty())
            response.setSuggestedFilename(suggestedFilename);
        else
            response.setSuggestedFilename(url.lastPathComponent());

321
        response.setHTTPStatusCode(statusCode);
322
        response.setHTTPStatusText(m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray().constData());
323

324
        // Add remaining headers.
325 326 327 328 329
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
        foreach (const QNetworkReply::RawHeaderPair& pair, m_reply->rawHeaderPairs()) {
            response.setHTTPHeaderField(QString::fromAscii(pair.first), QString::fromAscii(pair.second));
        }
#else
330 331 332
        foreach (const QByteArray& headerName, m_reply->rawHeaderList()) {
            response.setHTTPHeaderField(QString::fromAscii(headerName), QString::fromAscii(m_reply->rawHeader(headerName)));
        }
333
#endif
334
    }
335 336 337

    QUrl redirection = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
    if (redirection.isValid()) {
338 339 340 341 342 343 344 345 346
        QUrl newUrl = m_reply->url().resolved(redirection);
        if (newUrl == m_reply->url()) { // avoid redirecting to the same url as it causes infinite recursion
            ResourceError error(newUrl.host(), 400 /*bad request*/,
                                newUrl.toString(),
                                QCoreApplication::translate("QWebPage", "Infinite recursion in redirection request"));
            client->didFail(m_resourceHandle, error);
            return;
        }

347 348
        m_redirected = true;

349
        ResourceRequest newRequest = m_resourceHandle->request();
350
        newRequest.setURL(newUrl);
351

352
        if (((statusCode >= 301 && statusCode <= 303) || statusCode == 307) && m_method == QNetworkAccessManager::PostOperation) {
353
            m_method = QNetworkAccessManager::GetOperation;
354 355
            newRequest.setHTTPMethod("GET");
        }
356

357 358 359 360
        // Should not set Referer after a redirect from a secure resource to non-secure one.
        if (!newRequest.url().protocolIs("https") && protocolIs(newRequest.httpReferrer(), "https"))
            newRequest.clearHTTPReferrer();

361
        client->willSendRequest(m_resourceHandle, newRequest, response);
362 363 364
        if (!m_resourceHandle) // network error did cancel the request
            return;

365
        m_request = newRequest.toNetworkRequest(m_resourceHandle->getInternal()->m_frame);
366
        return;
367
    }
368 369

    client->didReceiveResponse(m_resourceHandle, response);
370 371 372 373
}

void QNetworkReplyHandler::forwardData()
{
374 375
    m_shouldForwardData = (m_loadMode != LoadNormal);
    if (m_shouldForwardData)
376 377
        return;

378 379 380 381 382 383
    sendResponseIfNeeded();

    // don't emit the "Document has moved here" type of HTML
    if (m_redirected)
        return;

384 385 386
    if (!m_resourceHandle)
        return;

387 388 389 390 391 392
    QByteArray data = m_reply->read(m_reply->bytesAvailable());

    ResourceHandleClient* client = m_resourceHandle->client();
    if (!client)
        return;

393 394
    if (!data.isEmpty()) {
        m_responseDataSent = true;
395
        client->didReceiveData(m_resourceHandle, data.constData(), data.length(), data.length() /*FixMe*/);
396
    }
397 398
}

399 400 401 402 403 404 405 406 407 408 409 410
void QNetworkReplyHandler::uploadProgress(qint64 bytesSent, qint64 bytesTotal)
{
    if (!m_resourceHandle)
        return;

    ResourceHandleClient* client = m_resourceHandle->client();
    if (!client)
        return;

    client->didSendData(m_resourceHandle, bytesSent, bytesTotal);
}

411 412
void QNetworkReplyHandler::start()
{
413 414
    m_shouldStart = false;

415 416 417 418
    ResourceHandleInternal* d = m_resourceHandle->getInternal();

    QNetworkAccessManager* manager = d->m_frame->page()->networkAccessManager();

419 420 421 422 423
    const QUrl url = m_request.url();
    const QString scheme = url.scheme();
    // Post requests on files and data don't really make sense, but for
    // fast/forms/form-post-urlencoded.html and for fast/forms/button-state-restore.html
    // we still need to retrieve the file/data, which means we map it to a Get instead.
424
    if (m_method == QNetworkAccessManager::PostOperation
425
        && (!url.toLocalFile().isEmpty() || url.scheme() == QLatin1String("data")))
426 427
        m_method = QNetworkAccessManager::GetOperation;

428 429 430 431 432
    switch (m_method) {
        case QNetworkAccessManager::GetOperation:
            m_reply = manager->get(m_request);
            break;
        case QNetworkAccessManager::PostOperation: {
433 434 435
            FormDataIODevice* postDevice = new FormDataIODevice(d->m_request.httpBody()); 
            m_reply = manager->post(m_request, postDevice);
            postDevice->setParent(m_reply);
436 437 438 439 440 441
            break;
        }
        case QNetworkAccessManager::HeadOperation:
            m_reply = manager->head(m_request);
            break;
        case QNetworkAccessManager::PutOperation: {
442 443 444
            FormDataIODevice* putDevice = new FormDataIODevice(d->m_request.httpBody()); 
            m_reply = manager->put(m_request, putDevice);
            putDevice->setParent(m_reply);
445 446
            break;
        }
447 448 449 450 451
#if QT_VERSION >= 0x040600
        case QNetworkAccessManager::DeleteOperation: {
            m_reply = manager->deleteResource(m_request);
            break;
        }
452 453 454 455 456
#endif
#if QT_VERSION >= 0x040700
        case QNetworkAccessManager::CustomOperation:
            m_reply = manager->sendCustomRequest(m_request, m_resourceHandle->request().httpMethod().latin1().data());
            break;
457
#endif
458 459 460 461 462 463 464 465 466 467 468
        case QNetworkAccessManager::UnknownOperation: {
            m_reply = 0;
            ResourceHandleClient* client = m_resourceHandle->client();
            if (client) {
                ResourceError error(url.host(), 400 /*bad request*/,
                                    url.toString(),
                                    QCoreApplication::translate("QWebPage", "Bad HTTP request"));
                client->didFail(m_resourceHandle, error);
            }
            return;
        }
469 470 471 472 473
    }

    m_reply->setParent(this);

    connect(m_reply, SIGNAL(finished()),
474
            this, SLOT(finish()), SIGNAL_CONN);
475 476 477 478 479

    // For http(s) we know that the headers are complete upon metaDataChanged() emission, so we
    // can send the response as early as possible
    if (scheme == QLatin1String("http") || scheme == QLatin1String("https"))
        connect(m_reply, SIGNAL(metaDataChanged()),
480
                this, SLOT(sendResponseIfNeeded()), SIGNAL_CONN);
481 482

    connect(m_reply, SIGNAL(readyRead()),
483 484
            this, SLOT(forwardData()), SIGNAL_CONN);

485 486 487 488 489
    if (m_resourceHandle->request().reportUploadProgress()) {
        connect(m_reply, SIGNAL(uploadProgress(qint64, qint64)),
                this, SLOT(uploadProgress(qint64, qint64)), SIGNAL_CONN);
    }

490
    // Make this a direct function call once we require 4.6.1+.
491
    connect(this, SIGNAL(processQueuedItems()),
492
            this, SLOT(sendQueuedItems()), SIGNAL_CONN);
493 494
}

495 496 497 498
void QNetworkReplyHandler::resetState()
{
    m_redirected = false;
    m_responseSent = false;
499
    m_responseDataSent = false;
500 501 502 503 504 505 506 507
    m_shouldStart = true;
    m_shouldFinish = false;
    m_shouldSendResponse = false;
    m_shouldForwardData = false;
}

void QNetworkReplyHandler::sendQueuedItems()
{
508 509 510
    if (m_loadMode != LoadResuming)
        return;
    m_loadMode = LoadNormal;
511 512 513 514 515 516 517 518 519 520 521 522 523 524

    if (m_shouldStart)
        start();

    if (m_shouldSendResponse)
        sendResponseIfNeeded();

    if (m_shouldForwardData)
        forwardData();

    if (m_shouldFinish)
        finish(); 
}

525 526 527
}

#include "moc_QNetworkReplyHandler.cpp"