1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/automation/url_request_automation_job.h" 6 7 #include "base/compiler_specific.h" 8 #include "base/message_loop.h" 9 #include "base/time.h" 10 #include "chrome/browser/automation/automation_resource_message_filter.h" 11 #include "chrome/common/automation_messages.h" 12 #include "content/browser/browser_thread.h" 13 #include "content/browser/renderer_host/render_view_host.h" 14 #include "content/browser/renderer_host/resource_dispatcher_host.h" 15 #include "content/browser/renderer_host/resource_dispatcher_host_request_info.h" 16 #include "net/base/cookie_monster.h" 17 #include "net/base/host_port_pair.h" 18 #include "net/base/io_buffer.h" 19 #include "net/base/net_errors.h" 20 #include "net/http/http_response_headers.h" 21 #include "net/http/http_request_headers.h" 22 #include "net/http/http_util.h" 23 #include "net/url_request/url_request_context.h" 24 25 using base::Time; 26 using base::TimeDelta; 27 28 // The list of filtered headers that are removed from requests sent via 29 // StartAsync(). These must be lower case. 30 static const char* const kFilteredHeaderStrings[] = { 31 "connection", 32 "cookie", 33 "expect", 34 "max-forwards", 35 "proxy-authorization", 36 "te", 37 "upgrade", 38 "via" 39 }; 40 41 int URLRequestAutomationJob::instance_count_ = 0; 42 bool URLRequestAutomationJob::is_protocol_factory_registered_ = false; 43 44 net::URLRequest::ProtocolFactory* URLRequestAutomationJob::old_http_factory_ 45 = NULL; 46 net::URLRequest::ProtocolFactory* URLRequestAutomationJob::old_https_factory_ 47 = NULL; 48 49 URLRequestAutomationJob::URLRequestAutomationJob( 50 net::URLRequest* request, 51 int tab, 52 int request_id, 53 AutomationResourceMessageFilter* filter, 54 bool is_pending) 55 : net::URLRequestJob(request), 56 id_(0), 57 tab_(tab), 58 message_filter_(filter), 59 pending_buf_size_(0), 60 redirect_status_(0), 61 request_id_(request_id), 62 is_pending_(is_pending), 63 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { 64 DVLOG(1) << "URLRequestAutomationJob create. Count: " << ++instance_count_; 65 DCHECK(message_filter_ != NULL); 66 67 if (message_filter_) { 68 id_ = message_filter_->NewAutomationRequestId(); 69 DCHECK_NE(id_, 0); 70 } 71 } 72 73 URLRequestAutomationJob::~URLRequestAutomationJob() { 74 DVLOG(1) << "URLRequestAutomationJob delete. Count: " << --instance_count_; 75 Cleanup(); 76 } 77 78 bool URLRequestAutomationJob::EnsureProtocolFactoryRegistered() { 79 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 80 81 if (!is_protocol_factory_registered_) { 82 old_http_factory_ = 83 net::URLRequest::RegisterProtocolFactory( 84 "http", &URLRequestAutomationJob::Factory); 85 old_https_factory_ = 86 net::URLRequest::RegisterProtocolFactory( 87 "https", &URLRequestAutomationJob::Factory); 88 is_protocol_factory_registered_ = true; 89 } 90 91 return true; 92 } 93 94 net::URLRequestJob* URLRequestAutomationJob::Factory( 95 net::URLRequest* request, 96 const std::string& scheme) { 97 bool scheme_is_http = request->url().SchemeIs("http"); 98 bool scheme_is_https = request->url().SchemeIs("https"); 99 100 // Returning null here just means that the built-in handler will be used. 101 if (scheme_is_http || scheme_is_https) { 102 ResourceDispatcherHostRequestInfo* request_info = NULL; 103 if (request->GetUserData(NULL)) 104 request_info = ResourceDispatcherHost::InfoForRequest(request); 105 if (request_info) { 106 int child_id = request_info->child_id(); 107 int route_id = request_info->route_id(); 108 AutomationResourceMessageFilter::AutomationDetails details; 109 if (AutomationResourceMessageFilter::LookupRegisteredRenderView( 110 child_id, route_id, &details)) { 111 URLRequestAutomationJob* job = new URLRequestAutomationJob(request, 112 details.tab_handle, request_info->request_id(), details.filter, 113 details.is_pending_render_view); 114 return job; 115 } 116 } 117 118 if (scheme_is_http && old_http_factory_) 119 return old_http_factory_(request, scheme); 120 else if (scheme_is_https && old_https_factory_) 121 return old_https_factory_(request, scheme); 122 } 123 return NULL; 124 } 125 126 // net::URLRequestJob Implementation. 127 void URLRequestAutomationJob::Start() { 128 if (!is_pending()) { 129 // Start reading asynchronously so that all error reporting and data 130 // callbacks happen as they would for network requests. 131 MessageLoop::current()->PostTask( 132 FROM_HERE, 133 method_factory_.NewRunnableMethod( 134 &URLRequestAutomationJob::StartAsync)); 135 } else { 136 // If this is a pending job, then register it immediately with the message 137 // filter so it can be serviced later when we receive a request from the 138 // external host to connect to the corresponding external tab. 139 message_filter_->RegisterRequest(this); 140 } 141 } 142 143 void URLRequestAutomationJob::Kill() { 144 if (message_filter_.get()) { 145 if (!is_pending()) { 146 message_filter_->Send(new AutomationMsg_RequestEnd(tab_, id_, 147 net::URLRequestStatus(net::URLRequestStatus::CANCELED, 148 net::ERR_ABORTED))); 149 } 150 } 151 DisconnectFromMessageFilter(); 152 net::URLRequestJob::Kill(); 153 } 154 155 bool URLRequestAutomationJob::ReadRawData( 156 net::IOBuffer* buf, int buf_size, int* bytes_read) { 157 DVLOG(1) << "URLRequestAutomationJob: " << request_->url().spec() 158 << " - read pending: " << buf_size; 159 160 // We should not receive a read request for a pending job. 161 DCHECK(!is_pending()); 162 163 pending_buf_ = buf; 164 pending_buf_size_ = buf_size; 165 166 if (message_filter_) { 167 message_filter_->Send(new AutomationMsg_RequestRead(tab_, id_, buf_size)); 168 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0)); 169 } else { 170 MessageLoop::current()->PostTask( 171 FROM_HERE, 172 method_factory_.NewRunnableMethod( 173 &URLRequestAutomationJob::NotifyJobCompletionTask)); 174 } 175 return false; 176 } 177 178 bool URLRequestAutomationJob::GetMimeType(std::string* mime_type) const { 179 if (!mime_type_.empty()) { 180 *mime_type = mime_type_; 181 } else if (headers_) { 182 headers_->GetMimeType(mime_type); 183 } 184 185 return (!mime_type->empty()); 186 } 187 188 bool URLRequestAutomationJob::GetCharset(std::string* charset) { 189 if (headers_) 190 return headers_->GetCharset(charset); 191 return false; 192 } 193 194 void URLRequestAutomationJob::GetResponseInfo(net::HttpResponseInfo* info) { 195 if (headers_) 196 info->headers = headers_; 197 if (request_->url().SchemeIsSecure()) { 198 // Make up a fake certificate for this response since we don't have 199 // access to the real SSL info. 200 const char* kCertIssuer = "Chrome Internal"; 201 const int kLifetimeDays = 100; 202 203 info->ssl_info.cert = 204 new net::X509Certificate(request_->url().GetWithEmptyPath().spec(), 205 kCertIssuer, 206 Time::Now(), 207 Time::Now() + 208 TimeDelta::FromDays(kLifetimeDays)); 209 info->ssl_info.cert_status = 0; 210 info->ssl_info.security_bits = -1; 211 } 212 } 213 214 int URLRequestAutomationJob::GetResponseCode() const { 215 if (headers_) 216 return headers_->response_code(); 217 218 static const int kDefaultResponseCode = 200; 219 return kDefaultResponseCode; 220 } 221 222 bool URLRequestAutomationJob::IsRedirectResponse( 223 GURL* location, int* http_status_code) { 224 if (!net::HttpResponseHeaders::IsRedirectResponseCode(redirect_status_)) 225 return false; 226 227 *http_status_code = redirect_status_; 228 *location = GURL(redirect_url_); 229 return true; 230 } 231 232 uint64 URLRequestAutomationJob::GetUploadProgress() const { 233 if (request_ && request_->status().is_success()) { 234 // We don't support incremental progress notifications in ChromeFrame. When 235 // we receive a response for the POST request from Chromeframe, it means 236 // that the upload is fully complete. 237 ResourceDispatcherHostRequestInfo* request_info = 238 ResourceDispatcherHost::InfoForRequest(request_); 239 if (request_info) { 240 return request_info->upload_size(); 241 } 242 } 243 return 0; 244 } 245 246 net::HostPortPair URLRequestAutomationJob::GetSocketAddress() const { 247 return socket_address_; 248 } 249 250 bool URLRequestAutomationJob::MayFilterMessage(const IPC::Message& message, 251 int* request_id) { 252 switch (message.type()) { 253 case AutomationMsg_RequestStarted::ID: 254 case AutomationMsg_RequestData::ID: 255 case AutomationMsg_RequestEnd::ID: { 256 void* iter = NULL; 257 if (message.ReadInt(&iter, request_id)) 258 return true; 259 break; 260 } 261 } 262 263 return false; 264 } 265 266 void URLRequestAutomationJob::OnMessage(const IPC::Message& message) { 267 if (!request_) { 268 NOTREACHED() << __FUNCTION__ 269 << ": Unexpected request received for job:" 270 << id(); 271 return; 272 } 273 274 IPC_BEGIN_MESSAGE_MAP(URLRequestAutomationJob, message) 275 IPC_MESSAGE_HANDLER(AutomationMsg_RequestStarted, OnRequestStarted) 276 IPC_MESSAGE_HANDLER(AutomationMsg_RequestData, OnDataAvailable) 277 IPC_MESSAGE_HANDLER(AutomationMsg_RequestEnd, OnRequestEnd) 278 IPC_END_MESSAGE_MAP() 279 } 280 281 void URLRequestAutomationJob::OnRequestStarted( 282 int id, const AutomationURLResponse& response) { 283 DVLOG(1) << "URLRequestAutomationJob: " << request_->url().spec() 284 << " - response started."; 285 set_expected_content_size(response.content_length); 286 mime_type_ = response.mime_type; 287 288 redirect_url_ = response.redirect_url; 289 redirect_status_ = response.redirect_status; 290 DCHECK(redirect_status_ == 0 || redirect_status_ == 200 || 291 (redirect_status_ >= 300 && redirect_status_ < 400)); 292 293 if (!response.headers.empty()) { 294 headers_ = new net::HttpResponseHeaders( 295 net::HttpUtil::AssembleRawHeaders(response.headers.data(), 296 response.headers.size())); 297 } 298 socket_address_ = response.socket_address; 299 NotifyHeadersComplete(); 300 } 301 302 void URLRequestAutomationJob::OnDataAvailable( 303 int id, const std::string& bytes) { 304 DVLOG(1) << "URLRequestAutomationJob: " << request_->url().spec() 305 << " - data available, Size: " << bytes.size(); 306 DCHECK(!bytes.empty()); 307 308 // The request completed, and we have all the data. 309 // Clear any IO pending status. 310 SetStatus(net::URLRequestStatus()); 311 312 if (pending_buf_ && pending_buf_->data()) { 313 DCHECK_GE(pending_buf_size_, bytes.size()); 314 const int bytes_to_copy = std::min(bytes.size(), pending_buf_size_); 315 memcpy(pending_buf_->data(), &bytes[0], bytes_to_copy); 316 317 pending_buf_ = NULL; 318 pending_buf_size_ = 0; 319 320 NotifyReadComplete(bytes_to_copy); 321 } else { 322 NOTREACHED() << "Received unexpected data of length:" << bytes.size(); 323 } 324 } 325 326 void URLRequestAutomationJob::OnRequestEnd( 327 int id, const net::URLRequestStatus& status) { 328 #ifndef NDEBUG 329 std::string url; 330 if (request_) 331 url = request_->url().spec(); 332 DVLOG(1) << "URLRequestAutomationJob: " << url << " - request end. Status: " 333 << status.status(); 334 #endif 335 336 // TODO(tommi): When we hit certificate errors, notify the delegate via 337 // OnSSLCertificateError(). Right now we don't have the certificate 338 // so we don't. We could possibly call OnSSLCertificateError with a NULL 339 // certificate, but I'm not sure if all implementations expect it. 340 // if (status.status() == net::URLRequestStatus::FAILED && 341 // net::IsCertificateError(status.os_error()) && request_->delegate()) { 342 // request_->delegate()->OnSSLCertificateError(request_, status.os_error()); 343 // } 344 345 DisconnectFromMessageFilter(); 346 // NotifyDone may have been called on the job if the original request was 347 // redirected. 348 if (!is_done()) { 349 // We can complete the job if we have a valid response or a pending read. 350 // An end request can be received in the following cases 351 // 1. We failed to connect to the server, in which case we did not receive 352 // a valid response. 353 // 2. In response to a read request. 354 if (!has_response_started() || pending_buf_) { 355 NotifyDone(status); 356 } else { 357 // Wait for the http stack to issue a Read request where we will notify 358 // that the job has completed. 359 request_status_ = status; 360 return; 361 } 362 } 363 364 // Reset any pending reads. 365 if (pending_buf_) { 366 pending_buf_ = NULL; 367 pending_buf_size_ = 0; 368 NotifyReadComplete(0); 369 } 370 } 371 372 void URLRequestAutomationJob::Cleanup() { 373 headers_ = NULL; 374 mime_type_.erase(); 375 376 id_ = 0; 377 tab_ = 0; 378 379 DCHECK(!message_filter_); 380 DisconnectFromMessageFilter(); 381 382 pending_buf_ = NULL; 383 pending_buf_size_ = 0; 384 } 385 386 void URLRequestAutomationJob::StartAsync() { 387 DVLOG(1) << "URLRequestAutomationJob: start request: " 388 << (request_ ? request_->url().spec() : "NULL request"); 389 390 // If the job is cancelled before we got a chance to start it 391 // we have nothing much to do here. 392 if (is_done()) 393 return; 394 395 // We should not receive a Start request for a pending job. 396 DCHECK(!is_pending()); 397 398 if (!request_) { 399 NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED, 400 net::ERR_FAILED)); 401 return; 402 } 403 404 // Register this request with automation message filter. 405 message_filter_->RegisterRequest(this); 406 407 // Strip unwanted headers. 408 net::HttpRequestHeaders new_request_headers; 409 new_request_headers.MergeFrom(request_->extra_request_headers()); 410 for (size_t i = 0; i < arraysize(kFilteredHeaderStrings); ++i) 411 new_request_headers.RemoveHeader(kFilteredHeaderStrings[i]); 412 413 if (request_->context()) { 414 // Only add default Accept-Language and Accept-Charset if the request 415 // didn't have them specified. 416 if (!new_request_headers.HasHeader( 417 net::HttpRequestHeaders::kAcceptLanguage) && 418 !request_->context()->accept_language().empty()) { 419 new_request_headers.SetHeader(net::HttpRequestHeaders::kAcceptLanguage, 420 request_->context()->accept_language()); 421 } 422 if (!new_request_headers.HasHeader( 423 net::HttpRequestHeaders::kAcceptCharset) && 424 !request_->context()->accept_charset().empty()) { 425 new_request_headers.SetHeader(net::HttpRequestHeaders::kAcceptCharset, 426 request_->context()->accept_charset()); 427 } 428 } 429 430 // Ensure that we do not send username and password fields in the referrer. 431 GURL referrer(request_->GetSanitizedReferrer()); 432 433 // The referrer header must be suppressed if the preceding URL was 434 // a secure one and the new one is not. 435 if (referrer.SchemeIsSecure() && !request_->url().SchemeIsSecure()) { 436 DVLOG(1) << "Suppressing referrer header since going from secure to " 437 "non-secure"; 438 referrer = GURL(); 439 } 440 441 // Get the resource type (main_frame/script/image/stylesheet etc. 442 ResourceDispatcherHostRequestInfo* request_info = 443 ResourceDispatcherHost::InfoForRequest(request_); 444 ResourceType::Type resource_type = ResourceType::MAIN_FRAME; 445 if (request_info) { 446 resource_type = request_info->resource_type(); 447 } 448 449 // Ask automation to start this request. 450 AutomationURLRequest automation_request( 451 request_->url().spec(), 452 request_->method(), 453 referrer.spec(), 454 new_request_headers.ToString(), 455 request_->get_upload(), 456 resource_type, 457 request_->load_flags()); 458 459 DCHECK(message_filter_); 460 message_filter_->Send(new AutomationMsg_RequestStart( 461 tab_, id_, automation_request)); 462 } 463 464 void URLRequestAutomationJob::DisconnectFromMessageFilter() { 465 if (message_filter_) { 466 message_filter_->UnRegisterRequest(this); 467 message_filter_ = NULL; 468 } 469 } 470 471 void URLRequestAutomationJob::StartPendingJob( 472 int new_tab_handle, 473 AutomationResourceMessageFilter* new_filter) { 474 DCHECK(new_filter != NULL); 475 tab_ = new_tab_handle; 476 message_filter_ = new_filter; 477 is_pending_ = false; 478 Start(); 479 } 480 481 void URLRequestAutomationJob::NotifyJobCompletionTask() { 482 if (!is_done()) { 483 NotifyDone(request_status_); 484 } 485 // Reset any pending reads. 486 if (pending_buf_) { 487 pending_buf_ = NULL; 488 pending_buf_size_ = 0; 489 NotifyReadComplete(0); 490 } 491 } 492