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