PageRenderTime 34ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Mojo/Server/Daemon.pm

http://github.com/kraih/mojo
Perl | 542 lines | 344 code | 162 blank | 36 comment | 55 complexity | 894a71db16b3c629014d6e79f5d49f94 MD5 | raw file
Possible License(s): AGPL-3.0
  1. package Mojo::Server::Daemon;
  2. use Mojo::Base 'Mojo::Server';
  3. use Carp qw(croak);
  4. use Mojo::IOLoop;
  5. use Mojo::Transaction::WebSocket;
  6. use Mojo::URL;
  7. use Mojo::Util qw(term_escape);
  8. use Mojo::WebSocket qw(server_handshake);
  9. use Scalar::Util qw(weaken);
  10. use constant DEBUG => $ENV{MOJO_SERVER_DEBUG} || 0;
  11. has acceptors => sub { [] };
  12. has [qw(backlog max_clients silent)];
  13. has inactivity_timeout => sub { $ENV{MOJO_INACTIVITY_TIMEOUT} // 30 };
  14. has ioloop => sub { Mojo::IOLoop->singleton };
  15. has keep_alive_timeout => sub { $ENV{MOJO_KEEP_ALIVE_TIMEOUT} // 5 };
  16. has listen => sub { [split ',', $ENV{MOJO_LISTEN} || 'http://*:3000'] };
  17. has max_requests => 100;
  18. sub DESTROY {
  19. return if Mojo::Util::_global_destruction();
  20. my $self = shift;
  21. my $loop = $self->ioloop;
  22. $loop->remove($_) for keys %{$self->{connections} || {}}, @{$self->acceptors};
  23. }
  24. sub ports { [map { $_[0]->ioloop->acceptor($_)->port } @{$_[0]->acceptors}] }
  25. sub run {
  26. my $self = shift;
  27. # Make sure the event loop can be stopped in regular intervals
  28. my $loop = $self->ioloop;
  29. my $int = $loop->recurring(1 => sub { });
  30. local $SIG{INT} = local $SIG{TERM} = sub { $loop->stop };
  31. $self->start->ioloop->start;
  32. $loop->remove($int);
  33. }
  34. sub start {
  35. my $self = shift;
  36. my $loop = $self->ioloop;
  37. if (my $max = $self->max_clients) { $loop->max_connections($max) }
  38. # Resume accepting connections
  39. if (my $servers = $self->{servers}) {
  40. push @{$self->acceptors}, $loop->acceptor(delete $servers->{$_})
  41. for keys %$servers;
  42. }
  43. # Start listening
  44. elsif (!@{$self->acceptors}) {
  45. $self->app->server($self);
  46. $self->_listen($_) for @{$self->listen};
  47. }
  48. return $self;
  49. }
  50. sub stop {
  51. my $self = shift;
  52. # Suspend accepting connections but keep listen sockets open
  53. my $loop = $self->ioloop;
  54. while (my $id = shift @{$self->acceptors}) {
  55. my $server = $self->{servers}{$id} = $loop->acceptor($id);
  56. $loop->remove($id);
  57. $server->stop;
  58. }
  59. return $self;
  60. }
  61. sub _build_tx {
  62. my ($self, $id, $c) = @_;
  63. my $tx = $self->build_tx->connection($id);
  64. $tx->res->headers->server('Mojolicious (Perl)');
  65. my $handle
  66. = $self->ioloop->stream($id)->timeout($self->inactivity_timeout)->handle;
  67. unless ($handle->isa('IO::Socket::UNIX')) {
  68. $tx->local_address($handle->sockhost)->local_port($handle->sockport);
  69. $tx->remote_address($handle->peerhost)->remote_port($handle->peerport);
  70. }
  71. $tx->req->url->base->scheme('https') if $c->{tls};
  72. weaken $self;
  73. $tx->on(
  74. request => sub {
  75. my $tx = shift;
  76. my $req = $tx->req;
  77. if (my $error = $req->error) { $self->_debug($id, $error->{message}) }
  78. # WebSocket
  79. if ($req->is_handshake) {
  80. my $ws = $self->{connections}{$id}{next}
  81. = Mojo::Transaction::WebSocket->new(handshake => $tx);
  82. $self->emit(request => server_handshake $ws);
  83. }
  84. # HTTP
  85. else { $self->emit(request => $tx) }
  86. # Last keep-alive request or corrupted connection
  87. my $c = $self->{connections}{$id};
  88. $tx->res->headers->connection('close')
  89. if ($c->{requests} || 1) >= $self->max_requests || $req->error;
  90. $tx->on(resume => sub { $self->_write($id) });
  91. $self->_write($id);
  92. }
  93. );
  94. # Kept alive if we have more than one request on the connection
  95. return ++$c->{requests} > 1 ? $tx->kept_alive(1) : $tx;
  96. }
  97. sub _close {
  98. my ($self, $id) = @_;
  99. if (my $tx = $self->{connections}{$id}{tx}) { $tx->closed }
  100. delete $self->{connections}{$id};
  101. }
  102. sub _debug { $_[0]->app->log->debug($_[2]) if $_[0]{connections}{$_[1]}{tx} }
  103. sub _finish {
  104. my ($self, $id) = @_;
  105. # Always remove connection for WebSockets
  106. my $c = $self->{connections}{$id};
  107. return unless my $tx = $c->{tx};
  108. return $self->_remove($id) if $tx->is_websocket;
  109. # Finish transaction
  110. delete($c->{tx})->closed;
  111. # Upgrade connection to WebSocket
  112. if (my $ws = delete $c->{next}) {
  113. # Successful upgrade
  114. if ($ws->handshake->res->code == 101) {
  115. $c->{tx} = $ws->established(1);
  116. weaken $self;
  117. $ws->on(resume => sub { $self->_write($id) });
  118. $self->_write($id);
  119. }
  120. # Failed upgrade
  121. else { $ws->closed }
  122. }
  123. # Close connection if necessary
  124. return $self->_remove($id) if $tx->error || !$tx->keep_alive;
  125. # Build new transaction for leftovers
  126. if (length(my $leftovers = $tx->req->content->leftovers)) {
  127. $tx = $c->{tx} = $self->_build_tx($id, $c);
  128. $tx->server_read($leftovers);
  129. }
  130. # Keep-alive connection
  131. $self->ioloop->stream($id)->timeout($self->keep_alive_timeout)
  132. unless $c->{tx};
  133. }
  134. sub _listen {
  135. my ($self, $listen) = @_;
  136. my $url = Mojo::URL->new($listen);
  137. my $proto = $url->protocol;
  138. croak qq{Invalid listen location "$listen"}
  139. unless $proto eq 'http' || $proto eq 'https' || $proto eq 'http+unix';
  140. my $query = $url->query;
  141. my $options = {backlog => $self->backlog};
  142. $options->{$_} = $query->param($_) for qw(fd single_accept reuse);
  143. if ($proto eq 'http+unix') { $options->{path} = $url->host }
  144. else {
  145. if ((my $host = $url->host) ne '*') { $options->{address} = $host }
  146. if (my $port = $url->port) { $options->{port} = $port }
  147. }
  148. $options->{"tls_$_"} = $query->param($_) for qw(ca ciphers version);
  149. /^(.*)_(cert|key)$/ and $options->{"tls_$2"}{$1} = $query->param($_)
  150. for @{$query->names};
  151. if (my $cert = $query->param('cert')) { $options->{'tls_cert'}{''} = $cert }
  152. if (my $key = $query->param('key')) { $options->{'tls_key'}{''} = $key }
  153. my $verify = $query->param('verify');
  154. $options->{tls_verify} = hex $verify if defined $verify;
  155. my $tls = $options->{tls} = $proto eq 'https';
  156. weaken $self;
  157. push @{$self->acceptors}, $self->ioloop->server(
  158. $options => sub {
  159. my ($loop, $stream, $id) = @_;
  160. $self->{connections}{$id} = {tls => $tls};
  161. warn "-- Accept $id (@{[$stream->handle->peerhost]})\n" if DEBUG;
  162. $stream->timeout($self->inactivity_timeout);
  163. $stream->on(close => sub { $self && $self->_close($id) });
  164. $stream->on(error =>
  165. sub { $self && $self->app->log->error(pop) && $self->_close($id) });
  166. $stream->on(read => sub { $self->_read($id => pop) });
  167. $stream->on(timeout => sub { $self->_debug($id, 'Inactivity timeout') });
  168. }
  169. );
  170. return if $self->silent;
  171. $self->app->log->info(qq{Listening at "$url"});
  172. $query->pairs([]);
  173. $url->host('127.0.0.1') if $url->host eq '*';
  174. say 'Server available at ', $options->{path} // $url;
  175. }
  176. sub _read {
  177. my ($self, $id, $chunk) = @_;
  178. # Make sure we have a transaction
  179. my $c = $self->{connections}{$id};
  180. my $tx = $c->{tx} ||= $self->_build_tx($id, $c);
  181. warn term_escape "-- Server <<< Client (@{[_url($tx)]})\n$chunk\n" if DEBUG;
  182. $tx->server_read($chunk);
  183. }
  184. sub _remove {
  185. my ($self, $id) = @_;
  186. $self->ioloop->remove($id);
  187. $self->_close($id);
  188. }
  189. sub _url { shift->req->url->to_abs }
  190. sub _write {
  191. my ($self, $id) = @_;
  192. # Protect from resume event recursion
  193. my $c = $self->{connections}{$id};
  194. return if !(my $tx = $c->{tx}) || $c->{writing};
  195. local $c->{writing} = 1;
  196. my $chunk = $tx->server_write;
  197. warn term_escape "-- Server >>> Client (@{[_url($tx)]})\n$chunk\n" if DEBUG;
  198. my $next = $tx->is_finished ? '_finish' : length $chunk ? '_write' : undef;
  199. return $self->ioloop->stream($id)->write($chunk) unless $next;
  200. weaken $self;
  201. $self->ioloop->stream($id)->write($chunk => sub { $self->$next($id) });
  202. }
  203. 1;
  204. =encoding utf8
  205. =head1 NAME
  206. Mojo::Server::Daemon - Non-blocking I/O HTTP and WebSocket server
  207. =head1 SYNOPSIS
  208. use Mojo::Server::Daemon;
  209. my $daemon = Mojo::Server::Daemon->new(listen => ['http://*:8080']);
  210. $daemon->unsubscribe('request')->on(request => sub {
  211. my ($daemon, $tx) = @_;
  212. # Request
  213. my $method = $tx->req->method;
  214. my $path = $tx->req->url->path;
  215. # Response
  216. $tx->res->code(200);
  217. $tx->res->headers->content_type('text/plain');
  218. $tx->res->body("$method request for $path!");
  219. # Resume transaction
  220. $tx->resume;
  221. });
  222. $daemon->run;
  223. =head1 DESCRIPTION
  224. L<Mojo::Server::Daemon> is a full featured, highly portable non-blocking I/O
  225. HTTP and WebSocket server, with IPv6, TLS, SNI, Comet (long polling), keep-alive
  226. and multiple event loop support.
  227. For better scalability (epoll, kqueue) and to provide non-blocking name
  228. resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.32+),
  229. L<Net::DNS::Native> (0.15+), L<IO::Socket::Socks> (0.64+) and
  230. L<IO::Socket::SSL> (2.009+) will be used automatically if possible. Individual
  231. features can also be disabled with the C<MOJO_NO_NNR>, C<MOJO_NO_SOCKS> and
  232. C<MOJO_NO_TLS> environment variables.
  233. See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
  234. =head1 SIGNALS
  235. The L<Mojo::Server::Daemon> process can be controlled at runtime with the
  236. following signals.
  237. =head2 INT, TERM
  238. Shut down server immediately.
  239. =head1 EVENTS
  240. L<Mojo::Server::Daemon> inherits all events from L<Mojo::Server>.
  241. =head1 ATTRIBUTES
  242. L<Mojo::Server::Daemon> inherits all attributes from L<Mojo::Server> and
  243. implements the following new ones.
  244. =head2 acceptors
  245. my $acceptors = $daemon->acceptors;
  246. $daemon = $daemon->acceptors(['6be0c140ef00a389c5d039536b56d139']);
  247. Active acceptor ids.
  248. # Check port
  249. mu $port = $daemon->ioloop->acceptor($daemon->acceptors->[0])->port;
  250. =head2 backlog
  251. my $backlog = $daemon->backlog;
  252. $daemon = $daemon->backlog(128);
  253. Listen backlog size, defaults to C<SOMAXCONN>.
  254. =head2 inactivity_timeout
  255. my $timeout = $daemon->inactivity_timeout;
  256. $daemon = $daemon->inactivity_timeout(5);
  257. Maximum amount of time in seconds a connection with an active request can be
  258. inactive before getting closed, defaults to the value of the
  259. C<MOJO_INACTIVITY_TIMEOUT> environment variable or C<30>. Setting the value to
  260. C<0> will allow connections to be inactive indefinitely.
  261. =head2 ioloop
  262. my $loop = $daemon->ioloop;
  263. $daemon = $daemon->ioloop(Mojo::IOLoop->new);
  264. Event loop object to use for I/O operations, defaults to the global
  265. L<Mojo::IOLoop> singleton.
  266. =head2 keep_alive_timeout
  267. my $timeout = $daemon->keep_alive_timeout;
  268. $daemon = $daemon->keep_alive_timeout(10);
  269. Maximum amount of time in seconds a connection without an active request can be
  270. inactive before getting closed, defaults to the value of the
  271. C<MOJO_KEEP_ALIVE_TIMEOUT> environment variable or C<5>. Setting the value to
  272. C<0> will allow connections to be inactive indefinitely.
  273. =head2 listen
  274. my $listen = $daemon->listen;
  275. $daemon = $daemon->listen(['https://127.0.0.1:8080']);
  276. Array reference with one or more locations to listen on, defaults to the value
  277. of the C<MOJO_LISTEN> environment variable or C<http://*:3000> (shortcut for
  278. C<http://0.0.0.0:3000>).
  279. # Listen on all IPv4 interfaces
  280. $daemon->listen(['http://*:3000']);
  281. # Listen on all IPv4 and IPv6 interfaces
  282. $daemon->listen(['http://[::]:3000']);
  283. # Listen on IPv6 interface
  284. $daemon->listen(['http://[::1]:4000']);
  285. # Listen on IPv4 and IPv6 interfaces
  286. $daemon->listen(['http://127.0.0.1:3000', 'http://[::1]:3000']);
  287. # Listen on UNIX domain socket "/tmp/myapp.sock" (percent encoded slash)
  288. $daemon->listen(['http+unix://%2Ftmp%2Fmyapp.sock']);
  289. # File descriptor, as used by systemd
  290. $daemon->listen(['http://127.0.0.1?fd=3']);
  291. # Allow multiple servers to use the same port (SO_REUSEPORT)
  292. $daemon->listen(['http://*:8080?reuse=1']);
  293. # Listen on two ports with HTTP and HTTPS at the same time
  294. $daemon->listen(['http://*:3000', 'https://*:4000']);
  295. # Use a custom certificate and key
  296. $daemon->listen(['https://*:3000?cert=/x/server.crt&key=/y/server.key']);
  297. # Domain specific certificates and keys (SNI)
  298. $daemon->listen(
  299. ['https://*:3000?example.com_cert=/x/my.crt&example.com_key=/y/my.key']);
  300. # Or even a custom certificate authority
  301. $daemon->listen(
  302. ['https://*:3000?cert=/x/server.crt&key=/y/server.key&ca=/z/ca.crt']);
  303. These parameters are currently available:
  304. =over 2
  305. =item ca
  306. ca=/etc/tls/ca.crt
  307. Path to TLS certificate authority file used to verify the peer certificate.
  308. =item cert
  309. cert=/etc/tls/server.crt
  310. mojolicious.org_cert=/etc/tls/mojo.crt
  311. Path to the TLS cert file, defaults to a built-in test certificate.
  312. =item ciphers
  313. ciphers=AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH
  314. TLS cipher specification string. For more information about the format see
  315. L<https://www.openssl.org/docs/manmaster/man1/ciphers.html#CIPHER-STRINGS>.
  316. =item fd
  317. fd=3
  318. File descriptor with an already prepared listen socket.
  319. =item key
  320. key=/etc/tls/server.key
  321. mojolicious.org_key=/etc/tls/mojo.key
  322. Path to the TLS key file, defaults to a built-in test key.
  323. =item reuse
  324. reuse=1
  325. Allow multiple servers to use the same port with the C<SO_REUSEPORT> socket
  326. option.
  327. =item single_accept
  328. single_accept=1
  329. Only accept one connection at a time.
  330. =item verify
  331. verify=0x00
  332. TLS verification mode.
  333. =item version
  334. version=TLSv1_2
  335. TLS protocol version.
  336. =back
  337. =head2 max_clients
  338. my $max = $daemon->max_clients;
  339. $daemon = $daemon->max_clients(100);
  340. Maximum number of accepted connections this server is allowed to handle
  341. concurrently, before stopping to accept new incoming connections, passed along
  342. to L<Mojo::IOLoop/"max_connections">.
  343. =head2 max_requests
  344. my $max = $daemon->max_requests;
  345. $daemon = $daemon->max_requests(250);
  346. Maximum number of keep-alive requests per connection, defaults to C<100>.
  347. =head2 silent
  348. my $bool = $daemon->silent;
  349. $daemon = $daemon->silent($bool);
  350. Disable console messages.
  351. =head1 METHODS
  352. L<Mojo::Server::Daemon> inherits all methods from L<Mojo::Server> and
  353. implements the following new ones.
  354. =head2 ports
  355. my $ports = $daemon->ports;
  356. Get all ports this server is currently listening on.
  357. # All ports
  358. say for @{$daemon->ports};
  359. =head2 run
  360. $daemon->run;
  361. Run server and wait for L</"SIGNALS">.
  362. =head2 start
  363. $daemon = $daemon->start;
  364. Start or resume accepting connections through L</"ioloop">.
  365. # Listen on random port
  366. my $port = $daemon->listen(['http://127.0.0.1'])->start->ports->[0];
  367. # Run multiple web servers concurrently
  368. my $daemon1 = Mojo::Server::Daemon->new(listen => ['http://*:3000'])->start;
  369. my $daemon2 = Mojo::Server::Daemon->new(listen => ['http://*:4000'])->start;
  370. Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
  371. =head2 stop
  372. $daemon = $daemon->stop;
  373. Stop accepting connections through L</"ioloop">.
  374. =head1 DEBUGGING
  375. You can set the C<MOJO_SERVER_DEBUG> environment variable to get some advanced
  376. diagnostics information printed to C<STDERR>.
  377. MOJO_SERVER_DEBUG=1
  378. =head1 SEE ALSO
  379. L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
  380. =cut