Newer Older
321 lines | 7.389kb
add files
Yuki Kimoto authored on 2014-03-26
1
package Mojo::IOLoop::Server;
2
use Mojo::Base 'Mojo::EventEmitter';
3

            
4
use Carp 'croak';
5
use File::Basename 'dirname';
6
use File::Spec::Functions 'catfile';
7
use IO::Socket::INET;
8
use Scalar::Util 'weaken';
9
use Socket qw(IPPROTO_TCP TCP_NODELAY);
10

            
11
# IPv6 support requires IO::Socket::IP
12
use constant IPV6 => $ENV{MOJO_NO_IPV6}
13
  ? 0
14
  : eval 'use IO::Socket::IP 0.16 (); 1';
15

            
16
# TLS support requires IO::Socket::SSL
17
use constant TLS => $ENV{MOJO_NO_TLS} ? 0
18
  : eval(IPV6 ? 'use IO::Socket::SSL 1.75 (); 1'
19
  : 'use IO::Socket::SSL 1.75 "inet4"; 1');
20
use constant TLS_READ  => TLS ? IO::Socket::SSL::SSL_WANT_READ()  : 0;
21
use constant TLS_WRITE => TLS ? IO::Socket::SSL::SSL_WANT_WRITE() : 0;
22

            
23
# To regenerate the certificate run this command (18.04.2012)
24
# openssl req -new -x509 -keyout server.key -out server.crt -nodes -days 7300
25
my $CERT = catfile dirname(__FILE__), 'server.crt';
26
my $KEY  = catfile dirname(__FILE__), 'server.key';
27

            
28
has multi_accept => 50;
29
has reactor      => sub {
30
  require Mojo::IOLoop;
31
  Mojo::IOLoop->singleton->reactor;
32
};
33

            
34
sub DESTROY {
35
  my $self = shift;
36
  if (my $port = $self->{port}) { $ENV{MOJO_REUSE} =~ s/(?:^|\,)${port}:\d+// }
37
  return unless my $reactor = $self->reactor;
38
  $self->stop if $self->{handle};
39
  $reactor->remove($_) for values %{$self->{handles}};
40
}
41

            
42
sub generate_port {
43
  IO::Socket::INET->new(Listen => 5, LocalAddr => '127.0.0.1')->sockport;
44
}
45

            
46
sub handle { shift->{handle} }
47

            
48
sub listen {
49
  my $self = shift;
50
  my $args = ref $_[0] ? $_[0] : {@_};
51

            
52
  # Look for reusable file descriptor
53
  my $reuse = my $port = $self->{port} = $args->{port} || 3000;
54
  $ENV{MOJO_REUSE} ||= '';
55
  my $fd;
56
  if ($ENV{MOJO_REUSE} =~ /(?:^|\,)${reuse}:(\d+)/) { $fd = $1 }
57

            
58
  # Allow file descriptor inheritance
59
  local $^F = 1000;
60

            
61
  # Reuse file descriptor
62
  my $handle;
63
  my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET';
64
  if (defined $fd) {
65
    $handle = $class->new_from_fd($fd, 'r')
66
      or croak "Can't open file descriptor $fd: $!";
67
  }
68

            
69
  # New socket
70
  else {
71
    my %options = (
72
      Listen => $args->{backlog} // SOMAXCONN,
73
      LocalAddr => $args->{address} || '0.0.0.0',
74
      LocalPort => $port,
75
      ReuseAddr => 1,
76
      ReusePort => $args->{reuse},
77
      Type      => SOCK_STREAM
78
    );
79
    $options{LocalAddr} =~ s/[\[\]]//g;
80
    $handle = $class->new(%options) or croak "Can't create listen socket: $@";
81
    $fd = fileno $handle;
82
    $ENV{MOJO_REUSE} .= length $ENV{MOJO_REUSE} ? ",$reuse:$fd" : "$reuse:$fd";
83
  }
84
  $handle->blocking(0);
85
  $self->{handle} = $handle;
86

            
87
  return unless $args->{tls};
88
  croak "IO::Socket::SSL 1.75 required for TLS support" unless TLS;
89

            
90
  # Prioritize RC4 to mitigate BEAST attack
91
  $self->{tls} = {
92
    SSL_ca_file => $args->{tls_ca}
93
      && -T $args->{tls_ca} ? $args->{tls_ca} : undef,
94
    SSL_cert_file => $args->{tls_cert} || $CERT,
95
    SSL_cipher_list => $args->{tls_ciphers}
96
      // 'ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH',
97
    SSL_honor_cipher_order => 1,
98
    SSL_key_file           => $args->{tls_key} || $KEY,
99
    SSL_startHandshake     => 0,
100
    SSL_verify_mode => $args->{tls_verify} // $args->{tls_ca} ? 0x03 : 0x00
101
  };
102
}
103

            
104
sub start {
105
  my $self = shift;
106
  weaken $self;
107
  $self->reactor->io(
108
    $self->{handle} => sub { $self->_accept for 1 .. $self->multi_accept });
109
}
110

            
111
sub stop { $_[0]->reactor->remove($_[0]{handle}) }
112

            
113
sub _accept {
114
  my $self = shift;
115

            
116
  return unless my $handle = $self->{handle}->accept;
117
  $handle->blocking(0);
118

            
119
  # Disable Nagle's algorithm
120
  setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
121

            
122
  # Start TLS handshake
123
  return $self->emit_safe(accept => $handle) unless my $tls = $self->{tls};
124
  weaken $self;
125
  $tls->{SSL_error_trap} = sub {
126
    return unless my $handle = delete $self->{handles}{shift()};
127
    $self->reactor->remove($handle);
128
    close $handle;
129
  };
130
  return unless $handle = IO::Socket::SSL->start_SSL($handle, %$tls);
131
  $self->reactor->io($handle => sub { $self->_tls($handle) });
132
  $self->{handles}{$handle} = $handle;
133
}
134

            
135
sub _tls {
136
  my ($self, $handle) = @_;
137

            
138
  # Accepted
139
  if ($handle->accept_SSL) {
140
    $self->reactor->remove($handle);
141
    return $self->emit_safe(accept => delete $self->{handles}{$handle});
142
  }
143

            
144
  # Switch between reading and writing
145
  my $err = $IO::Socket::SSL::SSL_ERROR;
146
  if    ($err == TLS_READ)  { $self->reactor->watch($handle, 1, 0) }
147
  elsif ($err == TLS_WRITE) { $self->reactor->watch($handle, 1, 1) }
148
}
149

            
150
1;
151

            
152
=encoding utf8
153

            
154
=head1 NAME
155

            
156
Mojo::IOLoop::Server - Non-blocking TCP server
157

            
158
=head1 SYNOPSIS
159

            
160
  use Mojo::IOLoop::Server;
161

            
162
  # Create listen socket
163
  my $server = Mojo::IOLoop::Server->new;
164
  $server->on(accept => sub {
165
    my ($server, $handle) = @_;
166
    ...
167
  });
168
  $server->listen(port => 3000);
169

            
170
  # Start and stop accepting connections
171
  $server->start;
172
  $server->stop;
173

            
174
  # Start reactor if necessary
175
  $server->reactor->start unless $server->reactor->is_running;
176

            
177
=head1 DESCRIPTION
178

            
179
L<Mojo::IOLoop::Server> accepts TCP connections for L<Mojo::IOLoop>.
180

            
181
=head1 EVENTS
182

            
183
L<Mojo::IOLoop::Server> inherits all events from L<Mojo::EventEmitter> and can
184
emit the following new ones.
185

            
186
=head2 accept
187

            
188
  $server->on(accept => sub {
189
    my ($server, $handle) = @_;
190
    ...
191
  });
192

            
193
Emitted safely for each accepted connection.
194

            
195
=head1 ATTRIBUTES
196

            
197
L<Mojo::IOLoop::Server> implements the following attributes.
198

            
199
=head2 multi_accept
200

            
201
  my $multi = $server->multi_accept;
202
  $server   = $server->multi_accept(100);
203

            
204
Number of connections to accept at once, defaults to C<50>.
205

            
206
=head2 reactor
207

            
208
  my $reactor = $server->reactor;
209
  $server     = $server->reactor(Mojo::Reactor::Poll->new);
210

            
211
Low level event reactor, defaults to the C<reactor> attribute value of the
212
global L<Mojo::IOLoop> singleton.
213

            
214
=head1 METHODS
215

            
216
L<Mojo::IOLoop::Server> inherits all methods from L<Mojo::EventEmitter> and
217
implements the following new ones.
218

            
219
=head2 generate_port
220

            
221
  my $port = $server->generate_port;
222

            
223
Find a free TCP port, this is a utility function primarily used for tests.
224

            
225
=head2 handle
226

            
227
  my $handle = $server->handle;
228

            
229
Get handle for server.
230

            
231
=head2 listen
232

            
233
  $server->listen(port => 3000);
234

            
235
Create a new listen socket. Note that TLS support depends on
236
L<IO::Socket::SSL> (1.75+) and IPv6 support on L<IO::Socket::IP> (0.16+).
237

            
238
These options are currently available:
239

            
240
=over 2
241

            
242
=item address
243

            
244
  address => '127.0.0.1'
245

            
246
Local address to listen on, defaults to all.
247

            
248
=item backlog
249

            
250
  backlog => 128
251

            
252
Maximum backlog size, defaults to C<SOMAXCONN>.
253

            
254
=item port
255

            
256
  port => 80
257

            
258
Port to listen on.
259

            
260
=item reuse
261

            
262
  reuse => 1
263

            
264
Allow multiple servers to use the same port with the C<SO_REUSEPORT> socket
265
option.
266

            
267
=item tls
268

            
269
  tls => 1
270

            
271
Enable TLS.
272

            
273
=item tls_ca
274

            
275
  tls_ca => '/etc/tls/ca.crt'
276

            
277
Path to TLS certificate authority file.
278

            
279
=item tls_cert
280

            
281
  tls_cert => '/etc/tls/server.crt'
282

            
283
Path to the TLS cert file, defaults to a built-in test certificate.
284

            
285
=item tls_ciphers
286

            
287
  tls_ciphers => 'AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH'
288

            
289
Cipher specification string.
290

            
291
=item tls_key
292

            
293
  tls_key => '/etc/tls/server.key'
294

            
295
Path to the TLS key file, defaults to a built-in test key.
296

            
297
=item tls_verify
298

            
299
  tls_verify => 0x00
300

            
301
TLS verification mode, defaults to C<0x03>.
302

            
303
=back
304

            
305
=head2 start
306

            
307
  $server->start;
308

            
309
Start accepting connections.
310

            
311
=head2 stop
312

            
313
  $server->stop;
314

            
315
Stop accepting connections.
316

            
317
=head1 SEE ALSO
318

            
319
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
320

            
321
=cut