• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
URLRequestAutomationJob(net::URLRequest * request,int tab,int request_id,AutomationResourceMessageFilter * filter,bool is_pending)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 
~URLRequestAutomationJob()73 URLRequestAutomationJob::~URLRequestAutomationJob() {
74   DVLOG(1) << "URLRequestAutomationJob delete. Count: " << --instance_count_;
75   Cleanup();
76 }
77 
EnsureProtocolFactoryRegistered()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 
Factory(net::URLRequest * request,const std::string & scheme)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.
Start()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 
Kill()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 
ReadRawData(net::IOBuffer * buf,int buf_size,int * bytes_read)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 
GetMimeType(std::string * mime_type) const178 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 
GetCharset(std::string * charset)188 bool URLRequestAutomationJob::GetCharset(std::string* charset) {
189   if (headers_)
190     return headers_->GetCharset(charset);
191   return false;
192 }
193 
GetResponseInfo(net::HttpResponseInfo * info)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 
GetResponseCode() const214 int URLRequestAutomationJob::GetResponseCode() const {
215   if (headers_)
216     return headers_->response_code();
217 
218   static const int kDefaultResponseCode = 200;
219   return kDefaultResponseCode;
220 }
221 
IsRedirectResponse(GURL * location,int * http_status_code)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 
GetUploadProgress() const232 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 
GetSocketAddress() const246 net::HostPortPair URLRequestAutomationJob::GetSocketAddress() const {
247   return socket_address_;
248 }
249 
MayFilterMessage(const IPC::Message & message,int * request_id)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 
OnMessage(const IPC::Message & message)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 
OnRequestStarted(int id,const AutomationURLResponse & response)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 
OnDataAvailable(int id,const std::string & bytes)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 
OnRequestEnd(int id,const net::URLRequestStatus & status)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 
Cleanup()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 
StartAsync()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 
DisconnectFromMessageFilter()464 void URLRequestAutomationJob::DisconnectFromMessageFilter() {
465   if (message_filter_) {
466     message_filter_->UnRegisterRequest(this);
467     message_filter_ = NULL;
468   }
469 }
470 
StartPendingJob(int new_tab_handle,AutomationResourceMessageFilter * new_filter)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 
NotifyJobCompletionTask()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