OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/sync/engine/net/server_connection_manager.h" | 5 #include "chrome/browser/sync/engine/net/server_connection_manager.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 | 8 |
9 #include <ostream> | 9 #include <ostream> |
10 #include <string> | 10 #include <string> |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
51 ENUM_CASE(SYNC_AUTH_ERROR); | 51 ENUM_CASE(SYNC_AUTH_ERROR); |
52 ENUM_CASE(SERVER_CONNECTION_OK); | 52 ENUM_CASE(SERVER_CONNECTION_OK); |
53 ENUM_CASE(RETRY); | 53 ENUM_CASE(RETRY); |
54 } | 54 } |
55 NOTREACHED(); | 55 NOTREACHED(); |
56 return ""; | 56 return ""; |
57 } | 57 } |
58 | 58 |
59 #undef ENUM_CASE | 59 #undef ENUM_CASE |
60 | 60 |
61 bool ServerConnectionManager::Post::ReadBufferResponse( | 61 ServerConnectionManager::Connection::Connection( |
62 ServerConnectionManager* scm) : scm_(scm) { | |
63 } | |
64 | |
65 ServerConnectionManager::Connection::~Connection() { | |
66 } | |
67 | |
68 bool ServerConnectionManager::Connection::ReadBufferResponse( | |
62 string* buffer_out, | 69 string* buffer_out, |
63 HttpResponse* response, | 70 HttpResponse* response, |
64 bool require_response) { | 71 bool require_response) { |
65 if (RC_REQUEST_OK != response->response_code) { | 72 if (RC_REQUEST_OK != response->response_code) { |
66 response->server_status = HttpResponse::SYNC_SERVER_ERROR; | 73 response->server_status = HttpResponse::SYNC_SERVER_ERROR; |
67 return false; | 74 return false; |
68 } | 75 } |
69 | 76 |
70 if (require_response && (1 > response->content_length)) | 77 if (require_response && (1 > response->content_length)) |
71 return false; | 78 return false; |
72 | 79 |
73 const int64 bytes_read = ReadResponse(buffer_out, | 80 const int64 bytes_read = ReadResponse(buffer_out, |
74 static_cast<int>(response->content_length)); | 81 static_cast<int>(response->content_length)); |
75 if (bytes_read != response->content_length) { | 82 if (bytes_read != response->content_length) { |
76 response->server_status = HttpResponse::IO_ERROR; | 83 response->server_status = HttpResponse::IO_ERROR; |
77 return false; | 84 return false; |
78 } | 85 } |
79 return true; | 86 return true; |
80 } | 87 } |
81 | 88 |
82 bool ServerConnectionManager::Post::ReadDownloadResponse( | 89 bool ServerConnectionManager::Connection::ReadDownloadResponse( |
83 HttpResponse* response, | 90 HttpResponse* response, |
84 string* buffer_out) { | 91 string* buffer_out) { |
85 const int64 bytes_read = ReadResponse(buffer_out, | 92 const int64 bytes_read = ReadResponse(buffer_out, |
86 static_cast<int>(response->content_length)); | 93 static_cast<int>(response->content_length)); |
87 | 94 |
88 if (bytes_read != response->content_length) { | 95 if (bytes_read != response->content_length) { |
89 LOG(ERROR) << "Mismatched content lengths, server claimed " << | 96 LOG(ERROR) << "Mismatched content lengths, server claimed " << |
90 response->content_length << ", but sent " << bytes_read; | 97 response->content_length << ", but sent " << bytes_read; |
91 response->server_status = HttpResponse::IO_ERROR; | 98 response->server_status = HttpResponse::IO_ERROR; |
92 return false; | 99 return false; |
93 } | 100 } |
94 return true; | 101 return true; |
95 } | 102 } |
96 | 103 |
104 ServerConnectionManager::ScopedConnectionHelper::ScopedConnectionHelper( | |
105 ServerConnectionManager* manager, Connection* connection) | |
106 : manager_(manager), connection_(connection) {} | |
107 | |
108 ServerConnectionManager::ScopedConnectionHelper::~ScopedConnectionHelper() { | |
109 if (connection_.get()) | |
110 manager_->OnConnectionDestroyed(connection_.get()); | |
111 connection_.reset(); | |
112 } | |
113 | |
114 ServerConnectionManager::Connection* | |
115 ServerConnectionManager::ScopedConnectionHelper::get() { | |
116 return connection_.get(); | |
117 } | |
118 | |
97 namespace { | 119 namespace { |
98 | 120 |
99 string StripTrailingSlash(const string& s) { | 121 string StripTrailingSlash(const string& s) { |
100 int stripped_end_pos = s.size(); | 122 int stripped_end_pos = s.size(); |
101 if (s.at(stripped_end_pos - 1) == '/') { | 123 if (s.at(stripped_end_pos - 1) == '/') { |
102 stripped_end_pos = stripped_end_pos - 1; | 124 stripped_end_pos = stripped_end_pos - 1; |
103 } | 125 } |
104 | 126 |
105 return s.substr(0, stripped_end_pos); | 127 return s.substr(0, stripped_end_pos); |
106 } | 128 } |
107 | 129 |
108 } // namespace | 130 } // namespace |
109 | 131 |
110 // TODO(chron): Use a GURL instead of string concatenation. | 132 // TODO(chron): Use a GURL instead of string concatenation. |
111 string ServerConnectionManager::Post::MakeConnectionURL( | 133 string ServerConnectionManager::Connection::MakeConnectionURL( |
112 const string& sync_server, | 134 const string& sync_server, |
113 const string& path, | 135 const string& path, |
114 bool use_ssl) const { | 136 bool use_ssl) const { |
115 string connection_url = (use_ssl ? "https://" : "http://"); | 137 string connection_url = (use_ssl ? "https://" : "http://"); |
116 connection_url += sync_server; | 138 connection_url += sync_server; |
117 connection_url = StripTrailingSlash(connection_url); | 139 connection_url = StripTrailingSlash(connection_url); |
118 connection_url += path; | 140 connection_url += path; |
119 | 141 |
120 return connection_url; | 142 return connection_url; |
121 } | 143 } |
122 | 144 |
123 int ServerConnectionManager::Post::ReadResponse(string* out_buffer, | 145 int ServerConnectionManager::Connection::ReadResponse(string* out_buffer, |
124 int length) { | 146 int length) { |
125 int bytes_read = buffer_.length(); | 147 int bytes_read = buffer_.length(); |
126 CHECK(length <= bytes_read); | 148 CHECK(length <= bytes_read); |
127 out_buffer->assign(buffer_); | 149 out_buffer->assign(buffer_); |
128 return bytes_read; | 150 return bytes_read; |
129 } | 151 } |
130 | 152 |
131 ScopedServerStatusWatcher::ScopedServerStatusWatcher( | 153 ScopedServerStatusWatcher::ScopedServerStatusWatcher( |
132 ServerConnectionManager* conn_mgr, HttpResponse* response) | 154 ServerConnectionManager* conn_mgr, HttpResponse* response) |
133 : conn_mgr_(conn_mgr), | 155 : conn_mgr_(conn_mgr), |
134 response_(response), | 156 response_(response), |
(...skipping 18 matching lines...) Expand all Loading... | |
153 bool use_ssl, | 175 bool use_ssl, |
154 const string& user_agent) | 176 const string& user_agent) |
155 : sync_server_(server), | 177 : sync_server_(server), |
156 sync_server_port_(port), | 178 sync_server_port_(port), |
157 user_agent_(user_agent), | 179 user_agent_(user_agent), |
158 use_ssl_(use_ssl), | 180 use_ssl_(use_ssl), |
159 proto_sync_path_(kSyncServerSyncPath), | 181 proto_sync_path_(kSyncServerSyncPath), |
160 get_time_path_(kSyncServerGetTimePath), | 182 get_time_path_(kSyncServerGetTimePath), |
161 error_count_(0), | 183 error_count_(0), |
162 server_status_(HttpResponse::NONE), | 184 server_status_(HttpResponse::NONE), |
163 server_reachable_(false) { | 185 server_reachable_(false), |
186 terminated_(false), | |
187 active_connection_(NULL) { | |
164 } | 188 } |
165 | 189 |
166 ServerConnectionManager::~ServerConnectionManager() { | 190 ServerConnectionManager::~ServerConnectionManager() { |
167 } | 191 } |
168 | 192 |
193 ServerConnectionManager::Connection* | |
194 ServerConnectionManager::MakeActiveConnection() { | |
195 base::AutoLock lock(terminate_connection_lock_); | |
196 DCHECK(!active_connection_); | |
197 if (terminated_) | |
198 return NULL; | |
199 | |
200 active_connection_ = MakeConnection(); | |
jar (doing other things)
2011/09/07 06:47:41
It is usually best practice to use locks to only p
tim (not reviewing)
2011/09/07 14:46:56
Thanks for your review Jim. I get the concerns wi
| |
201 return active_connection_; | |
202 } | |
203 | |
204 void ServerConnectionManager::OnConnectionDestroyed(Connection* connection) { | |
205 DCHECK(connection); | |
206 base::AutoLock lock(terminate_connection_lock_); | |
207 // |active_connection_| can be NULL already if it was aborted. Also, | |
208 // it can legitimately be a different Connection object if a new Connection | |
209 // was created after a previous one was Aborted and destroyed. | |
210 if (active_connection_ != connection) | |
211 return; | |
212 | |
213 active_connection_ = NULL; | |
214 } | |
215 | |
169 void ServerConnectionManager::NotifyStatusChanged() { | 216 void ServerConnectionManager::NotifyStatusChanged() { |
170 DCHECK(CalledOnValidThread()); | 217 DCHECK(thread_checker_.CalledOnValidThread()); |
171 FOR_EACH_OBSERVER(ServerConnectionEventListener, listeners_, | 218 FOR_EACH_OBSERVER(ServerConnectionEventListener, listeners_, |
172 OnServerConnectionEvent( | 219 OnServerConnectionEvent( |
173 ServerConnectionEvent(server_status_, server_reachable_))); | 220 ServerConnectionEvent(server_status_, server_reachable_))); |
174 } | 221 } |
175 | 222 |
176 bool ServerConnectionManager::PostBufferWithCachedAuth( | 223 bool ServerConnectionManager::PostBufferWithCachedAuth( |
177 PostBufferParams* params, ScopedServerStatusWatcher* watcher) { | 224 PostBufferParams* params, ScopedServerStatusWatcher* watcher) { |
178 DCHECK(CalledOnValidThread()); | 225 DCHECK(thread_checker_.CalledOnValidThread()); |
179 string path = | 226 string path = |
180 MakeSyncServerPath(proto_sync_path(), MakeSyncQueryString(client_id_)); | 227 MakeSyncServerPath(proto_sync_path(), MakeSyncQueryString(client_id_)); |
181 return PostBufferToPath(params, path, auth_token(), watcher); | 228 return PostBufferToPath(params, path, auth_token(), watcher); |
182 } | 229 } |
183 | 230 |
184 bool ServerConnectionManager::PostBufferToPath(PostBufferParams* params, | 231 bool ServerConnectionManager::PostBufferToPath(PostBufferParams* params, |
185 const string& path, const string& auth_token, | 232 const string& path, const string& auth_token, |
186 ScopedServerStatusWatcher* watcher) { | 233 ScopedServerStatusWatcher* watcher) { |
187 DCHECK(CalledOnValidThread()); | 234 DCHECK(thread_checker_.CalledOnValidThread()); |
188 DCHECK(watcher != NULL); | 235 DCHECK(watcher != NULL); |
189 | 236 |
190 if (auth_token.empty()) { | 237 if (auth_token.empty()) { |
191 params->response.server_status = HttpResponse::SYNC_AUTH_ERROR; | 238 params->response.server_status = HttpResponse::SYNC_AUTH_ERROR; |
192 return false; | 239 return false; |
193 } | 240 } |
194 | 241 |
195 scoped_ptr<Post> post(MakePost()); | 242 // When our connection object falls out of scope, it clears itself from |
196 bool ok = post->Init(path.c_str(), auth_token, params->buffer_in, | 243 // active_connection_. |
197 ¶ms->response); | 244 ScopedConnectionHelper post(this, MakeActiveConnection()); |
245 if (!post.get()) { | |
246 params->response.server_status = HttpResponse::CONNECTION_UNAVAILABLE; | |
247 return false; | |
248 } | |
249 | |
250 // Note that |post| may be aborted by now, which will just cause Init to fail | |
251 // with CONNECTION_UNAVAILABLE. | |
252 bool ok = post.get()->Init( | |
253 path.c_str(), auth_token, params->buffer_in, ¶ms->response); | |
198 | 254 |
199 if (params->response.server_status == HttpResponse::SYNC_AUTH_ERROR) { | 255 if (params->response.server_status == HttpResponse::SYNC_AUTH_ERROR) { |
200 InvalidateAndClearAuthToken(); | 256 InvalidateAndClearAuthToken(); |
201 } | 257 } |
202 | 258 |
203 if (!ok || RC_REQUEST_OK != params->response.response_code) { | 259 if (!ok || RC_REQUEST_OK != params->response.response_code) { |
204 IncrementErrorCount(); | 260 IncrementErrorCount(); |
205 return false; | 261 return false; |
206 } | 262 } |
207 | 263 |
208 if (post->ReadBufferResponse(¶ms->buffer_out, ¶ms->response, true)) { | 264 if (post.get()->ReadBufferResponse( |
265 ¶ms->buffer_out, ¶ms->response, true)) { | |
209 params->response.server_status = HttpResponse::SERVER_CONNECTION_OK; | 266 params->response.server_status = HttpResponse::SERVER_CONNECTION_OK; |
210 server_reachable_ = true; | 267 server_reachable_ = true; |
211 return true; | 268 return true; |
212 } | 269 } |
213 return false; | 270 return false; |
214 } | 271 } |
215 | 272 |
216 bool ServerConnectionManager::CheckTime(int32* out_time) { | 273 bool ServerConnectionManager::CheckTime(int32* out_time) { |
217 DCHECK(CalledOnValidThread()); | 274 DCHECK(thread_checker_.CalledOnValidThread()); |
275 | |
218 // Verify that the server really is reachable by checking the time. We need | 276 // Verify that the server really is reachable by checking the time. We need |
219 // to do this because of wifi interstitials that intercept messages from the | 277 // to do this because of wifi interstitials that intercept messages from the |
220 // client and return HTTP OK instead of a redirect. | 278 // client and return HTTP OK instead of a redirect. |
221 HttpResponse response; | 279 HttpResponse response; |
222 ScopedServerStatusWatcher watcher(this, &response); | 280 ScopedServerStatusWatcher watcher(this, &response); |
223 string post_body = "command=get_time"; | 281 string post_body = "command=get_time"; |
224 | 282 |
225 for (int i = 0 ; i < 3; i++) { | 283 for (int i = 0 ; i < 3; i++) { |
226 scoped_ptr<Post> post(MakePost()); | 284 ScopedConnectionHelper post(this, MakeActiveConnection()); |
285 if (!post.get()) | |
286 break; | |
227 | 287 |
228 // Note that the server's get_time path doesn't require authentication. | 288 // Note that the server's get_time path doesn't require authentication. |
229 string get_time_path = | 289 string get_time_path = |
230 MakeSyncServerPath(kSyncServerGetTimePath, post_body); | 290 MakeSyncServerPath(kSyncServerGetTimePath, post_body); |
231 VLOG(1) << "Requesting get_time from:" << get_time_path; | 291 VLOG(1) << "Requesting get_time from:" << get_time_path; |
232 | 292 |
233 string blank_post_body; | 293 string blank_post_body; |
234 bool ok = post->Init(get_time_path.c_str(), blank_post_body, | 294 bool ok = post.get()->Init(get_time_path.c_str(), blank_post_body, |
235 blank_post_body, &response); | 295 blank_post_body, &response); |
236 if (!ok) { | 296 if (!ok) { |
237 VLOG(1) << "Unable to check the time"; | 297 VLOG(1) << "Unable to check the time"; |
238 continue; | 298 continue; |
239 } | 299 } |
240 string time_response; | 300 string time_response; |
241 time_response.resize( | 301 time_response.resize( |
242 static_cast<string::size_type>(response.content_length)); | 302 static_cast<string::size_type>(response.content_length)); |
243 ok = post->ReadDownloadResponse(&response, &time_response); | 303 ok = post.get()->ReadDownloadResponse(&response, &time_response); |
244 if (!ok || string::npos != | 304 if (!ok || string::npos != |
245 time_response.find_first_not_of("0123456789")) { | 305 time_response.find_first_not_of("0123456789")) { |
246 LOG(ERROR) << "unable to read a non-numeric response from get_time:" | 306 LOG(ERROR) << "unable to read a non-numeric response from get_time:" |
247 << time_response; | 307 << time_response; |
248 continue; | 308 continue; |
249 } | 309 } |
250 *out_time = atoi(time_response.c_str()); | 310 *out_time = atoi(time_response.c_str()); |
251 VLOG(1) << "Server was reachable."; | 311 VLOG(1) << "Server was reachable."; |
252 return true; | 312 return true; |
253 } | 313 } |
254 IncrementErrorCount(); | 314 IncrementErrorCount(); |
255 return false; | 315 return false; |
256 } | 316 } |
257 | 317 |
258 bool ServerConnectionManager::IsServerReachable() { | 318 bool ServerConnectionManager::IsServerReachable() { |
259 DCHECK(CalledOnValidThread()); | 319 DCHECK(thread_checker_.CalledOnValidThread()); |
260 int32 time; | 320 int32 time; |
261 return CheckTime(&time); | 321 return CheckTime(&time); |
262 } | 322 } |
263 | 323 |
264 bool ServerConnectionManager::IsUserAuthenticated() { | 324 bool ServerConnectionManager::IsUserAuthenticated() { |
265 DCHECK(CalledOnValidThread()); | 325 DCHECK(thread_checker_.CalledOnValidThread()); |
266 return IsGoodReplyFromServer(server_status_); | 326 return IsGoodReplyFromServer(server_status_); |
267 } | 327 } |
268 | 328 |
269 bool ServerConnectionManager::CheckServerReachable() { | 329 bool ServerConnectionManager::CheckServerReachable() { |
270 DCHECK(CalledOnValidThread()); | 330 DCHECK(thread_checker_.CalledOnValidThread()); |
271 const bool server_is_reachable = IsServerReachable(); | 331 const bool server_is_reachable = IsServerReachable(); |
272 if (server_reachable_ != server_is_reachable) { | 332 if (server_reachable_ != server_is_reachable) { |
273 server_reachable_ = server_is_reachable; | 333 server_reachable_ = server_is_reachable; |
274 NotifyStatusChanged(); | 334 NotifyStatusChanged(); |
275 } | 335 } |
276 return server_is_reachable; | 336 return server_is_reachable; |
277 } | 337 } |
278 | 338 |
279 bool ServerConnectionManager::IncrementErrorCount() { | 339 bool ServerConnectionManager::IncrementErrorCount() { |
280 DCHECK(CalledOnValidThread()); | 340 DCHECK(thread_checker_.CalledOnValidThread()); |
281 error_count_++; | 341 error_count_++; |
282 | 342 |
283 if (error_count_ > kMaxConnectionErrorsBeforeReset) { | 343 if (error_count_ > kMaxConnectionErrorsBeforeReset) { |
284 error_count_ = 0; | 344 error_count_ = 0; |
285 | 345 |
286 if (!IsServerReachable()) { | 346 if (!IsServerReachable()) { |
287 LOG(WARNING) << "Too many connection failures, server is not reachable. " | 347 LOG(WARNING) << "Too many connection failures, server is not reachable. " |
288 << "Resetting connections."; | 348 << "Resetting connections."; |
289 } else { | 349 } else { |
290 LOG(WARNING) << "Multiple connection failures while server is reachable."; | 350 LOG(WARNING) << "Multiple connection failures while server is reachable."; |
291 } | 351 } |
292 return false; | 352 return false; |
293 } | 353 } |
294 | 354 |
295 return true; | 355 return true; |
296 } | 356 } |
297 | 357 |
298 void ServerConnectionManager::SetServerParameters(const string& server_url, | 358 void ServerConnectionManager::SetServerParameters(const string& server_url, |
299 int port, | 359 int port, |
300 bool use_ssl) { | 360 bool use_ssl) { |
301 DCHECK(CalledOnValidThread()); | 361 DCHECK(thread_checker_.CalledOnValidThread()); |
302 sync_server_ = server_url; | 362 sync_server_ = server_url; |
303 sync_server_port_ = port; | 363 sync_server_port_ = port; |
304 use_ssl_ = use_ssl; | 364 use_ssl_ = use_ssl; |
305 } | 365 } |
306 | 366 |
307 // Returns the current server parameters in server_url and port. | 367 // Returns the current server parameters in server_url and port. |
308 void ServerConnectionManager::GetServerParameters(string* server_url, | 368 void ServerConnectionManager::GetServerParameters(string* server_url, |
309 int* port, | 369 int* port, |
310 bool* use_ssl) const { | 370 bool* use_ssl) const { |
311 if (server_url != NULL) | 371 if (server_url != NULL) |
(...skipping 14 matching lines...) Expand all Loading... | |
326 return std::string(); | 386 return std::string(); |
327 // We just want the hostname, so we don't need to switch on use_ssl. | 387 // We just want the hostname, so we don't need to switch on use_ssl. |
328 server_url = "http://" + server_url; | 388 server_url = "http://" + server_url; |
329 GURL gurl(server_url); | 389 GURL gurl(server_url); |
330 DCHECK(gurl.is_valid()) << gurl; | 390 DCHECK(gurl.is_valid()) << gurl; |
331 return gurl.host(); | 391 return gurl.host(); |
332 } | 392 } |
333 | 393 |
334 void ServerConnectionManager::AddListener( | 394 void ServerConnectionManager::AddListener( |
335 ServerConnectionEventListener* listener) { | 395 ServerConnectionEventListener* listener) { |
336 DCHECK(CalledOnValidThread()); | 396 DCHECK(thread_checker_.CalledOnValidThread()); |
337 listeners_.AddObserver(listener); | 397 listeners_.AddObserver(listener); |
338 } | 398 } |
339 | 399 |
340 void ServerConnectionManager::RemoveListener( | 400 void ServerConnectionManager::RemoveListener( |
341 ServerConnectionEventListener* listener) { | 401 ServerConnectionEventListener* listener) { |
342 DCHECK(CalledOnValidThread()); | 402 DCHECK(thread_checker_.CalledOnValidThread()); |
343 listeners_.RemoveObserver(listener); | 403 listeners_.RemoveObserver(listener); |
344 } | 404 } |
345 | 405 |
346 ServerConnectionManager::Post* ServerConnectionManager::MakePost() { | 406 ServerConnectionManager::Connection* ServerConnectionManager::MakeConnection() |
407 { | |
347 return NULL; // For testing. | 408 return NULL; // For testing. |
348 } | 409 } |
349 | 410 |
411 void ServerConnectionManager::TerminateAllIO() { | |
412 base::AutoLock lock(terminate_connection_lock_); | |
413 terminated_ = true; | |
414 if (active_connection_) | |
415 active_connection_->Abort(); | |
416 | |
417 // Sever our ties to this connection object. Note that it still may exist, | |
418 // since we don't own it, but it has been neutered. | |
419 active_connection_ = NULL; | |
420 } | |
421 | |
350 bool FillMessageWithShareDetails(sync_pb::ClientToServerMessage* csm, | 422 bool FillMessageWithShareDetails(sync_pb::ClientToServerMessage* csm, |
351 syncable::DirectoryManager* manager, | 423 syncable::DirectoryManager* manager, |
352 const std::string& share) { | 424 const std::string& share) { |
353 syncable::ScopedDirLookup dir(manager, share); | 425 syncable::ScopedDirLookup dir(manager, share); |
354 if (!dir.good()) { | 426 if (!dir.good()) { |
355 VLOG(1) << "Dir lookup failed"; | 427 VLOG(1) << "Dir lookup failed"; |
356 return false; | 428 return false; |
357 } | 429 } |
358 string birthday = dir->store_birthday(); | 430 string birthday = dir->store_birthday(); |
359 if (!birthday.empty()) | 431 if (!birthday.empty()) |
360 csm->set_store_birthday(birthday); | 432 csm->set_store_birthday(birthday); |
361 csm->set_share(share); | 433 csm->set_share(share); |
362 return true; | 434 return true; |
363 } | 435 } |
364 | 436 |
365 std::ostream& operator << (std::ostream& s, const struct HttpResponse& hr) { | 437 std::ostream& operator << (std::ostream& s, const struct HttpResponse& hr) { |
366 s << " Response Code (bogus on error): " << hr.response_code; | 438 s << " Response Code (bogus on error): " << hr.response_code; |
367 s << " Content-Length (bogus on error): " << hr.content_length; | 439 s << " Content-Length (bogus on error): " << hr.content_length; |
368 s << " Server Status: " << hr.server_status; | 440 s << " Server Status: " << hr.server_status; |
369 return s; | 441 return s; |
370 } | 442 } |
371 | 443 |
372 } // namespace browser_sync | 444 } // namespace browser_sync |
OLD | NEW |