| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/tools/flip_server/acceptor_thread.h" |
| |
| #include <errno.h> |
| #include <netinet/in.h> |
| #include <string.h> // For strerror |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| |
| #include <string> |
| |
| #include "net/socket/tcp_socket.h" |
| #include "net/tools/flip_server/constants.h" |
| #include "net/tools/flip_server/flip_config.h" |
| #include "net/tools/flip_server/sm_connection.h" |
| #include "net/tools/flip_server/spdy_ssl.h" |
| #include "openssl/err.h" |
| #include "openssl/ssl.h" |
| |
| namespace net { |
| |
| SMAcceptorThread::SMAcceptorThread(FlipAcceptor* acceptor, |
| MemoryCache* memory_cache) |
| : SimpleThread("SMAcceptorThread"), |
| acceptor_(acceptor), |
| ssl_state_(NULL), |
| use_ssl_(false), |
| idle_socket_timeout_s_(acceptor->idle_socket_timeout_s_), |
| quitting_(false), |
| memory_cache_(memory_cache) { |
| if (!acceptor->ssl_cert_filename_.empty() && |
| !acceptor->ssl_key_filename_.empty()) { |
| ssl_state_ = new SSLState; |
| bool use_npn = true; |
| if (acceptor_->flip_handler_type_ == FLIP_HANDLER_HTTP_SERVER) { |
| use_npn = false; |
| } |
| InitSSL(ssl_state_, |
| acceptor_->ssl_cert_filename_, |
| acceptor_->ssl_key_filename_, |
| use_npn, |
| acceptor_->ssl_session_expiry_, |
| acceptor_->ssl_disable_compression_); |
| use_ssl_ = true; |
| } |
| } |
| |
| SMAcceptorThread::~SMAcceptorThread() { |
| for (std::vector<SMConnection*>::iterator i = |
| allocated_server_connections_.begin(); |
| i != allocated_server_connections_.end(); |
| ++i) { |
| delete *i; |
| } |
| delete ssl_state_; |
| } |
| |
| SMConnection* SMAcceptorThread::NewConnection() { |
| SMConnection* server = SMConnection::NewSMConnection( |
| &epoll_server_, ssl_state_, memory_cache_, acceptor_, "client_conn: "); |
| allocated_server_connections_.push_back(server); |
| VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Acceptor: Making new server."; |
| return server; |
| } |
| |
| SMConnection* SMAcceptorThread::FindOrMakeNewSMConnection() { |
| if (unused_server_connections_.empty()) { |
| return NewConnection(); |
| } |
| SMConnection* server = unused_server_connections_.back(); |
| unused_server_connections_.pop_back(); |
| VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Acceptor: Reusing server."; |
| return server; |
| } |
| |
| void SMAcceptorThread::InitWorker() { |
| epoll_server_.RegisterFD(acceptor_->listen_fd_, this, EPOLLIN | EPOLLET); |
| } |
| |
| void SMAcceptorThread::HandleConnection(int server_fd, |
| struct sockaddr_in* remote_addr) { |
| if (acceptor_->disable_nagle_) { |
| if (!SetTCPNoDelay(server_fd, /*no_delay=*/true)) { |
| close(server_fd); |
| LOG(FATAL) << "SetTCPNoDelay() failed on fd: " << server_fd; |
| return; |
| } |
| } |
| |
| SMConnection* server_connection = FindOrMakeNewSMConnection(); |
| if (server_connection == NULL) { |
| VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Acceptor: Closing fd " << server_fd; |
| close(server_fd); |
| return; |
| } |
| std::string remote_ip = inet_ntoa(remote_addr->sin_addr); |
| server_connection->InitSMConnection(this, |
| NULL, |
| &epoll_server_, |
| server_fd, |
| std::string(), |
| std::string(), |
| remote_ip, |
| use_ssl_); |
| if (server_connection->initialized()) |
| active_server_connections_.push_back(server_connection); |
| } |
| |
| void SMAcceptorThread::AcceptFromListenFD() { |
| if (acceptor_->accepts_per_wake_ > 0) { |
| for (int i = 0; i < acceptor_->accepts_per_wake_; ++i) { |
| struct sockaddr address; |
| socklen_t socklen = sizeof(address); |
| int fd = accept(acceptor_->listen_fd_, &address, &socklen); |
| if (fd == -1) { |
| if (errno != 11) { |
| VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Acceptor: accept fail(" |
| << acceptor_->listen_fd_ << "): " << errno << ": " |
| << strerror(errno); |
| } |
| break; |
| } |
| VLOG(1) << ACCEPTOR_CLIENT_IDENT << " Accepted connection"; |
| HandleConnection(fd, (struct sockaddr_in*)&address); |
| } |
| } else { |
| while (true) { |
| struct sockaddr address; |
| socklen_t socklen = sizeof(address); |
| int fd = accept(acceptor_->listen_fd_, &address, &socklen); |
| if (fd == -1) { |
| if (errno != 11) { |
| VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Acceptor: accept fail(" |
| << acceptor_->listen_fd_ << "): " << errno << ": " |
| << strerror(errno); |
| } |
| break; |
| } |
| VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Accepted connection"; |
| HandleConnection(fd, (struct sockaddr_in*)&address); |
| } |
| } |
| } |
| |
| void SMAcceptorThread::HandleConnectionIdleTimeout() { |
| static time_t oldest_time = time(NULL); |
| |
| int cur_time = time(NULL); |
| // Only iterate the list if we speculate that a connection is ready to be |
| // expired |
| if ((cur_time - oldest_time) < idle_socket_timeout_s_) |
| return; |
| |
| // TODO(mbelshe): This code could be optimized, active_server_connections_ |
| // is already in-order. |
| std::list<SMConnection*>::iterator iter = active_server_connections_.begin(); |
| while (iter != active_server_connections_.end()) { |
| SMConnection* conn = *iter; |
| int elapsed_time = (cur_time - conn->last_read_time_); |
| if (elapsed_time > idle_socket_timeout_s_) { |
| conn->Cleanup("Connection idle timeout reached."); |
| iter = active_server_connections_.erase(iter); |
| continue; |
| } |
| if (conn->last_read_time_ < oldest_time) |
| oldest_time = conn->last_read_time_; |
| iter++; |
| } |
| if ((cur_time - oldest_time) >= idle_socket_timeout_s_) |
| oldest_time = cur_time; |
| } |
| |
| void SMAcceptorThread::Run() { |
| while (!quitting_.HasBeenNotified()) { |
| epoll_server_.set_timeout_in_us(10 * 1000); // 10 ms |
| epoll_server_.WaitForEventsAndExecuteCallbacks(); |
| if (tmp_unused_server_connections_.size()) { |
| VLOG(2) << "have " << tmp_unused_server_connections_.size() |
| << " additional unused connections. Total = " |
| << unused_server_connections_.size(); |
| unused_server_connections_.insert(unused_server_connections_.end(), |
| tmp_unused_server_connections_.begin(), |
| tmp_unused_server_connections_.end()); |
| tmp_unused_server_connections_.clear(); |
| } |
| HandleConnectionIdleTimeout(); |
| } |
| } |
| |
| void SMAcceptorThread::OnEvent(int fd, EpollEvent* event) { |
| if (event->in_events | EPOLLIN) { |
| VLOG(2) << ACCEPTOR_CLIENT_IDENT |
| << "Acceptor: Accepting based upon epoll events"; |
| AcceptFromListenFD(); |
| } |
| } |
| |
| void SMAcceptorThread::SMConnectionDone(SMConnection* sc) { |
| VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Done with connection."; |
| tmp_unused_server_connections_.push_back(sc); |
| } |
| |
| } // namespace net |