blob: a6e0840bf13120152752186a1c7dff83534de409 [file] [log] [blame]
/*
Copyright (C) 2008, 2012 Digia Plc. and/or its subsidiary(-ies)
Copyright (C) 2007 Staikos Computing Services Inc. <info@staikos.net>
Copyright (C) 2008 Holger Hans Peter Freyther
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 "BlobData.h"
#include "HTTPParsers.h"
#include "MIMETypeRegistry.h"
#include "NetworkingContext.h"
#include "ResourceHandle.h"
#include "ResourceHandleClient.h"
#include "ResourceHandleInternal.h"
#include "ResourceRequest.h"
#include "ResourceResponse.h"
#include <QDateTime>
#include <QFile>
#include <QFileInfo>
#include <QMimeDatabase>
#include <QNetworkCookie>
#include <QNetworkReply>
#include <wtf/text/CString.h>
#include <QCoreApplication>
static const int gMaxRedirections = 10;
namespace WebCore {
FormDataIODevice::FormDataIODevice(FormData* data)
: m_currentFile(0)
, m_currentDelta(0)
, m_fileSize(0)
, m_dataSize(0)
{
setOpenMode(FormDataIODevice::ReadOnly);
prepareFormElements(data);
prepareCurrentElement();
computeSize();
}
FormDataIODevice::~FormDataIODevice()
{
delete m_currentFile;
}
void FormDataIODevice::prepareFormElements(FormData* formData)
{
if (!formData)
return;
RefPtr<FormData> formDataRef(formData);
#if ENABLE(BLOB)
formDataRef = formDataRef->resolveBlobReferences();
#endif
// Take a deep copy of the FormDataElements
m_formElements = formDataRef->elements();
}
qint64 FormDataIODevice::computeSize()
{
for (int i = 0; i < m_formElements.size(); ++i) {
const FormDataElement& element = m_formElements[i];
if (element.m_type == FormDataElement::data)
m_dataSize += element.m_data.size();
else {
QFileInfo fi(element.m_filename);
#if ENABLE(BLOB)
qint64 fileEnd = fi.size();
if (element.m_fileLength != BlobDataItem::toEndOfFile)
fileEnd = qMin<qint64>(fi.size(), element.m_fileStart + element.m_fileLength);
m_fileSize += qMax<qint64>(0, fileEnd - element.m_fileStart);
#else
m_fileSize += fi.size();
#endif
}
}
return m_dataSize + m_fileSize;
}
void FormDataIODevice::moveToNextElement()
{
if (m_currentFile)
m_currentFile->close();
m_currentDelta = 0;
m_formElements.remove(0);
prepareCurrentElement();
}
void FormDataIODevice::prepareCurrentElement()
{
if (m_formElements.isEmpty())
return;
switch (m_formElements[0].m_type) {
case FormDataElement::data:
return;
case FormDataElement::encodedFile:
openFileForCurrentElement();
break;
default:
// At this point encodedBlob should already have been handled.
ASSERT_NOT_REACHED();
}
}
void FormDataIODevice::openFileForCurrentElement()
{
if (!m_currentFile)
m_currentFile = new QFile;
m_currentFile->setFileName(m_formElements[0].m_filename);
m_currentFile->open(QFile::ReadOnly);
#if ENABLE(BLOB)
if (isValidFileTime(m_formElements[0].m_expectedFileModificationTime)) {
QFileInfo info(*m_currentFile);
if (!info.exists() || static_cast<time_t>(m_formElements[0].m_expectedFileModificationTime) < info.lastModified().toTime_t()) {
moveToNextElement();
return;
}
}
if (m_formElements[0].m_fileStart)
m_currentFile->seek(m_formElements[0].m_fileStart);
#endif
}
// 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 if (element.m_type == FormDataElement::encodedFile) {
quint64 toCopy = available;
#if ENABLE(BLOB)
if (element.m_fileLength != BlobDataItem::toEndOfFile)
toCopy = qMin<qint64>(toCopy, element.m_fileLength - m_currentDelta);
#endif
const QByteArray data = m_currentFile->read(toCopy);
memcpy(destination+copied, data.constData(), data.size());
m_currentDelta += data.size();
copied += data.size();
if (m_currentFile->atEnd() || !m_currentFile->isOpen())
moveToNextElement();
#if ENABLE(BLOB)
else if (element.m_fileLength != BlobDataItem::toEndOfFile && m_currentDelta == element.m_fileLength)
moveToNextElement();
#endif
}
}
return copied;
}
qint64 FormDataIODevice::writeData(const char*, qint64)
{
return -1;
}
bool FormDataIODevice::isSequential() const
{
return true;
}
QNetworkReplyHandlerCallQueue::QNetworkReplyHandlerCallQueue(QNetworkReplyHandler* handler, bool deferSignals)
: m_replyHandler(handler)
, m_locks(0)
, m_deferSignals(deferSignals)
, m_flushing(false)
{
Q_ASSERT(handler);
}
void QNetworkReplyHandlerCallQueue::push(EnqueuedCall method)
{
m_enqueuedCalls.append(method);
flush();
}
void QNetworkReplyHandlerCallQueue::lock()
{
++m_locks;
}
void QNetworkReplyHandlerCallQueue::unlock()
{
if (!m_locks)
return;
--m_locks;
flush();
}
void QNetworkReplyHandlerCallQueue::setDeferSignals(bool defer, bool sync)
{
m_deferSignals = defer;
if (sync)
flush();
else
QMetaObject::invokeMethod(this, "flush", Qt::QueuedConnection);
}
void QNetworkReplyHandlerCallQueue::flush()
{
if (m_flushing)
return;
m_flushing = true;
while (!m_deferSignals && !m_locks && !m_enqueuedCalls.isEmpty())
(m_replyHandler->*(m_enqueuedCalls.takeFirst()))();
m_flushing = false;
}
class QueueLocker {
public:
QueueLocker(QNetworkReplyHandlerCallQueue* queue) : m_queue(queue) { m_queue->lock(); }
~QueueLocker() { m_queue->unlock(); }
private:
QNetworkReplyHandlerCallQueue* m_queue;
};
QNetworkReplyWrapper::QNetworkReplyWrapper(QNetworkReplyHandlerCallQueue* queue, QNetworkReply* reply, bool sniffMIMETypes, QObject* parent)
: QObject(parent)
, m_reply(reply)
, m_queue(queue)
, m_responseContainsData(false)
, m_sniffMIMETypes(sniffMIMETypes)
{
Q_ASSERT(m_reply);
// setFinished() must be the first that we connect, so isFinished() is updated when running other slots.
connect(m_reply, SIGNAL(finished()), this, SLOT(setFinished()));
connect(m_reply, SIGNAL(finished()), this, SLOT(receiveMetaData()));
connect(m_reply, SIGNAL(readyRead()), this, SLOT(receiveMetaData()));
connect(m_reply, SIGNAL(destroyed()), this, SLOT(replyDestroyed()));
}
QNetworkReplyWrapper::~QNetworkReplyWrapper()
{
if (m_reply)
m_reply->deleteLater();
m_queue->clear();
}
QNetworkReply* QNetworkReplyWrapper::release()
{
if (!m_reply)
return 0;
m_reply->disconnect(this);
QNetworkReply* reply = m_reply;
m_reply = 0;
m_sniffer = nullptr;
return reply;
}
void QNetworkReplyWrapper::synchronousLoad()
{
setFinished();
receiveMetaData();
}
void QNetworkReplyWrapper::stopForwarding()
{
if (m_reply) {
// Disconnect all connections that might affect the ResourceHandleClient.
m_reply->disconnect(this, SLOT(receiveMetaData()));
m_reply->disconnect(this, SLOT(didReceiveFinished()));
m_reply->disconnect(this, SLOT(didReceiveReadyRead()));
}
QCoreApplication::removePostedEvents(this, QEvent::MetaCall);
}
void QNetworkReplyWrapper::receiveMetaData()
{
// This slot is only used to receive the first signal from the QNetworkReply object.
stopForwarding();
WTF::String contentType = m_reply->header(QNetworkRequest::ContentTypeHeader).toString();
m_encoding = extractCharsetFromMediaType(contentType);
m_advertisedMIMEType = extractMIMETypeFromMediaType(contentType);
m_redirectionTargetUrl = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
if (m_redirectionTargetUrl.isValid()) {
QueueLocker lock(m_queue);
m_queue->push(&QNetworkReplyHandler::sendResponseIfNeeded);
m_queue->push(&QNetworkReplyHandler::finish);
return;
}
if (!m_sniffMIMETypes) {
emitMetaDataChanged();
return;
}
bool isSupportedImageType = MIMETypeRegistry::isSupportedImageMIMEType(m_advertisedMIMEType);
Q_ASSERT(!m_sniffer);
m_sniffer = adoptPtr(new QtMIMETypeSniffer(m_reply, m_advertisedMIMEType, isSupportedImageType));
if (m_sniffer->isFinished()) {
receiveSniffedMIMEType();
return;
}
connect(m_sniffer.get(), SIGNAL(finished()), this, SLOT(receiveSniffedMIMEType()));
}
void QNetworkReplyWrapper::receiveSniffedMIMEType()
{
Q_ASSERT(m_sniffer);
m_sniffedMIMEType = m_sniffer->mimeType();
m_sniffer = nullptr;
emitMetaDataChanged();
}
void QNetworkReplyWrapper::setFinished()
{
// Due to a limitation of QNetworkReply public API, its subclasses never get the chance to
// change the result of QNetworkReply::isFinished() method. So we need to keep track of the
// finished state ourselves. This limitation is fixed in 4.8, but we'll still have applications
// that don't use the solution. See http://bugreports.qt.nokia.com/browse/QTBUG-11737.
Q_ASSERT(!isFinished());
m_reply->setProperty("_q_isFinished", true);
}
void QNetworkReplyWrapper::replyDestroyed()
{
m_reply = 0;
m_sniffer = nullptr;
}
void QNetworkReplyWrapper::emitMetaDataChanged()
{
QueueLocker lock(m_queue);
m_queue->push(&QNetworkReplyHandler::sendResponseIfNeeded);
if (m_reply->bytesAvailable()) {
m_responseContainsData = true;
m_queue->push(&QNetworkReplyHandler::forwardData);
}
if (isFinished()) {
m_queue->push(&QNetworkReplyHandler::finish);
return;
}
// If not finished, connect to the slots that will be used from this point on.
connect(m_reply, SIGNAL(readyRead()), this, SLOT(didReceiveReadyRead()));
connect(m_reply, SIGNAL(finished()), this, SLOT(didReceiveFinished()));
}
void QNetworkReplyWrapper::didReceiveReadyRead()
{
if (m_reply->bytesAvailable())
m_responseContainsData = true;
m_queue->push(&QNetworkReplyHandler::forwardData);
}
void QNetworkReplyWrapper::didReceiveFinished()
{
// Disconnecting will make sure that nothing will happen after emitting the finished signal.
stopForwarding();
m_queue->push(&QNetworkReplyHandler::finish);
}
String QNetworkReplyHandler::httpMethod() const
{
switch (m_method) {
case QNetworkAccessManager::GetOperation:
return "GET";
case QNetworkAccessManager::HeadOperation:
return "HEAD";
case QNetworkAccessManager::PostOperation:
return "POST";
case QNetworkAccessManager::PutOperation:
return "PUT";
case QNetworkAccessManager::DeleteOperation:
return "DELETE";
case QNetworkAccessManager::CustomOperation:
return m_resourceHandle->firstRequest().httpMethod();
default:
ASSERT_NOT_REACHED();
return "GET";
}
}
QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle* handle, LoadType loadType, bool deferred)
: QObject(0)
, m_resourceHandle(handle)
, m_loadType(loadType)
, m_redirectionTries(gMaxRedirections)
, m_queue(this, deferred)
{
const ResourceRequest &r = m_resourceHandle->firstRequest();
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;
else if (r.httpMethod() == "DELETE")
m_method = QNetworkAccessManager::DeleteOperation;
else
m_method = QNetworkAccessManager::CustomOperation;
m_request = r.toNetworkRequest(m_resourceHandle->getInternal()->m_context.get());
m_queue.push(&QNetworkReplyHandler::start);
}
void QNetworkReplyHandler::abort()
{
m_resourceHandle = 0;
if (QNetworkReply* reply = release()) {
reply->abort();
reply->deleteLater();
}
deleteLater();
}
QNetworkReply* QNetworkReplyHandler::release()
{
if (!m_replyWrapper)
return 0;
m_timeoutTimer.stop();
QNetworkReply* reply = m_replyWrapper->release();
m_replyWrapper = nullptr;
return reply;
}
static bool shouldIgnoreHttpError(QNetworkReply* reply, bool receivedData)
{
// An HEAD XmlHTTPRequest shouldn't be marked as failure for HTTP errors.
if (reply->operation() == QNetworkAccessManager::HeadOperation)
return true;
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (httpStatusCode == 401 || httpStatusCode == 407)
return true;
if (receivedData && (httpStatusCode >= 400 && httpStatusCode < 600))
return true;
return false;
}
void QNetworkReplyHandler::finish()
{
ASSERT(m_replyWrapper && m_replyWrapper->reply() && !wasAborted());
m_timeoutTimer.stop();
ResourceHandleClient* client = m_resourceHandle->client();
if (!client) {
m_replyWrapper = nullptr;
return;
}
if (m_replyWrapper->wasRedirected()) {
m_replyWrapper = nullptr;
m_queue.push(&QNetworkReplyHandler::start);
return;
}
if (!m_replyWrapper->reply()->error() || shouldIgnoreHttpError(m_replyWrapper->reply(), m_replyWrapper->responseContainsData()))
client->didFinishLoading(m_resourceHandle, 0);
else
client->didFail(m_resourceHandle, errorForReply(m_replyWrapper->reply()));
m_replyWrapper = nullptr;
}
void QNetworkReplyHandler::timeout()
{
if (!m_replyWrapper || wasAborted())
return;
// The request is already finished, but is probably just waiting in the queue to get processed.
// In this case we ignore the timeout and proceed as normal.
if (m_replyWrapper->isFinished())
return;
ResourceHandleClient* client = m_resourceHandle->client();
if (!client) {
m_replyWrapper.clear();
return;
}
ASSERT(m_replyWrapper->reply());
ResourceError timeoutError("QtNetwork", QNetworkReply::TimeoutError, m_replyWrapper->reply()->url().toString(), "Request timed out");
timeoutError.setIsTimeout(true);
client->didFail(m_resourceHandle, timeoutError);
m_replyWrapper.clear();
}
void QNetworkReplyHandler::timerEvent(QTimerEvent* timerEvent)
{
ASSERT_UNUSED(timerEvent, timerEvent->timerId()== m_timeoutTimer.timerId());
m_timeoutTimer.stop();
timeout();
}
void QNetworkReplyHandler::sendResponseIfNeeded()
{
ASSERT(m_replyWrapper && m_replyWrapper->reply() && !wasAborted());
if (m_replyWrapper->reply()->error() && m_replyWrapper->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).isNull())
return;
ResourceHandleClient* client = m_resourceHandle->client();
if (!client)
return;
WTF::String mimeType = m_replyWrapper->mimeType();
if (mimeType.isEmpty()) {
// let's try to guess from the extension
mimeType = MIMETypeRegistry::getMIMETypeForPath(m_replyWrapper->reply()->url().path());
}
KURL url(m_replyWrapper->reply()->url());
ResourceResponse response(url, mimeType.lower(),
m_replyWrapper->reply()->header(QNetworkRequest::ContentLengthHeader).toLongLong(),
m_replyWrapper->encoding(), String());
if (url.isLocalFile()) {
client->didReceiveResponse(m_resourceHandle, response);
return;
}
// The status code is equal to 0 for protocols not in the HTTP family.
int statusCode = m_replyWrapper->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (url.protocolIsInHTTPFamily()) {
String suggestedFilename = filenameFromHTTPContentDisposition(QString::fromLatin1(m_replyWrapper->reply()->rawHeader("Content-Disposition")));
if (!suggestedFilename.isEmpty())
response.setSuggestedFilename(suggestedFilename);
else {
Vector<String> extensions = MIMETypeRegistry::getExtensionsForMIMEType(mimeType);
if (extensions.isEmpty())
response.setSuggestedFilename(url.lastPathComponent());
else {
// If the suffix doesn't match the MIME type, correct the suffix.
QString filename = url.lastPathComponent();
const String suffix = QMimeDatabase().suffixForFileName(filename);
if (!extensions.contains(suffix)) {
filename.chop(suffix.length());
filename += MIMETypeRegistry::getPreferredExtensionForMIMEType(mimeType);
}
response.setSuggestedFilename(filename);
}
}
response.setHTTPStatusCode(statusCode);
response.setHTTPStatusText(m_replyWrapper->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray().constData());
// Add remaining headers.
foreach (const QNetworkReply::RawHeaderPair& pair, m_replyWrapper->reply()->rawHeaderPairs())
response.setHTTPHeaderField(QString::fromLatin1(pair.first), QString::fromLatin1(pair.second));
}
QUrl redirection = m_replyWrapper->reply()->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
if (redirection.isValid()) {
redirect(response, redirection);
return;
}
client->didReceiveResponse(m_resourceHandle, response);
}
void QNetworkReplyHandler::redirect(ResourceResponse& response, const QUrl& redirection)
{
QUrl newUrl = m_replyWrapper->reply()->url().resolved(redirection);
ResourceHandleClient* client = m_resourceHandle->client();
ASSERT(client);
int statusCode = m_replyWrapper->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
m_redirectionTries--;
if (!m_redirectionTries) {
ResourceError error("HTTP", 400 /*bad request*/,
newUrl.toString(),
QCoreApplication::translate("QWebPage", "Redirection limit reached"));
client->didFail(m_resourceHandle, error);
m_replyWrapper = nullptr;
return;
}
// Status Code 301 (Moved Permanently), 302 (Moved Temporarily), 303 (See Other):
// - If original request is POST convert to GET and redirect automatically
// Status Code 307 (Temporary Redirect) and all other redirect status codes:
// - Use the HTTP method from the previous request
if ((statusCode >= 301 && statusCode <= 303) && m_resourceHandle->firstRequest().httpMethod() == "POST")
m_method = QNetworkAccessManager::GetOperation;
ResourceRequest newRequest = m_resourceHandle->firstRequest();
newRequest.setHTTPMethod(httpMethod());
newRequest.setURL(newUrl);
// Should not set Referer after a redirect from a secure resource to non-secure one.
if (!newRequest.url().protocolIs("https") && protocolIs(newRequest.httpReferrer(), "https") && m_resourceHandle->context()->shouldClearReferrerOnHTTPSToHTTPRedirect())
newRequest.clearHTTPReferrer();
client->willSendRequest(m_resourceHandle, newRequest, response);
if (wasAborted()) // Network error cancelled the request.
return;
m_request = newRequest.toNetworkRequest(m_resourceHandle->getInternal()->m_context.get());
}
void QNetworkReplyHandler::forwardData()
{
ASSERT(m_replyWrapper && m_replyWrapper->reply() && !wasAborted() && !m_replyWrapper->wasRedirected());
QByteArray data = m_replyWrapper->reply()->read(m_replyWrapper->reply()->bytesAvailable());
ResourceHandleClient* client = m_resourceHandle->client();
if (!client)
return;
// FIXME: https://bugs.webkit.org/show_bug.cgi?id=19793
// -1 means we do not provide any data about transfer size to inspector so it would use
// Content-Length headers or content size to show transfer size.
if (!data.isEmpty())
client->didReceiveData(m_resourceHandle, data.constData(), data.length(), -1);
}
void QNetworkReplyHandler::uploadProgress(qint64 bytesSent, qint64 bytesTotal)
{
if (wasAborted())
return;
ResourceHandleClient* client = m_resourceHandle->client();
if (!client)
return;
if (!bytesTotal) {
// When finished QNetworkReply emits a progress of 0 bytes.
// Ignore that, to avoid firing twice.
return;
}
client->didSendData(m_resourceHandle, bytesSent, bytesTotal);
}
void QNetworkReplyHandler::clearContentHeaders()
{
// Clearing Content-length and Content-type of the requests that do not have contents.
// This is necessary to ensure POST requests redirected to GETs do not leak metadata
// about the POST content to the site they've been redirected to.
m_request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant());
m_request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant());
}
FormDataIODevice* QNetworkReplyHandler::getIODevice(const ResourceRequest& request)
{
FormDataIODevice* device = new FormDataIODevice(request.httpBody());
// We may be uploading files so prevent QNR from buffering data.
m_request.setHeader(QNetworkRequest::ContentLengthHeader, device->getFormDataSize());
m_request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, QVariant(true));
return device;
}
QNetworkReply* QNetworkReplyHandler::sendNetworkRequest(QNetworkAccessManager* manager, const ResourceRequest& request)
{
if (m_loadType == SynchronousLoad)
m_request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, true);
if (!manager)
return 0;
const QUrl url = m_request.url();
// 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.
if (m_method == QNetworkAccessManager::PostOperation
&& (!url.toLocalFile().isEmpty() || url.scheme() == QLatin1String("data")))
m_method = QNetworkAccessManager::GetOperation;
switch (m_method) {
case QNetworkAccessManager::GetOperation:
clearContentHeaders();
return manager->get(m_request);
case QNetworkAccessManager::PostOperation: {
FormDataIODevice* postDevice = getIODevice(request);
QNetworkReply* result = manager->post(m_request, postDevice);
postDevice->setParent(result);
return result;
}
case QNetworkAccessManager::HeadOperation:
clearContentHeaders();
return manager->head(m_request);
case QNetworkAccessManager::PutOperation: {
FormDataIODevice* putDevice = getIODevice(request);
QNetworkReply* result = manager->put(m_request, putDevice);
putDevice->setParent(result);
return result;
}
case QNetworkAccessManager::DeleteOperation: {
clearContentHeaders();
return manager->deleteResource(m_request);
}
case QNetworkAccessManager::CustomOperation: {
FormDataIODevice* customDevice = getIODevice(request);
QNetworkReply* result = manager->sendCustomRequest(m_request, m_resourceHandle->firstRequest().httpMethod().latin1().data(), customDevice);
customDevice->setParent(result);
return result;
}
case QNetworkAccessManager::UnknownOperation:
ASSERT_NOT_REACHED();
return 0;
}
return 0;
}
void QNetworkReplyHandler::start()
{
ResourceHandleInternal* d = m_resourceHandle->getInternal();
if (!d || !d->m_context)
return;
QNetworkReply* reply = sendNetworkRequest(d->m_context->networkAccessManager(), d->m_firstRequest);
if (!reply)
return;
m_replyWrapper = adoptPtr(new QNetworkReplyWrapper(&m_queue, reply, m_resourceHandle->shouldContentSniff() && d->m_context->mimeSniffingEnabled(), this));
if (m_loadType == SynchronousLoad) {
m_replyWrapper->synchronousLoad();
// If supported, a synchronous request will be finished at this point, no need to hook up the signals.
return;
}
double timeoutInSeconds = d->m_firstRequest.timeoutInterval();
if (timeoutInSeconds > 0 && timeoutInSeconds < (INT_MAX / 1000))
m_timeoutTimer.start(timeoutInSeconds * 1000, this);
if (m_resourceHandle->firstRequest().reportUploadProgress())
connect(m_replyWrapper->reply(), SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64)));
}
ResourceError QNetworkReplyHandler::errorForReply(QNetworkReply* reply)
{
QUrl url = reply->url();
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (httpStatusCode)
return ResourceError("HTTP", httpStatusCode, url.toString(), reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString());
return ResourceError("QtNetwork", reply->error(), url.toString(), reply->errorString());
}
}
#include "moc_QNetworkReplyHandler.cpp"