| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 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. | |
| 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/public/browser/browser_thread.h" | |
| 11 #include "content/public/common/content_client.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/cookies/cookie_monster.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_fetcher.h" | |
| 21 #include "net/url_request/url_request_context.h" | |
| 22 #include "net/url_request/url_request_status.h" | |
| 23 | |
| 24 using content::BrowserThread; | |
| 25 | |
| 26 namespace browser_sync { | |
| 27 | |
| 28 HttpBridge::RequestContextGetter::RequestContextGetter( | |
| 29 net::URLRequestContextGetter* baseline_context_getter) | |
| 30 : baseline_context_getter_(baseline_context_getter) { | |
| 31 } | |
| 32 | |
| 33 HttpBridge::RequestContextGetter::~RequestContextGetter() {} | |
| 34 | |
| 35 net::URLRequestContext* | |
| 36 HttpBridge::RequestContextGetter::GetURLRequestContext() { | |
| 37 // Lazily create the context. | |
| 38 if (!context_.get()) { | |
| 39 net::URLRequestContext* baseline_context = | |
| 40 baseline_context_getter_->GetURLRequestContext(); | |
| 41 context_.reset(new RequestContext(baseline_context)); | |
| 42 baseline_context_getter_ = NULL; | |
| 43 } | |
| 44 | |
| 45 // Apply the user agent which was set earlier. | |
| 46 if (is_user_agent_set()) | |
| 47 context_->set_user_agent(user_agent_); | |
| 48 | |
| 49 return context_.get(); | |
| 50 } | |
| 51 | |
| 52 scoped_refptr<base::SingleThreadTaskRunner> | |
| 53 HttpBridge::RequestContextGetter::GetNetworkTaskRunner() const { | |
| 54 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); | |
| 55 } | |
| 56 | |
| 57 HttpBridgeFactory::HttpBridgeFactory( | |
| 58 net::URLRequestContextGetter* baseline_context_getter) { | |
| 59 DCHECK(baseline_context_getter != NULL); | |
| 60 request_context_getter_ = | |
| 61 new HttpBridge::RequestContextGetter(baseline_context_getter); | |
| 62 } | |
| 63 | |
| 64 HttpBridgeFactory::~HttpBridgeFactory() { | |
| 65 } | |
| 66 | |
| 67 csync::HttpPostProviderInterface* HttpBridgeFactory::Create() { | |
| 68 HttpBridge* http = new HttpBridge(request_context_getter_); | |
| 69 http->AddRef(); | |
| 70 return http; | |
| 71 } | |
| 72 | |
| 73 void HttpBridgeFactory::Destroy(csync::HttpPostProviderInterface* http) { | |
| 74 static_cast<HttpBridge*>(http)->Release(); | |
| 75 } | |
| 76 | |
| 77 HttpBridge::RequestContext::RequestContext( | |
| 78 net::URLRequestContext* baseline_context) | |
| 79 : baseline_context_(baseline_context) { | |
| 80 | |
| 81 // Create empty, in-memory cookie store. | |
| 82 set_cookie_store(new net::CookieMonster(NULL, NULL)); | |
| 83 | |
| 84 // We don't use a cache for bridged loads, but we do want to share proxy info. | |
| 85 set_host_resolver(baseline_context->host_resolver()); | |
| 86 set_proxy_service(baseline_context->proxy_service()); | |
| 87 set_ssl_config_service(baseline_context->ssl_config_service()); | |
| 88 | |
| 89 // We want to share the HTTP session data with the network layer factory, | |
| 90 // which includes auth_cache for proxies. | |
| 91 // Session is not refcounted so we need to be careful to not lose the parent | |
| 92 // context. | |
| 93 net::HttpNetworkSession* session = | |
| 94 baseline_context->http_transaction_factory()->GetSession(); | |
| 95 DCHECK(session); | |
| 96 set_http_transaction_factory(new net::HttpNetworkLayer(session)); | |
| 97 | |
| 98 // TODO(timsteele): We don't currently listen for pref changes of these | |
| 99 // fields or CookiePolicy; I'm not sure we want to strictly follow the | |
| 100 // default settings, since for example if the user chooses to block all | |
| 101 // cookies, sync will start failing. Also it seems like accept_lang/charset | |
| 102 // should be tied to whatever the sync servers expect (if anything). These | |
| 103 // fields should probably just be settable by sync backend; though we should | |
| 104 // figure out if we need to give the user explicit control over policies etc. | |
| 105 set_accept_language(baseline_context->accept_language()); | |
| 106 set_accept_charset(baseline_context->accept_charset()); | |
| 107 | |
| 108 // We default to the browser's user agent. This can (and should) be overridden | |
| 109 // with set_user_agent. | |
| 110 set_user_agent(content::GetUserAgent(GURL())); | |
| 111 | |
| 112 set_net_log(baseline_context->net_log()); | |
| 113 } | |
| 114 | |
| 115 HttpBridge::RequestContext::~RequestContext() { | |
| 116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 117 delete http_transaction_factory(); | |
| 118 } | |
| 119 | |
| 120 HttpBridge::URLFetchState::URLFetchState() : url_poster(NULL), | |
| 121 aborted(false), | |
| 122 request_completed(false), | |
| 123 request_succeeded(false), | |
| 124 http_response_code(-1), | |
| 125 error_code(-1) {} | |
| 126 HttpBridge::URLFetchState::~URLFetchState() {} | |
| 127 | |
| 128 HttpBridge::HttpBridge(HttpBridge::RequestContextGetter* context_getter) | |
| 129 : context_getter_for_request_(context_getter), | |
| 130 created_on_loop_(MessageLoop::current()), | |
| 131 http_post_completed_(false, false) { | |
| 132 } | |
| 133 | |
| 134 HttpBridge::~HttpBridge() { | |
| 135 } | |
| 136 | |
| 137 void HttpBridge::SetUserAgent(const char* user_agent) { | |
| 138 DCHECK_EQ(MessageLoop::current(), created_on_loop_); | |
| 139 if (DCHECK_IS_ON()) { | |
| 140 base::AutoLock lock(fetch_state_lock_); | |
| 141 DCHECK(!fetch_state_.request_completed); | |
| 142 } | |
| 143 context_getter_for_request_->set_user_agent(user_agent); | |
| 144 } | |
| 145 | |
| 146 void HttpBridge::SetExtraRequestHeaders(const char * headers) { | |
| 147 DCHECK(extra_headers_.empty()) | |
| 148 << "HttpBridge::SetExtraRequestHeaders called twice."; | |
| 149 extra_headers_.assign(headers); | |
| 150 } | |
| 151 | |
| 152 void HttpBridge::SetURL(const char* url, int port) { | |
| 153 DCHECK_EQ(MessageLoop::current(), created_on_loop_); | |
| 154 if (DCHECK_IS_ON()) { | |
| 155 base::AutoLock lock(fetch_state_lock_); | |
| 156 DCHECK(!fetch_state_.request_completed); | |
| 157 } | |
| 158 DCHECK(url_for_request_.is_empty()) | |
| 159 << "HttpBridge::SetURL called more than once?!"; | |
| 160 GURL temp(url); | |
| 161 GURL::Replacements replacements; | |
| 162 std::string port_str = base::IntToString(port); | |
| 163 replacements.SetPort(port_str.c_str(), | |
| 164 url_parse::Component(0, port_str.length())); | |
| 165 url_for_request_ = temp.ReplaceComponents(replacements); | |
| 166 } | |
| 167 | |
| 168 void HttpBridge::SetPostPayload(const char* content_type, | |
| 169 int content_length, | |
| 170 const char* content) { | |
| 171 DCHECK_EQ(MessageLoop::current(), created_on_loop_); | |
| 172 if (DCHECK_IS_ON()) { | |
| 173 base::AutoLock lock(fetch_state_lock_); | |
| 174 DCHECK(!fetch_state_.request_completed); | |
| 175 } | |
| 176 DCHECK(content_type_.empty()) << "Bridge payload already set."; | |
| 177 DCHECK_GE(content_length, 0) << "Content length < 0"; | |
| 178 content_type_ = content_type; | |
| 179 if (!content || (content_length == 0)) { | |
| 180 DCHECK_EQ(content_length, 0); | |
| 181 request_content_ = " "; // TODO(timsteele): URLFetcher requires non-empty | |
| 182 // content for POSTs whereas CURL does not, for now | |
| 183 // we hack this to support the sync backend. | |
| 184 } else { | |
| 185 request_content_.assign(content, content_length); | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 bool HttpBridge::MakeSynchronousPost(int* error_code, int* response_code) { | |
| 190 DCHECK_EQ(MessageLoop::current(), created_on_loop_); | |
| 191 if (DCHECK_IS_ON()) { | |
| 192 base::AutoLock lock(fetch_state_lock_); | |
| 193 DCHECK(!fetch_state_.request_completed); | |
| 194 } | |
| 195 DCHECK(url_for_request_.is_valid()) << "Invalid URL for request"; | |
| 196 DCHECK(!content_type_.empty()) << "Payload not set"; | |
| 197 | |
| 198 if (!BrowserThread::PostTask( | |
| 199 BrowserThread::IO, FROM_HERE, | |
| 200 base::Bind(&HttpBridge::CallMakeAsynchronousPost, this))) { | |
| 201 // This usually happens when we're in a unit test. | |
| 202 LOG(WARNING) << "Could not post CallMakeAsynchronousPost task"; | |
| 203 return false; | |
| 204 } | |
| 205 | |
| 206 // Block until network request completes or is aborted. See | |
| 207 // OnURLFetchComplete and Abort. | |
| 208 http_post_completed_.Wait(); | |
| 209 | |
| 210 base::AutoLock lock(fetch_state_lock_); | |
| 211 DCHECK(fetch_state_.request_completed || fetch_state_.aborted); | |
| 212 *error_code = fetch_state_.error_code; | |
| 213 *response_code = fetch_state_.http_response_code; | |
| 214 return fetch_state_.request_succeeded; | |
| 215 } | |
| 216 | |
| 217 void HttpBridge::MakeAsynchronousPost() { | |
| 218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 219 base::AutoLock lock(fetch_state_lock_); | |
| 220 DCHECK(!fetch_state_.request_completed); | |
| 221 if (fetch_state_.aborted) | |
| 222 return; | |
| 223 | |
| 224 fetch_state_.url_poster = net::URLFetcher::Create( | |
| 225 url_for_request_, net::URLFetcher::POST, this); | |
| 226 fetch_state_.url_poster->SetRequestContext(context_getter_for_request_); | |
| 227 fetch_state_.url_poster->SetUploadData(content_type_, request_content_); | |
| 228 fetch_state_.url_poster->SetExtraRequestHeaders(extra_headers_); | |
| 229 fetch_state_.url_poster->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES); | |
| 230 fetch_state_.url_poster->Start(); | |
| 231 } | |
| 232 | |
| 233 int HttpBridge::GetResponseContentLength() const { | |
| 234 DCHECK_EQ(MessageLoop::current(), created_on_loop_); | |
| 235 base::AutoLock lock(fetch_state_lock_); | |
| 236 DCHECK(fetch_state_.request_completed); | |
| 237 return fetch_state_.response_content.size(); | |
| 238 } | |
| 239 | |
| 240 const char* HttpBridge::GetResponseContent() const { | |
| 241 DCHECK_EQ(MessageLoop::current(), created_on_loop_); | |
| 242 base::AutoLock lock(fetch_state_lock_); | |
| 243 DCHECK(fetch_state_.request_completed); | |
| 244 return fetch_state_.response_content.data(); | |
| 245 } | |
| 246 | |
| 247 const std::string HttpBridge::GetResponseHeaderValue( | |
| 248 const std::string& name) const { | |
| 249 | |
| 250 DCHECK_EQ(MessageLoop::current(), created_on_loop_); | |
| 251 base::AutoLock lock(fetch_state_lock_); | |
| 252 DCHECK(fetch_state_.request_completed); | |
| 253 | |
| 254 std::string value; | |
| 255 fetch_state_.response_headers->EnumerateHeader(NULL, name, &value); | |
| 256 return value; | |
| 257 } | |
| 258 | |
| 259 void HttpBridge::Abort() { | |
| 260 base::AutoLock lock(fetch_state_lock_); | |
| 261 DCHECK(!fetch_state_.aborted); | |
| 262 if (fetch_state_.aborted || fetch_state_.request_completed) | |
| 263 return; | |
| 264 | |
| 265 fetch_state_.aborted = true; | |
| 266 if (!BrowserThread::PostTask( | |
| 267 BrowserThread::IO, FROM_HERE, | |
| 268 base::Bind(&HttpBridge::DestroyURLFetcherOnIOThread, this, | |
| 269 fetch_state_.url_poster))) { | |
| 270 // Madness ensues. | |
| 271 NOTREACHED() << "Could not post task to delete URLFetcher"; | |
| 272 } | |
| 273 | |
| 274 fetch_state_.url_poster = NULL; | |
| 275 fetch_state_.error_code = net::ERR_ABORTED; | |
| 276 http_post_completed_.Signal(); | |
| 277 } | |
| 278 | |
| 279 void HttpBridge::DestroyURLFetcherOnIOThread(net::URLFetcher* fetcher) { | |
| 280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 281 delete fetcher; | |
| 282 } | |
| 283 | |
| 284 void HttpBridge::OnURLFetchComplete(const net::URLFetcher* source) { | |
| 285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 286 base::AutoLock lock(fetch_state_lock_); | |
| 287 if (fetch_state_.aborted) | |
| 288 return; | |
| 289 | |
| 290 fetch_state_.request_completed = true; | |
| 291 fetch_state_.request_succeeded = | |
| 292 (net::URLRequestStatus::SUCCESS == source->GetStatus().status()); | |
| 293 fetch_state_.http_response_code = source->GetResponseCode(); | |
| 294 fetch_state_.error_code = source->GetStatus().error(); | |
| 295 | |
| 296 // Use a real (non-debug) log to facilitate troubleshooting in the wild. | |
| 297 VLOG(2) << "HttpBridge::OnURLFetchComplete for: " | |
| 298 << fetch_state_.url_poster->GetURL().spec(); | |
| 299 VLOG(1) << "HttpBridge received response code: " | |
| 300 << fetch_state_.http_response_code; | |
| 301 | |
| 302 source->GetResponseAsString(&fetch_state_.response_content); | |
| 303 fetch_state_.response_headers = source->GetResponseHeaders(); | |
| 304 | |
| 305 // End of the line for url_poster_. It lives only on the IO loop. | |
| 306 // We defer deletion because we're inside a callback from a component of the | |
| 307 // URLFetcher, so it seems most natural / "polite" to let the stack unwind. | |
| 308 MessageLoop::current()->DeleteSoon(FROM_HERE, fetch_state_.url_poster); | |
| 309 fetch_state_.url_poster = NULL; | |
| 310 | |
| 311 // Wake the blocked syncer thread in MakeSynchronousPost. | |
| 312 // WARNING: DONT DO ANYTHING AFTER THIS CALL! |this| may be deleted! | |
| 313 http_post_completed_.Signal(); | |
| 314 } | |
| 315 | |
| 316 } // namespace browser_sync | |
| OLD | NEW |