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
23namespace browser_sync {
24
25HttpBridge::RequestContextGetter::RequestContextGetter(
26    net::URLRequestContextGetter* baseline_context_getter)
27    : baseline_context_getter_(baseline_context_getter) {
28}
29
30net::URLRequestContext*
31HttpBridge::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
47scoped_refptr<base::MessageLoopProxy>
48HttpBridge::RequestContextGetter::GetIOMessageLoopProxy() const {
49  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
50}
51
52HttpBridgeFactory::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
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(
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
110HttpBridge::RequestContext::~RequestContext() {
111  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
112  delete http_transaction_factory();
113}
114
115HttpBridge::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) {}
121HttpBridge::URLFetchState::~URLFetchState() {}
122
123HttpBridge::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
129HttpBridge::~HttpBridge() {
130}
131
132void 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
141void HttpBridge::SetExtraRequestHeaders(const char * headers) {
142  DCHECK(extra_headers_.empty())
143      << "HttpBridge::SetExtraRequestHeaders called twice.";
144  extra_headers_.assign(headers);
145}
146
147void 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
163void 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
184bool 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
212void 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
228int 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
235const 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
242const 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
254void 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
268void 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