• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #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.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/streamer_interface.h"
26 #include "net/tools/flip_server/split.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 
split(const std::string & s,char delim,std::vector<std::string> & elems)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 
split(const std::string & s,char delim)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 
GotQuitFromStdin()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 
BoolToStr(bool b)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;
SignalHandler(int signum)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 
OpenPidFile(const char * pidfile)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 
main(int argc,char ** argv)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_ONLY_TO_FILE;
231     } else if (log_dest_value.compare("system") == 0) {
232       g_proxy_config.log_destination_ = logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG;
233     } else if (log_dest_value.compare("both") == 0) {
234       g_proxy_config.log_destination_ =
235         logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG;
236     } else {
237       LOG(FATAL) << "Invalid logging destination value: " << log_dest_value;
238     }
239   } else {
240     g_proxy_config.log_destination_ = logging::LOG_NONE;
241   }
242 
243   if (cl.HasSwitch("logfile")) {
244     g_proxy_config.log_filename_ = cl.GetSwitchValueASCII("logfile");
245     if (g_proxy_config.log_destination_ == logging::LOG_NONE) {
246       g_proxy_config.log_destination_ = logging::LOG_ONLY_TO_FILE;
247     }
248   } else if (g_proxy_config.log_destination_ == logging::LOG_ONLY_TO_FILE ||
249              g_proxy_config.log_destination_ ==
250              logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG) {
251     LOG(FATAL) << "Logging destination requires a log file to be specified.";
252   }
253 
254   if (cl.HasSwitch("wait-for-iface")) {
255     wait_for_iface = true;
256   }
257 
258   if (cl.HasSwitch("ssl-session-expiry")) {
259     std::string session_expiry = cl.GetSwitchValueASCII("ssl-session-expiry");
260     g_proxy_config.ssl_session_expiry_ = atoi(session_expiry.c_str());
261   }
262 
263   if (cl.HasSwitch("ssl-disable-compression")) {
264     g_proxy_config.ssl_disable_compression_ = true;
265   }
266 
267   if (cl.HasSwitch("idle-timeout")) {
268     g_proxy_config.idle_socket_timeout_s_ =
269       atoi(cl.GetSwitchValueASCII("idle-timeout").c_str());
270   }
271 
272   if (cl.HasSwitch("force_spdy"))
273     net::SMConnection::set_force_spdy(true);
274 
275   InitLogging(g_proxy_config.log_filename_.c_str(),
276               g_proxy_config.log_destination_,
277               logging::DONT_LOCK_LOG_FILE,
278               logging::APPEND_TO_OLD_LOG_FILE,
279               logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
280 
281   LOG(INFO) << "Flip SPDY proxy started with configuration:";
282   LOG(INFO) << "Logging destination     : " << g_proxy_config.log_destination_;
283   LOG(INFO) << "Log file                : " << g_proxy_config.log_filename_;
284   LOG(INFO) << "Forward IP Header       : "
285             << (net::SpdySM::forward_ip_header().length() ?
286                net::SpdySM::forward_ip_header() : "<disabled>");
287   LOG(INFO) << "Wait for interfaces     : " << (wait_for_iface?"true":"false");
288   LOG(INFO) << "Accept backlog size     : " << FLAGS_accept_backlog_size;
289   LOG(INFO) << "Accepts per wake        : " << FLAGS_accepts_per_wake;
290   LOG(INFO) << "Disable nagle           : "
291             << (FLAGS_disable_nagle?"true":"false");
292   LOG(INFO) << "Reuseport               : "
293             << (FLAGS_reuseport?"true":"false");
294   LOG(INFO) << "Force SPDY              : "
295             << (FLAGS_force_spdy?"true":"false");
296   LOG(INFO) << "SSL session expiry      : "
297             << g_proxy_config.ssl_session_expiry_;
298   LOG(INFO) << "SSL disable compression : "
299             << g_proxy_config.ssl_disable_compression_;
300   LOG(INFO) << "Connection idle timeout : "
301             << g_proxy_config.idle_socket_timeout_s_;
302 
303   // Proxy Acceptors
304   while (true) {
305     i += 1;
306     std::stringstream name;
307     name << "proxy" << i;
308     if (!cl.HasSwitch(name.str())) {
309       break;
310     }
311     std::string value = cl.GetSwitchValueASCII(name.str());
312     std::vector<std::string> valueArgs = split(value, ',');
313     CHECK_EQ((unsigned int)9, valueArgs.size());
314     int spdy_only = atoi(valueArgs[8].c_str());
315     // If wait_for_iface is enabled, then this call will block
316     // indefinitely until the interface is raised.
317     g_proxy_config.AddAcceptor(net::FLIP_HANDLER_PROXY,
318                                valueArgs[0], valueArgs[1],
319                                valueArgs[2], valueArgs[3],
320                                valueArgs[4], valueArgs[5],
321                                valueArgs[6], valueArgs[7],
322                                spdy_only,
323                                FLAGS_accept_backlog_size,
324                                FLAGS_disable_nagle,
325                                FLAGS_accepts_per_wake,
326                                FLAGS_reuseport,
327                                wait_for_iface,
328                                NULL);
329   }
330 
331   // Spdy Server Acceptor
332   net::MemoryCache spdy_memory_cache;
333   if (cl.HasSwitch("spdy-server")) {
334     spdy_memory_cache.AddFiles();
335     std::string value = cl.GetSwitchValueASCII("spdy-server");
336     std::vector<std::string> valueArgs = split(value, ',');
337     g_proxy_config.AddAcceptor(net::FLIP_HANDLER_SPDY_SERVER,
338                                valueArgs[0], valueArgs[1],
339                                valueArgs[2], valueArgs[3],
340                                "", "", "", "",
341                                0,
342                                FLAGS_accept_backlog_size,
343                                FLAGS_disable_nagle,
344                                FLAGS_accepts_per_wake,
345                                FLAGS_reuseport,
346                                wait_for_iface,
347                                &spdy_memory_cache);
348   }
349 
350   // Spdy Server Acceptor
351   net::MemoryCache http_memory_cache;
352   if (cl.HasSwitch("http-server")) {
353     http_memory_cache.AddFiles();
354     std::string value = cl.GetSwitchValueASCII("http-server");
355     std::vector<std::string> valueArgs = split(value, ',');
356     g_proxy_config.AddAcceptor(net::FLIP_HANDLER_HTTP_SERVER,
357                                valueArgs[0], valueArgs[1],
358                                valueArgs[2], valueArgs[3],
359                                "", "", "", "",
360                                0,
361                                FLAGS_accept_backlog_size,
362                                FLAGS_disable_nagle,
363                                FLAGS_accepts_per_wake,
364                                FLAGS_reuseport,
365                                wait_for_iface,
366                                &http_memory_cache);
367   }
368 
369   std::vector<net::SMAcceptorThread*> sm_worker_threads_;
370 
371   for (i = 0; i < g_proxy_config.acceptors_.size(); i++) {
372     net::FlipAcceptor *acceptor = g_proxy_config.acceptors_[i];
373 
374     sm_worker_threads_.push_back(
375         new net::SMAcceptorThread(acceptor,
376                                   (net::MemoryCache *)acceptor->memory_cache_));
377     // Note that spdy_memory_cache is not threadsafe, it is merely
378     // thread compatible. Thus, if ever we are to spawn multiple threads,
379     // we either must make the MemoryCache threadsafe, or use
380     // a separate MemoryCache for each thread.
381     //
382     // The latter is what is currently being done as we spawn
383     // a separate thread for each http and spdy server acceptor.
384 
385     sm_worker_threads_.back()->InitWorker();
386     sm_worker_threads_.back()->Start();
387   }
388 
389   while (!wantExit) {
390     // Close logfile when HUP signal is received. Logging system will
391     // automatically reopen on next log message.
392     if ( wantLogClose ) {
393       wantLogClose = false;
394       VLOG(1) << "HUP received, reopening log file.";
395       logging::CloseLogFile();
396     }
397     if (GotQuitFromStdin()) {
398       for (unsigned int i = 0; i < sm_worker_threads_.size(); ++i) {
399         sm_worker_threads_[i]->Quit();
400       }
401       for (unsigned int i = 0; i < sm_worker_threads_.size(); ++i) {
402         sm_worker_threads_[i]->Join();
403       }
404       break;
405     }
406     usleep(1000*10);  // 10 ms
407   }
408 
409   unlink(PIDFILE);
410   close(pidfile_fd);
411   return 0;
412 }
413 
414