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