2 * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved.
3 * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
28 #include "ResourceHandle.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"
40 #include "UnusedParam.h"
41 #include <wtf/text/CString.h>
47 static inline HINTERNET createInternetHandle(const String& userAgent, bool asynchronous)
49 String userAgentString = userAgent;
50 HINTERNET internetHandle = InternetOpenW(userAgentString.charactersWithNullTermination(), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, asynchronous ? INTERNET_FLAG_ASYNC : 0);
53 InternetSetStatusCallback(internetHandle, &ResourceHandle::internetStatusCallback);
55 return internetHandle;
58 static HINTERNET asynchronousInternetHandle(const String& userAgent)
60 static HINTERNET internetHandle = createInternetHandle(userAgent, true);
61 return internetHandle;
64 static String queryHTTPHeader(HINTERNET requestHandle, DWORD infoLevel)
67 HttpQueryInfoW(requestHandle, infoLevel, 0, &bufferSize, 0);
69 Vector<UChar> characters(bufferSize / sizeof(UChar));
71 if (!HttpQueryInfoW(requestHandle, infoLevel, characters.data(), &bufferSize, 0))
74 characters.removeLast(); // Remove NullTermination.
75 return String::adopt(characters);
79 class WebCoreSynchronousLoader : public ResourceHandleClient {
80 WTF_MAKE_NONCOPYABLE(WebCoreSynchronousLoader);
82 WebCoreSynchronousLoader(ResourceError&, ResourceResponse&, Vector<char>&, const String& userAgent);
83 ~WebCoreSynchronousLoader();
85 HINTERNET internetHandle() const { return m_internetHandle; }
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&);
93 ResourceError& m_error;
94 ResourceResponse& m_response;
96 HINTERNET m_internetHandle;
99 WebCoreSynchronousLoader::WebCoreSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data, const String& userAgent)
101 , m_response(response)
103 , m_internetHandle(createInternetHandle(userAgent, false))
107 WebCoreSynchronousLoader::~WebCoreSynchronousLoader()
109 InternetCloseHandle(m_internetHandle);
112 void WebCoreSynchronousLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
114 m_response = response;
117 void WebCoreSynchronousLoader::didReceiveData(ResourceHandle*, const char* data, int length, int)
119 m_data.append(data, length);
122 void WebCoreSynchronousLoader::didFinishLoading(ResourceHandle*, double)
126 void WebCoreSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error)
132 ResourceHandleInternal::~ResourceHandleInternal()
136 ResourceHandle::~ResourceHandle()
140 static void callOnRedirect(void* context)
142 ResourceHandle* handle = static_cast<ResourceHandle*>(context);
143 handle->onRedirect();
146 static void callOnRequestComplete(void* context)
148 ResourceHandle* handle = static_cast<ResourceHandle*>(context);
149 handle->onRequestComplete();
152 void ResourceHandle::internetStatusCallback(HINTERNET internetHandle, DWORD_PTR context, DWORD internetStatus,
153 LPVOID statusInformation, DWORD statusInformationLength)
155 ResourceHandle* handle = reinterpret_cast<ResourceHandle*>(context);
157 switch (internetStatus) {
158 case INTERNET_STATUS_REDIRECT:
159 handle->d->m_redirectUrl = String(static_cast<UChar*>(statusInformation), statusInformationLength);
160 callOnMainThread(callOnRedirect, handle);
163 case INTERNET_STATUS_REQUEST_COMPLETE:
164 callOnMainThread(callOnRequestComplete, handle);
172 void ResourceHandle::onRedirect()
174 ResourceRequest newRequest = firstRequest();
175 newRequest.setURL(KURL(ParsedURLString, d->m_redirectUrl));
177 ResourceResponse response(firstRequest().url(), String(), 0, String(), String());
179 if (ResourceHandleClient* resourceHandleClient = client())
180 resourceHandleClient->willSendRequest(this, newRequest, response);
183 bool ResourceHandle::onRequestComplete()
185 if (!d->m_internetHandle) { // 0 if canceled.
186 deref(); // balances ref in start
190 if (d->m_bytesRemainingToWrite) {
192 InternetWriteFile(d->m_requestHandle,
193 d->m_formData.data() + (d->m_formData.size() - d->m_bytesRemainingToWrite),
194 d->m_bytesRemainingToWrite,
196 d->m_bytesRemainingToWrite -= bytesWritten;
197 if (d->m_bytesRemainingToWrite)
199 d->m_formData.clear();
202 if (!d->m_sentEndRequest) {
203 HttpEndRequestW(d->m_requestHandle, 0, 0, reinterpret_cast<DWORD_PTR>(this));
204 d->m_sentEndRequest = true;
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;
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;
220 ResourceResponse response;
221 response.setURL(firstRequest().url());
223 String httpStatusText = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_STATUS_TEXT);
224 if (!httpStatusText.isNull())
225 response.setHTTPStatusText(httpStatusText);
227 String httpStatusCode = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_STATUS_CODE);
228 if (!httpStatusCode.isNull())
229 response.setHTTPStatusCode(httpStatusCode.toInt());
231 String httpContentLength = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_CONTENT_LENGTH);
232 if (!httpContentLength.isNull())
233 response.setExpectedContentLength(httpContentLength.toInt());
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));
241 if (ResourceHandleClient* resourceHandleClient = client())
242 resourceHandleClient->didReceiveResponse(this, response);
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;
253 if (!ok && GetLastError() == ERROR_IO_PENDING)
256 if (ResourceHandleClient* resourceHandleClient = client())
257 resourceHandleClient->didFinishLoading(this, 0);
259 InternetCloseHandle(d->m_requestHandle);
260 InternetCloseHandle(d->m_connectHandle);
261 deref(); // balances ref in start
265 bool ResourceHandle::start(NetworkingContext* context)
267 if (firstRequest().url().isLocalFile() || firstRequest().url().protocolIsData()) {
268 ref(); // balanced by deref in fileLoadTimer
269 if (d->m_loadSynchronously)
272 d->m_fileLoadTimer.startOneShot(0.0);
276 if (!d->m_internetHandle)
277 d->m_internetHandle = asynchronousInternetHandle(context->userAgent());
279 if (!d->m_internetHandle)
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;
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));
291 if (!d->m_connectHandle)
294 String urlStr = firstRequest().url().path();
295 String urlQuery = firstRequest().url().query();
297 if (!urlQuery.isEmpty()) {
299 urlStr.append(urlQuery);
302 String httpMethod = firstRequest().httpMethod();
303 String httpReferrer = firstRequest().httpReferrer();
305 LPCWSTR httpAccept[] = { L"*/*", 0 };
307 d->m_requestHandle = HttpOpenRequestW(d->m_connectHandle, httpMethod.charactersWithNullTermination(), urlStr.charactersWithNullTermination(),
308 0, httpReferrer.charactersWithNullTermination(), httpAccept, flags, reinterpret_cast<DWORD_PTR>(this));
310 if (!d->m_requestHandle) {
311 InternetCloseHandle(d->m_connectHandle);
315 if (firstRequest().httpBody()) {
316 firstRequest().httpBody()->flatten(d->m_formData);
317 d->m_bytesRemainingToWrite = d->m_formData.size();
320 Vector<UChar> httpHeaders;
321 const HTTPHeaderMap& httpHeaderFields = firstRequest().httpHeaderFields();
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"))
327 if (!httpHeaders.isEmpty())
328 httpHeaders.append('\n');
330 httpHeaders.append(it->first.characters(), it->first.length());
331 httpHeaders.append(':');
332 httpHeaders.append(it->second.characters(), it->second.length());
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;
342 HttpSendRequestExW(d->m_requestHandle, &internetBuffers, 0, 0, reinterpret_cast<DWORD_PTR>(this));
344 ref(); // balanced by deref in onRequestComplete
346 if (d->m_loadSynchronously)
347 while (onRequestComplete()) {
348 // Loop until finished.
354 void ResourceHandle::fileLoadTimer(Timer<ResourceHandle>*)
356 RefPtr<ResourceHandle> protector(this);
357 deref(); // balances ref in start
359 if (firstRequest().url().protocolIsData()) {
364 String fileName = firstRequest().url().fileSystemPath();
365 HANDLE fileHandle = CreateFileW(fileName.charactersWithNullTermination(), GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
367 if (fileHandle == INVALID_HANDLE_VALUE) {
368 client()->didFail(this, ResourceError());
372 ResourceResponse response;
374 int dotPos = fileName.reverseFind('.');
375 int slashPos = fileName.reverseFind('/');
377 if (slashPos < dotPos && dotPos != -1) {
378 String ext = fileName.substring(dotPos + 1);
379 response.setMimeType(MIMETypeRegistry::getMIMETypeForExtension(ext));
382 client()->didReceiveResponse(this, response);
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);
399 CloseHandle(fileHandle);
401 client()->didFinishLoading(this, 0);
404 void ResourceHandle::cancel()
406 if (d->m_requestHandle) {
407 d->m_internetHandle = 0;
408 InternetCloseHandle(d->m_requestHandle);
409 InternetCloseHandle(d->m_connectHandle);
411 d->m_fileLoadTimer.stop();
414 void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
416 UNUSED_PARAM(storedCredentials);
418 WebCoreSynchronousLoader syncLoader(error, response, data, request.httpUserAgent());
419 ResourceHandle handle(request, &syncLoader, true, false);
421 handle.setSynchronousInternetHandle(syncLoader.internetHandle());
422 handle.start(context);
425 void ResourceHandle::setSynchronousInternetHandle(HINTERNET internetHandle)
427 d->m_internetHandle = internetHandle;
428 d->m_loadSynchronously = true;
431 bool ResourceHandle::willLoadFromCache(ResourceRequest&, Frame*)
437 void prefetchDNS(const String&)
442 bool ResourceHandle::loadsBlocked()
447 void ResourceHandle::platformSetDefersLoading(bool)
452 } // namespace WebCore