1 // Copyright 2012 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.
5 #include "sync/internal_api/public/http_bridge.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "net/base/load_flags.h"
11 #include "net/base/net_errors.h"
12 #include "net/cookies/cookie_monster.h"
13 #include "net/http/http_cache.h"
14 #include "net/http/http_network_layer.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/url_request/static_http_user_agent_settings.h"
17 #include "net/url_request/url_fetcher.h"
18 #include "net/url_request/url_request_context.h"
19 #include "net/url_request/url_request_job_factory_impl.h"
20 #include "net/url_request/url_request_status.h"
21 #include "sync/internal_api/public/base/cancelation_signal.h"
27 // It's possible for an http request to be silently stalled. We set a time
28 // limit for all http requests, beyond which the request is cancelled and
29 // treated as a transient failure.
30 const int kMaxHttpRequestTimeSeconds
= 60 * 5; // 5 minutes.
32 // Helper method for logging timeouts via UMA.
33 void LogTimeout(bool timed_out
) {
34 UMA_HISTOGRAM_BOOLEAN("Sync.URLFetchTimedOut", timed_out
);
39 HttpBridge::RequestContextGetter::RequestContextGetter(
40 net::URLRequestContextGetter
* baseline_context_getter
,
41 const std::string
& user_agent
)
42 : baseline_context_getter_(baseline_context_getter
),
44 baseline_context_getter_
->GetNetworkTaskRunner()),
45 user_agent_(user_agent
) {
46 DCHECK(baseline_context_getter_
.get());
47 DCHECK(network_task_runner_
.get());
48 DCHECK(!user_agent_
.empty());
51 HttpBridge::RequestContextGetter::~RequestContextGetter() {}
53 net::URLRequestContext
*
54 HttpBridge::RequestContextGetter::GetURLRequestContext() {
55 // Lazily create the context.
57 net::URLRequestContext
* baseline_context
=
58 baseline_context_getter_
->GetURLRequestContext();
60 new RequestContext(baseline_context
, GetNetworkTaskRunner(),
62 baseline_context_getter_
= NULL
;
65 return context_
.get();
68 scoped_refptr
<base::SingleThreadTaskRunner
>
69 HttpBridge::RequestContextGetter::GetNetworkTaskRunner() const {
70 return network_task_runner_
;
73 HttpBridgeFactory::HttpBridgeFactory(
74 const scoped_refptr
<net::URLRequestContextGetter
>& baseline_context_getter
,
75 const NetworkTimeUpdateCallback
& network_time_update_callback
,
76 CancelationSignal
* cancelation_signal
)
77 : baseline_request_context_getter_(baseline_context_getter
),
78 network_time_update_callback_(network_time_update_callback
),
79 cancelation_signal_(cancelation_signal
) {
80 // Registration should never fail. This should happen on the UI thread during
81 // init. It would be impossible for a shutdown to have been requested at this
83 bool result
= cancelation_signal_
->TryRegisterHandler(this);
87 HttpBridgeFactory::~HttpBridgeFactory() {
88 cancelation_signal_
->UnregisterHandler(this);
91 void HttpBridgeFactory::Init(const std::string
& user_agent
) {
92 base::AutoLock
lock(context_getter_lock_
);
94 if (!baseline_request_context_getter_
.get()) {
95 // Uh oh. We've been aborted before we finished initializing. There's no
96 // point in initializating further; let's just return right away.
100 request_context_getter_
= new HttpBridge::RequestContextGetter(
101 baseline_request_context_getter_
.get(), user_agent
);
104 HttpPostProviderInterface
* HttpBridgeFactory::Create() {
105 base::AutoLock
lock(context_getter_lock_
);
107 // If we've been asked to shut down (something which may happen asynchronously
108 // and at pretty much any time), then we won't have a request_context_getter_.
109 // Some external mechanism must ensure that this function is not called after
110 // we've been asked to shut down.
111 CHECK(request_context_getter_
.get());
113 HttpBridge
* http
= new HttpBridge(request_context_getter_
.get(),
114 network_time_update_callback_
);
119 void HttpBridgeFactory::Destroy(HttpPostProviderInterface
* http
) {
120 static_cast<HttpBridge
*>(http
)->Release();
123 void HttpBridgeFactory::OnSignalReceived() {
124 base::AutoLock
lock(context_getter_lock_
);
125 // Release |baseline_request_context_getter_| as soon as possible so that it
126 // is destroyed in the right order on its network task runner. The
127 // |request_context_getter_| has a reference to the baseline, so we must
128 // drop our reference to it, too.
129 baseline_request_context_getter_
= NULL
;
130 request_context_getter_
= NULL
;
133 HttpBridge::RequestContext::RequestContext(
134 net::URLRequestContext
* baseline_context
,
135 const scoped_refptr
<base::SingleThreadTaskRunner
>&
137 const std::string
& user_agent
)
138 : network_task_runner_(network_task_runner
),
139 job_factory_(new net::URLRequestJobFactoryImpl()) {
140 DCHECK(!user_agent
.empty());
142 // Create empty, in-memory cookie store.
143 set_cookie_store(new net::CookieMonster(NULL
, NULL
));
145 // We don't use a cache for bridged loads, but we do want to share proxy info.
146 set_host_resolver(baseline_context
->host_resolver());
147 set_proxy_service(baseline_context
->proxy_service());
148 set_ssl_config_service(baseline_context
->ssl_config_service());
150 // Use its own job factory, which only supports http and https.
151 set_job_factory(job_factory_
.get());
153 // We want to share the HTTP session data with the network layer factory,
154 // which includes auth_cache for proxies.
155 // Session is not refcounted so we need to be careful to not lose the parent
157 net::HttpNetworkSession
* session
=
158 baseline_context
->http_transaction_factory()->GetSession();
160 set_http_transaction_factory(new net::HttpNetworkLayer(session
));
162 // TODO(timsteele): We don't currently listen for pref changes of these
163 // fields or CookiePolicy; I'm not sure we want to strictly follow the
164 // default settings, since for example if the user chooses to block all
165 // cookies, sync will start failing. Also it seems like accept_lang/charset
166 // should be tied to whatever the sync servers expect (if anything). These
167 // fields should probably just be settable by sync backend; though we should
168 // figure out if we need to give the user explicit control over policies etc.
169 std::string accepted_language_list
;
170 if (baseline_context
->http_user_agent_settings()) {
171 accepted_language_list
=
172 baseline_context
->http_user_agent_settings()->GetAcceptLanguage();
174 http_user_agent_settings_
.reset(new net::StaticHttpUserAgentSettings(
175 accepted_language_list
,
177 set_http_user_agent_settings(http_user_agent_settings_
.get());
179 set_net_log(baseline_context
->net_log());
182 HttpBridge::RequestContext::~RequestContext() {
183 AssertNoURLRequests();
184 DCHECK(network_task_runner_
->BelongsToCurrentThread());
185 delete http_transaction_factory();
188 HttpBridge::URLFetchState::URLFetchState()
191 request_completed(false),
192 request_succeeded(false),
193 http_response_code(-1),
196 HttpBridge::URLFetchState::~URLFetchState() {}
198 HttpBridge::HttpBridge(
199 HttpBridge::RequestContextGetter
* context_getter
,
200 const NetworkTimeUpdateCallback
& network_time_update_callback
)
201 : created_on_loop_(base::MessageLoop::current()),
202 http_post_completed_(false, false),
203 context_getter_for_request_(context_getter
),
204 network_task_runner_(
205 context_getter_for_request_
->GetNetworkTaskRunner()),
206 network_time_update_callback_(network_time_update_callback
) {
209 HttpBridge::~HttpBridge() {
212 void HttpBridge::SetExtraRequestHeaders(const char * headers
) {
213 DCHECK(extra_headers_
.empty())
214 << "HttpBridge::SetExtraRequestHeaders called twice.";
215 extra_headers_
.assign(headers
);
218 void HttpBridge::SetURL(const char* url
, int port
) {
220 DCHECK_EQ(base::MessageLoop::current(), created_on_loop_
);
222 base::AutoLock
lock(fetch_state_lock_
);
223 DCHECK(!fetch_state_
.request_completed
);
225 DCHECK(url_for_request_
.is_empty())
226 << "HttpBridge::SetURL called more than once?!";
229 GURL::Replacements replacements
;
230 std::string port_str
= base::IntToString(port
);
231 replacements
.SetPort(port_str
.c_str(), url::Component(0, port_str
.length()));
232 url_for_request_
= temp
.ReplaceComponents(replacements
);
235 void HttpBridge::SetPostPayload(const char* content_type
,
237 const char* content
) {
239 DCHECK_EQ(base::MessageLoop::current(), created_on_loop_
);
241 base::AutoLock
lock(fetch_state_lock_
);
242 DCHECK(!fetch_state_
.request_completed
);
244 DCHECK(content_type_
.empty()) << "Bridge payload already set.";
245 DCHECK_GE(content_length
, 0) << "Content length < 0";
247 content_type_
= content_type
;
248 if (!content
|| (content_length
== 0)) {
249 DCHECK_EQ(content_length
, 0);
250 request_content_
= " "; // TODO(timsteele): URLFetcher requires non-empty
251 // content for POSTs whereas CURL does not, for now
252 // we hack this to support the sync backend.
254 request_content_
.assign(content
, content_length
);
258 bool HttpBridge::MakeSynchronousPost(int* error_code
, int* response_code
) {
260 DCHECK_EQ(base::MessageLoop::current(), created_on_loop_
);
262 base::AutoLock
lock(fetch_state_lock_
);
263 DCHECK(!fetch_state_
.request_completed
);
265 DCHECK(url_for_request_
.is_valid()) << "Invalid URL for request";
266 DCHECK(!content_type_
.empty()) << "Payload not set";
269 if (!network_task_runner_
->PostTask(
271 base::Bind(&HttpBridge::CallMakeAsynchronousPost
, this))) {
272 // This usually happens when we're in a unit test.
273 LOG(WARNING
) << "Could not post CallMakeAsynchronousPost task";
277 // Block until network request completes or is aborted. See
278 // OnURLFetchComplete and Abort.
279 http_post_completed_
.Wait();
281 base::AutoLock
lock(fetch_state_lock_
);
282 DCHECK(fetch_state_
.request_completed
|| fetch_state_
.aborted
);
283 *error_code
= fetch_state_
.error_code
;
284 *response_code
= fetch_state_
.http_response_code
;
285 return fetch_state_
.request_succeeded
;
288 void HttpBridge::MakeAsynchronousPost() {
289 DCHECK(network_task_runner_
->BelongsToCurrentThread());
291 base::AutoLock
lock(fetch_state_lock_
);
292 DCHECK(!fetch_state_
.request_completed
);
293 if (fetch_state_
.aborted
)
296 // Start the timer on the network thread (the same thread progress is made
297 // on, and on which the url fetcher lives).
298 DCHECK(!fetch_state_
.http_request_timeout_timer
.get());
299 fetch_state_
.http_request_timeout_timer
.reset(new base::Timer(false, false));
300 fetch_state_
.http_request_timeout_timer
->Start(
301 FROM_HERE
, base::TimeDelta::FromSeconds(kMaxHttpRequestTimeSeconds
),
302 base::Bind(&HttpBridge::OnURLFetchTimedOut
, this));
304 DCHECK(context_getter_for_request_
.get());
305 fetch_state_
.url_poster
= net::URLFetcher::Create(
306 url_for_request_
, net::URLFetcher::POST
, this);
307 fetch_state_
.url_poster
->SetRequestContext(context_getter_for_request_
.get());
308 fetch_state_
.url_poster
->SetUploadData(content_type_
, request_content_
);
309 fetch_state_
.url_poster
->SetExtraRequestHeaders(extra_headers_
);
310 fetch_state_
.url_poster
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
);
311 fetch_state_
.start_time
= base::Time::Now();
313 fetch_state_
.url_poster
->Start();
316 int HttpBridge::GetResponseContentLength() const {
317 DCHECK_EQ(base::MessageLoop::current(), created_on_loop_
);
318 base::AutoLock
lock(fetch_state_lock_
);
319 DCHECK(fetch_state_
.request_completed
);
320 return fetch_state_
.response_content
.size();
323 const char* HttpBridge::GetResponseContent() const {
324 DCHECK_EQ(base::MessageLoop::current(), created_on_loop_
);
325 base::AutoLock
lock(fetch_state_lock_
);
326 DCHECK(fetch_state_
.request_completed
);
327 return fetch_state_
.response_content
.data();
330 const std::string
HttpBridge::GetResponseHeaderValue(
331 const std::string
& name
) const {
333 DCHECK_EQ(base::MessageLoop::current(), created_on_loop_
);
334 base::AutoLock
lock(fetch_state_lock_
);
335 DCHECK(fetch_state_
.request_completed
);
338 fetch_state_
.response_headers
->EnumerateHeader(NULL
, name
, &value
);
342 void HttpBridge::Abort() {
343 base::AutoLock
lock(fetch_state_lock_
);
345 // Release |request_context_getter_| as soon as possible so that it is
346 // destroyed in the right order on its network task runner.
347 context_getter_for_request_
= NULL
;
349 DCHECK(!fetch_state_
.aborted
);
350 if (fetch_state_
.aborted
|| fetch_state_
.request_completed
)
353 fetch_state_
.aborted
= true;
354 if (!network_task_runner_
->PostTask(
356 base::Bind(&HttpBridge::DestroyURLFetcherOnIOThread
, this,
357 fetch_state_
.url_poster
,
358 fetch_state_
.http_request_timeout_timer
.release()))) {
360 NOTREACHED() << "Could not post task to delete URLFetcher";
363 fetch_state_
.url_poster
= NULL
;
364 fetch_state_
.error_code
= net::ERR_ABORTED
;
365 http_post_completed_
.Signal();
368 void HttpBridge::DestroyURLFetcherOnIOThread(
369 net::URLFetcher
* fetcher
,
370 base::Timer
* fetch_timer
) {
371 DCHECK(network_task_runner_
->BelongsToCurrentThread());
377 void HttpBridge::OnURLFetchComplete(const net::URLFetcher
* source
) {
378 DCHECK(network_task_runner_
->BelongsToCurrentThread());
380 base::AutoLock
lock(fetch_state_lock_
);
382 // Stop the request timer now that the request completed.
383 if (fetch_state_
.http_request_timeout_timer
.get())
384 fetch_state_
.http_request_timeout_timer
.reset();
386 if (fetch_state_
.aborted
)
389 fetch_state_
.end_time
= base::Time::Now();
390 fetch_state_
.request_completed
= true;
391 fetch_state_
.request_succeeded
=
392 (net::URLRequestStatus::SUCCESS
== source
->GetStatus().status());
393 fetch_state_
.http_response_code
= source
->GetResponseCode();
394 fetch_state_
.error_code
= source
->GetStatus().error();
396 if (fetch_state_
.request_succeeded
)
398 UMA_HISTOGRAM_LONG_TIMES("Sync.URLFetchTime",
399 fetch_state_
.end_time
- fetch_state_
.start_time
);
401 // Use a real (non-debug) log to facilitate troubleshooting in the wild.
402 VLOG(2) << "HttpBridge::OnURLFetchComplete for: "
403 << fetch_state_
.url_poster
->GetURL().spec();
404 VLOG(1) << "HttpBridge received response code: "
405 << fetch_state_
.http_response_code
;
407 source
->GetResponseAsString(&fetch_state_
.response_content
);
408 fetch_state_
.response_headers
= source
->GetResponseHeaders();
411 // End of the line for url_poster_. It lives only on the IO loop.
412 // We defer deletion because we're inside a callback from a component of the
413 // URLFetcher, so it seems most natural / "polite" to let the stack unwind.
414 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, fetch_state_
.url_poster
);
415 fetch_state_
.url_poster
= NULL
;
417 // Wake the blocked syncer thread in MakeSynchronousPost.
418 // WARNING: DONT DO ANYTHING AFTER THIS CALL! |this| may be deleted!
419 http_post_completed_
.Signal();
422 void HttpBridge::OnURLFetchDownloadProgress(const net::URLFetcher
* source
,
423 int64 current
, int64 total
) {
424 DCHECK(network_task_runner_
->BelongsToCurrentThread());
425 // Reset the delay when forward progress is made.
426 base::AutoLock
lock(fetch_state_lock_
);
427 if (fetch_state_
.http_request_timeout_timer
.get())
428 fetch_state_
.http_request_timeout_timer
->Reset();
431 void HttpBridge::OnURLFetchUploadProgress(const net::URLFetcher
* source
,
432 int64 current
, int64 total
) {
433 DCHECK(network_task_runner_
->BelongsToCurrentThread());
434 // Reset the delay when forward progress is made.
435 base::AutoLock
lock(fetch_state_lock_
);
436 if (fetch_state_
.http_request_timeout_timer
.get())
437 fetch_state_
.http_request_timeout_timer
->Reset();
440 void HttpBridge::OnURLFetchTimedOut() {
441 DCHECK(network_task_runner_
->BelongsToCurrentThread());
443 base::AutoLock
lock(fetch_state_lock_
);
444 if (!fetch_state_
.url_poster
)
448 DVLOG(1) << "Sync url fetch timed out. Canceling.";
450 fetch_state_
.end_time
= base::Time::Now();
451 fetch_state_
.request_completed
= true;
452 fetch_state_
.request_succeeded
= false;
453 fetch_state_
.http_response_code
= -1;
454 fetch_state_
.error_code
= net::URLRequestStatus::FAILED
;
456 // This method is called by the timer, not the url fetcher implementation,
457 // so it's safe to delete the fetcher here.
458 delete fetch_state_
.url_poster
;
459 fetch_state_
.url_poster
= NULL
;
461 // Timer is smart enough to handle being deleted as part of the invoked task.
462 fetch_state_
.http_request_timeout_timer
.reset();
464 // Wake the blocked syncer thread in MakeSynchronousPost.
465 // WARNING: DONT DO ANYTHING AFTER THIS CALL! |this| may be deleted!
466 http_post_completed_
.Signal();
469 net::URLRequestContextGetter
* HttpBridge::GetRequestContextGetterForTest()
471 base::AutoLock
lock(fetch_state_lock_
);
472 return context_getter_for_request_
.get();
475 void HttpBridge::UpdateNetworkTime() {
476 std::string sane_time_str
;
477 if (!fetch_state_
.request_succeeded
|| fetch_state_
.start_time
.is_null() ||
478 fetch_state_
.end_time
< fetch_state_
.start_time
||
479 !fetch_state_
.response_headers
||
480 !fetch_state_
.response_headers
->EnumerateHeader(NULL
, "Sane-Time-Millis",
485 int64 sane_time_ms
= 0;
486 if (base::StringToInt64(sane_time_str
, &sane_time_ms
)) {
487 network_time_update_callback_
.Run(
488 base::Time::FromJsTime(sane_time_ms
),
489 base::TimeDelta::FromMilliseconds(1),
490 fetch_state_
.end_time
- fetch_state_
.start_time
);
494 } // namespace syncer