sm_connection.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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_ = new SpdySM( 396 this, NULL, epoll_server_, memory_cache_, acceptor_, version); 397 sm_interface_ = sm_spdy_interface_; 398 break; 399 } 400 } 401 402 CorkSocket(); 403 if (!sm_interface_->PostAcceptHook()) 404 return false; 405 406 return true; 407} 408 409bool SMConnection::DoRead() { 410 VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "DoRead()"; 411 while (!read_buffer_.Full()) { 412 char* bytes; 413 int size; 414 if (fd_ == -1) { 415 VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 416 << "DoRead(): fd_ == -1. Invalid FD. Returning false"; 417 return false; 418 } 419 read_buffer_.GetWritablePtr(&bytes, &size); 420 ssize_t bytes_read = 0; 421 if (ssl_) { 422 bytes_read = SSL_read(ssl_, bytes, size); 423 if (bytes_read < 0) { 424 int err = SSL_get_error(ssl_, bytes_read); 425 switch (err) { 426 case SSL_ERROR_WANT_READ: 427 case SSL_ERROR_WANT_WRITE: 428 case SSL_ERROR_WANT_ACCEPT: 429 case SSL_ERROR_WANT_CONNECT: 430 events_ &= ~EPOLLIN; 431 VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 432 << "DoRead: SSL WANT_XXX: " << err; 433 goto done; 434 default: 435 PrintSslError(); 436 goto error_or_close; 437 } 438 } 439 } else { 440 bytes_read = recv(fd_, bytes, size, MSG_DONTWAIT); 441 } 442 int stored_errno = errno; 443 if (bytes_read == -1) { 444 switch (stored_errno) { 445 case EAGAIN: 446 events_ &= ~EPOLLIN; 447 VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 448 << "Got EAGAIN while reading"; 449 goto done; 450 case EINTR: 451 VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 452 << "Got EINTR while reading"; 453 continue; 454 default: 455 VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 456 << "While calling recv, got error: " 457 << (ssl_ ? "(ssl error)" : strerror(stored_errno)); 458 goto error_or_close; 459 } 460 } else if (bytes_read > 0) { 461 VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "read " << bytes_read 462 << " bytes"; 463 last_read_time_ = time(NULL); 464 // If the protocol hasn't been detected yet, set up the handlers 465 // we'll need. 466 if (!protocol_detected_) { 467 if (!SetupProtocolInterfaces()) { 468 LOG(ERROR) << "Error setting up protocol interfaces."; 469 goto error_or_close; 470 } 471 } 472 read_buffer_.AdvanceWritablePtr(bytes_read); 473 if (!DoConsumeReadData()) 474 goto error_or_close; 475 continue; 476 } else { // bytes_read == 0 477 VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 478 << "0 bytes read with recv call."; 479 } 480 goto error_or_close; 481 } 482 done: 483 VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "DoRead done!"; 484 return true; 485 486 error_or_close: 487 VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 488 << "DoRead(): error_or_close. " 489 << "Cleaning up, then returning false"; 490 Cleanup("DoRead"); 491 return false; 492} 493 494bool SMConnection::DoConsumeReadData() { 495 char* bytes; 496 int size; 497 read_buffer_.GetReadablePtr(&bytes, &size); 498 while (size != 0) { 499 size_t bytes_consumed = sm_interface_->ProcessReadInput(bytes, size); 500 VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "consumed " 501 << bytes_consumed << " bytes"; 502 if (bytes_consumed == 0) { 503 break; 504 } 505 read_buffer_.AdvanceReadablePtr(bytes_consumed); 506 if (sm_interface_->MessageFullyRead()) { 507 VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 508 << "HandleRequestFullyRead: Setting EPOLLOUT"; 509 HandleResponseFullyRead(); 510 events_ |= EPOLLOUT; 511 } else if (sm_interface_->Error()) { 512 LOG(ERROR) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 513 << "Framer error detected: Setting EPOLLOUT: " 514 << sm_interface_->ErrorAsString(); 515 // this causes everything to be closed/cleaned up. 516 events_ |= EPOLLOUT; 517 return false; 518 } 519 read_buffer_.GetReadablePtr(&bytes, &size); 520 } 521 return true; 522} 523 524void SMConnection::HandleResponseFullyRead() { sm_interface_->Cleanup(); } 525 526bool SMConnection::DoWrite() { 527 size_t bytes_sent = 0; 528 int flags = MSG_NOSIGNAL | MSG_DONTWAIT; 529 if (fd_ == -1) { 530 VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 531 << "DoWrite: fd == -1. Returning false."; 532 return false; 533 } 534 if (output_list_.empty()) { 535 VLOG(2) << log_prefix_ << "DoWrite: Output list empty."; 536 if (sm_interface_) { 537 sm_interface_->GetOutput(); 538 } 539 if (output_list_.empty()) { 540 events_ &= ~EPOLLOUT; 541 } 542 } 543 while (!output_list_.empty()) { 544 VLOG(2) << log_prefix_ 545 << "DoWrite: Items in output list: " << output_list_.size(); 546 if (bytes_sent >= max_bytes_sent_per_dowrite_) { 547 VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 548 << " byte sent >= max bytes sent per write: Setting EPOLLOUT: " 549 << bytes_sent; 550 events_ |= EPOLLOUT; 551 break; 552 } 553 if (sm_interface_ && output_list_.size() < 2) { 554 sm_interface_->GetOutput(); 555 } 556 DataFrame* data_frame = output_list_.front(); 557 const char* bytes = data_frame->data; 558 int size = data_frame->size; 559 bytes += data_frame->index; 560 size -= data_frame->index; 561 DCHECK_GE(size, 0); 562 if (size <= 0) { 563 output_list_.pop_front(); 564 delete data_frame; 565 continue; 566 } 567 568 flags = MSG_NOSIGNAL | MSG_DONTWAIT; 569 // Look for a queue size > 1 because |this| frame is remains on the list 570 // until it has finished sending. 571 if (output_list_.size() > 1) { 572 VLOG(2) << log_prefix_ << "Outlist size: " << output_list_.size() 573 << ": Adding MSG_MORE flag"; 574 flags |= MSG_MORE; 575 } 576 VLOG(2) << log_prefix_ << "Attempting to send " << size << " bytes."; 577 ssize_t bytes_written = Send(bytes, size, flags); 578 int stored_errno = errno; 579 if (bytes_written == -1) { 580 switch (stored_errno) { 581 case EAGAIN: 582 events_ &= ~EPOLLOUT; 583 VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 584 << "Got EAGAIN while writing"; 585 goto done; 586 case EINTR: 587 VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 588 << "Got EINTR while writing"; 589 continue; 590 default: 591 VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 592 << "While calling send, got error: " << stored_errno << ": " 593 << (ssl_ ? "" : strerror(stored_errno)); 594 goto error_or_close; 595 } 596 } else if (bytes_written > 0) { 597 VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 598 << "Wrote: " << bytes_written << " bytes"; 599 data_frame->index += bytes_written; 600 bytes_sent += bytes_written; 601 continue; 602 } else if (bytes_written == -2) { 603 // -2 handles SSL_ERROR_WANT_* errors 604 events_ &= ~EPOLLOUT; 605 goto done; 606 } 607 VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 608 << "0 bytes written with send call."; 609 goto error_or_close; 610 } 611 done: 612 UncorkSocket(); 613 return true; 614 615 error_or_close: 616 VLOG(1) << log_prefix_ << ACCEPTOR_CLIENT_IDENT 617 << "DoWrite: error_or_close. Returning false " 618 << "after cleaning up"; 619 Cleanup("DoWrite"); 620 UncorkSocket(); 621 return false; 622} 623 624void SMConnection::Reset() { 625 VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "Resetting"; 626 if (ssl_) { 627 SSL_shutdown(ssl_); 628 PrintSslError(); 629 SSL_free(ssl_); 630 PrintSslError(); 631 ssl_ = NULL; 632 } 633 if (registered_in_epoll_server_) { 634 epoll_server_->UnregisterFD(fd_); 635 registered_in_epoll_server_ = false; 636 } 637 if (fd_ >= 0) { 638 VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << "Closing connection"; 639 close(fd_); 640 fd_ = -1; 641 } 642 read_buffer_.Clear(); 643 initialized_ = false; 644 protocol_detected_ = false; 645 events_ = 0; 646 for (std::list<DataFrame*>::iterator i = output_list_.begin(); 647 i != output_list_.end(); 648 ++i) { 649 delete *i; 650 } 651 output_list_.clear(); 652} 653 654// static 655SMConnection* SMConnection::NewSMConnection(EpollServer* epoll_server, 656 SSLState* ssl_state, 657 MemoryCache* memory_cache, 658 FlipAcceptor* acceptor, 659 std::string log_prefix) { 660 return new SMConnection( 661 epoll_server, ssl_state, memory_cache, acceptor, log_prefix); 662} 663 664} // namespace net 665