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