1// Copyright (c) 2012 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/acceptor_thread.h"
6
7#include <errno.h>
8#include <netinet/in.h>
9#include <netinet/tcp.h>  // For TCP_NODELAY
10#include <sys/socket.h>
11#include <sys/types.h>
12
13#include <string>
14
15#include "net/tools/flip_server/constants.h"
16#include "net/tools/flip_server/flip_config.h"
17#include "net/tools/flip_server/sm_connection.h"
18#include "net/tools/flip_server/spdy_ssl.h"
19#include "openssl/err.h"
20#include "openssl/ssl.h"
21
22namespace net {
23
24SMAcceptorThread::SMAcceptorThread(FlipAcceptor* acceptor,
25                                   MemoryCache* memory_cache)
26    : SimpleThread("SMAcceptorThread"),
27      acceptor_(acceptor),
28      ssl_state_(NULL),
29      use_ssl_(false),
30      idle_socket_timeout_s_(acceptor->idle_socket_timeout_s_),
31      quitting_(false),
32      memory_cache_(memory_cache) {
33  if (!acceptor->ssl_cert_filename_.empty() &&
34      !acceptor->ssl_key_filename_.empty()) {
35    ssl_state_ = new SSLState;
36    bool use_npn = true;
37    if (acceptor_->flip_handler_type_ == FLIP_HANDLER_HTTP_SERVER) {
38      use_npn = false;
39    }
40    InitSSL(ssl_state_,
41            acceptor_->ssl_cert_filename_,
42            acceptor_->ssl_key_filename_,
43            use_npn,
44            acceptor_->ssl_session_expiry_,
45            acceptor_->ssl_disable_compression_);
46    use_ssl_ = true;
47  }
48}
49
50SMAcceptorThread::~SMAcceptorThread() {
51  for (std::vector<SMConnection*>::iterator i =
52           allocated_server_connections_.begin();
53       i != allocated_server_connections_.end();
54       ++i) {
55    delete *i;
56  }
57  delete ssl_state_;
58}
59
60SMConnection* SMAcceptorThread::NewConnection() {
61  SMConnection* server = SMConnection::NewSMConnection(
62      &epoll_server_, ssl_state_, memory_cache_, acceptor_, "client_conn: ");
63  allocated_server_connections_.push_back(server);
64  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Acceptor: Making new server.";
65  return server;
66}
67
68SMConnection* SMAcceptorThread::FindOrMakeNewSMConnection() {
69  if (unused_server_connections_.empty()) {
70    return NewConnection();
71  }
72  SMConnection* server = unused_server_connections_.back();
73  unused_server_connections_.pop_back();
74  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Acceptor: Reusing server.";
75  return server;
76}
77
78void SMAcceptorThread::InitWorker() {
79  epoll_server_.RegisterFD(acceptor_->listen_fd_, this, EPOLLIN | EPOLLET);
80}
81
82void SMAcceptorThread::HandleConnection(int server_fd,
83                                        struct sockaddr_in* remote_addr) {
84  int on = 1;
85  int rc;
86  if (acceptor_->disable_nagle_) {
87    rc = setsockopt(server_fd,
88                    IPPROTO_TCP,
89                    TCP_NODELAY,
90                    reinterpret_cast<char*>(&on),
91                    sizeof(on));
92    if (rc < 0) {
93      close(server_fd);
94      LOG(ERROR) << "setsockopt() failed fd=" << server_fd;
95      return;
96    }
97  }
98
99  SMConnection* server_connection = FindOrMakeNewSMConnection();
100  if (server_connection == NULL) {
101    VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Acceptor: Closing fd " << server_fd;
102    close(server_fd);
103    return;
104  }
105  std::string remote_ip = inet_ntoa(remote_addr->sin_addr);
106  server_connection->InitSMConnection(this,
107                                      NULL,
108                                      &epoll_server_,
109                                      server_fd,
110                                      std::string(),
111                                      std::string(),
112                                      remote_ip,
113                                      use_ssl_);
114  if (server_connection->initialized())
115    active_server_connections_.push_back(server_connection);
116}
117
118void SMAcceptorThread::AcceptFromListenFD() {
119  if (acceptor_->accepts_per_wake_ > 0) {
120    for (int i = 0; i < acceptor_->accepts_per_wake_; ++i) {
121      struct sockaddr address;
122      socklen_t socklen = sizeof(address);
123      int fd = accept(acceptor_->listen_fd_, &address, &socklen);
124      if (fd == -1) {
125        if (errno != 11) {
126          VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Acceptor: accept fail("
127                  << acceptor_->listen_fd_ << "): " << errno << ": "
128                  << strerror(errno);
129        }
130        break;
131      }
132      VLOG(1) << ACCEPTOR_CLIENT_IDENT << " Accepted connection";
133      HandleConnection(fd, (struct sockaddr_in*)&address);
134    }
135  } else {
136    while (true) {
137      struct sockaddr address;
138      socklen_t socklen = sizeof(address);
139      int fd = accept(acceptor_->listen_fd_, &address, &socklen);
140      if (fd == -1) {
141        if (errno != 11) {
142          VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Acceptor: accept fail("
143                  << acceptor_->listen_fd_ << "): " << errno << ": "
144                  << strerror(errno);
145        }
146        break;
147      }
148      VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Accepted connection";
149      HandleConnection(fd, (struct sockaddr_in*)&address);
150    }
151  }
152}
153
154void SMAcceptorThread::HandleConnectionIdleTimeout() {
155  static time_t oldest_time = time(NULL);
156
157  int cur_time = time(NULL);
158  // Only iterate the list if we speculate that a connection is ready to be
159  // expired
160  if ((cur_time - oldest_time) < idle_socket_timeout_s_)
161    return;
162
163  // TODO(mbelshe): This code could be optimized, active_server_connections_
164  //                is already in-order.
165  std::list<SMConnection*>::iterator iter = active_server_connections_.begin();
166  while (iter != active_server_connections_.end()) {
167    SMConnection* conn = *iter;
168    int elapsed_time = (cur_time - conn->last_read_time_);
169    if (elapsed_time > idle_socket_timeout_s_) {
170      conn->Cleanup("Connection idle timeout reached.");
171      iter = active_server_connections_.erase(iter);
172      continue;
173    }
174    if (conn->last_read_time_ < oldest_time)
175      oldest_time = conn->last_read_time_;
176    iter++;
177  }
178  if ((cur_time - oldest_time) >= idle_socket_timeout_s_)
179    oldest_time = cur_time;
180}
181
182void SMAcceptorThread::Run() {
183  while (!quitting_.HasBeenNotified()) {
184    epoll_server_.set_timeout_in_us(10 * 1000);  // 10 ms
185    epoll_server_.WaitForEventsAndExecuteCallbacks();
186    if (tmp_unused_server_connections_.size()) {
187      VLOG(2) << "have " << tmp_unused_server_connections_.size()
188              << " additional unused connections.  Total = "
189              << unused_server_connections_.size();
190      unused_server_connections_.insert(unused_server_connections_.end(),
191                                        tmp_unused_server_connections_.begin(),
192                                        tmp_unused_server_connections_.end());
193      tmp_unused_server_connections_.clear();
194    }
195    HandleConnectionIdleTimeout();
196  }
197}
198
199void SMAcceptorThread::OnEvent(int fd, EpollEvent* event) {
200  if (event->in_events | EPOLLIN) {
201    VLOG(2) << ACCEPTOR_CLIENT_IDENT
202            << "Acceptor: Accepting based upon epoll events";
203    AcceptFromListenFD();
204  }
205}
206
207void SMAcceptorThread::SMConnectionDone(SMConnection* sc) {
208  VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Done with connection.";
209  tmp_unused_server_connections_.push_back(sc);
210}
211
212}  // namespace net
213