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