Home | History | Annotate | Download | only in win
      1 /*
      2  * Copyright (C) 2004, 2006 Apple Computer, Inc.  All rights reserved.
      3  * Copyright (C) 2010 Patrick Gansterer <paroga (at) 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 
     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 
     58 static HINTERNET asynchronousInternetHandle(const String& userAgent)
     59 {
     60     static HINTERNET internetHandle = createInternetHandle(userAgent, true);
     61     return internetHandle;
     62 }
     63 
     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 
     85     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 
     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 
    107 WebCoreSynchronousLoader::~WebCoreSynchronousLoader()
    108 {
    109     InternetCloseHandle(m_internetHandle);
    110 }
    111 
    112 void WebCoreSynchronousLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
    113 {
    114     m_response = response;
    115 }
    116 
    117 void WebCoreSynchronousLoader::didReceiveData(ResourceHandle*, const char* data, int length, int)
    118 {
    119     m_data.append(data, length);
    120 }
    121 
    122 void WebCoreSynchronousLoader::didFinishLoading(ResourceHandle*, double)
    123 {
    124 }
    125 
    126 void WebCoreSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error)
    127 {
    128     m_error = error;
    129 }
    130 
    131 
    132 ResourceHandleInternal::~ResourceHandleInternal()
    133 {
    134 }
    135 
    136 ResourceHandle::~ResourceHandle()
    137 {
    138 }
    139 
    140 static void callOnRedirect(void* context)
    141 {
    142     ResourceHandle* handle = static_cast<ResourceHandle*>(context);
    143     handle->onRedirect();
    144 }
    145 
    146 static void callOnRequestComplete(void* context)
    147 {
    148     ResourceHandle* handle = static_cast<ResourceHandle*>(context);
    149     handle->onRequestComplete();
    150 }
    151 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    425 void ResourceHandle::setSynchronousInternetHandle(HINTERNET internetHandle)
    426 {
    427     d->m_internetHandle = internetHandle;
    428     d->m_loadSynchronously = true;
    429 }
    430 
    431 bool ResourceHandle::willLoadFromCache(ResourceRequest&, Frame*)
    432 {
    433     notImplemented();
    434     return false;
    435 }
    436 
    437 void prefetchDNS(const String&)
    438 {
    439     notImplemented();
    440 }
    441 
    442 PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
    443 {
    444     ASSERT_NOT_REACHED();
    445     return 0;
    446 }
    447 
    448 bool ResourceHandle::supportsBufferedData()
    449 {
    450     return false;
    451 }
    452 
    453 bool ResourceHandle::loadsBlocked()
    454 {
    455     return false;
    456 }
    457 
    458 void ResourceHandle::platformSetDefersLoading(bool)
    459 {
    460     notImplemented();
    461 }
    462 
    463 } // namespace WebCore
    464