• 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/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 
RequestContextGetter(net::URLRequestContextGetter * baseline_context_getter)25 HttpBridge::RequestContextGetter::RequestContextGetter(
26     net::URLRequestContextGetter* baseline_context_getter)
27     : baseline_context_getter_(baseline_context_getter) {
28 }
29 
30 net::URLRequestContext*
GetURLRequestContext()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>
GetIOMessageLoopProxy() const48 HttpBridge::RequestContextGetter::GetIOMessageLoopProxy() const {
49   return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
50 }
51 
HttpBridgeFactory(net::URLRequestContextGetter * baseline_context_getter)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 
~HttpBridgeFactory()59 HttpBridgeFactory::~HttpBridgeFactory() {
60 }
61 
Create()62 sync_api::HttpPostProviderInterface* HttpBridgeFactory::Create() {
63   HttpBridge* http = new HttpBridge(request_context_getter_);
64   http->AddRef();
65   return http;
66 }
67 
Destroy(sync_api::HttpPostProviderInterface * http)68 void HttpBridgeFactory::Destroy(sync_api::HttpPostProviderInterface* http) {
69   static_cast<HttpBridge*>(http)->Release();
70 }
71 
RequestContext(net::URLRequestContext * baseline_context)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 
~RequestContext()110 HttpBridge::RequestContext::~RequestContext() {
111   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
112   delete http_transaction_factory();
113 }
114 
URLFetchState()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) {}
~URLFetchState()121 HttpBridge::URLFetchState::~URLFetchState() {}
122 
HttpBridge(HttpBridge::RequestContextGetter * context_getter)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 
~HttpBridge()129 HttpBridge::~HttpBridge() {
130 }
131 
SetUserAgent(const char * user_agent)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 
SetExtraRequestHeaders(const char * headers)141 void HttpBridge::SetExtraRequestHeaders(const char * headers) {
142   DCHECK(extra_headers_.empty())
143       << "HttpBridge::SetExtraRequestHeaders called twice.";
144   extra_headers_.assign(headers);
145 }
146 
SetURL(const char * url,int port)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 
SetPostPayload(const char * content_type,int content_length,const char * content)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 
MakeSynchronousPost(int * os_error_code,int * response_code)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 
MakeAsynchronousPost()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 
GetResponseContentLength() const228 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 
GetResponseContent() const235 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 
GetResponseHeaderValue(const std::string & name) const242 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 
Abort()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 
OnURLFetchComplete(const URLFetcher * source,const GURL & url,const net::URLRequestStatus & status,int response_code,const ResponseCookies & cookies,const std::string & data)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