Home | History | Annotate | Download | only in glue
      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/sync/glue/http_bridge.h"
      6 
      7 #include "base/message_loop.h"
      8 #include "base/message_loop_proxy.h"
      9 #include "base/string_number_conversions.h"
     10 #include "content/browser/browser_thread.h"
     11 #include "net/base/cookie_monster.h"
     12 #include "net/base/host_resolver.h"
     13 #include "net/base/load_flags.h"
     14 #include "net/base/net_errors.h"
     15 #include "net/http/http_cache.h"
     16 #include "net/http/http_network_layer.h"
     17 #include "net/http/http_response_headers.h"
     18 #include "net/proxy/proxy_service.h"
     19 #include "net/url_request/url_request_context.h"
     20 #include "net/url_request/url_request_status.h"
     21 #include "webkit/glue/webkit_glue.h"
     22 
     23 namespace browser_sync {
     24 
     25 HttpBridge::RequestContextGetter::RequestContextGetter(
     26     net::URLRequestContextGetter* baseline_context_getter)
     27     : baseline_context_getter_(baseline_context_getter) {
     28 }
     29 
     30 net::URLRequestContext*
     31 HttpBridge::RequestContextGetter::GetURLRequestContext() {
     32   // Lazily create the context.
     33   if (!context_) {
     34     net::URLRequestContext* baseline_context =
     35         baseline_context_getter_->GetURLRequestContext();
     36     context_ = new RequestContext(baseline_context);
     37     baseline_context_getter_ = NULL;
     38   }
     39 
     40   // Apply the user agent which was set earlier.
     41   if (is_user_agent_set())
     42     context_->set_user_agent(user_agent_);
     43 
     44   return context_;
     45 }
     46 
     47 scoped_refptr<base::MessageLoopProxy>
     48 HttpBridge::RequestContextGetter::GetIOMessageLoopProxy() const {
     49   return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
     50 }
     51 
     52 HttpBridgeFactory::HttpBridgeFactory(
     53     net::URLRequestContextGetter* baseline_context_getter) {
     54   DCHECK(baseline_context_getter != NULL);
     55   request_context_getter_ =
     56       new HttpBridge::RequestContextGetter(baseline_context_getter);
     57 }
     58 
     59 HttpBridgeFactory::~HttpBridgeFactory() {
     60 }
     61 
     62 sync_api::HttpPostProviderInterface* HttpBridgeFactory::Create() {
     63   HttpBridge* http = new HttpBridge(request_context_getter_);
     64   http->AddRef();
     65   return http;
     66 }
     67 
     68 void HttpBridgeFactory::Destroy(sync_api::HttpPostProviderInterface* http) {
     69   static_cast<HttpBridge*>(http)->Release();
     70 }
     71 
     72 HttpBridge::RequestContext::RequestContext(
     73     net::URLRequestContext* baseline_context)
     74     : baseline_context_(baseline_context) {
     75 
     76   // Create empty, in-memory cookie store.
     77   set_cookie_store(new net::CookieMonster(NULL, NULL));
     78 
     79   // We don't use a cache for bridged loads, but we do want to share proxy info.
     80   set_host_resolver(baseline_context->host_resolver());
     81   set_proxy_service(baseline_context->proxy_service());
     82   set_ssl_config_service(baseline_context->ssl_config_service());
     83 
     84   // We want to share the HTTP session data with the network layer factory,
     85   // which includes auth_cache for proxies.
     86   // Session is not refcounted so we need to be careful to not lose the parent
     87   // context.
     88   net::HttpNetworkSession* session =
     89       baseline_context->http_transaction_factory()->GetSession();
     90   DCHECK(session);
     91   set_http_transaction_factory(new net::HttpNetworkLayer(session));
     92 
     93   // TODO(timsteele): We don't currently listen for pref changes of these
     94   // fields or CookiePolicy; I'm not sure we want to strictly follow the
     95   // default settings, since for example if the user chooses to block all
     96   // cookies, sync will start failing. Also it seems like accept_lang/charset
     97   // should be tied to whatever the sync servers expect (if anything). These
     98   // fields should probably just be settable by sync backend; though we should
     99   // figure out if we need to give the user explicit control over policies etc.
    100   set_accept_language(baseline_context->accept_language());
    101   set_accept_charset(baseline_context->accept_charset());
    102 
    103   // We default to the browser's user agent. This can (and should) be overridden
    104   // with set_user_agent.
    105   set_user_agent(webkit_glue::GetUserAgent(GURL()));
    106 
    107   set_net_log(baseline_context->net_log());
    108 }
    109 
    110 HttpBridge::RequestContext::~RequestContext() {
    111   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    112   delete http_transaction_factory();
    113 }
    114 
    115 HttpBridge::URLFetchState::URLFetchState() : url_poster(NULL),
    116                                              aborted(false),
    117                                              request_completed(false),
    118                                              request_succeeded(false),
    119                                              http_response_code(-1),
    120                                              os_error_code(-1) {}
    121 HttpBridge::URLFetchState::~URLFetchState() {}
    122 
    123 HttpBridge::HttpBridge(HttpBridge::RequestContextGetter* context_getter)
    124     : context_getter_for_request_(context_getter),
    125       created_on_loop_(MessageLoop::current()),
    126       http_post_completed_(false, false) {
    127 }
    128 
    129 HttpBridge::~HttpBridge() {
    130 }
    131 
    132 void HttpBridge::SetUserAgent(const char* user_agent) {
    133   DCHECK_EQ(MessageLoop::current(), created_on_loop_);
    134   if (DCHECK_IS_ON()) {
    135     base::AutoLock lock(fetch_state_lock_);
    136     DCHECK(!fetch_state_.request_completed);
    137   }
    138   context_getter_for_request_->set_user_agent(user_agent);
    139 }
    140 
    141 void HttpBridge::SetExtraRequestHeaders(const char * headers) {
    142   DCHECK(extra_headers_.empty())
    143       << "HttpBridge::SetExtraRequestHeaders called twice.";
    144   extra_headers_.assign(headers);
    145 }
    146 
    147 void HttpBridge::SetURL(const char* url, int port) {
    148   DCHECK_EQ(MessageLoop::current(), created_on_loop_);
    149   if (DCHECK_IS_ON()) {
    150     base::AutoLock lock(fetch_state_lock_);
    151     DCHECK(!fetch_state_.request_completed);
    152   }
    153   DCHECK(url_for_request_.is_empty())
    154       << "HttpBridge::SetURL called more than once?!";
    155   GURL temp(url);
    156   GURL::Replacements replacements;
    157   std::string port_str = base::IntToString(port);
    158   replacements.SetPort(port_str.c_str(),
    159                        url_parse::Component(0, port_str.length()));
    160   url_for_request_ = temp.ReplaceComponents(replacements);
    161 }
    162 
    163 void HttpBridge::SetPostPayload(const char* content_type,
    164                                 int content_length,
    165                                 const char* content) {
    166   DCHECK_EQ(MessageLoop::current(), created_on_loop_);
    167   if (DCHECK_IS_ON()) {
    168     base::AutoLock lock(fetch_state_lock_);
    169     DCHECK(!fetch_state_.request_completed);
    170   }
    171   DCHECK(content_type_.empty()) << "Bridge payload already set.";
    172   DCHECK_GE(content_length, 0) << "Content length < 0";
    173   content_type_ = content_type;
    174   if (!content || (content_length == 0)) {
    175     DCHECK_EQ(content_length, 0);
    176     request_content_ = " ";  // TODO(timsteele): URLFetcher requires non-empty
    177                              // content for POSTs whereas CURL does not, for now
    178                              // we hack this to support the sync backend.
    179   } else {
    180     request_content_.assign(content, content_length);
    181   }
    182 }
    183 
    184 bool HttpBridge::MakeSynchronousPost(int* os_error_code, int* response_code) {
    185   DCHECK_EQ(MessageLoop::current(), created_on_loop_);
    186   if (DCHECK_IS_ON()) {
    187     base::AutoLock lock(fetch_state_lock_);
    188     DCHECK(!fetch_state_.request_completed);
    189   }
    190   DCHECK(url_for_request_.is_valid()) << "Invalid URL for request";
    191   DCHECK(!content_type_.empty()) << "Payload not set";
    192 
    193   if (!BrowserThread::PostTask(
    194           BrowserThread::IO, FROM_HERE,
    195           NewRunnableMethod(this, &HttpBridge::CallMakeAsynchronousPost))) {
    196     // This usually happens when we're in a unit test.
    197     LOG(WARNING) << "Could not post CallMakeAsynchronousPost task";
    198     return false;
    199   }
    200 
    201   if (!http_post_completed_.Wait())  // Block until network request completes
    202     NOTREACHED();                    // or is aborted. See OnURLFetchComplete
    203                                      // and Abort.
    204 
    205   base::AutoLock lock(fetch_state_lock_);
    206   DCHECK(fetch_state_.request_completed || fetch_state_.aborted);
    207   *os_error_code = fetch_state_.os_error_code;
    208   *response_code = fetch_state_.http_response_code;
    209   return fetch_state_.request_succeeded;
    210 }
    211 
    212 void HttpBridge::MakeAsynchronousPost() {
    213   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    214   base::AutoLock lock(fetch_state_lock_);
    215   DCHECK(!fetch_state_.request_completed);
    216   if (fetch_state_.aborted)
    217     return;
    218 
    219   fetch_state_.url_poster = new URLFetcher(url_for_request_,
    220                                            URLFetcher::POST, this);
    221   fetch_state_.url_poster->set_request_context(context_getter_for_request_);
    222   fetch_state_.url_poster->set_upload_data(content_type_, request_content_);
    223   fetch_state_.url_poster->set_extra_request_headers(extra_headers_);
    224   fetch_state_.url_poster->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES);
    225   fetch_state_.url_poster->Start();
    226 }
    227 
    228 int HttpBridge::GetResponseContentLength() const {
    229   DCHECK_EQ(MessageLoop::current(), created_on_loop_);
    230   base::AutoLock lock(fetch_state_lock_);
    231   DCHECK(fetch_state_.request_completed);
    232   return fetch_state_.response_content.size();
    233 }
    234 
    235 const char* HttpBridge::GetResponseContent() const {
    236   DCHECK_EQ(MessageLoop::current(), created_on_loop_);
    237   base::AutoLock lock(fetch_state_lock_);
    238   DCHECK(fetch_state_.request_completed);
    239   return fetch_state_.response_content.data();
    240 }
    241 
    242 const std::string HttpBridge::GetResponseHeaderValue(
    243     const std::string& name) const {
    244 
    245   DCHECK_EQ(MessageLoop::current(), created_on_loop_);
    246   base::AutoLock lock(fetch_state_lock_);
    247   DCHECK(fetch_state_.request_completed);
    248 
    249   std::string value;
    250   fetch_state_.response_headers->EnumerateHeader(NULL, name, &value);
    251   return value;
    252 }
    253 
    254 void HttpBridge::Abort() {
    255   base::AutoLock lock(fetch_state_lock_);
    256   DCHECK(!fetch_state_.aborted);
    257   if (fetch_state_.aborted || fetch_state_.request_completed)
    258     return;
    259 
    260   fetch_state_.aborted = true;
    261   BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE,
    262                             fetch_state_.url_poster);
    263   fetch_state_.url_poster = NULL;
    264   fetch_state_.os_error_code = net::ERR_ABORTED;
    265   http_post_completed_.Signal();
    266 }
    267 
    268 void HttpBridge::OnURLFetchComplete(const URLFetcher *source,
    269                                     const GURL &url,
    270                                     const net::URLRequestStatus &status,
    271                                     int response_code,
    272                                     const ResponseCookies &cookies,
    273                                     const std::string &data) {
    274   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    275   base::AutoLock lock(fetch_state_lock_);
    276   if (fetch_state_.aborted)
    277     return;
    278 
    279   fetch_state_.request_completed = true;
    280   fetch_state_.request_succeeded =
    281       (net::URLRequestStatus::SUCCESS == status.status());
    282   fetch_state_.http_response_code = response_code;
    283   fetch_state_.os_error_code = status.os_error();
    284 
    285   fetch_state_.response_content = data;
    286   fetch_state_.response_headers = source->response_headers();
    287 
    288   // End of the line for url_poster_. It lives only on the IO loop.
    289   // We defer deletion because we're inside a callback from a component of the
    290   // URLFetcher, so it seems most natural / "polite" to let the stack unwind.
    291   MessageLoop::current()->DeleteSoon(FROM_HERE, fetch_state_.url_poster);
    292   fetch_state_.url_poster = NULL;
    293 
    294   // Wake the blocked syncer thread in MakeSynchronousPost.
    295   // WARNING: DONT DO ANYTHING AFTER THIS CALL! |this| may be deleted!
    296   http_post_completed_.Signal();
    297 }
    298 
    299 }  // namespace browser_sync
    300