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