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
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.
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.
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.
21 #include "QNetworkReplyHandler.h"
23 #include "WebCore_classes.h"
26 #include <QNetworkReply>
27 #include <QNetworkCookie>
28 #include <qwebframe.h>
32 #include <QCoreApplication>
36 QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle* handle, LoadMode loadMode)
39 , m_resourceHandle(handle)
41 , m_responseSent(false)
42 , m_responseDataSent(false)
43 , m_loadMode(loadMode)
45 , m_shouldFinish(false)
46 , m_shouldSendResponse(false)
47 , m_shouldForwardData(false)
49 const ResourceRequest &r = m_resourceHandle->request();
51 if (r.httpMethod() == "GET")
52 m_method = QNetworkAccessManager::GetOperation;
53 else if (r.httpMethod() == "HEAD")
54 m_method = QNetworkAccessManager::HeadOperation;
55 else if (r.httpMethod() == "POST")
56 m_method = QNetworkAccessManager::PostOperation;
57 else if (r.httpMethod() == "PUT")
58 m_method = QNetworkAccessManager::PutOperation;
60 m_method = QNetworkAccessManager::UnknownOperation;
62 m_request = r.toNetworkRequest();
64 if (m_loadMode == LoadNormal)
68 void QNetworkReplyHandler::setLoadMode(LoadMode mode)
70 // https://bugs.webkit.org/show_bug.cgi?id=26556
71 // We cannot call sendQueuedItems() from here, because the signal that
72 // caused us to get into deferred mode, might not be processed yet.
75 m_loadMode = LoadResuming;
76 emit processQueuedItems();
79 m_loadMode = LoadDeferred;
82 Q_ASSERT(0); // should never happen
87 void QNetworkReplyHandler::abort()
91 QNetworkReply* reply = release();
98 QNetworkReply* QNetworkReplyHandler::release()
100 QNetworkReply* reply = m_reply;
102 disconnect(m_reply, 0, this, 0);
103 // We have queued connections to the QNetworkReply. Make sure any
104 // posted meta call events that were the result of a signal emission
105 // don't reach the slots in our instance.
106 QCoreApplication::removePostedEvents(this, QEvent::MetaCall);
112 void QNetworkReplyHandler::finish()
114 m_shouldFinish = (m_loadMode != LoadNormal);
118 sendResponseIfNeeded();
120 if (!m_resourceHandle)
122 ResourceHandleClient* client = m_resourceHandle->client();
124 m_reply->deleteLater();
128 QNetworkReply* oldReply = m_reply;
132 } else if (m_reply->error() != QNetworkReply::NoError
133 // a web page that returns 401/403/404 can still have content
134 && ((m_reply->error() != QNetworkReply::ContentOperationNotPermittedError
135 && m_reply->error() != QNetworkReply::ContentNotFoundError
136 && m_reply->error() != QNetworkReply::ProtocolUnknownError
137 && m_reply->error() != QNetworkReply::UnknownContentError)
138 // If the web page sent content, let's give it to the user.
139 || !m_responseDataSent)
140 && m_reply->error() != QNetworkReply::AuthenticationRequiredError
141 && m_reply->error() != QNetworkReply::ProxyAuthenticationRequiredError) {
142 QUrl url = m_reply->url();
143 ResourceError error(url.host(), m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(),
144 url.toString(), m_reply->errorString());
145 client->didFail(m_resourceHandle, error);
147 client->didFinishLoading(m_resourceHandle);
149 oldReply->deleteLater();
150 if (oldReply == m_reply)
154 void QNetworkReplyHandler::sendResponseIfNeeded()
156 m_shouldSendResponse = (m_loadMode != LoadNormal);
157 if (m_shouldSendResponse)
160 if (m_responseSent || !m_resourceHandle)
162 m_responseSent = true;
164 ResourceHandleClient* client = m_resourceHandle->client();
168 QString contentType = m_reply->header(QNetworkRequest::ContentTypeHeader).toString();
173 QString encoding = extractCharsetFromMediaType(contentType);
174 QString mimeType = extractMIMETypeFromMediaType(contentType);
176 if (mimeType.isEmpty()) {
177 // let's try to guess from the extension
178 QString extension = m_reply->url().path();
179 int index = extension.lastIndexOf(QLatin1Char('.'));
181 extension = extension.mid(index + 1);
182 mimeType = MIMETypeRegistry::getMIMETypeForExtension(extension);
187 KURL url(m_reply->url());
189 QString suggestedFilename = filenameFromHTTPContentDisposition(QString::fromAscii(m_reply->rawHeader("Content-Disposition")));
191 if (suggestedFilename.isEmpty())
192 suggestedFilename = url.lastPathComponent();
195 ResourceResponse response(url, mimeType,
196 m_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(),
200 const bool isLocalFileReply = (m_reply->url().scheme() == QLatin1String("file"));
201 int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
202 if (!isLocalFileReply) {
203 response.setHTTPStatusCode(statusCode);
204 response.setHTTPStatusText(m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray().constData());
206 else if (m_reply->error() == QNetworkReply::ContentNotFoundError)
207 response.setHTTPStatusCode(404);
210 /* Fill in the other fields
211 * For local file requests remove the content length and the last-modified
212 * headers as required by fast/dom/xmlhttprequest-get.xhtml
215 foreach (const QNetworkReply::RawHeaderPair& headerPair, m_reply->rawHeaderPairs()) {
216 const QByteArray& headerName = headerPair.first;
217 const QByteArray& headerValue = headerPair.second;
220 && (headerName == "Content-Length" || headerName == "Last-Modified"))
223 response.setHTTPHeaderField(QString::fromAscii(headerName), QString::fromAscii(headerValue));
226 foreach (QNetworkReply::RawHeaderPair rawHeader, m_reply->rawHeaderPairs()) {
227 const QByteArray headerName = rawHeader.first;
229 && (headerName == "Content-Length" || headerName == "Last-Modified"))
232 response.setHTTPHeaderField(QString::fromAscii(headerName), QString::fromAscii(rawHeader.second));
237 if (isLocalFileReply)
238 response.setHTTPHeaderField(QString::fromAscii("Cache-Control"), QString::fromAscii("no-cache"));
240 QUrl redirection = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
241 if (redirection.isValid()) {
242 QUrl newUrl = m_reply->url().resolved(redirection);
243 ResourceRequest newRequest = m_resourceHandle->request();
244 newRequest.setURL(newUrl);
246 if (((statusCode >= 301 && statusCode <= 303) || statusCode == 307) && m_method == QNetworkAccessManager::PostOperation) {
247 m_method = QNetworkAccessManager::GetOperation;
248 newRequest.setHTTPMethod("GET");
251 client->willSendRequest(m_resourceHandle, newRequest, response);
253 m_request = newRequest.toNetworkRequest();
255 client->didReceiveResponse(m_resourceHandle, response);
259 void QNetworkReplyHandler::forwardData()
261 m_shouldForwardData = (m_loadMode != LoadNormal);
262 if (m_shouldForwardData)
265 sendResponseIfNeeded();
267 // don't emit the "Document has moved here" type of HTML
271 if (!m_resourceHandle)
274 QByteArray data = m_reply->read(m_reply->bytesAvailable());
276 ResourceHandleClient* client = m_resourceHandle->client();
280 if (!data.isEmpty()) {
281 m_responseDataSent = true;
282 client->didReceiveData(m_resourceHandle, data.constData(), data.length(), data.length() /*FixMe*/);
286 void QNetworkReplyHandler::start()
288 m_shouldStart = false;
290 QNetworkAccessManager* manager = globalManager;
292 ResourceHandleInternal* d = m_resourceHandle->getInternal();
294 QNetworkAccessManager* manager = d->m_frame->page()->networkAccessManager();
297 const QUrl url = m_request.url();
298 const QString scheme = url.scheme();
299 // Post requests on files and data don't really make sense, but for
300 // fast/forms/form-post-urlencoded.html and for fast/forms/button-state-restore.html
301 // we still need to retrieve the file/data, which means we map it to a Get instead.
302 if (m_method == QNetworkAccessManager::PostOperation
303 && (!url.toLocalFile().isEmpty() || url.scheme() == QLatin1String("data")))
304 m_method = QNetworkAccessManager::GetOperation;
307 case QNetworkAccessManager::GetOperation:
308 m_reply = manager->get(m_request);
310 case QNetworkAccessManager::PostOperation: {
311 qFatal("POST OPERATION is not allowed");
314 case QNetworkAccessManager::HeadOperation:
315 m_reply = manager->head(m_request);
317 case QNetworkAccessManager::PutOperation: {
318 qFatal("PUT OPERATION is not allowed");
321 case QNetworkAccessManager::UnknownOperation: {
323 ResourceHandleClient* client = m_resourceHandle->client();
325 ResourceError error(url.host(), 400 /*bad request*/,
327 QCoreApplication::translate("QWebPage", "Bad HTTP request"));
328 client->didFail(m_resourceHandle, error);
334 m_reply->setParent(this);
336 connect(m_reply, SIGNAL(finished()),
337 this, SLOT(finish()), Qt::QueuedConnection);
339 // For http(s) we know that the headers are complete upon metaDataChanged() emission, so we
340 // can send the response as early as possible
341 if (scheme == QLatin1String("http") || scheme == QLatin1String("https"))
342 connect(m_reply, SIGNAL(metaDataChanged()),
343 this, SLOT(sendResponseIfNeeded()), Qt::QueuedConnection);
345 connect(m_reply, SIGNAL(readyRead()),
346 this, SLOT(forwardData()), Qt::QueuedConnection);
347 connect(this, SIGNAL(processQueuedItems()),
348 this, SLOT(sendQueuedItems()), Qt::QueuedConnection);
351 void QNetworkReplyHandler::resetState()
353 m_redirected = false;
354 m_responseSent = false;
355 m_responseDataSent = false;
356 m_shouldStart = true;
357 m_shouldFinish = false;
358 m_shouldSendResponse = false;
359 m_shouldForwardData = false;
362 void QNetworkReplyHandler::sendQueuedItems()
364 if (m_loadMode != LoadResuming)
366 m_loadMode = LoadNormal;
371 if (m_shouldSendResponse)
372 sendResponseIfNeeded();
374 if (m_shouldForwardData)
383 #include "moc_QNetworkReplyHandler.cpp"