flip_in_mem_edsm_server.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
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 <errno.h> 6#include <signal.h> 7#include <sys/file.h> 8 9#include <iostream> 10#include <string> 11#include <vector> 12 13#include "base/command_line.h" 14#include "base/logging.h" 15#include "base/synchronization/lock.h" 16#include "base/timer.h" 17#include "net/tools/flip_server/acceptor_thread.h" 18#include "net/tools/flip_server/constants.h" 19#include "net/tools/flip_server/flip_config.h" 20#include "net/tools/flip_server/output_ordering.h" 21#include "net/tools/flip_server/sm_connection.h" 22#include "net/tools/flip_server/sm_interface.h" 23#include "net/tools/flip_server/spdy_interface.h" 24#include "net/tools/flip_server/split.h" 25 26using std::cout; 27using std::cerr; 28 29// If true, then disables the nagle algorithm); 30bool FLAGS_disable_nagle = true; 31 32// The number of times that accept() will be called when the 33// alarm goes off when the accept_using_alarm flag is set to true. 34// If set to 0, accept() will be performed until the accept queue 35// is completely drained and the accept() call returns an error); 36int32 FLAGS_accepts_per_wake = 0; 37 38// The size of the TCP accept backlog); 39int32 FLAGS_accept_backlog_size = 1024; 40 41// If set to false a single socket will be used. If set to true 42// then a new socket will be created for each accept thread. 43// Note that this only works with kernels that support 44// SO_REUSEPORT); 45bool FLAGS_reuseport = false; 46 47// Flag to force spdy, even if NPN is not negotiated. 48bool FLAGS_force_spdy = false; 49 50// The amount of time the server delays before sending back the 51// reply); 52double FLAGS_server_think_time_in_s = 0; 53 54net::FlipConfig g_proxy_config; 55 56//////////////////////////////////////////////////////////////////////////////// 57 58std::vector<std::string> &split(const std::string &s, 59 char delim, 60 std::vector<std::string> &elems) { 61 std::stringstream ss(s); 62 std::string item; 63 while(std::getline(ss, item, delim)) { 64 elems.push_back(item); 65 } 66 return elems; 67} 68 69std::vector<std::string> split(const std::string &s, char delim) { 70 std::vector<std::string> elems; 71 return split(s, delim, elems); 72} 73 74bool GotQuitFromStdin() { 75 // Make stdin nonblocking. Yes this is done each time. Oh well. 76 fcntl(0, F_SETFL, O_NONBLOCK); 77 char c; 78 std::string maybequit; 79 while (read(0, &c, 1) > 0) { 80 maybequit += c; 81 } 82 if (maybequit.size()) { 83 VLOG(1) << "scanning string: \"" << maybequit << "\""; 84 } 85 return (maybequit.size() > 1 && 86 (maybequit.c_str()[0] == 'q' || 87 maybequit.c_str()[0] == 'Q')); 88} 89 90const char* BoolToStr(bool b) { 91 if (b) 92 return "true"; 93 return "false"; 94} 95 96//////////////////////////////////////////////////////////////////////////////// 97 98static bool wantExit = false; 99static bool wantLogClose = false; 100void SignalHandler(int signum) 101{ 102 switch(signum) { 103 case SIGTERM: 104 case SIGINT: 105 wantExit = true; 106 break; 107 case SIGHUP: 108 wantLogClose = true; 109 break; 110 } 111} 112 113static int OpenPidFile(const char *pidfile) 114{ 115 int fd; 116 struct stat pid_stat; 117 int ret; 118 119 fd = open(pidfile, O_RDWR | O_CREAT, 0600); 120 if (fd == -1) { 121 cerr << "Could not open pid file '" << pidfile << "' for reading.\n"; 122 exit(1); 123 } 124 125 ret = flock(fd, LOCK_EX | LOCK_NB); 126 if (ret == -1) { 127 if (errno == EWOULDBLOCK) { 128 cerr << "Flip server is already running.\n"; 129 } else { 130 cerr << "Error getting lock on pid file: " << strerror(errno) << "\n"; 131 } 132 exit(1); 133 } 134 135 if (fstat(fd, &pid_stat) == -1) { 136 cerr << "Could not stat pid file '" << pidfile << "': " << strerror(errno) 137 << "\n"; 138 } 139 if (pid_stat.st_size != 0) { 140 if (ftruncate(fd, pid_stat.st_size) == -1) { 141 cerr << "Could not truncate pid file '" << pidfile << "': " 142 << strerror(errno) << "\n"; 143 } 144 } 145 146 char pid_str[8]; 147 snprintf(pid_str, sizeof(pid_str), "%d", getpid()); 148 int bytes = static_cast<int>(strlen(pid_str)); 149 if (write(fd, pid_str, strlen(pid_str)) != bytes) { 150 cerr << "Could not write pid file: " << strerror(errno) << "\n"; 151 close(fd); 152 exit(1); 153 } 154 155 return fd; 156} 157 158int main (int argc, char**argv) 159{ 160 unsigned int i = 0; 161 bool wait_for_iface = false; 162 int pidfile_fd; 163 164 signal(SIGPIPE, SIG_IGN); 165 signal(SIGTERM, SignalHandler); 166 signal(SIGINT, SignalHandler); 167 signal(SIGHUP, SignalHandler); 168 169 CommandLine::Init(argc, argv); 170 CommandLine cl(argc, argv); 171 172 if (cl.HasSwitch("help") || argc < 2) { 173 cout << argv[0] << " <options>\n"; 174 cout << " Proxy options:\n"; 175 cout << "\t--proxy<1..n>=\"<listen ip>,<listen port>," 176 << "<ssl cert filename>,\n" 177 << "\t <ssl key filename>,<http server ip>," 178 << "<http server port>,\n" 179 << "\t [https server ip],[https server port]," 180 << "<spdy only 0|1>\"\n"; 181 cout << "\t * The https server ip and port may be left empty if they are" 182 << " the same as\n" 183 << "\t the http server fields.\n"; 184 cout << "\t * spdy only prevents non-spdy https connections from being" 185 << " passed\n" 186 << "\t through the proxy listen ip:port.\n"; 187 cout << "\t--forward-ip-header=<header name>\n"; 188 cout << "\n Server options:\n"; 189 cout << "\t--spdy-server=\"<listen ip>,<listen port>,[ssl cert filename]," 190 << "\n\t [ssl key filename]\"\n"; 191 cout << "\t--http-server=\"<listen ip>,<listen port>,[ssl cert filename]," 192 << "\n\t [ssl key filename]\"\n"; 193 cout << "\t * Leaving the ssl cert and key fields empty will disable ssl" 194 << " for the\n" 195 << "\t http and spdy flip servers\n"; 196 cout << "\n Global options:\n"; 197 cout << "\t--logdest=<file|system|both>\n"; 198 cout << "\t--logfile=<logfile>\n"; 199 cout << "\t--wait-for-iface\n"; 200 cout << "\t * The flip server will block until the listen ip has been" 201 << " raised.\n"; 202 cout << "\t--ssl-session-expiry=<seconds> (default is 300)\n"; 203 cout << "\t--ssl-disable-compression\n"; 204 cout << "\t--idle-timeout=<seconds> (default is 300)\n"; 205 cout << "\t--pidfile=<filepath> (default /var/run/flip-server.pid)\n"; 206 cout << "\t--help\n"; 207 exit(0); 208 } 209 210 if (cl.HasSwitch("pidfile")) { 211 pidfile_fd = OpenPidFile(cl.GetSwitchValueASCII("pidfile").c_str()); 212 } else { 213 pidfile_fd = OpenPidFile(PIDFILE); 214 } 215 216 net::OutputOrdering::set_server_think_time_in_s(FLAGS_server_think_time_in_s); 217 218 if (cl.HasSwitch("forward-ip-header")) { 219 net::SpdySM::set_forward_ip_header( 220 cl.GetSwitchValueASCII("forward-ip-header")); 221 } 222 223 if (cl.HasSwitch("logdest")) { 224 std::string log_dest_value = cl.GetSwitchValueASCII("logdest"); 225 if (log_dest_value.compare("file") == 0) { 226 g_proxy_config.log_destination_ = logging::LOG_ONLY_TO_FILE; 227 } else if (log_dest_value.compare("system") == 0) { 228 g_proxy_config.log_destination_ = logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG; 229 } else if (log_dest_value.compare("both") == 0) { 230 g_proxy_config.log_destination_ = 231 logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG; 232 } else { 233 LOG(FATAL) << "Invalid logging destination value: " << log_dest_value; 234 } 235 } else { 236 g_proxy_config.log_destination_ = logging::LOG_NONE; 237 } 238 239 if (cl.HasSwitch("logfile")) { 240 g_proxy_config.log_filename_ = cl.GetSwitchValueASCII("logfile"); 241 if (g_proxy_config.log_destination_ == logging::LOG_NONE) { 242 g_proxy_config.log_destination_ = logging::LOG_ONLY_TO_FILE; 243 } 244 } else if (g_proxy_config.log_destination_ == logging::LOG_ONLY_TO_FILE || 245 g_proxy_config.log_destination_ == 246 logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG) { 247 LOG(FATAL) << "Logging destination requires a log file to be specified."; 248 } 249 250 if (cl.HasSwitch("wait-for-iface")) { 251 wait_for_iface = true; 252 } 253 254 if (cl.HasSwitch("ssl-session-expiry")) { 255 std::string session_expiry = cl.GetSwitchValueASCII("ssl-session-expiry"); 256 g_proxy_config.ssl_session_expiry_ = atoi(session_expiry.c_str()); 257 } 258 259 if (cl.HasSwitch("ssl-disable-compression")) { 260 g_proxy_config.ssl_disable_compression_ = true; 261 } 262 263 if (cl.HasSwitch("idle-timeout")) { 264 g_proxy_config.idle_socket_timeout_s_ = 265 atoi(cl.GetSwitchValueASCII("idle-timeout").c_str()); 266 } 267 268 if (cl.HasSwitch("force_spdy")) 269 net::SMConnection::set_force_spdy(true); 270 271 InitLogging(g_proxy_config.log_filename_.c_str(), 272 g_proxy_config.log_destination_, 273 logging::DONT_LOCK_LOG_FILE, 274 logging::APPEND_TO_OLD_LOG_FILE, 275 logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS); 276 277 LOG(INFO) << "Flip SPDY proxy started with configuration:"; 278 LOG(INFO) << "Logging destination : " << g_proxy_config.log_destination_; 279 LOG(INFO) << "Log file : " << g_proxy_config.log_filename_; 280 LOG(INFO) << "Forward IP Header : " 281 << (net::SpdySM::forward_ip_header().length() ? 282 net::SpdySM::forward_ip_header() : "<disabled>"); 283 LOG(INFO) << "Wait for interfaces : " << (wait_for_iface?"true":"false"); 284 LOG(INFO) << "Accept backlog size : " << FLAGS_accept_backlog_size; 285 LOG(INFO) << "Accepts per wake : " << FLAGS_accepts_per_wake; 286 LOG(INFO) << "Disable nagle : " 287 << (FLAGS_disable_nagle?"true":"false"); 288 LOG(INFO) << "Reuseport : " 289 << (FLAGS_reuseport?"true":"false"); 290 LOG(INFO) << "Force SPDY : " 291 << (FLAGS_force_spdy?"true":"false"); 292 LOG(INFO) << "SSL session expiry : " 293 << g_proxy_config.ssl_session_expiry_; 294 LOG(INFO) << "SSL disable compression : " 295 << g_proxy_config.ssl_disable_compression_; 296 LOG(INFO) << "Connection idle timeout : " 297 << g_proxy_config.idle_socket_timeout_s_; 298 299 // Proxy Acceptors 300 while (true) { 301 i += 1; 302 std::stringstream name; 303 name << "proxy" << i; 304 if (!cl.HasSwitch(name.str())) { 305 break; 306 } 307 std::string value = cl.GetSwitchValueASCII(name.str()); 308 std::vector<std::string> valueArgs = split(value, ','); 309 CHECK_EQ((unsigned int)9, valueArgs.size()); 310 int spdy_only = atoi(valueArgs[8].c_str()); 311 // If wait_for_iface is enabled, then this call will block 312 // indefinitely until the interface is raised. 313 g_proxy_config.AddAcceptor(net::FLIP_HANDLER_PROXY, 314 valueArgs[0], valueArgs[1], 315 valueArgs[2], valueArgs[3], 316 valueArgs[4], valueArgs[5], 317 valueArgs[6], valueArgs[7], 318 spdy_only, 319 FLAGS_accept_backlog_size, 320 FLAGS_disable_nagle, 321 FLAGS_accepts_per_wake, 322 FLAGS_reuseport, 323 wait_for_iface, 324 NULL); 325 } 326 327 // Spdy Server Acceptor 328 net::MemoryCache spdy_memory_cache; 329 if (cl.HasSwitch("spdy-server")) { 330 spdy_memory_cache.AddFiles(); 331 std::string value = cl.GetSwitchValueASCII("spdy-server"); 332 std::vector<std::string> valueArgs = split(value, ','); 333 g_proxy_config.AddAcceptor(net::FLIP_HANDLER_SPDY_SERVER, 334 valueArgs[0], valueArgs[1], 335 valueArgs[2], valueArgs[3], 336 "", "", "", "", 337 0, 338 FLAGS_accept_backlog_size, 339 FLAGS_disable_nagle, 340 FLAGS_accepts_per_wake, 341 FLAGS_reuseport, 342 wait_for_iface, 343 &spdy_memory_cache); 344 } 345 346 // Spdy Server Acceptor 347 net::MemoryCache http_memory_cache; 348 if (cl.HasSwitch("http-server")) { 349 http_memory_cache.AddFiles(); 350 std::string value = cl.GetSwitchValueASCII("http-server"); 351 std::vector<std::string> valueArgs = split(value, ','); 352 g_proxy_config.AddAcceptor(net::FLIP_HANDLER_HTTP_SERVER, 353 valueArgs[0], valueArgs[1], 354 valueArgs[2], valueArgs[3], 355 "", "", "", "", 356 0, 357 FLAGS_accept_backlog_size, 358 FLAGS_disable_nagle, 359 FLAGS_accepts_per_wake, 360 FLAGS_reuseport, 361 wait_for_iface, 362 &http_memory_cache); 363 } 364 365 std::vector<net::SMAcceptorThread*> sm_worker_threads_; 366 367 for (i = 0; i < g_proxy_config.acceptors_.size(); i++) { 368 net::FlipAcceptor *acceptor = g_proxy_config.acceptors_[i]; 369 370 sm_worker_threads_.push_back( 371 new net::SMAcceptorThread(acceptor, 372 (net::MemoryCache *)acceptor->memory_cache_)); 373 // Note that spdy_memory_cache is not threadsafe, it is merely 374 // thread compatible. Thus, if ever we are to spawn multiple threads, 375 // we either must make the MemoryCache threadsafe, or use 376 // a separate MemoryCache for each thread. 377 // 378 // The latter is what is currently being done as we spawn 379 // a separate thread for each http and spdy server acceptor. 380 381 sm_worker_threads_.back()->InitWorker(); 382 sm_worker_threads_.back()->Start(); 383 } 384 385 while (!wantExit) { 386 // Close logfile when HUP signal is received. Logging system will 387 // automatically reopen on next log message. 388 if ( wantLogClose ) { 389 wantLogClose = false; 390 VLOG(1) << "HUP received, reopening log file."; 391 logging::CloseLogFile(); 392 } 393 if (GotQuitFromStdin()) { 394 for (unsigned int i = 0; i < sm_worker_threads_.size(); ++i) { 395 sm_worker_threads_[i]->Quit(); 396 } 397 for (unsigned int i = 0; i < sm_worker_threads_.size(); ++i) { 398 sm_worker_threads_[i]->Join(); 399 } 400 break; 401 } 402 usleep(1000*10); // 10 ms 403 } 404 405 unlink(PIDFILE); 406 close(pidfile_fd); 407 return 0; 408} 409 410