1// Copyright (c) 2009 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/tools/flip_server/sm_connection.h"
6
7#include <errno.h>
8#include <netinet/tcp.h>
9#include <sys/socket.h>
10#include <unistd.h>
11
12#include <algorithm>
13#include <list>
14#include <string>
15
16#include "net/tools/flip_server/constants.h"
17#include "net/tools/flip_server/flip_config.h"
18#include "net/tools/flip_server/http_interface.h"
19#include "net/tools/flip_server/spdy_interface.h"
20#include "net/tools/flip_server/spdy_ssl.h"
21#include "net/tools/flip_server/streamer_interface.h"
22
23namespace net {
24
25// static
26bool SMConnection::force_spdy_ = false;
27
28DataFrame::~DataFrame() {
29  if (delete_when_done)
30    delete[] data;
31}
32
33SMConnection::SMConnection(EpollServer* epoll_server,
34                           SSLState* ssl_state,
35                           MemoryCache* memory_cache,
36                           FlipAcceptor* acceptor,
37                           std::string log_prefix)
38    : last_read_time_(0),
39      fd_(-1),
40      events_(0),
41      registered_in_epoll_server_(false),
42      initialized_(false),
43      protocol_detected_(false),
44      connection_complete_(false),
45      connection_pool_(NULL),
46      epoll_server_(epoll_server),
47      ssl_state_(ssl_state),
48      memory_cache_(memory_cache),
49      acceptor_(acceptor),
50      read_buffer_(kSpdySegmentSize * 40),
51      sm_spdy_interface_(NULL),
52      sm_http_interface_(NULL),
53      sm_streamer_interface_(NULL),
54      sm_interface_(NULL),
55      log_prefix_(log_prefix),
56      max_bytes_sent_per_dowrite_(4096),
57      ssl_(NULL) {}
58
59SMConnection::~SMConnection() {
60  if (initialized())
61    Reset();
62}
63
64EpollServer* SMConnection::epoll_server() { return epoll_server_; }
65
66void SMConnection::ReadyToSend() {
67  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
68          << "Setting ready to send: EPOLLIN | EPOLLOUT";
69  epoll_server_->SetFDReady(fd_, EPOLLIN | EPOLLOUT);
70}
71
72void SMConnection::EnqueueDataFrame(DataFrame* df) {
73  output_list_.push_back(df);
74  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "EnqueueDataFrame: "
75          << "size = " << df->size << ": Setting FD ready.";
76  ReadyToSend();
77}
78
79void SMConnection::InitSMConnection(SMConnectionPoolInterface* connection_pool,
80                                    SMInterface* sm_interface,
81                                    EpollServer* epoll_server,
82                                    int fd,
83                                    std::string server_ip,
84                                    std::string server_port,
85                                    std::string remote_ip,
86                                    bool use_ssl) {
87  if (initialized_) {
88    LOG(FATAL) << "Attempted to initialize already initialized server";
89    return;
90  }
91
92  client_ip_ = remote_ip;
93
94  if (fd == -1) {
95    // If fd == -1, then we are initializing a new connection that will
96    // connect to the backend.
97    //
98    // ret:  -1 == error
99    //        0 == connection in progress
100    //        1 == connection complete
101    // TODO(kelindsay): is_numeric_host_address value needs to be detected
102    server_ip_ = server_ip;
103    server_port_ = server_port;
104    int ret = CreateConnectedSocket(
105        &fd_, server_ip, server_port, true, acceptor_->disable_nagle_);
106
107    if (ret < 0) {
108      LOG(ERROR) << "-1 Could not create connected socket";
109      return;
110    } else if (ret == 1) {
111      DCHECK_NE(-1, fd_);
112      connection_complete_ = true;
113      VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
114              << "Connection complete to: " << server_ip_ << ":" << server_port_
115              << " ";
116    }
117    VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
118            << "Connecting to server: " << server_ip_ << ":" << server_port_
119            << " ";
120  } else {
121    // If fd != -1 then we are initializing a connection that has just been
122    // accepted from the listen socket.
123    connection_complete_ = true;
124    if (epoll_server_ && registered_in_epoll_server_ && fd_ != -1) {
125      epoll_server_->UnregisterFD(fd_);
126    }
127    if (fd_ != -1) {
128      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
129              << "Closing pre-existing fd";
130      close(fd_);
131      fd_ = -1;
132    }
133
134    fd_ = fd;
135  }
136
137  registered_in_epoll_server_ = false;
138  // Set the last read time here as the idle checker will start from
139  // now.
140  last_read_time_ = time(NULL);
141  initialized_ = true;
142
143  connection_pool_ = connection_pool;
144  epoll_server_ = epoll_server;
145
146  if (sm_interface) {
147    sm_interface_ = sm_interface;
148    protocol_detected_ = true;
149  }
150
151  read_buffer_.Clear();
152
153  epoll_server_->RegisterFD(fd_, this, EPOLLIN | EPOLLOUT | EPOLLET);
154
155  if (use_ssl) {
156    ssl_ = CreateSSLContext(ssl_state_->ssl_ctx);
157    SSL_set_fd(ssl_, fd_);
158    PrintSslError();
159  }
160}
161
162void SMConnection::CorkSocket() {
163  int state = 1;
164  int rv = setsockopt(fd_, IPPROTO_TCP, TCP_CORK, &state, sizeof(state));
165  if (rv < 0)
166    VLOG(1) << "setsockopt(CORK): " << errno;
167}
168
169void SMConnection::UncorkSocket() {
170  int state = 0;
171  int rv = setsockopt(fd_, IPPROTO_TCP, TCP_CORK, &state, sizeof(state));
172  if (rv < 0)
173    VLOG(1) << "setsockopt(CORK): " << errno;
174}
175
176int SMConnection::Send(const char* data, int len, int flags) {
177  int rv = 0;
178  CorkSocket();
179  if (ssl_) {
180    ssize_t bytes_written = 0;
181    // Write smallish chunks to SSL so that we don't have large
182    // multi-packet TLS records to receive before being able to handle
183    // the data.  We don't have to be too careful here, because our data
184    // frames are already getting chunked appropriately, and those are
185    // the most likely "big" frames.
186    while (len > 0) {
187      const int kMaxTLSRecordSize = 1500;
188      const char* ptr = &(data[bytes_written]);
189      int chunksize = std::min(len, kMaxTLSRecordSize);
190      rv = SSL_write(ssl_, ptr, chunksize);
191      VLOG(2) << "SSLWrite(" << chunksize << " bytes): " << rv;
192      if (rv <= 0) {
193        switch (SSL_get_error(ssl_, rv)) {
194          case SSL_ERROR_WANT_READ:
195          case SSL_ERROR_WANT_WRITE:
196          case SSL_ERROR_WANT_ACCEPT:
197          case SSL_ERROR_WANT_CONNECT:
198            rv = -2;
199            break;
200          default:
201            PrintSslError();
202            break;
203        }
204        break;
205      }
206      bytes_written += rv;
207      len -= rv;
208      if (rv != chunksize)
209        break;  // If we couldn't write everything, we're implicitly stalled
210    }
211    // If we wrote some data, return that count.  Otherwise
212    // return the stall error.
213    if (bytes_written > 0)
214      rv = bytes_written;
215  } else {
216    rv = send(fd_, data, len, flags);
217  }
218  if (!(flags & MSG_MORE))
219    UncorkSocket();
220  return rv;
221}
222
223void SMConnection::OnRegistration(EpollServer* eps, int fd, int event_mask) {
224  registered_in_epoll_server_ = true;
225}
226
227void SMConnection::OnEvent(int fd, EpollEvent* event) {
228  events_ |= event->in_events;
229  HandleEvents();
230  if (events_) {
231    event->out_ready_mask = events_;
232    events_ = 0;
233  }
234}
235
236void SMConnection::OnUnregistration(int fd, bool replaced) {
237  registered_in_epoll_server_ = false;
238}
239
240void SMConnection::OnShutdown(EpollServer* eps, int fd) {
241  Cleanup("OnShutdown");
242  return;
243}
244
245void SMConnection::Cleanup(const char* cleanup) {
246  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "Cleanup: " << cleanup;
247  if (!initialized_)
248    return;
249  Reset();
250  if (connection_pool_)
251    connection_pool_->SMConnectionDone(this);
252  if (sm_interface_)
253    sm_interface_->ResetForNewConnection();
254  last_read_time_ = 0;
255}
256
257void SMConnection::HandleEvents() {
258  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
259          << "Received: " << EpollServer::EventMaskToString(events_).c_str();
260
261  if (events_ & EPOLLIN) {
262    if (!DoRead())
263      goto handle_close_or_error;
264  }
265
266  if (events_ & EPOLLOUT) {
267    // Check if we have connected or not
268    if (connection_complete_ == false) {
269      int sock_error;
270      socklen_t sock_error_len = sizeof(sock_error);
271      int ret =
272          getsockopt(fd_, SOL_SOCKET, SO_ERROR, &sock_error, &sock_error_len);
273      if (ret != 0) {
274        VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
275                << "getsockopt error: " << errno << ": " << strerror(errno);
276        goto handle_close_or_error;
277      }
278      if (sock_error == 0) {
279        connection_complete_ = true;
280        VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
281                << "Connection complete to " << server_ip_ << ":"
282                << server_port_ << " ";
283      } else if (sock_error == EINPROGRESS) {
284        return;
285      } else {
286        VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
287                << "error connecting to server";
288        goto handle_close_or_error;
289      }
290    }
291    if (!DoWrite())
292      goto handle_close_or_error;
293  }
294
295  if (events_ & (EPOLLHUP | EPOLLERR)) {
296    VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "!!! Got HUP or ERR";
297    goto handle_close_or_error;
298  }
299  return;
300
301 handle_close_or_error:
302  Cleanup("HandleEvents");
303}
304
305// Decide if SPDY was negotiated.
306bool SMConnection::WasSpdyNegotiated(SpdyMajorVersion* version_negotiated) {
307  *version_negotiated = SPDY3;
308  if (force_spdy())
309    return true;
310
311  // If this is an SSL connection, check if NPN specifies SPDY.
312  if (ssl_) {
313    const unsigned char* npn_proto;
314    unsigned int npn_proto_len;
315    SSL_get0_next_proto_negotiated(ssl_, &npn_proto, &npn_proto_len);
316    if (npn_proto_len > 0) {
317      std::string npn_proto_str((const char*)npn_proto, npn_proto_len);
318      VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
319              << "NPN protocol detected: " << npn_proto_str;
320      if (!strncmp(reinterpret_cast<const char*>(npn_proto),
321                   "spdy/2",
322                   npn_proto_len)) {
323        *version_negotiated = SPDY2;
324        return true;
325      }
326      if (!strncmp(reinterpret_cast<const char*>(npn_proto),
327                   "spdy/3",
328                   npn_proto_len)) {
329        *version_negotiated = SPDY3;
330        return true;
331      }
332      if (!strncmp(reinterpret_cast<const char*>(npn_proto),
333                   "spdy/4a2",
334                   npn_proto_len)) {
335        *version_negotiated = SPDY4;
336        return true;
337      }
338    }
339  }
340
341  return false;
342}
343
344bool SMConnection::SetupProtocolInterfaces() {
345  DCHECK(!protocol_detected_);
346  protocol_detected_ = true;
347
348  SpdyMajorVersion version;
349  bool spdy_negotiated = WasSpdyNegotiated(&version);
350  bool using_ssl = ssl_ != NULL;
351
352  if (using_ssl)
353    VLOG(1) << (SSL_session_reused(ssl_) ? "Resumed" : "Renegotiated")
354            << " SSL Session.";
355
356  if (acceptor_->spdy_only_ && !spdy_negotiated) {
357    VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
358            << "SPDY proxy only, closing HTTPS connection.";
359    return false;
360  }
361
362  switch (acceptor_->flip_handler_type_) {
363    case FLIP_HANDLER_HTTP_SERVER: {
364      DCHECK(!spdy_negotiated);
365      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
366              << (sm_http_interface_ ? "Creating" : "Reusing")
367              << " HTTP interface.";
368      if (!sm_http_interface_)
369        sm_http_interface_ = new HttpSM(this, NULL, memory_cache_, acceptor_);
370      sm_interface_ = sm_http_interface_;
371      break;
372    }
373    case FLIP_HANDLER_PROXY: {
374      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
375              << (sm_streamer_interface_ ? "Creating" : "Reusing")
376              << " PROXY Streamer interface.";
377      if (!sm_streamer_interface_) {
378        sm_streamer_interface_ =
379            new StreamerSM(this, NULL, epoll_server_, acceptor_);
380        sm_streamer_interface_->set_is_request();
381      }
382      sm_interface_ = sm_streamer_interface_;
383      // If spdy is not negotiated, the streamer interface will proxy all
384      // data to the origin server.
385      if (!spdy_negotiated)
386        break;
387    }
388    // Otherwise fall through into the case below.
389    case FLIP_HANDLER_SPDY_SERVER: {
390      DCHECK(spdy_negotiated);
391      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
392              << (sm_spdy_interface_ ? "Creating" : "Reusing")
393              << " SPDY interface.";
394      if (sm_spdy_interface_)
395        sm_spdy_interface_->CreateFramer(version);
396      else
397        sm_spdy_interface_ = new SpdySM(
398            this, NULL, epoll_server_, memory_cache_, acceptor_, version);
399      sm_interface_ = sm_spdy_interface_;
400      break;
401    }
402  }
403
404  CorkSocket();
405  if (!sm_interface_->PostAcceptHook())
406    return false;
407
408  return true;
409}
410
411bool SMConnection::DoRead() {
412  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "DoRead()";
413  while (!read_buffer_.Full()) {
414    char* bytes;
415    int size;
416    if (fd_ == -1) {
417      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
418              << "DoRead(): fd_ == -1. Invalid FD. Returning false";
419      return false;
420    }
421    read_buffer_.GetWritablePtr(&bytes, &size);
422    ssize_t bytes_read = 0;
423    if (ssl_) {
424      bytes_read = SSL_read(ssl_, bytes, size);
425      if (bytes_read < 0) {
426        int err = SSL_get_error(ssl_, bytes_read);
427        switch (err) {
428          case SSL_ERROR_WANT_READ:
429          case SSL_ERROR_WANT_WRITE:
430          case SSL_ERROR_WANT_ACCEPT:
431          case SSL_ERROR_WANT_CONNECT:
432            events_ &= ~EPOLLIN;
433            VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
434                    << "DoRead: SSL WANT_XXX: " << err;
435            goto done;
436          default:
437            PrintSslError();
438            goto error_or_close;
439        }
440      }
441    } else {
442      bytes_read = recv(fd_, bytes, size, MSG_DONTWAIT);
443    }
444    int stored_errno = errno;
445    if (bytes_read == -1) {
446      switch (stored_errno) {
447        case EAGAIN:
448          events_ &= ~EPOLLIN;
449          VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
450                  << "Got EAGAIN while reading";
451          goto done;
452        case EINTR:
453          VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
454                  << "Got EINTR while reading";
455          continue;
456        default:
457          VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
458                  << "While calling recv, got error: "
459                  << (ssl_ ? "(ssl error)" : strerror(stored_errno));
460          goto error_or_close;
461      }
462    } else if (bytes_read > 0) {
463      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "read " << bytes_read
464              << " bytes";
465      last_read_time_ = time(NULL);
466      // If the protocol hasn't been detected yet, set up the handlers
467      // we'll need.
468      if (!protocol_detected_) {
469        if (!SetupProtocolInterfaces()) {
470          LOG(ERROR) << "Error setting up protocol interfaces.";
471          goto error_or_close;
472        }
473      }
474      read_buffer_.AdvanceWritablePtr(bytes_read);
475      if (!DoConsumeReadData())
476        goto error_or_close;
477      continue;
478    } else {  // bytes_read == 0
479      VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
480              << "0 bytes read with recv call.";
481    }
482    goto error_or_close;
483  }
484 done:
485  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "DoRead done!";
486  return true;
487
488 error_or_close:
489  VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
490          << "DoRead(): error_or_close. "
491          << "Cleaning up, then returning false";
492  Cleanup("DoRead");
493  return false;
494}
495
496bool SMConnection::DoConsumeReadData() {
497  char* bytes;
498  int size;
499  read_buffer_.GetReadablePtr(&bytes, &size);
500  while (size != 0) {
501    size_t bytes_consumed = sm_interface_->ProcessReadInput(bytes, size);
502    VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "consumed "
503            << bytes_consumed << " bytes";
504    if (bytes_consumed == 0) {
505      break;
506    }
507    read_buffer_.AdvanceReadablePtr(bytes_consumed);
508    if (sm_interface_->MessageFullyRead()) {
509      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
510              << "HandleRequestFullyRead: Setting EPOLLOUT";
511      HandleResponseFullyRead();
512      events_ |= EPOLLOUT;
513    } else if (sm_interface_->Error()) {
514      LOG(ERROR) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
515                 << "Framer error detected: Setting EPOLLOUT: "
516                 << sm_interface_->ErrorAsString();
517      // this causes everything to be closed/cleaned up.
518      events_ |= EPOLLOUT;
519      return false;
520    }
521    read_buffer_.GetReadablePtr(&bytes, &size);
522  }
523  return true;
524}
525
526void SMConnection::HandleResponseFullyRead() { sm_interface_->Cleanup(); }
527
528bool SMConnection::DoWrite() {
529  size_t bytes_sent = 0;
530  int flags = MSG_NOSIGNAL | MSG_DONTWAIT;
531  if (fd_ == -1) {
532    VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
533            << "DoWrite: fd == -1. Returning false.";
534    return false;
535  }
536  if (output_list_.empty()) {
537    VLOG(2) << log_prefix_ << "DoWrite: Output list empty.";
538    if (sm_interface_) {
539      sm_interface_->GetOutput();
540    }
541    if (output_list_.empty()) {
542      events_ &= ~EPOLLOUT;
543    }
544  }
545  while (!output_list_.empty()) {
546    VLOG(2) << log_prefix_
547            << "DoWrite: Items in output list: " << output_list_.size();
548    if (bytes_sent >= max_bytes_sent_per_dowrite_) {
549      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
550              << " byte sent >= max bytes sent per write: Setting EPOLLOUT: "
551              << bytes_sent;
552      events_ |= EPOLLOUT;
553      break;
554    }
555    if (sm_interface_ && output_list_.size() < 2) {
556      sm_interface_->GetOutput();
557    }
558    DataFrame* data_frame = output_list_.front();
559    const char* bytes = data_frame->data;
560    int size = data_frame->size;
561    bytes += data_frame->index;
562    size -= data_frame->index;
563    DCHECK_GE(size, 0);
564    if (size <= 0) {
565      output_list_.pop_front();
566      delete data_frame;
567      continue;
568    }
569
570    flags = MSG_NOSIGNAL | MSG_DONTWAIT;
571    // Look for a queue size > 1 because |this| frame is remains on the list
572    // until it has finished sending.
573    if (output_list_.size() > 1) {
574      VLOG(2) << log_prefix_ << "Outlist size: " << output_list_.size()
575              << ": Adding MSG_MORE flag";
576      flags |= MSG_MORE;
577    }
578    VLOG(2) << log_prefix_ << "Attempting to send " << size << " bytes.";
579    ssize_t bytes_written = Send(bytes, size, flags);
580    int stored_errno = errno;
581    if (bytes_written == -1) {
582      switch (stored_errno) {
583        case EAGAIN:
584          events_ &= ~EPOLLOUT;
585          VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
586                  << "Got EAGAIN while writing";
587          goto done;
588        case EINTR:
589          VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
590                  << "Got EINTR while writing";
591          continue;
592        default:
593          VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
594                  << "While calling send, got error: " << stored_errno << ": "
595                  << (ssl_ ? "" : strerror(stored_errno));
596          goto error_or_close;
597      }
598    } else if (bytes_written > 0) {
599      VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
600              << "Wrote: " << bytes_written << " bytes";
601      data_frame->index += bytes_written;
602      bytes_sent += bytes_written;
603      continue;
604    } else if (bytes_written == -2) {
605      // -2 handles SSL_ERROR_WANT_* errors
606      events_ &= ~EPOLLOUT;
607      goto done;
608    }
609    VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
610            << "0 bytes written with send call.";
611    goto error_or_close;
612  }
613 done:
614  UncorkSocket();
615  return true;
616
617 error_or_close:
618  VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
619          << "DoWrite: error_or_close. Returning false "
620          << "after cleaning up";
621  Cleanup("DoWrite");
622  UncorkSocket();
623  return false;
624}
625
626void SMConnection::Reset() {
627  VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "Resetting";
628  if (ssl_) {
629    SSL_shutdown(ssl_);
630    PrintSslError();
631    SSL_free(ssl_);
632    PrintSslError();
633    ssl_ = NULL;
634  }
635  if (registered_in_epoll_server_) {
636    epoll_server_->UnregisterFD(fd_);
637    registered_in_epoll_server_ = false;
638  }
639  if (fd_ >= 0) {
640    VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "Closing connection";
641    close(fd_);
642    fd_ = -1;
643  }
644  read_buffer_.Clear();
645  initialized_ = false;
646  protocol_detected_ = false;
647  events_ = 0;
648  for (std::list<DataFrame*>::iterator i = output_list_.begin();
649       i != output_list_.end();
650       ++i) {
651    delete *i;
652  }
653  output_list_.clear();
654}
655
656// static
657SMConnection* SMConnection::NewSMConnection(EpollServer* epoll_server,
658                                            SSLState* ssl_state,
659                                            MemoryCache* memory_cache,
660                                            FlipAcceptor* acceptor,
661                                            std::string log_prefix) {
662  return new SMConnection(
663      epoll_server, ssl_state, memory_cache, acceptor, log_prefix);
664}
665
666}  // namespace net
667