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