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.
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"
31 // If true, then disables the nagle algorithm);
32 bool FLAGS_disable_nagle
= true;
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;
40 // The size of the TCP accept backlog);
41 int32 FLAGS_accept_backlog_size
= 1024;
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
47 bool FLAGS_reuseport
= false;
49 // Flag to force spdy, even if NPN is not negotiated.
50 bool FLAGS_force_spdy
= false;
52 // The amount of time the server delays before sending back the
54 double FLAGS_server_think_time_in_s
= 0;
56 net::FlipConfig g_proxy_config
;
58 ////////////////////////////////////////////////////////////////////////////////
60 std::vector
<std::string
> &split(const std::string
&s
,
62 std::vector
<std::string
> &elems
) {
63 std::stringstream
ss(s
);
65 while(std::getline(ss
, item
, delim
)) {
66 elems
.push_back(item
);
71 std::vector
<std::string
> split(const std::string
&s
, char delim
) {
72 std::vector
<std::string
> elems
;
73 return split(s
, delim
, elems
);
76 bool GotQuitFromStdin() {
77 // Make stdin nonblocking. Yes this is done each time. Oh well.
78 fcntl(0, F_SETFL
, O_NONBLOCK
);
80 std::string maybequit
;
81 while (read(0, &c
, 1) > 0) {
84 if (maybequit
.size()) {
85 VLOG(1) << "scanning string: \"" << maybequit
<< "\"";
87 return (maybequit
.size() > 1 &&
88 (maybequit
.c_str()[0] == 'q' ||
89 maybequit
.c_str()[0] == 'Q'));
92 const char* BoolToStr(bool b
) {
98 ////////////////////////////////////////////////////////////////////////////////
100 static bool wantExit
= false;
101 static bool wantLogClose
= false;
102 void SignalHandler(int signum
)
115 static int OpenPidFile(const char *pidfile
)
118 struct stat pid_stat
;
121 fd
= open(pidfile
, O_RDWR
| O_CREAT
, 0600);
123 cerr
<< "Could not open pid file '" << pidfile
<< "' for reading.\n";
127 ret
= flock(fd
, LOCK_EX
| LOCK_NB
);
129 if (errno
== EWOULDBLOCK
) {
130 cerr
<< "Flip server is already running.\n";
132 cerr
<< "Error getting lock on pid file: " << strerror(errno
) << "\n";
137 if (fstat(fd
, &pid_stat
) == -1) {
138 cerr
<< "Could not stat pid file '" << pidfile
<< "': " << strerror(errno
)
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";
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";
160 int main (int argc
, char**argv
)
163 bool wait_for_iface
= false;
166 signal(SIGPIPE
, SIG_IGN
);
167 signal(SIGTERM
, SignalHandler
);
168 signal(SIGINT
, SignalHandler
);
169 signal(SIGHUP
, SignalHandler
);
171 CommandLine::Init(argc
, argv
);
172 CommandLine
cl(argc
, argv
);
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"
185 << "\t the http server fields.\n";
186 cout
<< "\t * spdy only prevents non-spdy https connections from being"
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"
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"
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";
212 if (cl
.HasSwitch("pidfile")) {
213 pidfile_fd
= OpenPidFile(cl
.GetSwitchValueASCII("pidfile").c_str());
215 pidfile_fd
= OpenPidFile(PIDFILE
);
218 net::OutputOrdering::set_server_think_time_in_s(FLAGS_server_think_time_in_s
);
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"));
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
;
237 LOG(FATAL
) << "Invalid logging destination value: " << log_dest_value
;
240 g_proxy_config
.log_destination_
= logging::LOG_NONE
;
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
;
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.";
254 if (cl
.HasSwitch("wait-for-iface")) {
255 wait_for_iface
= true;
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());
263 if (cl
.HasSwitch("ssl-disable-compression")) {
264 g_proxy_config
.ssl_disable_compression_
= true;
267 if (cl
.HasSwitch("idle-timeout")) {
268 g_proxy_config
.idle_socket_timeout_s_
=
269 atoi(cl
.GetSwitchValueASCII("idle-timeout").c_str());
272 if (cl
.HasSwitch("force_spdy"))
273 net::SMConnection::set_force_spdy(true);
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
);
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_
;
306 std::stringstream name
;
307 name
<< "proxy" << i
;
308 if (!cl
.HasSwitch(name
.str())) {
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],
323 FLAGS_accept_backlog_size
,
325 FLAGS_accepts_per_wake
,
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 while (valueArgs
.size() < 4)
338 valueArgs
.push_back("");
339 g_proxy_config
.AddAcceptor(net::FLIP_HANDLER_SPDY_SERVER
,
340 valueArgs
[0], valueArgs
[1],
341 valueArgs
[2], valueArgs
[3],
344 FLAGS_accept_backlog_size
,
346 FLAGS_accepts_per_wake
,
352 // Spdy Server Acceptor
353 net::MemoryCache http_memory_cache
;
354 if (cl
.HasSwitch("http-server")) {
355 http_memory_cache
.AddFiles();
356 std::string value
= cl
.GetSwitchValueASCII("http-server");
357 std::vector
<std::string
> valueArgs
= split(value
, ',');
358 while (valueArgs
.size() < 4)
359 valueArgs
.push_back("");
360 g_proxy_config
.AddAcceptor(net::FLIP_HANDLER_HTTP_SERVER
,
361 valueArgs
[0], valueArgs
[1],
362 valueArgs
[2], valueArgs
[3],
365 FLAGS_accept_backlog_size
,
367 FLAGS_accepts_per_wake
,
373 std::vector
<net::SMAcceptorThread
*> sm_worker_threads_
;
375 for (i
= 0; i
< g_proxy_config
.acceptors_
.size(); i
++) {
376 net::FlipAcceptor
*acceptor
= g_proxy_config
.acceptors_
[i
];
378 sm_worker_threads_
.push_back(
379 new net::SMAcceptorThread(acceptor
,
380 (net::MemoryCache
*)acceptor
->memory_cache_
));
381 // Note that spdy_memory_cache is not threadsafe, it is merely
382 // thread compatible. Thus, if ever we are to spawn multiple threads,
383 // we either must make the MemoryCache threadsafe, or use
384 // a separate MemoryCache for each thread.
386 // The latter is what is currently being done as we spawn
387 // a separate thread for each http and spdy server acceptor.
389 sm_worker_threads_
.back()->InitWorker();
390 sm_worker_threads_
.back()->Start();
394 // Close logfile when HUP signal is received. Logging system will
395 // automatically reopen on next log message.
396 if ( wantLogClose
) {
397 wantLogClose
= false;
398 VLOG(1) << "HUP received, reopening log file.";
399 logging::CloseLogFile();
401 if (GotQuitFromStdin()) {
402 for (unsigned int i
= 0; i
< sm_worker_threads_
.size(); ++i
) {
403 sm_worker_threads_
[i
]->Quit();
405 for (unsigned int i
= 0; i
< sm_worker_threads_
.size(); ++i
) {
406 sm_worker_threads_
[i
]->Join();
410 usleep(1000*10); // 10 ms