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