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