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 "MainThread.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 #include "UnusedParam.h"
41 #include <wtf/text/CString.h>
42 #include <windows.h>
43 #include <wininet.h>
44
45 namespace WebCore {
46
createInternetHandle(const String & userAgent,bool asynchronous)47 static inline HINTERNET createInternetHandle(const String& userAgent, bool asynchronous)
48 {
49 String userAgentString = userAgent;
50 HINTERNET internetHandle = InternetOpenW(userAgentString.charactersWithNullTermination(), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, asynchronous ? INTERNET_FLAG_ASYNC : 0);
51
52 if (asynchronous)
53 InternetSetStatusCallback(internetHandle, &ResourceHandle::internetStatusCallback);
54
55 return internetHandle;
56 }
57
asynchronousInternetHandle(const String & userAgent)58 static HINTERNET asynchronousInternetHandle(const String& userAgent)
59 {
60 static HINTERNET internetHandle = createInternetHandle(userAgent, true);
61 return internetHandle;
62 }
63
queryHTTPHeader(HINTERNET requestHandle,DWORD infoLevel)64 static String queryHTTPHeader(HINTERNET requestHandle, DWORD infoLevel)
65 {
66 DWORD bufferSize = 0;
67 HttpQueryInfoW(requestHandle, infoLevel, 0, &bufferSize, 0);
68
69 Vector<UChar> characters(bufferSize / sizeof(UChar));
70
71 if (!HttpQueryInfoW(requestHandle, infoLevel, characters.data(), &bufferSize, 0))
72 return String();
73
74 characters.removeLast(); // Remove NullTermination.
75 return String::adopt(characters);
76 }
77
78
79 class WebCoreSynchronousLoader : public ResourceHandleClient {
80 WTF_MAKE_NONCOPYABLE(WebCoreSynchronousLoader);
81 public:
82 WebCoreSynchronousLoader(ResourceError&, ResourceResponse&, Vector<char>&, const String& userAgent);
83 ~WebCoreSynchronousLoader();
84
internetHandle() const85 HINTERNET internetHandle() const { return m_internetHandle; }
86
87 virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
88 virtual void didReceiveData(ResourceHandle*, const char*, int, int encodedDataLength);
89 virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/);
90 virtual void didFail(ResourceHandle*, const ResourceError&);
91
92 private:
93 ResourceError& m_error;
94 ResourceResponse& m_response;
95 Vector<char>& m_data;
96 HINTERNET m_internetHandle;
97 };
98
WebCoreSynchronousLoader(ResourceError & error,ResourceResponse & response,Vector<char> & data,const String & userAgent)99 WebCoreSynchronousLoader::WebCoreSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data, const String& userAgent)
100 : m_error(error)
101 , m_response(response)
102 , m_data(data)
103 , m_internetHandle(createInternetHandle(userAgent, false))
104 {
105 }
106
~WebCoreSynchronousLoader()107 WebCoreSynchronousLoader::~WebCoreSynchronousLoader()
108 {
109 InternetCloseHandle(m_internetHandle);
110 }
111
didReceiveResponse(ResourceHandle *,const ResourceResponse & response)112 void WebCoreSynchronousLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
113 {
114 m_response = response;
115 }
116
didReceiveData(ResourceHandle *,const char * data,int length,int)117 void WebCoreSynchronousLoader::didReceiveData(ResourceHandle*, const char* data, int length, int)
118 {
119 m_data.append(data, length);
120 }
121
didFinishLoading(ResourceHandle *,double)122 void WebCoreSynchronousLoader::didFinishLoading(ResourceHandle*, double)
123 {
124 }
125
didFail(ResourceHandle *,const ResourceError & error)126 void WebCoreSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error)
127 {
128 m_error = error;
129 }
130
131
~ResourceHandleInternal()132 ResourceHandleInternal::~ResourceHandleInternal()
133 {
134 }
135
~ResourceHandle()136 ResourceHandle::~ResourceHandle()
137 {
138 }
139
callOnRedirect(void * context)140 static void callOnRedirect(void* context)
141 {
142 ResourceHandle* handle = static_cast<ResourceHandle*>(context);
143 handle->onRedirect();
144 }
145
callOnRequestComplete(void * context)146 static void callOnRequestComplete(void* context)
147 {
148 ResourceHandle* handle = static_cast<ResourceHandle*>(context);
149 handle->onRequestComplete();
150 }
151
internetStatusCallback(HINTERNET internetHandle,DWORD_PTR context,DWORD internetStatus,LPVOID statusInformation,DWORD statusInformationLength)152 void ResourceHandle::internetStatusCallback(HINTERNET internetHandle, DWORD_PTR context, DWORD internetStatus,
153 LPVOID statusInformation, DWORD statusInformationLength)
154 {
155 ResourceHandle* handle = reinterpret_cast<ResourceHandle*>(context);
156
157 switch (internetStatus) {
158 case INTERNET_STATUS_REDIRECT:
159 handle->d->m_redirectUrl = String(static_cast<UChar*>(statusInformation), statusInformationLength);
160 callOnMainThread(callOnRedirect, handle);
161 break;
162
163 case INTERNET_STATUS_REQUEST_COMPLETE:
164 callOnMainThread(callOnRequestComplete, handle);
165 break;
166
167 default:
168 break;
169 }
170 }
171
onRedirect()172 void ResourceHandle::onRedirect()
173 {
174 ResourceRequest newRequest = firstRequest();
175 newRequest.setURL(KURL(ParsedURLString, d->m_redirectUrl));
176
177 ResourceResponse response(firstRequest().url(), String(), 0, String(), String());
178
179 if (ResourceHandleClient* resourceHandleClient = client())
180 resourceHandleClient->willSendRequest(this, newRequest, response);
181 }
182
onRequestComplete()183 bool ResourceHandle::onRequestComplete()
184 {
185 if (!d->m_internetHandle) { // 0 if canceled.
186 deref(); // balances ref in start
187 return false;
188 }
189
190 if (d->m_bytesRemainingToWrite) {
191 DWORD bytesWritten;
192 InternetWriteFile(d->m_requestHandle,
193 d->m_formData.data() + (d->m_formData.size() - d->m_bytesRemainingToWrite),
194 d->m_bytesRemainingToWrite,
195 &bytesWritten);
196 d->m_bytesRemainingToWrite -= bytesWritten;
197 if (d->m_bytesRemainingToWrite)
198 return true;
199 d->m_formData.clear();
200 }
201
202 if (!d->m_sentEndRequest) {
203 HttpEndRequestW(d->m_requestHandle, 0, 0, reinterpret_cast<DWORD_PTR>(this));
204 d->m_sentEndRequest = true;
205 return true;
206 }
207
208 static const int bufferSize = 32768;
209 char buffer[bufferSize];
210 INTERNET_BUFFERSA buffers;
211 buffers.dwStructSize = sizeof(INTERNET_BUFFERSA);
212 buffers.lpvBuffer = buffer;
213 buffers.dwBufferLength = bufferSize;
214
215 BOOL ok = FALSE;
216 while ((ok = InternetReadFileExA(d->m_requestHandle, &buffers, d->m_loadSynchronously ? 0 : IRF_NO_WAIT, reinterpret_cast<DWORD_PTR>(this))) && buffers.dwBufferLength) {
217 if (!d->m_hasReceivedResponse) {
218 d->m_hasReceivedResponse = true;
219
220 ResourceResponse response;
221 response.setURL(firstRequest().url());
222
223 String httpStatusText = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_STATUS_TEXT);
224 if (!httpStatusText.isNull())
225 response.setHTTPStatusText(httpStatusText);
226
227 String httpStatusCode = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_STATUS_CODE);
228 if (!httpStatusCode.isNull())
229 response.setHTTPStatusCode(httpStatusCode.toInt());
230
231 String httpContentLength = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_CONTENT_LENGTH);
232 if (!httpContentLength.isNull())
233 response.setExpectedContentLength(httpContentLength.toInt());
234
235 String httpContentType = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_CONTENT_TYPE);
236 if (!httpContentType.isNull()) {
237 response.setMimeType(extractMIMETypeFromMediaType(httpContentType));
238 response.setTextEncodingName(extractCharsetFromMediaType(httpContentType));
239 }
240
241 if (ResourceHandleClient* resourceHandleClient = client())
242 resourceHandleClient->didReceiveResponse(this, response);
243 }
244
245 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=19793
246 // -1 means we do not provide any data about transfer size to inspector so it would use
247 // Content-Length headers or content size to show transfer size.
248 if (ResourceHandleClient* resourceHandleClient = client())
249 resourceHandleClient->didReceiveData(this, buffer, buffers.dwBufferLength, -1);
250 buffers.dwBufferLength = bufferSize;
251 }
252
253 if (!ok && GetLastError() == ERROR_IO_PENDING)
254 return true;
255
256 if (ResourceHandleClient* resourceHandleClient = client())
257 resourceHandleClient->didFinishLoading(this, 0);
258
259 InternetCloseHandle(d->m_requestHandle);
260 InternetCloseHandle(d->m_connectHandle);
261 deref(); // balances ref in start
262 return false;
263 }
264
start(NetworkingContext * context)265 bool ResourceHandle::start(NetworkingContext* context)
266 {
267 if (firstRequest().url().isLocalFile() || firstRequest().url().protocolIsData()) {
268 ref(); // balanced by deref in fileLoadTimer
269 if (d->m_loadSynchronously)
270 fileLoadTimer(0);
271 else
272 d->m_fileLoadTimer.startOneShot(0.0);
273 return true;
274 }
275
276 if (!d->m_internetHandle)
277 d->m_internetHandle = asynchronousInternetHandle(context->userAgent());
278
279 if (!d->m_internetHandle)
280 return false;
281
282 DWORD flags = INTERNET_FLAG_KEEP_CONNECTION
283 | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS
284 | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP
285 | INTERNET_FLAG_DONT_CACHE
286 | INTERNET_FLAG_RELOAD;
287
288 d->m_connectHandle = InternetConnectW(d->m_internetHandle, firstRequest().url().host().charactersWithNullTermination(), firstRequest().url().port(),
289 0, 0, INTERNET_SERVICE_HTTP, flags, reinterpret_cast<DWORD_PTR>(this));
290
291 if (!d->m_connectHandle)
292 return false;
293
294 String urlStr = firstRequest().url().path();
295 String urlQuery = firstRequest().url().query();
296
297 if (!urlQuery.isEmpty()) {
298 urlStr.append('?');
299 urlStr.append(urlQuery);
300 }
301
302 String httpMethod = firstRequest().httpMethod();
303 String httpReferrer = firstRequest().httpReferrer();
304
305 LPCWSTR httpAccept[] = { L"*/*", 0 };
306
307 d->m_requestHandle = HttpOpenRequestW(d->m_connectHandle, httpMethod.charactersWithNullTermination(), urlStr.charactersWithNullTermination(),
308 0, httpReferrer.charactersWithNullTermination(), httpAccept, flags, reinterpret_cast<DWORD_PTR>(this));
309
310 if (!d->m_requestHandle) {
311 InternetCloseHandle(d->m_connectHandle);
312 return false;
313 }
314
315 if (firstRequest().httpBody()) {
316 firstRequest().httpBody()->flatten(d->m_formData);
317 d->m_bytesRemainingToWrite = d->m_formData.size();
318 }
319
320 Vector<UChar> httpHeaders;
321 const HTTPHeaderMap& httpHeaderFields = firstRequest().httpHeaderFields();
322
323 for (HTTPHeaderMap::const_iterator it = httpHeaderFields.begin(); it != httpHeaderFields.end(); ++it) {
324 if (equalIgnoringCase(it->first, "Accept") || equalIgnoringCase(it->first, "Referer") || equalIgnoringCase(it->first, "User-Agent"))
325 continue;
326
327 if (!httpHeaders.isEmpty())
328 httpHeaders.append('\n');
329
330 httpHeaders.append(it->first.characters(), it->first.length());
331 httpHeaders.append(':');
332 httpHeaders.append(it->second.characters(), it->second.length());
333 }
334
335 INTERNET_BUFFERSW internetBuffers;
336 ZeroMemory(&internetBuffers, sizeof(internetBuffers));
337 internetBuffers.dwStructSize = sizeof(internetBuffers);
338 internetBuffers.lpcszHeader = httpHeaders.data();
339 internetBuffers.dwHeadersLength = httpHeaders.size();
340 internetBuffers.dwBufferTotal = d->m_bytesRemainingToWrite;
341
342 HttpSendRequestExW(d->m_requestHandle, &internetBuffers, 0, 0, reinterpret_cast<DWORD_PTR>(this));
343
344 ref(); // balanced by deref in onRequestComplete
345
346 if (d->m_loadSynchronously)
347 while (onRequestComplete()) {
348 // Loop until finished.
349 }
350
351 return true;
352 }
353
fileLoadTimer(Timer<ResourceHandle> *)354 void ResourceHandle::fileLoadTimer(Timer<ResourceHandle>*)
355 {
356 RefPtr<ResourceHandle> protector(this);
357 deref(); // balances ref in start
358
359 if (firstRequest().url().protocolIsData()) {
360 handleDataURL(this);
361 return;
362 }
363
364 String fileName = firstRequest().url().fileSystemPath();
365 HANDLE fileHandle = CreateFileW(fileName.charactersWithNullTermination(), GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
366
367 if (fileHandle == INVALID_HANDLE_VALUE) {
368 client()->didFail(this, ResourceError());
369 return;
370 }
371
372 ResourceResponse response;
373
374 int dotPos = fileName.reverseFind('.');
375 int slashPos = fileName.reverseFind('/');
376
377 if (slashPos < dotPos && dotPos != -1) {
378 String ext = fileName.substring(dotPos + 1);
379 response.setMimeType(MIMETypeRegistry::getMIMETypeForExtension(ext));
380 }
381
382 client()->didReceiveResponse(this, response);
383
384 bool result = false;
385 DWORD bytesRead = 0;
386
387 do {
388 const int bufferSize = 8192;
389 char buffer[bufferSize];
390 result = ReadFile(fileHandle, &buffer, bufferSize, &bytesRead, 0);
391 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=19793
392 // -1 means we do not provide any data about transfer size to inspector so it would use
393 // Content-Length headers or content size to show transfer size.
394 if (result && bytesRead)
395 client()->didReceiveData(this, buffer, bytesRead, -1);
396 // Check for end of file.
397 } while (result && bytesRead);
398
399 CloseHandle(fileHandle);
400
401 client()->didFinishLoading(this, 0);
402 }
403
cancel()404 void ResourceHandle::cancel()
405 {
406 if (d->m_requestHandle) {
407 d->m_internetHandle = 0;
408 InternetCloseHandle(d->m_requestHandle);
409 InternetCloseHandle(d->m_connectHandle);
410 } else
411 d->m_fileLoadTimer.stop();
412 }
413
loadResourceSynchronously(NetworkingContext * context,const ResourceRequest & request,StoredCredentials storedCredentials,ResourceError & error,ResourceResponse & response,Vector<char> & data)414 void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
415 {
416 UNUSED_PARAM(storedCredentials);
417
418 WebCoreSynchronousLoader syncLoader(error, response, data, request.httpUserAgent());
419 ResourceHandle handle(request, &syncLoader, true, false);
420
421 handle.setSynchronousInternetHandle(syncLoader.internetHandle());
422 handle.start(context);
423 }
424
setSynchronousInternetHandle(HINTERNET internetHandle)425 void ResourceHandle::setSynchronousInternetHandle(HINTERNET internetHandle)
426 {
427 d->m_internetHandle = internetHandle;
428 d->m_loadSynchronously = true;
429 }
430
willLoadFromCache(ResourceRequest &,Frame *)431 bool ResourceHandle::willLoadFromCache(ResourceRequest&, Frame*)
432 {
433 notImplemented();
434 return false;
435 }
436
prefetchDNS(const String &)437 void prefetchDNS(const String&)
438 {
439 notImplemented();
440 }
441
bufferedData()442 PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
443 {
444 ASSERT_NOT_REACHED();
445 return 0;
446 }
447
supportsBufferedData()448 bool ResourceHandle::supportsBufferedData()
449 {
450 return false;
451 }
452
loadsBlocked()453 bool ResourceHandle::loadsBlocked()
454 {
455 return false;
456 }
457
platformSetDefersLoading(bool)458 void ResourceHandle::platformSetDefersLoading(bool)
459 {
460 notImplemented();
461 }
462
463 } // namespace WebCore
464