Take referrer policy into account when clearing the referrer header
[WebKit-https.git] / Source / WebCore / platform / network / win / ResourceHandleWin.cpp
1 /*
2  * Copyright (C) 2004, 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "ResourceHandle.h"
29
30 #include "DataURL.h"
31 #include "HTTPParsers.h"
32 #include "MIMETypeRegistry.h"
33 #include "NetworkingContext.h"
34 #include "NotImplemented.h"
35 #include "ResourceError.h"
36 #include "ResourceHandleClient.h"
37 #include "ResourceHandleInternal.h"
38 #include "SharedBuffer.h"
39 #include "Timer.h"
40
41 #include <wtf/MainThread.h>
42 #include <wtf/UnusedParam.h>
43 #include <wtf/text/CString.h>
44
45 #include <windows.h>
46 #include <wininet.h>
47
48 namespace WebCore {
49
50 static inline HINTERNET createInternetHandle(const String& userAgent, bool asynchronous)
51 {
52     String userAgentString = userAgent;
53     HINTERNET internetHandle = InternetOpenW(userAgentString.charactersWithNullTermination(), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, asynchronous ? INTERNET_FLAG_ASYNC : 0);
54
55     if (asynchronous)
56         InternetSetStatusCallback(internetHandle, &ResourceHandle::internetStatusCallback);
57
58     return internetHandle;
59 }
60
61 static HINTERNET asynchronousInternetHandle(const String& userAgent)
62 {
63     static HINTERNET internetHandle = createInternetHandle(userAgent, true);
64     return internetHandle;
65 }
66
67 static String queryHTTPHeader(HINTERNET requestHandle, DWORD infoLevel)
68 {
69     DWORD bufferSize = 0;
70     HttpQueryInfoW(requestHandle, infoLevel, 0, &bufferSize, 0);
71
72     Vector<UChar> characters(bufferSize / sizeof(UChar));
73
74     if (!HttpQueryInfoW(requestHandle, infoLevel, characters.data(), &bufferSize, 0))
75         return String();
76
77     characters.removeLast(); // Remove NullTermination.
78     return String::adopt(characters);
79 }
80
81
82 class WebCoreSynchronousLoader : public ResourceHandleClient {
83     WTF_MAKE_NONCOPYABLE(WebCoreSynchronousLoader);
84 public:
85     WebCoreSynchronousLoader(ResourceError&, ResourceResponse&, Vector<char>&, const String& userAgent);
86     ~WebCoreSynchronousLoader();
87
88     HINTERNET internetHandle() const { return m_internetHandle; }
89
90     virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
91     virtual void didReceiveData(ResourceHandle*, const char*, int, int encodedDataLength);
92     virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/);
93     virtual void didFail(ResourceHandle*, const ResourceError&);
94
95 private:
96     ResourceError& m_error;
97     ResourceResponse& m_response;
98     Vector<char>& m_data;
99     HINTERNET m_internetHandle;
100 };
101
102 WebCoreSynchronousLoader::WebCoreSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data, const String& userAgent)
103     : m_error(error)
104     , m_response(response)
105     , m_data(data)
106     , m_internetHandle(createInternetHandle(userAgent, false))
107 {
108 }
109
110 WebCoreSynchronousLoader::~WebCoreSynchronousLoader()
111 {
112     InternetCloseHandle(m_internetHandle);
113 }
114
115 void WebCoreSynchronousLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
116 {
117     m_response = response;
118 }
119
120 void WebCoreSynchronousLoader::didReceiveData(ResourceHandle*, const char* data, int length, int)
121 {
122     m_data.append(data, length);
123 }
124
125 void WebCoreSynchronousLoader::didFinishLoading(ResourceHandle*, double)
126 {
127 }
128
129 void WebCoreSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error)
130 {
131     m_error = error;
132 }
133
134
135 ResourceHandleInternal::~ResourceHandleInternal()
136 {
137 }
138
139 ResourceHandle::~ResourceHandle()
140 {
141 }
142
143 static void callOnRedirect(void* context)
144 {
145     ResourceHandle* handle = static_cast<ResourceHandle*>(context);
146     handle->onRedirect();
147 }
148
149 static void callOnRequestComplete(void* context)
150 {
151     ResourceHandle* handle = static_cast<ResourceHandle*>(context);
152     handle->onRequestComplete();
153 }
154
155 void ResourceHandle::internetStatusCallback(HINTERNET internetHandle, DWORD_PTR context, DWORD internetStatus,
156                                                      LPVOID statusInformation, DWORD statusInformationLength)
157 {
158     ResourceHandle* handle = reinterpret_cast<ResourceHandle*>(context);
159
160     switch (internetStatus) {
161     case INTERNET_STATUS_REDIRECT:
162         handle->d->m_redirectUrl = String(static_cast<UChar*>(statusInformation), statusInformationLength);
163         callOnMainThread(callOnRedirect, handle);
164         break;
165
166     case INTERNET_STATUS_REQUEST_COMPLETE:
167         callOnMainThread(callOnRequestComplete, handle);
168         break;
169
170     default:
171         break;
172     }
173 }
174
175 void ResourceHandle::onRedirect()
176 {
177     ResourceRequest newRequest = firstRequest();
178     newRequest.setURL(KURL(ParsedURLString, d->m_redirectUrl));
179
180     ResourceResponse response(firstRequest().url(), String(), 0, String(), String());
181
182     if (ResourceHandleClient* resourceHandleClient = client())
183         resourceHandleClient->willSendRequest(this, newRequest, response);
184 }
185
186 bool ResourceHandle::onRequestComplete()
187 {
188     if (!d->m_internetHandle) { // 0 if canceled.
189         deref(); // balances ref in start
190         return false;
191     }
192
193     if (d->m_bytesRemainingToWrite) {
194         DWORD bytesWritten;
195         InternetWriteFile(d->m_requestHandle,
196                           d->m_formData.data() + (d->m_formData.size() - d->m_bytesRemainingToWrite),
197                           d->m_bytesRemainingToWrite,
198                           &bytesWritten);
199         d->m_bytesRemainingToWrite -= bytesWritten;
200         if (d->m_bytesRemainingToWrite)
201             return true;
202         d->m_formData.clear();
203     }
204
205     if (!d->m_sentEndRequest) {
206         HttpEndRequestW(d->m_requestHandle, 0, 0, reinterpret_cast<DWORD_PTR>(this));
207         d->m_sentEndRequest = true;
208         return true;
209     }
210
211     static const int bufferSize = 32768;
212     char buffer[bufferSize];
213     INTERNET_BUFFERSA buffers;
214     buffers.dwStructSize = sizeof(INTERNET_BUFFERSA);
215     buffers.lpvBuffer = buffer;
216     buffers.dwBufferLength = bufferSize;
217
218     BOOL ok = FALSE;
219     while ((ok = InternetReadFileExA(d->m_requestHandle, &buffers, d->m_loadSynchronously ? 0 : IRF_NO_WAIT, reinterpret_cast<DWORD_PTR>(this))) && buffers.dwBufferLength) {
220         if (!d->m_hasReceivedResponse) {
221             d->m_hasReceivedResponse = true;
222
223             ResourceResponse response;
224             response.setURL(firstRequest().url());
225
226             String httpStatusText = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_STATUS_TEXT);
227             if (!httpStatusText.isNull())
228                 response.setHTTPStatusText(httpStatusText);
229
230             String httpStatusCode = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_STATUS_CODE);
231             if (!httpStatusCode.isNull())
232                 response.setHTTPStatusCode(httpStatusCode.toInt());
233
234             String httpContentLength = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_CONTENT_LENGTH);
235             if (!httpContentLength.isNull())
236                 response.setExpectedContentLength(httpContentLength.toInt());
237
238             String httpContentType = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_CONTENT_TYPE);
239             if (!httpContentType.isNull()) {
240                 response.setMimeType(extractMIMETypeFromMediaType(httpContentType));
241                 response.setTextEncodingName(extractCharsetFromMediaType(httpContentType));
242             }
243
244             if (ResourceHandleClient* resourceHandleClient = client())
245                 resourceHandleClient->didReceiveResponse(this, response);
246         }
247
248         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=19793
249         // -1 means we do not provide any data about transfer size to inspector so it would use
250         // Content-Length headers or content size to show transfer size.
251         if (ResourceHandleClient* resourceHandleClient = client())
252             resourceHandleClient->didReceiveData(this, buffer, buffers.dwBufferLength, -1);
253         buffers.dwBufferLength = bufferSize;
254     }
255
256     if (!ok && GetLastError() == ERROR_IO_PENDING)
257         return true;
258
259     if (ResourceHandleClient* resourceHandleClient = client())
260         resourceHandleClient->didFinishLoading(this, 0);
261
262     InternetCloseHandle(d->m_requestHandle);
263     InternetCloseHandle(d->m_connectHandle);
264     deref(); // balances ref in start
265     return false;
266 }
267
268 bool ResourceHandle::start()
269 {
270     if (firstRequest().url().isLocalFile() || firstRequest().url().protocolIsData()) {
271         ref(); // balanced by deref in fileLoadTimer
272         if (d->m_loadSynchronously)
273             fileLoadTimer(0);
274         else
275             d->m_fileLoadTimer.startOneShot(0.0);
276         return true;
277     }
278
279     if (!d->m_internetHandle)
280         d->m_internetHandle = asynchronousInternetHandle(d->m_context->userAgent());
281
282     if (!d->m_internetHandle)
283         return false;
284
285     DWORD flags = INTERNET_FLAG_KEEP_CONNECTION
286         | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS
287         | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP
288         | INTERNET_FLAG_DONT_CACHE
289         | INTERNET_FLAG_RELOAD;
290
291     d->m_connectHandle = InternetConnectW(d->m_internetHandle, firstRequest().url().host().charactersWithNullTermination(), firstRequest().url().port(),
292                                           0, 0, INTERNET_SERVICE_HTTP, flags, reinterpret_cast<DWORD_PTR>(this));
293
294     if (!d->m_connectHandle)
295         return false;
296
297     String urlStr = firstRequest().url().path();
298     String urlQuery = firstRequest().url().query();
299
300     if (!urlQuery.isEmpty()) {
301         urlStr.append('?');
302         urlStr.append(urlQuery);
303     }
304
305     String httpMethod = firstRequest().httpMethod();
306     String httpReferrer = firstRequest().httpReferrer();
307
308     LPCWSTR httpAccept[] = { L"*/*", 0 };
309
310     d->m_requestHandle = HttpOpenRequestW(d->m_connectHandle, httpMethod.charactersWithNullTermination(), urlStr.charactersWithNullTermination(),
311                                           0, httpReferrer.charactersWithNullTermination(), httpAccept, flags, reinterpret_cast<DWORD_PTR>(this));
312
313     if (!d->m_requestHandle) {
314         InternetCloseHandle(d->m_connectHandle);
315         return false;
316     }
317
318     if (firstRequest().httpBody()) {
319         firstRequest().httpBody()->flatten(d->m_formData);
320         d->m_bytesRemainingToWrite = d->m_formData.size();
321     }
322
323     Vector<UChar> httpHeaders;
324     const HTTPHeaderMap& httpHeaderFields = firstRequest().httpHeaderFields();
325
326     for (HTTPHeaderMap::const_iterator it = httpHeaderFields.begin(); it != httpHeaderFields.end(); ++it) {
327         if (equalIgnoringCase(it->key, "Accept") || equalIgnoringCase(it->key, "Referer") || equalIgnoringCase(it->key, "User-Agent"))
328             continue;
329
330         if (!httpHeaders.isEmpty())
331             httpHeaders.append('\n');
332
333         httpHeaders.append(it->key.characters(), it->key.length());
334         httpHeaders.append(':');
335         httpHeaders.append(it->value.characters(), it->value.length());
336     }
337
338     INTERNET_BUFFERSW internetBuffers;
339     ZeroMemory(&internetBuffers, sizeof(internetBuffers));
340     internetBuffers.dwStructSize = sizeof(internetBuffers);
341     internetBuffers.lpcszHeader = httpHeaders.data();
342     internetBuffers.dwHeadersLength = httpHeaders.size();
343     internetBuffers.dwBufferTotal = d->m_bytesRemainingToWrite;
344
345     HttpSendRequestExW(d->m_requestHandle, &internetBuffers, 0, 0, reinterpret_cast<DWORD_PTR>(this));
346
347     ref(); // balanced by deref in onRequestComplete
348
349     if (d->m_loadSynchronously)
350         while (onRequestComplete()) {
351             // Loop until finished.
352         }
353
354     return true;
355 }
356
357 void ResourceHandle::fileLoadTimer(Timer<ResourceHandle>*)
358 {
359     RefPtr<ResourceHandle> protector(this);
360     deref(); // balances ref in start
361
362     if (firstRequest().url().protocolIsData()) {
363         handleDataURL(this);
364         return;
365     }
366
367     String fileName = firstRequest().url().fileSystemPath();
368     HANDLE fileHandle = CreateFileW(fileName.charactersWithNullTermination(), GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
369
370     if (fileHandle == INVALID_HANDLE_VALUE) {
371         client()->didFail(this, ResourceError());
372         return;
373     }
374
375     ResourceResponse response;
376
377     int dotPos = fileName.reverseFind('.');
378     int slashPos = fileName.reverseFind('/');
379
380     if (slashPos < dotPos && dotPos != -1) {
381         String ext = fileName.substring(dotPos + 1);
382         response.setMimeType(MIMETypeRegistry::getMIMETypeForExtension(ext));
383     }
384
385     client()->didReceiveResponse(this, response);
386
387     bool result = false;
388     DWORD bytesRead = 0;
389
390     do {
391         const int bufferSize = 8192;
392         char buffer[bufferSize];
393         result = ReadFile(fileHandle, &buffer, bufferSize, &bytesRead, 0);
394         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=19793
395         // -1 means we do not provide any data about transfer size to inspector so it would use
396         // Content-Length headers or content size to show transfer size.
397         if (result && bytesRead)
398             client()->didReceiveData(this, buffer, bytesRead, -1);
399         // Check for end of file.
400     } while (result && bytesRead);
401
402     CloseHandle(fileHandle);
403
404     client()->didFinishLoading(this, 0);
405 }
406
407 void ResourceHandle::cancel()
408 {
409     if (d->m_requestHandle) {
410         d->m_internetHandle = 0;
411         InternetCloseHandle(d->m_requestHandle);
412         InternetCloseHandle(d->m_connectHandle);
413     } else
414         d->m_fileLoadTimer.stop();
415 }
416
417 void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
418 {
419     UNUSED_PARAM(storedCredentials);
420
421     WebCoreSynchronousLoader syncLoader(error, response, data, request.httpUserAgent());
422     ResourceHandle handle(request, &syncLoader, true, false, context);
423
424     handle.setSynchronousInternetHandle(syncLoader.internetHandle());
425     handle.start();
426 }
427
428 void ResourceHandle::setSynchronousInternetHandle(HINTERNET internetHandle)
429 {
430     d->m_internetHandle = internetHandle;
431     d->m_loadSynchronously = true;
432 }
433
434 void prefetchDNS(const String&)
435 {
436     notImplemented();
437 }
438
439 bool ResourceHandle::loadsBlocked()
440 {
441     return false;
442 }
443
444 void ResourceHandle::platformSetDefersLoading(bool)
445 {
446     notImplemented();
447 }
448
449 } // namespace WebCore