OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2006-2008 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 #ifdef CHROME_PERSONALIZATION |
| 6 |
| 7 #include "chrome/browser/sync/glue/http_bridge.h" |
| 8 |
| 9 #include "base/message_loop.h" |
| 10 #include "base/string_util.h" |
| 11 #include "chrome/browser/chrome_thread.h" |
| 12 #include "chrome/browser/profile.h" |
| 13 #include "net/base/cookie_monster.h" |
| 14 #include "net/base/load_flags.h" |
| 15 #include "net/http/http_network_layer.h" |
| 16 #include "net/proxy/proxy_service.h" |
| 17 #include "net/url_request/url_request_status.h" |
| 18 #include "webkit/glue/webkit_glue.h" |
| 19 |
| 20 namespace browser_sync { |
| 21 |
| 22 HttpBridge::RequestContext* HttpBridgeFactory::GetRequestContext() { |
| 23 if (!request_context_) { |
| 24 request_context_ = |
| 25 new HttpBridge::RequestContext(Profile::GetDefaultRequestContext()); |
| 26 request_context_->AddRef(); |
| 27 } |
| 28 return request_context_; |
| 29 } |
| 30 |
| 31 HttpBridgeFactory::~HttpBridgeFactory() { |
| 32 if (request_context_) { |
| 33 // Clean up request context on IO thread. |
| 34 ChromeThread::GetMessageLoop(ChromeThread::IO)->ReleaseSoon(FROM_HERE, |
| 35 request_context_); |
| 36 request_context_ = NULL; |
| 37 } |
| 38 } |
| 39 |
| 40 sync_api::HttpPostProviderInterface* HttpBridgeFactory::Create() { |
| 41 // TODO(timsteele): We want the active profile request context. |
| 42 HttpBridge* http = new HttpBridge(GetRequestContext(), |
| 43 ChromeThread::GetMessageLoop(ChromeThread::IO)); |
| 44 http->AddRef(); |
| 45 return http; |
| 46 } |
| 47 |
| 48 void HttpBridgeFactory::Destroy(sync_api::HttpPostProviderInterface* http) { |
| 49 static_cast<HttpBridge*>(http)->Release(); |
| 50 } |
| 51 |
| 52 HttpBridge::RequestContext::RequestContext( |
| 53 const URLRequestContext* baseline_context) { |
| 54 |
| 55 // Create empty, in-memory cookie store. |
| 56 cookie_store_ = new net::CookieMonster(); |
| 57 |
| 58 // We don't use a cache for bridged loads, but we do want to share proxy info. |
| 59 host_resolver_ = baseline_context->host_resolver(); |
| 60 proxy_service_ = baseline_context->proxy_service(); |
| 61 http_transaction_factory_ = |
| 62 net::HttpNetworkLayer::CreateFactory(host_resolver_, proxy_service_); |
| 63 |
| 64 // TODO(timsteele): We don't currently listen for pref changes of these |
| 65 // fields or CookiePolicy; I'm not sure we want to strictly follow the |
| 66 // default settings, since for example if the user chooses to block all |
| 67 // cookies, sync will start failing. Also it seems like accept_lang/charset |
| 68 // should be tied to whatever the sync servers expect (if anything). These |
| 69 // fields should probably just be settable by sync backend; though we should |
| 70 // figure out if we need to give the user explicit control over policies etc. |
| 71 accept_language_ = baseline_context->accept_language(); |
| 72 accept_charset_ = baseline_context->accept_charset(); |
| 73 |
| 74 // We default to the browser's user agent. This can (and should) be overridden |
| 75 // with set_user_agent. |
| 76 user_agent_ = webkit_glue::GetUserAgent(GURL()); |
| 77 } |
| 78 |
| 79 HttpBridge::RequestContext::~RequestContext() { |
| 80 delete cookie_store_; |
| 81 delete http_transaction_factory_; |
| 82 } |
| 83 |
| 84 HttpBridge::HttpBridge(HttpBridge::RequestContext* context, |
| 85 MessageLoop* io_loop) |
| 86 : context_for_request_(context), |
| 87 url_poster_(NULL), |
| 88 created_on_loop_(MessageLoop::current()), |
| 89 io_loop_(io_loop), |
| 90 request_completed_(false), |
| 91 request_succeeded_(false), |
| 92 http_response_code_(-1), |
| 93 http_post_completed_(false, false), |
| 94 use_io_loop_for_testing_(false) { |
| 95 context_for_request_->AddRef(); |
| 96 } |
| 97 |
| 98 HttpBridge::~HttpBridge() { |
| 99 io_loop_->ReleaseSoon(FROM_HERE, context_for_request_); |
| 100 } |
| 101 |
| 102 void HttpBridge::SetUserAgent(const char* user_agent) { |
| 103 DCHECK_EQ(MessageLoop::current(), created_on_loop_); |
| 104 DCHECK(!request_completed_); |
| 105 context_for_request_->set_user_agent(user_agent); |
| 106 } |
| 107 |
| 108 void HttpBridge::SetURL(const char* url, int port) { |
| 109 DCHECK_EQ(MessageLoop::current(), created_on_loop_); |
| 110 DCHECK(!request_completed_); |
| 111 DCHECK(url_for_request_.is_empty()) |
| 112 << "HttpBridge::SetURL called more than once?!"; |
| 113 GURL temp(url); |
| 114 GURL::Replacements replacements; |
| 115 std::string port_str = IntToString(port); |
| 116 replacements.SetPort(port_str.c_str(), |
| 117 url_parse::Component(0, port_str.length())); |
| 118 url_for_request_ = temp.ReplaceComponents(replacements); |
| 119 } |
| 120 |
| 121 void HttpBridge::SetPostPayload(const char* content_type, |
| 122 int content_length, |
| 123 const char* content) { |
| 124 DCHECK_EQ(MessageLoop::current(), created_on_loop_); |
| 125 DCHECK(!request_completed_); |
| 126 DCHECK(content_type_.empty()) << "Bridge payload already set."; |
| 127 DCHECK_GE(content_length, 0) << "Content length < 0"; |
| 128 content_type_ = content_type; |
| 129 if (!content || (content_length == 0)) { |
| 130 DCHECK_EQ(content_length, 0); |
| 131 request_content_ = " "; // TODO(timsteele): URLFetcher requires non-empty |
| 132 // content for POSTs whereas CURL does not, for now |
| 133 // we hack this to support the sync backend. |
| 134 } else { |
| 135 request_content_.assign(content, content_length); |
| 136 } |
| 137 } |
| 138 |
| 139 void HttpBridge::AddCookieForRequest(const char* cookie) { |
| 140 DCHECK_EQ(MessageLoop::current(), created_on_loop_); |
| 141 DCHECK(!request_completed_); |
| 142 DCHECK(url_for_request_.is_valid()) << "Valid URL not set."; |
| 143 if (!url_for_request_.is_valid()) return; |
| 144 |
| 145 if (!context_for_request_->cookie_store()->SetCookie(url_for_request_, |
| 146 cookie)) { |
| 147 DLOG(WARNING) << "Cookie " << cookie |
| 148 << " could not be added for url: " << url_for_request_ << "."; |
| 149 } |
| 150 } |
| 151 |
| 152 bool HttpBridge::MakeSynchronousPost(int* os_error_code, int* response_code) { |
| 153 DCHECK_EQ(MessageLoop::current(), created_on_loop_); |
| 154 DCHECK(!request_completed_); |
| 155 DCHECK(url_for_request_.is_valid()) << "Invalid URL for request"; |
| 156 DCHECK(!content_type_.empty()) << "Payload not set"; |
| 157 DCHECK(context_for_request_->is_user_agent_set()) << "User agent not set"; |
| 158 |
| 159 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, |
| 160 &HttpBridge::CallMakeAsynchronousPost)); |
| 161 |
| 162 if (!http_post_completed_.Wait()) // Block until network request completes. |
| 163 NOTREACHED(); // See OnURLFetchComplete. |
| 164 |
| 165 DCHECK(request_completed_); |
| 166 *os_error_code = os_error_code_; |
| 167 *response_code = http_response_code_; |
| 168 return request_succeeded_; |
| 169 } |
| 170 |
| 171 void HttpBridge::MakeAsynchronousPost() { |
| 172 DCHECK_EQ(MessageLoop::current(), io_loop_); |
| 173 DCHECK(!request_completed_); |
| 174 |
| 175 url_poster_ = new URLFetcher(url_for_request_, URLFetcher::POST, this); |
| 176 url_poster_->set_request_context(context_for_request_); |
| 177 url_poster_->set_upload_data(content_type_, request_content_); |
| 178 |
| 179 if (use_io_loop_for_testing_) |
| 180 url_poster_->set_io_loop(io_loop_); |
| 181 |
| 182 url_poster_->Start(); |
| 183 } |
| 184 |
| 185 int HttpBridge::GetResponseContentLength() const { |
| 186 DCHECK_EQ(MessageLoop::current(), created_on_loop_); |
| 187 DCHECK(request_completed_); |
| 188 return response_content_.size(); |
| 189 } |
| 190 |
| 191 const char* HttpBridge::GetResponseContent() const { |
| 192 DCHECK_EQ(MessageLoop::current(), created_on_loop_); |
| 193 DCHECK(request_completed_); |
| 194 return response_content_.c_str(); |
| 195 } |
| 196 |
| 197 int HttpBridge::GetResponseCookieCount() const { |
| 198 DCHECK_EQ(MessageLoop::current(), created_on_loop_); |
| 199 DCHECK(request_completed_); |
| 200 return response_cookies_.size(); |
| 201 } |
| 202 |
| 203 const char* HttpBridge::GetResponseCookieAt(int cookie_number) const { |
| 204 DCHECK_EQ(MessageLoop::current(), created_on_loop_); |
| 205 DCHECK(request_completed_); |
| 206 bool valid_number = (cookie_number >= 0) && |
| 207 (static_cast<size_t>(cookie_number) < response_cookies_.size()); |
| 208 DCHECK(valid_number); |
| 209 if (!valid_number) |
| 210 return NULL; |
| 211 return response_cookies_[cookie_number].c_str(); |
| 212 } |
| 213 |
| 214 void HttpBridge::OnURLFetchComplete(const URLFetcher *source, const GURL &url, |
| 215 const URLRequestStatus &status, |
| 216 int response_code, |
| 217 const ResponseCookies &cookies, |
| 218 const std::string &data) { |
| 219 DCHECK_EQ(MessageLoop::current(), io_loop_); |
| 220 |
| 221 request_completed_ = true; |
| 222 request_succeeded_ = (URLRequestStatus::SUCCESS == status.status()); |
| 223 http_response_code_ = response_code; |
| 224 os_error_code_ = status.os_error(); |
| 225 |
| 226 // TODO(timsteele): For now we need this "fixup" to match up with what the |
| 227 // sync backend expects. This seems to be non-standard and shouldn't be done |
| 228 // here in HttpBridge, and it breaks part of the unittest. |
| 229 for (size_t i = 0; i < cookies.size(); ++i) { |
| 230 net::CookieMonster::ParsedCookie parsed_cookie(cookies[i]); |
| 231 std::string cookie = " \t \t \t \t \t"; |
| 232 cookie += parsed_cookie.Name() + "\t"; |
| 233 cookie += parsed_cookie.Value(); |
| 234 response_cookies_.push_back(cookie); |
| 235 } |
| 236 |
| 237 response_content_ = data; |
| 238 |
| 239 // End of the line for url_poster_. It lives only on the io_loop. |
| 240 // We defer deletion because we're inside a callback from a component of the |
| 241 // URLFetcher, so it seems most natural / "polite" to let the stack unwind. |
| 242 io_loop_->DeleteSoon(FROM_HERE, url_poster_); |
| 243 url_poster_ = NULL; |
| 244 |
| 245 // Wake the blocked syncer thread in MakeSynchronousPost. |
| 246 // WARNING: DONT DO ANYTHING AFTER THIS CALL! |this| may be deleted! |
| 247 http_post_completed_.Signal(); |
| 248 } |
| 249 |
| 250 } // namespace browser_sync |
| 251 |
| 252 #endif // CHROME_PERSONALIZATION |
OLD | NEW |