1 // Copyright 2014 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 #include "net/http/http_transaction_test_util.h"
6 
7 #include <algorithm>
8 #include <unordered_map>
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/location.h"
13 #include "base/run_loop.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/stl_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/threading/thread_task_runner_handle.h"
18 #include "base/time/clock.h"
19 #include "base/time/time.h"
20 #include "net/base/ip_address.h"
21 #include "net/base/ip_endpoint.h"
22 #include "net/base/load_flags.h"
23 #include "net/base/load_timing_info.h"
24 #include "net/base/net_errors.h"
25 #include "net/base/network_isolation_key.h"
26 #include "net/cert/x509_certificate.h"
27 #include "net/disk_cache/disk_cache.h"
28 #include "net/http/http_cache.h"
29 #include "net/http/http_request_info.h"
30 #include "net/http/http_response_info.h"
31 #include "net/http/http_transaction.h"
32 #include "net/log/net_log.h"
33 #include "net/log/net_log_source.h"
34 #include "net/log/net_log_with_source.h"
35 #include "testing/gtest/include/gtest/gtest.h"
36 
37 namespace net {
38 
39 namespace {
40 using MockTransactionMap =
41     std::unordered_map<std::string, const MockTransaction*>;
42 static MockTransactionMap mock_transactions;
43 }  // namespace
44 
DefaultTransportInfo()45 TransportInfo DefaultTransportInfo() {
46   return TransportInfo(TransportType::kDirect,
47                        IPEndPoint(IPAddress::IPv4Localhost(), 80));
48 }
49 
50 //-----------------------------------------------------------------------------
51 // mock transaction data
52 
53 const MockTransaction kSimpleGET_Transaction = {
54     "http://www.google.com/",
55     "GET",
56     base::Time(),
57     "",
58     LOAD_NORMAL,
59     DefaultTransportInfo(),
60     "HTTP/1.1 200 OK",
61     "Cache-Control: max-age=10000\n",
62     base::Time(),
63     "<html><body>Google Blah Blah</body></html>",
64     TEST_MODE_NORMAL,
65     nullptr,
66     nullptr,
67     nullptr,
68     0,
69     0,
70     OK,
71     OK,
72 };
73 
74 const MockTransaction kSimplePOST_Transaction = {
75     "http://bugdatabase.com/edit",
76     "POST",
77     base::Time(),
78     "",
79     LOAD_NORMAL,
80     DefaultTransportInfo(),
81     "HTTP/1.1 200 OK",
82     "",
83     base::Time(),
84     "<html><body>Google Blah Blah</body></html>",
85     TEST_MODE_NORMAL,
86     nullptr,
87     nullptr,
88     nullptr,
89     0,
90     0,
91     OK,
92     OK,
93 };
94 
95 const MockTransaction kTypicalGET_Transaction = {
96     "http://www.example.com/~foo/bar.html",
97     "GET",
98     base::Time(),
99     "",
100     LOAD_NORMAL,
101     DefaultTransportInfo(),
102     "HTTP/1.1 200 OK",
103     "Date: Wed, 28 Nov 2007 09:40:09 GMT\n"
104     "Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n",
105     base::Time(),
106     "<html><body>Google Blah Blah</body></html>",
107     TEST_MODE_NORMAL,
108     nullptr,
109     nullptr,
110     nullptr,
111     0,
112     0,
113     OK,
114     OK,
115 };
116 
117 const MockTransaction kETagGET_Transaction = {
118     "http://www.google.com/foopy",
119     "GET",
120     base::Time(),
121     "",
122     LOAD_NORMAL,
123     DefaultTransportInfo(),
124     "HTTP/1.1 200 OK",
125     "Cache-Control: max-age=10000\n"
126     "Etag: \"foopy\"\n",
127     base::Time(),
128     "<html><body>Google Blah Blah</body></html>",
129     TEST_MODE_NORMAL,
130     nullptr,
131     nullptr,
132     nullptr,
133     0,
134     0,
135     OK,
136     OK,
137 };
138 
139 const MockTransaction kRangeGET_Transaction = {
140     "http://www.google.com/",
141     "GET",
142     base::Time(),
143     "Range: 0-100\r\n",
144     LOAD_NORMAL,
145     DefaultTransportInfo(),
146     "HTTP/1.1 200 OK",
147     "Cache-Control: max-age=10000\n",
148     base::Time(),
149     "<html><body>Google Blah Blah</body></html>",
150     TEST_MODE_NORMAL,
151     nullptr,
152     nullptr,
153     nullptr,
154     0,
155     0,
156     OK,
157     OK,
158 };
159 
160 static const MockTransaction* const kBuiltinMockTransactions[] = {
161   &kSimpleGET_Transaction,
162   &kSimplePOST_Transaction,
163   &kTypicalGET_Transaction,
164   &kETagGET_Transaction,
165   &kRangeGET_Transaction
166 };
167 
FindMockTransaction(const GURL & url)168 const MockTransaction* FindMockTransaction(const GURL& url) {
169   // look for overrides:
170   MockTransactionMap::const_iterator it = mock_transactions.find(url.spec());
171   if (it != mock_transactions.end())
172     return it->second;
173 
174   // look for builtins:
175   for (size_t i = 0; i < base::size(kBuiltinMockTransactions); ++i) {
176     if (url == GURL(kBuiltinMockTransactions[i]->url))
177       return kBuiltinMockTransactions[i];
178   }
179   return nullptr;
180 }
181 
AddMockTransaction(const MockTransaction * trans)182 void AddMockTransaction(const MockTransaction* trans) {
183   mock_transactions[GURL(trans->url).spec()] = trans;
184 }
185 
RemoveMockTransaction(const MockTransaction * trans)186 void RemoveMockTransaction(const MockTransaction* trans) {
187   mock_transactions.erase(GURL(trans->url).spec());
188 }
189 
MockHttpRequest(const MockTransaction & t)190 MockHttpRequest::MockHttpRequest(const MockTransaction& t) {
191   url = GURL(t.url);
192   method = t.method;
193   extra_headers.AddHeadersFromString(t.request_headers);
194   load_flags = t.load_flags;
195   url::Origin origin = url::Origin::Create(url);
196   network_isolation_key = NetworkIsolationKey(origin, origin);
197 }
198 
CacheKey()199 std::string MockHttpRequest::CacheKey() {
200   return HttpCache::GenerateCacheKeyForTest(this);
201 }
202 
203 //-----------------------------------------------------------------------------
204 
205 // static
206 int TestTransactionConsumer::quit_counter_ = 0;
207 
TestTransactionConsumer(RequestPriority priority,HttpTransactionFactory * factory)208 TestTransactionConsumer::TestTransactionConsumer(
209     RequestPriority priority,
210     HttpTransactionFactory* factory)
211     : state_(IDLE), error_(OK) {
212   // Disregard the error code.
213   factory->CreateTransaction(priority, &trans_);
214   ++quit_counter_;
215 }
216 
217 TestTransactionConsumer::~TestTransactionConsumer() = default;
218 
Start(const HttpRequestInfo * request,const NetLogWithSource & net_log)219 void TestTransactionConsumer::Start(const HttpRequestInfo* request,
220                                     const NetLogWithSource& net_log) {
221   state_ = STARTING;
222   int result =
223       trans_->Start(request,
224                     base::BindOnce(&TestTransactionConsumer::OnIOComplete,
225                                    base::Unretained(this)),
226                     net_log);
227   if (result != ERR_IO_PENDING)
228     DidStart(result);
229 }
230 
DidStart(int result)231 void TestTransactionConsumer::DidStart(int result) {
232   if (result != OK) {
233     DidFinish(result);
234   } else {
235     Read();
236   }
237 }
238 
DidRead(int result)239 void TestTransactionConsumer::DidRead(int result) {
240   if (result <= 0) {
241     DidFinish(result);
242   } else {
243     content_.append(read_buf_->data(), result);
244     Read();
245   }
246 }
247 
DidFinish(int result)248 void TestTransactionConsumer::DidFinish(int result) {
249   state_ = DONE;
250   error_ = result;
251   if (--quit_counter_ == 0)
252     base::RunLoop::QuitCurrentWhenIdleDeprecated();
253 }
254 
Read()255 void TestTransactionConsumer::Read() {
256   state_ = READING;
257   read_buf_ = base::MakeRefCounted<IOBuffer>(1024);
258   int result =
259       trans_->Read(read_buf_.get(), 1024,
260                    base::BindOnce(&TestTransactionConsumer::OnIOComplete,
261                                   base::Unretained(this)));
262   if (result != ERR_IO_PENDING)
263     DidRead(result);
264 }
265 
OnIOComplete(int result)266 void TestTransactionConsumer::OnIOComplete(int result) {
267   switch (state_) {
268     case STARTING:
269       DidStart(result);
270       break;
271     case READING:
272       DidRead(result);
273       break;
274     default:
275       NOTREACHED();
276   }
277 }
278 
MockNetworkTransaction(RequestPriority priority,MockNetworkLayer * factory)279 MockNetworkTransaction::MockNetworkTransaction(RequestPriority priority,
280                                                MockNetworkLayer* factory)
281     : request_(nullptr),
282       data_cursor_(0),
283       content_length_(0),
284       priority_(priority),
285       read_handler_(nullptr),
286       websocket_handshake_stream_create_helper_(nullptr),
287       transaction_factory_(factory->AsWeakPtr()),
288       received_bytes_(0),
289       sent_bytes_(0),
290       socket_log_id_(NetLogSource::kInvalidId),
291       done_reading_called_(false),
292       reading_(false) {}
293 
~MockNetworkTransaction()294 MockNetworkTransaction::~MockNetworkTransaction() {
295   // Use request_ as in ~HttpNetworkTransaction to make sure its valid and not
296   // already freed by the consumer. Only check till Read is invoked since
297   // HttpNetworkTransaction sets request_ to nullptr when Read is invoked.
298   // See crbug.com/734037.
299   if (request_ && !reading_)
300     DCHECK(request_->load_flags >= 0);
301 }
302 
Start(const HttpRequestInfo * request,CompletionOnceCallback callback,const NetLogWithSource & net_log)303 int MockNetworkTransaction::Start(const HttpRequestInfo* request,
304                                   CompletionOnceCallback callback,
305                                   const NetLogWithSource& net_log) {
306   if (request_)
307     return ERR_FAILED;
308 
309   request_ = request;
310   return StartInternal(request, std::move(callback), net_log);
311 }
312 
RestartIgnoringLastError(CompletionOnceCallback callback)313 int MockNetworkTransaction::RestartIgnoringLastError(
314     CompletionOnceCallback callback) {
315   return ERR_FAILED;
316 }
317 
RestartWithCertificate(scoped_refptr<X509Certificate> client_cert,scoped_refptr<SSLPrivateKey> client_private_key,CompletionOnceCallback callback)318 int MockNetworkTransaction::RestartWithCertificate(
319     scoped_refptr<X509Certificate> client_cert,
320     scoped_refptr<SSLPrivateKey> client_private_key,
321     CompletionOnceCallback callback) {
322   return ERR_FAILED;
323 }
324 
RestartWithAuth(const AuthCredentials & credentials,CompletionOnceCallback callback)325 int MockNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials,
326                                             CompletionOnceCallback callback) {
327   if (!IsReadyToRestartForAuth())
328     return ERR_FAILED;
329 
330   HttpRequestInfo auth_request_info = *request_;
331   auth_request_info.extra_headers.SetHeader("Authorization", "Bar");
332 
333   // Let the MockTransactionHandler worry about this: the only way for this
334   // test to succeed is by using an explicit handler for the transaction so
335   // that server behavior can be simulated.
336   return StartInternal(&auth_request_info, std::move(callback),
337                        NetLogWithSource());
338 }
339 
PopulateNetErrorDetails(NetErrorDetails *) const340 void MockNetworkTransaction::PopulateNetErrorDetails(
341     NetErrorDetails* /*details*/) const {
342   NOTIMPLEMENTED();
343 }
344 
IsReadyToRestartForAuth()345 bool MockNetworkTransaction::IsReadyToRestartForAuth() {
346   if (!request_)
347     return false;
348 
349   if (!request_->extra_headers.HasHeader("X-Require-Mock-Auth"))
350     return false;
351 
352   // Allow the mock server to decide whether authentication is required or not.
353   std::string status_line = response_.headers->GetStatusLine();
354   return status_line.find(" 401 ") != std::string::npos ||
355       status_line.find(" 407 ") != std::string::npos;
356 }
357 
Read(net::IOBuffer * buf,int buf_len,CompletionOnceCallback callback)358 int MockNetworkTransaction::Read(net::IOBuffer* buf,
359                                  int buf_len,
360                                  CompletionOnceCallback callback) {
361   const MockTransaction* t = FindMockTransaction(request_->url);
362   DCHECK(t);
363 
364   CHECK(!done_reading_called_);
365   reading_ = true;
366 
367   int num = t->read_return_code;
368 
369   if (OK == num) {
370     if (read_handler_) {
371       num = (*read_handler_)(content_length_, data_cursor_, buf, buf_len);
372       data_cursor_ += num;
373     } else {
374       int data_len = static_cast<int>(data_.size());
375       num = std::min(static_cast<int64_t>(buf_len), data_len - data_cursor_);
376       if (test_mode_ & TEST_MODE_SLOW_READ)
377         num = std::min(num, 1);
378       if (num) {
379         memcpy(buf->data(), data_.data() + data_cursor_, num);
380         data_cursor_ += num;
381       }
382     }
383   }
384 
385   if (test_mode_ & TEST_MODE_SYNC_NET_READ)
386     return num;
387 
388   CallbackLater(std::move(callback), num);
389   return ERR_IO_PENDING;
390 }
391 
StopCaching()392 void MockNetworkTransaction::StopCaching() {
393   if (transaction_factory_.get())
394     transaction_factory_->TransactionStopCaching();
395 }
396 
GetTotalReceivedBytes() const397 int64_t MockNetworkTransaction::GetTotalReceivedBytes() const {
398   return received_bytes_;
399 }
400 
GetTotalSentBytes() const401 int64_t MockNetworkTransaction::GetTotalSentBytes() const {
402   return sent_bytes_;
403 }
404 
DoneReading()405 void MockNetworkTransaction::DoneReading() {
406   CHECK(!done_reading_called_);
407   done_reading_called_ = true;
408   if (transaction_factory_.get())
409     transaction_factory_->TransactionDoneReading();
410 }
411 
GetResponseInfo() const412 const HttpResponseInfo* MockNetworkTransaction::GetResponseInfo() const {
413   return &response_;
414 }
415 
GetLoadState() const416 LoadState MockNetworkTransaction::GetLoadState() const {
417   if (data_cursor_)
418     return LOAD_STATE_READING_RESPONSE;
419   return LOAD_STATE_IDLE;
420 }
421 
SetQuicServerInfo(QuicServerInfo * quic_server_info)422 void MockNetworkTransaction::SetQuicServerInfo(
423     QuicServerInfo* quic_server_info) {
424 }
425 
GetLoadTimingInfo(LoadTimingInfo * load_timing_info) const426 bool MockNetworkTransaction::GetLoadTimingInfo(
427     LoadTimingInfo* load_timing_info) const {
428   if (socket_log_id_ != NetLogSource::kInvalidId) {
429     // The minimal set of times for a request that gets a response, assuming it
430     // gets a new socket.
431     load_timing_info->socket_reused = false;
432     load_timing_info->socket_log_id = socket_log_id_;
433     load_timing_info->connect_timing.connect_start = base::TimeTicks::Now();
434     load_timing_info->connect_timing.connect_end = base::TimeTicks::Now();
435     load_timing_info->send_start = base::TimeTicks::Now();
436     load_timing_info->send_end = base::TimeTicks::Now();
437   } else {
438     // If there's no valid socket ID, just use the generic socket reused values.
439     // No tests currently depend on this, just should not match the values set
440     // by a cache hit.
441     load_timing_info->socket_reused = true;
442     load_timing_info->send_start = base::TimeTicks::Now();
443     load_timing_info->send_end = base::TimeTicks::Now();
444   }
445   return true;
446 }
447 
GetRemoteEndpoint(IPEndPoint * endpoint) const448 bool MockNetworkTransaction::GetRemoteEndpoint(IPEndPoint* endpoint) const {
449   *endpoint = IPEndPoint(IPAddress(127, 0, 0, 1), 80);
450   return true;
451 }
452 
SetPriority(RequestPriority priority)453 void MockNetworkTransaction::SetPriority(RequestPriority priority) {
454   priority_ = priority;
455 }
456 
SetWebSocketHandshakeStreamCreateHelper(WebSocketHandshakeStreamBase::CreateHelper * create_helper)457 void MockNetworkTransaction::SetWebSocketHandshakeStreamCreateHelper(
458     WebSocketHandshakeStreamBase::CreateHelper* create_helper) {
459   websocket_handshake_stream_create_helper_ = create_helper;
460 }
461 
462 // static
463 const int64_t MockNetworkTransaction::kTotalReceivedBytes = 1000;
464 
465 // static
466 const int64_t MockNetworkTransaction::kTotalSentBytes = 100;
467 
StartInternal(const HttpRequestInfo * request,CompletionOnceCallback callback,const NetLogWithSource & net_log)468 int MockNetworkTransaction::StartInternal(const HttpRequestInfo* request,
469                                           CompletionOnceCallback callback,
470                                           const NetLogWithSource& net_log) {
471   const MockTransaction* t = FindMockTransaction(request->url);
472   if (!t)
473     return ERR_FAILED;
474 
475   test_mode_ = t->test_mode;
476 
477   // Return immediately if we're returning an error.
478   if (OK != t->start_return_code) {
479     if (test_mode_ & TEST_MODE_SYNC_NET_START)
480       return t->start_return_code;
481     CallbackLater(std::move(callback), t->start_return_code);
482     return ERR_IO_PENDING;
483   }
484 
485   sent_bytes_ = kTotalSentBytes;
486   received_bytes_ = kTotalReceivedBytes;
487 
488   std::string resp_status = t->status;
489   std::string resp_headers = t->response_headers;
490   std::string resp_data = t->data;
491   if (t->handler)
492     (t->handler)(request, &resp_status, &resp_headers, &resp_data);
493   if (t->read_handler)
494     read_handler_ = t->read_handler;
495 
496   std::string header_data = base::StringPrintf(
497       "%s\n%s\n", resp_status.c_str(), resp_headers.c_str());
498   std::replace(header_data.begin(), header_data.end(), '\n', '\0');
499 
500   response_.request_time = transaction_factory_->Now();
501   if (!t->request_time.is_null())
502     response_.request_time = t->request_time;
503 
504   response_.was_cached = false;
505   response_.network_accessed = true;
506 
507   response_.response_time = transaction_factory_->Now();
508   if (!t->response_time.is_null())
509     response_.response_time = t->response_time;
510 
511   response_.headers = new HttpResponseHeaders(header_data);
512   response_.vary_data.Init(*request, *response_.headers.get());
513   response_.ssl_info.cert = t->cert;
514   response_.ssl_info.cert_status = t->cert_status;
515   response_.ssl_info.connection_status = t->ssl_connection_status;
516   data_ = resp_data;
517   content_length_ = response_.headers->GetContentLength();
518 
519   if (net_log.net_log())
520     socket_log_id_ = net_log.net_log()->NextID();
521 
522   if (request_->load_flags & LOAD_PREFETCH)
523     response_.unused_since_prefetch = true;
524 
525   if (request_->load_flags & LOAD_RESTRICTED_PREFETCH) {
526     DCHECK(response_.unused_since_prefetch);
527     response_.restricted_prefetch = true;
528   }
529 
530   // Pause and resume.
531   if (!before_network_start_callback_.is_null()) {
532     bool defer = false;
533     std::move(before_network_start_callback_).Run(&defer);
534     if (defer) {
535       resume_start_callback_ = std::move(callback);
536       return net::ERR_IO_PENDING;
537     }
538   }
539 
540   if (test_mode_ & TEST_MODE_SYNC_NET_START)
541     return OK;
542 
543   int result = OK;
544   if (!connected_callback_.is_null()) {
545     result = connected_callback_.Run(t->transport_info);
546   }
547 
548   CallbackLater(std::move(callback), result);
549   return ERR_IO_PENDING;
550 }
551 
SetBeforeNetworkStartCallback(BeforeNetworkStartCallback callback)552 void MockNetworkTransaction::SetBeforeNetworkStartCallback(
553     BeforeNetworkStartCallback callback) {
554   before_network_start_callback_ = std::move(callback);
555 }
556 
SetConnectedCallback(const ConnectedCallback & callback)557 void MockNetworkTransaction::SetConnectedCallback(
558     const ConnectedCallback& callback) {
559   connected_callback_ = callback;
560 }
561 
ResumeNetworkStart()562 int MockNetworkTransaction::ResumeNetworkStart() {
563   DCHECK(!resume_start_callback_.is_null());
564   CallbackLater(std::move(resume_start_callback_), OK);
565   return ERR_IO_PENDING;
566 }
567 
GetConnectionAttempts(ConnectionAttempts * out) const568 void MockNetworkTransaction::GetConnectionAttempts(
569     ConnectionAttempts* out) const {
570   NOTIMPLEMENTED();
571 }
572 
CallbackLater(CompletionOnceCallback callback,int result)573 void MockNetworkTransaction::CallbackLater(CompletionOnceCallback callback,
574                                            int result) {
575   base::ThreadTaskRunnerHandle::Get()->PostTask(
576       FROM_HERE,
577       base::BindOnce(&MockNetworkTransaction::RunCallback,
578                      weak_factory_.GetWeakPtr(), std::move(callback), result));
579 }
580 
RunCallback(CompletionOnceCallback callback,int result)581 void MockNetworkTransaction::RunCallback(CompletionOnceCallback callback,
582                                          int result) {
583   std::move(callback).Run(result);
584 }
585 
MockNetworkLayer()586 MockNetworkLayer::MockNetworkLayer()
587     : transaction_count_(0),
588       done_reading_called_(false),
589       stop_caching_called_(false),
590       last_create_transaction_priority_(DEFAULT_PRIORITY),
591       clock_(nullptr) {
592 }
593 
594 MockNetworkLayer::~MockNetworkLayer() = default;
595 
TransactionDoneReading()596 void MockNetworkLayer::TransactionDoneReading() {
597   CHECK(!done_reading_called_);
598   done_reading_called_ = true;
599 }
600 
TransactionStopCaching()601 void MockNetworkLayer::TransactionStopCaching() {
602   stop_caching_called_ = true;
603 }
604 
ResetTransactionCount()605 void MockNetworkLayer::ResetTransactionCount() {
606   transaction_count_ = 0;
607 }
608 
CreateTransaction(RequestPriority priority,std::unique_ptr<HttpTransaction> * trans)609 int MockNetworkLayer::CreateTransaction(
610     RequestPriority priority,
611     std::unique_ptr<HttpTransaction>* trans) {
612   transaction_count_++;
613   last_create_transaction_priority_ = priority;
614   std::unique_ptr<MockNetworkTransaction> mock_transaction(
615       new MockNetworkTransaction(priority, this));
616   last_transaction_ = mock_transaction->AsWeakPtr();
617   *trans = std::move(mock_transaction);
618   return OK;
619 }
620 
GetCache()621 HttpCache* MockNetworkLayer::GetCache() {
622   return nullptr;
623 }
624 
GetSession()625 HttpNetworkSession* MockNetworkLayer::GetSession() {
626   return nullptr;
627 }
628 
SetClock(base::Clock * clock)629 void MockNetworkLayer::SetClock(base::Clock* clock) {
630   DCHECK(!clock_);
631   clock_ = clock;
632 }
633 
Now()634 base::Time MockNetworkLayer::Now() {
635   if (clock_)
636     return clock_->Now();
637   return base::Time::Now();
638 }
639 
640 //-----------------------------------------------------------------------------
641 // helpers
642 
ReadTransaction(HttpTransaction * trans,std::string * result)643 int ReadTransaction(HttpTransaction* trans, std::string* result) {
644   int rv;
645 
646   std::string content;
647   do {
648     TestCompletionCallback callback;
649     scoped_refptr<IOBuffer> buf = base::MakeRefCounted<IOBuffer>(256);
650     rv = trans->Read(buf.get(), 256, callback.callback());
651     if (rv == ERR_IO_PENDING) {
652       rv = callback.WaitForResult();
653       base::RunLoop().RunUntilIdle();
654     }
655 
656     if (rv > 0)
657       content.append(buf->data(), rv);
658     else if (rv < 0)
659       return rv;
660   } while (rv > 0);
661 
662   result->swap(content);
663   return OK;
664 }
665 
666 //-----------------------------------------------------------------------------
667 // connected callback handler
668 
669 ConnectedHandler::ConnectedHandler() = default;
670 ConnectedHandler::~ConnectedHandler() = default;
671 
672 ConnectedHandler::ConnectedHandler(const ConnectedHandler&) = default;
673 ConnectedHandler& ConnectedHandler::operator=(const ConnectedHandler&) =
674     default;
675 ConnectedHandler::ConnectedHandler(ConnectedHandler&&) = default;
676 ConnectedHandler& ConnectedHandler::operator=(ConnectedHandler&&) = default;
677 
OnConnected(const TransportInfo & info)678 int ConnectedHandler::OnConnected(const TransportInfo& info) {
679   transports_.push_back(info);
680   return result_;
681 }
682 
683 }  // namespace net
684