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