1 /* 2 * Copyright 2010, The Android Open Source Project 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "WebRequest.h" 28 29 #include "JNIUtility.h" 30 #include "MainThread.h" 31 #include "UrlInterceptResponse.h" 32 #include "WebCoreFrameBridge.h" 33 #include "WebCoreJni.h" 34 #include "WebRequestContext.h" 35 #include "WebResourceRequest.h" 36 #include "WebUrlLoaderClient.h" 37 #include "jni.h" 38 39 #include <cutils/log.h> 40 #include <openssl/x509.h> 41 #include <string> 42 #include <androidfw/AssetManager.h> 43 44 extern android::AssetManager* globalAssetManager(); 45 46 // TODO: 47 // - Finish the file upload. Testcase is mobile buzz 48 // - Add network throttle needed by Android plugins 49 50 // TODO: Turn off asserts crashing before release 51 // http://b/issue?id=2951985 52 #undef ASSERT 53 #define ASSERT(assertion, ...) do \ 54 if (!(assertion)) { \ 55 android_printLog(ANDROID_LOG_ERROR, __FILE__, __VA_ARGS__); \ 56 } \ 57 while (0) 58 59 namespace android { 60 61 namespace { 62 const int kInitialReadBufSize = 32768; 63 const char* kXRequestedWithHeader = "X-Requested-With"; 64 65 struct RequestPackageName { 66 std::string value; 67 RequestPackageName(); 68 }; 69 70 RequestPackageName::RequestPackageName() { 71 JNIEnv* env = JSC::Bindings::getJNIEnv(); 72 jclass bridgeClass = env->FindClass("android/webkit/JniUtil"); 73 jmethodID method = env->GetStaticMethodID(bridgeClass, "getPackageName", "()Ljava/lang/String;"); 74 value = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method))); 75 env->DeleteLocalRef(bridgeClass); 76 } 77 78 base::LazyInstance<RequestPackageName> s_packageName(base::LINKER_INITIALIZED); 79 80 } 81 82 WebRequest::WebRequest(WebUrlLoaderClient* loader, const WebResourceRequest& webResourceRequest) 83 : m_urlLoader(loader) 84 , m_url(webResourceRequest.url()) 85 , m_userAgent(webResourceRequest.userAgent()) 86 , m_referer(webResourceRequest.referrer()) 87 , m_loadState(Created) 88 , m_authRequestCount(0) 89 , m_cacheMode(0) 90 , m_runnableFactory(this) 91 , m_wantToPause(false) 92 , m_isPaused(false) 93 , m_isSync(false) 94 { 95 GURL gurl(m_url); 96 97 m_request = new net::URLRequest(gurl, this); 98 99 m_request->SetExtraRequestHeaders(webResourceRequest.requestHeaders()); 100 m_request->SetExtraRequestHeaderByName(kXRequestedWithHeader, s_packageName.Get().value, false); 101 m_request->set_referrer(webResourceRequest.referrer()); 102 m_request->set_method(webResourceRequest.method()); 103 m_request->set_load_flags(webResourceRequest.loadFlags()); 104 } 105 106 // This is a special URL for Android. Query the Java InputStream 107 // for data and send to WebCore 108 WebRequest::WebRequest(WebUrlLoaderClient* loader, const WebResourceRequest& webResourceRequest, UrlInterceptResponse* intercept) 109 : m_urlLoader(loader) 110 , m_interceptResponse(intercept) 111 , m_url(webResourceRequest.url()) 112 , m_userAgent(webResourceRequest.userAgent()) 113 , m_referer(webResourceRequest.referrer()) 114 , m_loadState(Created) 115 , m_authRequestCount(0) 116 , m_cacheMode(0) 117 , m_runnableFactory(this) 118 , m_wantToPause(false) 119 , m_isPaused(false) 120 , m_isSync(false) 121 { 122 } 123 124 WebRequest::~WebRequest() 125 { 126 ASSERT(m_loadState == Finished, "dtor called on a WebRequest in a different state than finished (%d)", m_loadState); 127 128 m_loadState = Deleted; 129 } 130 131 const std::string& WebRequest::getUrl() const 132 { 133 return m_url; 134 } 135 136 const std::string& WebRequest::getUserAgent() const 137 { 138 return m_userAgent; 139 } 140 141 const std::string& WebRequest::getReferer() const 142 { 143 return m_referer; 144 } 145 146 #ifdef LOG_REQUESTS 147 namespace { 148 int remaining = 0; 149 } 150 #endif 151 152 void WebRequest::finish(bool success) 153 { 154 m_runnableFactory.RevokeAll(); 155 ASSERT(m_loadState < Finished, "(%p) called finish on an already finished WebRequest (%d) (%s)", this, m_loadState, m_url.c_str()); 156 if (m_loadState >= Finished) 157 return; 158 #ifdef LOG_REQUESTS 159 time_t finish; 160 time(&finish); 161 finish = finish - m_startTime; 162 struct tm * timeinfo; 163 char buffer[80]; 164 timeinfo = localtime(&finish); 165 strftime(buffer, 80, "Time: %M:%S",timeinfo); 166 android_printLog(ANDROID_LOG_DEBUG, "KM", "(%p) finish (%d) (%s) (%d) (%s)", this, --remaining, buffer, success, m_url.c_str()); 167 #endif 168 169 // Make sure WebUrlLoaderClient doesn't delete us in the middle of this method. 170 scoped_refptr<WebRequest> guard(this); 171 172 m_loadState = Finished; 173 if (success) { 174 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( 175 m_urlLoader.get(), &WebUrlLoaderClient::didFinishLoading)); 176 } else { 177 if (m_interceptResponse == NULL) { 178 OwnPtr<WebResponse> webResponse(new WebResponse(m_request.get())); 179 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( 180 m_urlLoader.get(), &WebUrlLoaderClient::didFail, webResponse.release())); 181 } else { 182 OwnPtr<WebResponse> webResponse(new WebResponse(m_url, m_interceptResponse->mimeType(), 0, 183 m_interceptResponse->encoding(), m_interceptResponse->status())); 184 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( 185 m_urlLoader.get(), &WebUrlLoaderClient::didFail, webResponse.release())); 186 } 187 } 188 m_networkBuffer = 0; 189 m_request = 0; 190 m_urlLoader = 0; 191 } 192 193 void WebRequest::appendFileToUpload(const std::string& filename) 194 { 195 // AppendFileToUpload is only valid before calling start 196 ASSERT(m_loadState == Created, "appendFileToUpload called on a WebRequest not in CREATED state: (%s)", m_url.c_str()); 197 FilePath filePath(filename); 198 m_request->AppendFileToUpload(filePath); 199 } 200 201 void WebRequest::appendBytesToUpload(WTF::Vector<char>* data) 202 { 203 // AppendBytesToUpload is only valid before calling start 204 ASSERT(m_loadState == Created, "appendBytesToUpload called on a WebRequest not in CREATED state: (%s)", m_url.c_str()); 205 m_request->AppendBytesToUpload(data->data(), data->size()); 206 delete data; 207 } 208 209 void WebRequest::setRequestContext(WebRequestContext* context) 210 { 211 m_cacheMode = context->getCacheMode(); 212 if (m_request) 213 m_request->set_context(context); 214 } 215 216 void WebRequest::updateLoadFlags(int& loadFlags) 217 { 218 if (m_cacheMode == 1) { // LOAD_CACHE_ELSE_NETWORK 219 loadFlags |= net::LOAD_PREFERRING_CACHE; 220 loadFlags &= ~net::LOAD_VALIDATE_CACHE; 221 } 222 if (m_cacheMode == 2) // LOAD_NO_CACHE 223 loadFlags |= net::LOAD_BYPASS_CACHE; 224 if (m_cacheMode == 3) // LOAD_CACHE_ONLY 225 loadFlags |= net::LOAD_ONLY_FROM_CACHE; 226 227 if (m_isSync) 228 loadFlags |= net::LOAD_IGNORE_LIMITS; 229 } 230 231 void WebRequest::start() 232 { 233 ASSERT(m_loadState == Created, "Start called on a WebRequest not in CREATED state: (%s)", m_url.c_str()); 234 #ifdef LOG_REQUESTS 235 android_printLog(ANDROID_LOG_DEBUG, "KM", "(%p) start (%d) (%s)", this, ++remaining, m_url.c_str()); 236 time(&m_startTime); 237 #endif 238 239 m_loadState = Started; 240 241 if (m_interceptResponse != NULL) 242 return handleInterceptedURL(); 243 244 // Handle data urls before we send it off to the http stack 245 if (m_request->url().SchemeIs("data")) 246 return handleDataURL(m_request->url()); 247 248 if (m_request->url().SchemeIs("browser")) 249 return handleBrowserURL(m_request->url()); 250 251 // Update load flags with settings from WebSettings 252 int loadFlags = m_request->load_flags(); 253 updateLoadFlags(loadFlags); 254 m_request->set_load_flags(loadFlags); 255 256 m_request->Start(); 257 } 258 259 void WebRequest::cancel() 260 { 261 ASSERT(m_loadState >= Started, "Cancel called on a not started WebRequest: (%s)", m_url.c_str()); 262 ASSERT(m_loadState != Cancelled, "Cancel called on an already cancelled WebRequest: (%s)", m_url.c_str()); 263 264 // There is a possible race condition between the IO thread finishing the request and 265 // the WebCore thread cancelling it. If the request has already finished, do 266 // nothing to avoid sending duplicate finish messages to WebCore. 267 if (m_loadState > Cancelled) { 268 return; 269 } 270 ASSERT(m_request, "Request set to 0 before it is finished"); 271 272 m_loadState = Cancelled; 273 274 m_request->Cancel(); 275 finish(true); 276 } 277 278 void WebRequest::pauseLoad(bool pause) 279 { 280 ASSERT(m_loadState >= GotData, "PauseLoad in state other than RESPONSE and GOTDATA"); 281 if (pause) { 282 if (!m_isPaused) 283 m_wantToPause = true; 284 } else { 285 m_wantToPause = false; 286 if (m_isPaused) { 287 m_isPaused = false; 288 MessageLoop::current()->PostTask(FROM_HERE, m_runnableFactory.NewRunnableMethod(&WebRequest::startReading)); 289 } 290 } 291 } 292 293 void WebRequest::handleInterceptedURL() 294 { 295 m_loadState = Response; 296 297 const std::string& mime = m_interceptResponse->mimeType(); 298 // Get the MIME type from the URL. "text/html" is a last resort, hopefully overridden. 299 std::string mimeType("text/html"); 300 if (mime == "") { 301 // Get the MIME type from the file extension, if any. 302 FilePath path(m_url); 303 net::GetMimeTypeFromFile(path, &mimeType); 304 } else { 305 // Set from the intercept response. 306 mimeType = mime; 307 } 308 309 310 OwnPtr<WebResponse> webResponse(new WebResponse(m_url, mimeType, 0, m_interceptResponse->encoding(), m_interceptResponse->status())); 311 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( 312 m_urlLoader.get(), &WebUrlLoaderClient::didReceiveResponse, webResponse.release())); 313 314 do { 315 // data is deleted in WebUrlLoaderClient::didReceiveAndroidFileData 316 // data is sent to the webcore thread 317 OwnPtr<std::vector<char> > data(new std::vector<char>); 318 data->reserve(kInitialReadBufSize); 319 320 // Read returns false on error and size of 0 on eof. 321 if (!m_interceptResponse->readStream(data.get()) || data->size() == 0) 322 break; 323 324 m_loadState = GotData; 325 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( 326 m_urlLoader.get(), &WebUrlLoaderClient::didReceiveAndroidFileData, data.release())); 327 } while (true); 328 329 finish(m_interceptResponse->status() == 200); 330 } 331 332 void WebRequest::handleDataURL(GURL url) 333 { 334 OwnPtr<std::string> data(new std::string); 335 std::string mimeType; 336 std::string charset; 337 338 if (net::DataURL::Parse(url, &mimeType, &charset, data.get())) { 339 // PopulateURLResponse from chrome implementation 340 // weburlloader_impl.cc 341 m_loadState = Response; 342 OwnPtr<WebResponse> webResponse(new WebResponse(url.spec(), mimeType, data->size(), charset, 200)); 343 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( 344 m_urlLoader.get(), &WebUrlLoaderClient::didReceiveResponse, webResponse.release())); 345 346 if (!data->empty()) { 347 m_loadState = GotData; 348 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( 349 m_urlLoader.get(), &WebUrlLoaderClient::didReceiveDataUrl, data.release())); 350 } 351 } else { 352 // handle the failed case 353 } 354 355 finish(true); 356 } 357 358 void WebRequest::handleBrowserURL(GURL url) 359 { 360 std::string data("data:text/html;charset=utf-8,"); 361 if (url.spec() == "browser:incognito") { 362 AssetManager* assetManager = globalAssetManager(); 363 Asset* asset = assetManager->open("webkit/incognito_mode_start_page.html", Asset::ACCESS_BUFFER); 364 if (asset) { 365 data.append((const char*)asset->getBuffer(false), asset->getLength()); 366 delete asset; 367 } 368 } 369 GURL dataURL(data.c_str()); 370 handleDataURL(dataURL); 371 } 372 373 // Called upon a server-initiated redirect. The delegate may call the 374 // request's Cancel method to prevent the redirect from being followed. 375 // Since there may be multiple chained redirects, there may also be more 376 // than one redirect call. 377 // 378 // When this function is called, the request will still contain the 379 // original URL, the destination of the redirect is provided in 'new_url'. 380 // If the delegate does not cancel the request and |*defer_redirect| is 381 // false, then the redirect will be followed, and the request's URL will be 382 // changed to the new URL. Otherwise if the delegate does not cancel the 383 // request and |*defer_redirect| is true, then the redirect will be 384 // followed once FollowDeferredRedirect is called on the URLRequest. 385 // 386 // The caller must set |*defer_redirect| to false, so that delegates do not 387 // need to set it if they are happy with the default behavior of not 388 // deferring redirect. 389 void WebRequest::OnReceivedRedirect(net::URLRequest* newRequest, const GURL& newUrl, bool* deferRedirect) 390 { 391 ASSERT(m_loadState < Response, "Redirect after receiving response"); 392 ASSERT(newRequest && newRequest->status().is_success(), "Invalid redirect"); 393 394 OwnPtr<WebResponse> webResponse(new WebResponse(newRequest)); 395 webResponse->setUrl(newUrl.spec()); 396 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( 397 m_urlLoader.get(), &WebUrlLoaderClient::willSendRequest, webResponse.release())); 398 399 // Defer the redirect until followDeferredRedirect() is called. 400 *deferRedirect = true; 401 } 402 403 // Called when we receive an authentication failure. The delegate should 404 // call request->SetAuth() with the user's credentials once it obtains them, 405 // or request->CancelAuth() to cancel the login and display the error page. 406 // When it does so, the request will be reissued, restarting the sequence 407 // of On* callbacks. 408 void WebRequest::OnAuthRequired(net::URLRequest* request, net::AuthChallengeInfo* authInfo) 409 { 410 ASSERT(m_loadState == Started, "OnAuthRequired called on a WebRequest not in STARTED state (state=%d)", m_loadState); 411 412 scoped_refptr<net::AuthChallengeInfo> authInfoPtr(authInfo); 413 bool firstTime = (m_authRequestCount == 0); 414 ++m_authRequestCount; 415 416 bool suppressDialog = (request->load_flags() & net::LOAD_DO_NOT_PROMPT_FOR_LOGIN); 417 418 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( 419 m_urlLoader.get(), &WebUrlLoaderClient::authRequired, authInfoPtr, firstTime, suppressDialog)); 420 } 421 422 // Called when we received an SSL certificate error. The delegate will provide 423 // the user the options to proceed, cancel, or view certificates. 424 void WebRequest::OnSSLCertificateError(net::URLRequest* request, int cert_error, net::X509Certificate* cert) 425 { 426 scoped_refptr<net::X509Certificate> scoped_cert = cert; 427 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( 428 m_urlLoader.get(), &WebUrlLoaderClient::reportSslCertError, cert_error, scoped_cert)); 429 } 430 431 void WebRequest::OnCertificateRequested(net::URLRequest* request, net::SSLCertRequestInfo* cert_request_info) 432 { 433 scoped_refptr<net::SSLCertRequestInfo> scoped_cert_request_info = cert_request_info; 434 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( 435 m_urlLoader.get(), &WebUrlLoaderClient::requestClientCert, scoped_cert_request_info)); 436 } 437 438 439 // After calling Start(), the delegate will receive an OnResponseStarted 440 // callback when the request has completed. If an error occurred, the 441 // request->status() will be set. On success, all redirects have been 442 // followed and the final response is beginning to arrive. At this point, 443 // meta data about the response is available, including for example HTTP 444 // response headers if this is a request for a HTTP resource. 445 void WebRequest::OnResponseStarted(net::URLRequest* request) 446 { 447 ASSERT(m_loadState == Started, "Got response after receiving response"); 448 449 m_loadState = Response; 450 if (request && request->status().is_success()) { 451 OwnPtr<WebResponse> webResponse(new WebResponse(request)); 452 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( 453 m_urlLoader.get(), &WebUrlLoaderClient::didReceiveResponse, webResponse.release())); 454 455 // Start reading the response 456 startReading(); 457 } else { 458 finish(false); 459 } 460 } 461 462 void WebRequest::setAuth(const string16& username, const string16& password) 463 { 464 ASSERT(m_loadState == Started, "setAuth called on a WebRequest not in STARTED state (state=%d)", m_loadState); 465 466 m_request->SetAuth(username, password); 467 } 468 469 void WebRequest::cancelAuth() 470 { 471 ASSERT(m_loadState == Started, "cancelAuth called on a WebRequest not in STARTED state (state=%d)", m_loadState); 472 473 m_request->CancelAuth(); 474 } 475 476 void WebRequest::followDeferredRedirect() 477 { 478 ASSERT(m_loadState < Response, "Redirect after receiving response"); 479 480 m_request->FollowDeferredRedirect(); 481 } 482 483 void WebRequest::proceedSslCertError() 484 { 485 m_request->ContinueDespiteLastError(); 486 } 487 488 void WebRequest::cancelSslCertError(int cert_error) 489 { 490 m_request->SimulateError(cert_error); 491 } 492 493 void WebRequest::sslClientCert(EVP_PKEY* pkey, scoped_refptr<net::X509Certificate> chain) 494 { 495 base::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> privateKey(pkey); 496 if (privateKey.get() == NULL || chain.get() == NULL) { 497 m_request->ContinueWithCertificate(NULL); 498 return; 499 } 500 GURL gurl(m_url); 501 net::OpenSSLPrivateKeyStore::GetInstance()->StorePrivateKey(gurl, privateKey.release()); 502 m_request->ContinueWithCertificate(chain.release()); 503 } 504 505 void WebRequest::startReading() 506 { 507 ASSERT(m_networkBuffer == 0, "startReading called with a nonzero buffer"); 508 ASSERT(m_isPaused == 0, "startReading called in paused state"); 509 ASSERT(m_loadState == Response || m_loadState == GotData, "StartReading in state other than RESPONSE and GOTDATA"); 510 if (m_loadState > GotData) // We have been cancelled between reads 511 return; 512 513 if (m_wantToPause) { 514 m_isPaused = true; 515 return; 516 } 517 518 int bytesRead = 0; 519 520 if (!read(&bytesRead)) { 521 if (m_request && m_request->status().is_io_pending()) 522 return; // Wait for OnReadCompleted() 523 return finish(false); 524 } 525 526 // bytesRead == 0 indicates finished 527 if (!bytesRead) 528 return finish(true); 529 530 m_loadState = GotData; 531 // Read ok, forward buffer to webcore 532 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(m_urlLoader.get(), &WebUrlLoaderClient::didReceiveData, m_networkBuffer, bytesRead)); 533 m_networkBuffer = 0; 534 MessageLoop::current()->PostTask(FROM_HERE, m_runnableFactory.NewRunnableMethod(&WebRequest::startReading)); 535 } 536 537 bool WebRequest::read(int* bytesRead) 538 { 539 ASSERT(m_loadState == Response || m_loadState == GotData, "read in state other than RESPONSE and GOTDATA"); 540 ASSERT(m_networkBuffer == 0, "Read called with a nonzero buffer"); 541 542 // TODO: when asserts work, check that the buffer is 0 here 543 m_networkBuffer = new net::IOBuffer(kInitialReadBufSize); 544 return m_request->Read(m_networkBuffer, kInitialReadBufSize, bytesRead); 545 } 546 547 // This is called when there is data available 548 549 // Called when the a Read of the response body is completed after an 550 // IO_PENDING status from a Read() call. 551 // The data read is filled into the buffer which the caller passed 552 // to Read() previously. 553 // 554 // If an error occurred, request->status() will contain the error, 555 // and bytes read will be -1. 556 void WebRequest::OnReadCompleted(net::URLRequest* request, int bytesRead) 557 { 558 ASSERT(m_loadState == Response || m_loadState == GotData, "OnReadCompleted in state other than RESPONSE and GOTDATA"); 559 560 if (request->status().is_success()) { 561 m_loadState = GotData; 562 m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( 563 m_urlLoader.get(), &WebUrlLoaderClient::didReceiveData, m_networkBuffer, bytesRead)); 564 m_networkBuffer = 0; 565 566 // Get the rest of the data 567 startReading(); 568 } else { 569 finish(false); 570 } 571 } 572 573 } // namespace android 574