diff options
Diffstat (limited to 'chrome/browser/sync/glue/http_bridge.cc')
-rw-r--r-- | chrome/browser/sync/glue/http_bridge.cc | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/chrome/browser/sync/glue/http_bridge.cc b/chrome/browser/sync/glue/http_bridge.cc new file mode 100644 index 0000000..1a01c87 --- /dev/null +++ b/chrome/browser/sync/glue/http_bridge.cc @@ -0,0 +1,252 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifdef CHROME_PERSONALIZATION + +#include "chrome/browser/sync/glue/http_bridge.h" + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/profile.h" +#include "net/base/cookie_monster.h" +#include "net/base/load_flags.h" +#include "net/http/http_network_layer.h" +#include "net/proxy/proxy_service.h" +#include "net/url_request/url_request_status.h" +#include "webkit/glue/webkit_glue.h" + +namespace browser_sync { + +HttpBridge::RequestContext* HttpBridgeFactory::GetRequestContext() { + if (!request_context_) { + request_context_ = + new HttpBridge::RequestContext(Profile::GetDefaultRequestContext()); + request_context_->AddRef(); + } + return request_context_; +} + +HttpBridgeFactory::~HttpBridgeFactory() { + if (request_context_) { + // Clean up request context on IO thread. + ChromeThread::GetMessageLoop(ChromeThread::IO)->ReleaseSoon(FROM_HERE, + request_context_); + request_context_ = NULL; + } +} + +sync_api::HttpPostProviderInterface* HttpBridgeFactory::Create() { + // TODO(timsteele): We want the active profile request context. + HttpBridge* http = new HttpBridge(GetRequestContext(), + ChromeThread::GetMessageLoop(ChromeThread::IO)); + http->AddRef(); + return http; +} + +void HttpBridgeFactory::Destroy(sync_api::HttpPostProviderInterface* http) { + static_cast<HttpBridge*>(http)->Release(); +} + +HttpBridge::RequestContext::RequestContext( + const URLRequestContext* baseline_context) { + + // Create empty, in-memory cookie store. + cookie_store_ = new net::CookieMonster(); + + // We don't use a cache for bridged loads, but we do want to share proxy info. + host_resolver_ = baseline_context->host_resolver(); + proxy_service_ = baseline_context->proxy_service(); + http_transaction_factory_ = + net::HttpNetworkLayer::CreateFactory(host_resolver_, proxy_service_); + + // TODO(timsteele): We don't currently listen for pref changes of these + // fields or CookiePolicy; I'm not sure we want to strictly follow the + // default settings, since for example if the user chooses to block all + // cookies, sync will start failing. Also it seems like accept_lang/charset + // should be tied to whatever the sync servers expect (if anything). These + // fields should probably just be settable by sync backend; though we should + // figure out if we need to give the user explicit control over policies etc. + accept_language_ = baseline_context->accept_language(); + accept_charset_ = baseline_context->accept_charset(); + + // We default to the browser's user agent. This can (and should) be overridden + // with set_user_agent. + user_agent_ = webkit_glue::GetUserAgent(GURL()); +} + +HttpBridge::RequestContext::~RequestContext() { + delete cookie_store_; + delete http_transaction_factory_; +} + +HttpBridge::HttpBridge(HttpBridge::RequestContext* context, + MessageLoop* io_loop) + : context_for_request_(context), + url_poster_(NULL), + created_on_loop_(MessageLoop::current()), + io_loop_(io_loop), + request_completed_(false), + request_succeeded_(false), + http_response_code_(-1), + http_post_completed_(false, false), + use_io_loop_for_testing_(false) { + context_for_request_->AddRef(); +} + +HttpBridge::~HttpBridge() { + io_loop_->ReleaseSoon(FROM_HERE, context_for_request_); +} + +void HttpBridge::SetUserAgent(const char* user_agent) { + DCHECK_EQ(MessageLoop::current(), created_on_loop_); + DCHECK(!request_completed_); + context_for_request_->set_user_agent(user_agent); +} + +void HttpBridge::SetURL(const char* url, int port) { + DCHECK_EQ(MessageLoop::current(), created_on_loop_); + DCHECK(!request_completed_); + DCHECK(url_for_request_.is_empty()) + << "HttpBridge::SetURL called more than once?!"; + GURL temp(url); + GURL::Replacements replacements; + std::string port_str = IntToString(port); + replacements.SetPort(port_str.c_str(), + url_parse::Component(0, port_str.length())); + url_for_request_ = temp.ReplaceComponents(replacements); +} + +void HttpBridge::SetPostPayload(const char* content_type, + int content_length, + const char* content) { + DCHECK_EQ(MessageLoop::current(), created_on_loop_); + DCHECK(!request_completed_); + DCHECK(content_type_.empty()) << "Bridge payload already set."; + DCHECK_GE(content_length, 0) << "Content length < 0"; + content_type_ = content_type; + if (!content || (content_length == 0)) { + DCHECK_EQ(content_length, 0); + request_content_ = " "; // TODO(timsteele): URLFetcher requires non-empty + // content for POSTs whereas CURL does not, for now + // we hack this to support the sync backend. + } else { + request_content_.assign(content, content_length); + } +} + +void HttpBridge::AddCookieForRequest(const char* cookie) { + DCHECK_EQ(MessageLoop::current(), created_on_loop_); + DCHECK(!request_completed_); + DCHECK(url_for_request_.is_valid()) << "Valid URL not set."; + if (!url_for_request_.is_valid()) return; + + if (!context_for_request_->cookie_store()->SetCookie(url_for_request_, + cookie)) { + DLOG(WARNING) << "Cookie " << cookie + << " could not be added for url: " << url_for_request_ << "."; + } +} + +bool HttpBridge::MakeSynchronousPost(int* os_error_code, int* response_code) { + DCHECK_EQ(MessageLoop::current(), created_on_loop_); + DCHECK(!request_completed_); + DCHECK(url_for_request_.is_valid()) << "Invalid URL for request"; + DCHECK(!content_type_.empty()) << "Payload not set"; + DCHECK(context_for_request_->is_user_agent_set()) << "User agent not set"; + + io_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, + &HttpBridge::CallMakeAsynchronousPost)); + + if (!http_post_completed_.Wait()) // Block until network request completes. + NOTREACHED(); // See OnURLFetchComplete. + + DCHECK(request_completed_); + *os_error_code = os_error_code_; + *response_code = http_response_code_; + return request_succeeded_; +} + +void HttpBridge::MakeAsynchronousPost() { + DCHECK_EQ(MessageLoop::current(), io_loop_); + DCHECK(!request_completed_); + + url_poster_ = new URLFetcher(url_for_request_, URLFetcher::POST, this); + url_poster_->set_request_context(context_for_request_); + url_poster_->set_upload_data(content_type_, request_content_); + + if (use_io_loop_for_testing_) + url_poster_->set_io_loop(io_loop_); + + url_poster_->Start(); +} + +int HttpBridge::GetResponseContentLength() const { + DCHECK_EQ(MessageLoop::current(), created_on_loop_); + DCHECK(request_completed_); + return response_content_.size(); +} + +const char* HttpBridge::GetResponseContent() const { + DCHECK_EQ(MessageLoop::current(), created_on_loop_); + DCHECK(request_completed_); + return response_content_.c_str(); +} + +int HttpBridge::GetResponseCookieCount() const { + DCHECK_EQ(MessageLoop::current(), created_on_loop_); + DCHECK(request_completed_); + return response_cookies_.size(); +} + +const char* HttpBridge::GetResponseCookieAt(int cookie_number) const { + DCHECK_EQ(MessageLoop::current(), created_on_loop_); + DCHECK(request_completed_); + bool valid_number = (cookie_number >= 0) && + (static_cast<size_t>(cookie_number) < response_cookies_.size()); + DCHECK(valid_number); + if (!valid_number) + return NULL; + return response_cookies_[cookie_number].c_str(); +} + +void HttpBridge::OnURLFetchComplete(const URLFetcher *source, const GURL &url, + const URLRequestStatus &status, + int response_code, + const ResponseCookies &cookies, + const std::string &data) { + DCHECK_EQ(MessageLoop::current(), io_loop_); + + request_completed_ = true; + request_succeeded_ = (URLRequestStatus::SUCCESS == status.status()); + http_response_code_ = response_code; + os_error_code_ = status.os_error(); + + // TODO(timsteele): For now we need this "fixup" to match up with what the + // sync backend expects. This seems to be non-standard and shouldn't be done + // here in HttpBridge, and it breaks part of the unittest. + for (size_t i = 0; i < cookies.size(); ++i) { + net::CookieMonster::ParsedCookie parsed_cookie(cookies[i]); + std::string cookie = " \t \t \t \t \t"; + cookie += parsed_cookie.Name() + "\t"; + cookie += parsed_cookie.Value(); + response_cookies_.push_back(cookie); + } + + response_content_ = data; + + // End of the line for url_poster_. It lives only on the io_loop. + // We defer deletion because we're inside a callback from a component of the + // URLFetcher, so it seems most natural / "polite" to let the stack unwind. + io_loop_->DeleteSoon(FROM_HERE, url_poster_); + url_poster_ = NULL; + + // Wake the blocked syncer thread in MakeSynchronousPost. + // WARNING: DONT DO ANYTHING AFTER THIS CALL! |this| may be deleted! + http_post_completed_.Signal(); +} + +} // namespace browser_sync + +#endif // CHROME_PERSONALIZATION
\ No newline at end of file |