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