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