Holger Hans Peter Freyther <holger.freyther@trolltech.com>
[WebKit-https.git] / WebCore / platform / network / qt / QNetworkReplyHandler.cpp
1 /*
2     Copyright (C) 2007 Trolltech ASA
3     Copyright (C) 2007 Staikos Computing Services Inc.  <info@staikos.net>
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Library General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Library General Public License for more details.
14
15     You should have received a copy of the GNU Library General Public License
16     along with this library; see the file COPYING.LIB.  If not, write to
17     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18     Boston, MA 02110-1301, USA.
19 */
20 #include "config.h"
21 #include "QNetworkReplyHandler.h"
22
23 #if QT_VERSION >= 0x040400
24
25 #include "HTTPParsers.h"
26 #include "MIMETypeRegistry.h"
27 #include "ResourceHandle.h"
28 #include "ResourceHandleClient.h"
29 #include "ResourceHandleInternal.h"
30 #include "ResourceResponse.h"
31 #include "ResourceRequest.h"
32 #include <QNetworkReply>
33 #include <QNetworkCookie>
34 #include <qwebframe.h>
35 #include <qwebpage.h>
36
37 namespace WebCore {
38
39 QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle *handle)
40     : QObject(0)
41     , m_resourceHandle(handle)
42     , m_reply(0)
43     , m_redirected(false)
44     , m_responseSent(false)
45 {
46     const ResourceRequest &r = m_resourceHandle->request();
47
48     if (r.httpMethod() == "GET")
49         m_method = QNetworkAccessManager::GetOperation;
50     else if (r.httpMethod() == "HEAD")
51         m_method = QNetworkAccessManager::HeadOperation;
52     else if (r.httpMethod() == "POST")
53         m_method = QNetworkAccessManager::PostOperation;
54     else if (r.httpMethod() == "PUT")
55         m_method = QNetworkAccessManager::PutOperation;
56     else
57         m_method = QNetworkAccessManager::UnknownOperation;
58
59     m_request = r.toNetworkRequest();
60
61     start();
62 }
63
64 void QNetworkReplyHandler::abort()
65 {
66     m_resourceHandle = 0;
67     if (m_reply) {
68         disconnect(m_reply, 0, this, 0);
69         m_reply->abort();
70         deleteLater();
71         m_reply = 0;
72     }
73 }
74
75 QNetworkReply *QNetworkReplyHandler::release()
76 {
77     QNetworkReply *reply = m_reply;
78     if (m_reply) {
79         disconnect(m_reply, 0, this, 0);
80         m_reply = 0;
81     }
82     return reply;
83 }
84
85 void QNetworkReplyHandler::finish()
86 {
87     sendResponseIfNeeded();
88
89     if (!m_resourceHandle)
90         return;
91     ResourceHandleClient* client = m_resourceHandle->client();
92     m_reply->deleteLater();
93     if (!client)
94         return;
95     if (m_redirected) {
96         m_redirected = false;
97         m_responseSent = false;
98         start();
99     } else if (m_reply->error() != QNetworkReply::NoError) {
100         QUrl url = m_reply->url();
101         ResourceError error(url.host(), m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(),
102                             url.toString(), m_reply->errorString());
103         client->didFail(m_resourceHandle, error);
104     } else {
105         client->didFinishLoading(m_resourceHandle);
106     }
107 }
108
109 void QNetworkReplyHandler::sendResponseIfNeeded()
110 {
111     if (m_responseSent || !m_resourceHandle)
112         return;
113     m_responseSent = true;
114
115     ResourceHandleClient* client = m_resourceHandle->client();
116     if (!client)
117         return;
118
119     WebCore::String contentType = m_reply->header(QNetworkRequest::ContentTypeHeader).toString();
120     WebCore::String encoding = extractCharsetFromMediaType(contentType);
121     WebCore::String mimeType = extractMIMETypeFromMediaType(contentType);
122
123     if (mimeType.isEmpty()) {
124         // let's try to guess from the extension
125         QString extension = m_reply->url().path();
126         int index = extension.lastIndexOf(QLatin1Char('.'));
127         if (index > 0) {
128             extension = extension.mid(index + 1);
129             mimeType = MIMETypeRegistry::getMIMETypeForExtension(extension);
130         }
131     }
132
133     KURL url(m_reply->url());
134     String contentDisposition = QString::fromAscii(m_reply->rawHeader("Content-Disposition"));
135
136     ResourceResponse response(url, mimeType,
137                               m_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(),
138                               encoding,
139                               filenameFromHTTPContentDisposition(contentDisposition));
140
141     const bool isLocalFileReply = (m_reply->url().scheme() == QLatin1String("file"));
142     int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
143     if (!isLocalFileReply)
144         response.setHTTPStatusCode(statusCode);
145     else if (m_reply->error() == QNetworkReply::ContentNotFoundError)
146         response.setHTTPStatusCode(404);
147
148
149     /* Fill in the other fields
150      * For local file requests remove the content length and the last-modified
151      * headers as required by fast/dom/xmlhttprequest-get.xhtml
152      */
153     foreach (QByteArray headerName, m_reply->rawHeaderList()) {
154
155         if (isLocalFileReply
156             && (headerName == "Content-Length" || headerName == "Last-Modified"))
157             continue;
158
159         response.setHTTPHeaderField(QString::fromAscii(headerName), QString::fromAscii(m_reply->rawHeader(headerName)));
160     }
161
162     QUrl redirection = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
163     if (redirection.isValid()) {
164         QUrl newUrl = m_reply->url().resolved(redirection);
165         ResourceRequest newRequest = m_resourceHandle->request();
166         newRequest.setURL(newUrl);
167
168         if (((statusCode >= 301 && statusCode <= 303) || statusCode == 307) && m_method == QNetworkAccessManager::PostOperation) {
169             m_method = QNetworkAccessManager::GetOperation;
170             newRequest.setHTTPMethod("GET");
171         }
172
173         client->willSendRequest(m_resourceHandle, newRequest, response);
174         m_redirected = true;
175         m_request = newRequest.toNetworkRequest();
176     } else {
177         client->didReceiveResponse(m_resourceHandle, response);
178     }
179 }
180
181 void QNetworkReplyHandler::forwardData()
182 {
183     sendResponseIfNeeded();
184
185     // don't emit the "Document has moved here" type of HTML
186     if (m_redirected)
187         return;
188
189     if (!m_resourceHandle)
190         return;
191
192     QByteArray data = m_reply->read(m_reply->bytesAvailable());
193
194     ResourceHandleClient* client = m_resourceHandle->client();
195     if (!client)
196         return;
197
198     if (!data.isEmpty())
199         client->didReceiveData(m_resourceHandle, data.constData(), data.length(), data.length() /*FixMe*/);
200 }
201
202 void QNetworkReplyHandler::start()
203 {
204     ResourceHandleInternal* d = m_resourceHandle->getInternal();
205
206     QNetworkAccessManager* manager = d->m_frame->page()->networkAccessManager();
207
208     const QUrl url = m_request.url();
209     const QString scheme = url.scheme();
210     // Post requests on files and data don't really make sense, but for
211     // fast/forms/form-post-urlencoded.html and for fast/forms/button-state-restore.html
212     // we still need to retrieve the file/data, which means we map it to a Get instead.
213     if (m_method == QNetworkAccessManager::PostOperation
214         && (!url.toLocalFile().isEmpty() || url.scheme() == QLatin1String("data")))
215         m_method = QNetworkAccessManager::GetOperation;
216
217     switch (m_method) {
218         case QNetworkAccessManager::GetOperation:
219             m_reply = manager->get(m_request);
220             break;
221         case QNetworkAccessManager::PostOperation: {
222             Vector<char> bytes;
223             if (d->m_request.httpBody())
224                 d->m_request.httpBody()->flatten(bytes);
225             m_reply = manager->post(m_request, QByteArray(bytes.data(), bytes.size()));
226             break;
227         }
228         case QNetworkAccessManager::HeadOperation:
229             m_reply = manager->head(m_request);
230             break;
231         case QNetworkAccessManager::PutOperation: {
232             // ### data?
233             Vector<char> bytes;
234             if (d->m_request.httpBody())
235                 d->m_request.httpBody()->flatten(bytes);
236             m_reply = manager->put(m_request, QByteArray(bytes.data(), bytes.size()));
237             break;
238         }
239         case QNetworkAccessManager::UnknownOperation:
240             break; // eh?
241     }
242
243     m_reply->setParent(this);
244
245     connect(m_reply, SIGNAL(finished()),
246             this, SLOT(finish()));
247
248     // For http(s) we know that the headers are complete upon metaDataChanged() emission, so we
249     // can send the response as early as possible
250     if (scheme == QLatin1String("http") || scheme == QLatin1String("https"))
251         connect(m_reply, SIGNAL(metaDataChanged()),
252                 this, SLOT(sendResponseIfNeeded()));
253
254     connect(m_reply, SIGNAL(readyRead()),
255             this, SLOT(forwardData()));
256 }
257
258 }
259
260 #include "moc_QNetworkReplyHandler.cpp"
261
262 #endif