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 |