| Index: chrome/browser/sync/glue/http_bridge.cc
 | 
| ===================================================================
 | 
| --- chrome/browser/sync/glue/http_bridge.cc	(revision 0)
 | 
| +++ chrome/browser/sync/glue/http_bridge.cc	(revision 0)
 | 
| @@ -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
 | 
| 
 | 
| Property changes on: chrome\browser\sync\glue\http_bridge.cc
 | 
| ___________________________________________________________________
 | 
| Added: svn:eol-style
 | 
|    + LF
 | 
| 
 | 
| 
 |