puma 4.1.1 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +149 -10
- data/LICENSE +23 -20
- data/README.md +30 -46
- data/docs/architecture.md +3 -3
- data/docs/deployment.md +9 -3
- data/docs/fork_worker.md +31 -0
- data/docs/jungle/README.md +13 -0
- data/{tools → docs}/jungle/rc.d/README.md +0 -0
- data/{tools → docs}/jungle/rc.d/puma +0 -0
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/{tools → docs}/jungle/upstart/README.md +0 -0
- data/{tools → docs}/jungle/upstart/puma-manager.conf +0 -0
- data/{tools → docs}/jungle/upstart/puma.conf +0 -0
- data/docs/plugins.md +20 -10
- data/docs/signals.md +7 -6
- data/docs/systemd.md +1 -63
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/extconf.rb +6 -0
- data/ext/puma_http11/http11_parser.c +40 -63
- data/ext/puma_http11/http11_parser.java.rl +21 -37
- data/ext/puma_http11/http11_parser.rl +3 -1
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/ext/puma_http11/mini_ssl.c +15 -2
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +91 -106
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +77 -18
- data/ext/puma_http11/puma_http11.c +9 -38
- data/lib/puma.rb +23 -0
- data/lib/puma/app/status.rb +46 -30
- data/lib/puma/binder.rb +112 -124
- data/lib/puma/cli.rb +11 -15
- data/lib/puma/client.rb +250 -209
- data/lib/puma/cluster.rb +203 -85
- data/lib/puma/commonlogger.rb +2 -2
- data/lib/puma/configuration.rb +31 -42
- data/lib/puma/const.rb +24 -19
- data/lib/puma/control_cli.rb +46 -17
- data/lib/puma/detect.rb +17 -0
- data/lib/puma/dsl.rb +162 -70
- data/lib/puma/error_logger.rb +97 -0
- data/lib/puma/events.rb +35 -31
- data/lib/puma/io_buffer.rb +9 -2
- data/lib/puma/jruby_restart.rb +0 -58
- data/lib/puma/launcher.rb +117 -58
- data/lib/puma/minissl.rb +60 -18
- data/lib/puma/minissl/context_builder.rb +73 -0
- data/lib/puma/null_io.rb +1 -1
- data/lib/puma/plugin.rb +6 -12
- data/lib/puma/rack/builder.rb +0 -4
- data/lib/puma/reactor.rb +16 -9
- data/lib/puma/runner.rb +11 -32
- data/lib/puma/server.rb +173 -193
- data/lib/puma/single.rb +7 -64
- data/lib/puma/state_file.rb +6 -3
- data/lib/puma/thread_pool.rb +104 -81
- data/lib/rack/handler/puma.rb +1 -5
- data/tools/Dockerfile +16 -0
- data/tools/trickletest.rb +0 -1
- metadata +23 -24
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/convenient.rb +0 -25
- data/lib/puma/daemon_ext.rb +0 -33
- data/lib/puma/delegation.rb +0 -13
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
@@ -10,6 +10,7 @@
|
|
10
10
|
#include "ext_help.h"
|
11
11
|
#include <assert.h>
|
12
12
|
#include <string.h>
|
13
|
+
#include <ctype.h>
|
13
14
|
#include "http11_parser.h"
|
14
15
|
|
15
16
|
#ifndef MANAGED_STRINGS
|
@@ -53,7 +54,7 @@ DEF_MAX_LENGTH(FIELD_NAME, 256);
|
|
53
54
|
DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
|
54
55
|
DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
|
55
56
|
DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
|
56
|
-
DEF_MAX_LENGTH(REQUEST_PATH,
|
57
|
+
DEF_MAX_LENGTH(REQUEST_PATH, 8192);
|
57
58
|
DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
|
58
59
|
DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
|
59
60
|
|
@@ -111,21 +112,6 @@ static struct common_field common_http_fields[] = {
|
|
111
112
|
# undef f
|
112
113
|
};
|
113
114
|
|
114
|
-
/*
|
115
|
-
* qsort(3) and bsearch(3) improve average performance slightly, but may
|
116
|
-
* not be worth it for lack of portability to certain platforms...
|
117
|
-
*/
|
118
|
-
#if defined(HAVE_QSORT_BSEARCH)
|
119
|
-
/* sort by length, then by name if there's a tie */
|
120
|
-
static int common_field_cmp(const void *a, const void *b)
|
121
|
-
{
|
122
|
-
struct common_field *cfa = (struct common_field *)a;
|
123
|
-
struct common_field *cfb = (struct common_field *)b;
|
124
|
-
signed long diff = cfa->len - cfb->len;
|
125
|
-
return diff ? diff : memcmp(cfa->name, cfb->name, cfa->len);
|
126
|
-
}
|
127
|
-
#endif /* HAVE_QSORT_BSEARCH */
|
128
|
-
|
129
115
|
static void init_common_fields(void)
|
130
116
|
{
|
131
117
|
unsigned i;
|
@@ -142,28 +128,10 @@ static void init_common_fields(void)
|
|
142
128
|
}
|
143
129
|
rb_global_variable(&cf->value);
|
144
130
|
}
|
145
|
-
|
146
|
-
#if defined(HAVE_QSORT_BSEARCH)
|
147
|
-
qsort(common_http_fields,
|
148
|
-
ARRAY_SIZE(common_http_fields),
|
149
|
-
sizeof(struct common_field),
|
150
|
-
common_field_cmp);
|
151
|
-
#endif /* HAVE_QSORT_BSEARCH */
|
152
131
|
}
|
153
132
|
|
154
133
|
static VALUE find_common_field_value(const char *field, size_t flen)
|
155
134
|
{
|
156
|
-
#if defined(HAVE_QSORT_BSEARCH)
|
157
|
-
struct common_field key;
|
158
|
-
struct common_field *found;
|
159
|
-
key.name = field;
|
160
|
-
key.len = (signed long)flen;
|
161
|
-
found = (struct common_field *)bsearch(&key, common_http_fields,
|
162
|
-
ARRAY_SIZE(common_http_fields),
|
163
|
-
sizeof(struct common_field),
|
164
|
-
common_field_cmp);
|
165
|
-
return found ? found->value : Qnil;
|
166
|
-
#else /* !HAVE_QSORT_BSEARCH */
|
167
135
|
unsigned i;
|
168
136
|
struct common_field *cf = common_http_fields;
|
169
137
|
for(i = 0; i < ARRAY_SIZE(common_http_fields); i++, cf++) {
|
@@ -171,7 +139,6 @@ static VALUE find_common_field_value(const char *field, size_t flen)
|
|
171
139
|
return cf->value;
|
172
140
|
}
|
173
141
|
return Qnil;
|
174
|
-
#endif /* !HAVE_QSORT_BSEARCH */
|
175
142
|
}
|
176
143
|
|
177
144
|
void http_field(puma_parser* hp, const char *field, size_t flen,
|
@@ -200,6 +167,8 @@ void http_field(puma_parser* hp, const char *field, size_t flen,
|
|
200
167
|
f = rb_str_new(hp->buf, new_size);
|
201
168
|
}
|
202
169
|
|
170
|
+
while (vlen > 0 && isspace(value[vlen - 1])) vlen--;
|
171
|
+
|
203
172
|
/* check for duplicate header */
|
204
173
|
v = rb_hash_aref(hp->request, f);
|
205
174
|
|
@@ -398,7 +367,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
|
|
398
367
|
VALIDATE_MAX_LENGTH(puma_parser_nread(http), HEADER);
|
399
368
|
|
400
369
|
if(puma_parser_has_error(http)) {
|
401
|
-
rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails.");
|
370
|
+
rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails. Are you trying to open an SSL connection to a non-SSL Puma?");
|
402
371
|
} else {
|
403
372
|
return INT2FIX(puma_parser_nread(http));
|
404
373
|
}
|
@@ -465,8 +434,9 @@ VALUE HttpParser_body(VALUE self) {
|
|
465
434
|
return http->body;
|
466
435
|
}
|
467
436
|
|
468
|
-
|
437
|
+
#ifdef HAVE_OPENSSL_BIO_H
|
469
438
|
void Init_mini_ssl(VALUE mod);
|
439
|
+
#endif
|
470
440
|
|
471
441
|
void Init_puma_http11()
|
472
442
|
{
|
@@ -495,6 +465,7 @@ void Init_puma_http11()
|
|
495
465
|
rb_define_method(cHttpParser, "body", HttpParser_body, 0);
|
496
466
|
init_common_fields();
|
497
467
|
|
498
|
-
|
468
|
+
#ifdef HAVE_OPENSSL_BIO_H
|
499
469
|
Init_mini_ssl(mPuma);
|
470
|
+
#endif
|
500
471
|
}
|
data/lib/puma.rb
CHANGED
@@ -10,6 +10,9 @@ require 'stringio'
|
|
10
10
|
|
11
11
|
require 'thread'
|
12
12
|
|
13
|
+
require_relative 'puma/puma_http11'
|
14
|
+
require_relative 'puma/detect'
|
15
|
+
|
13
16
|
module Puma
|
14
17
|
autoload :Const, 'puma/const'
|
15
18
|
autoload :Server, 'puma/server'
|
@@ -20,6 +23,26 @@ module Puma
|
|
20
23
|
end
|
21
24
|
|
22
25
|
def self.stats
|
26
|
+
require 'json'
|
27
|
+
@get_stats.stats.to_json
|
28
|
+
end
|
29
|
+
|
30
|
+
# @version 5.0.0
|
31
|
+
def self.stats_hash
|
23
32
|
@get_stats.stats
|
24
33
|
end
|
34
|
+
|
35
|
+
# Thread name is new in Ruby 2.3
|
36
|
+
def self.set_thread_name(name)
|
37
|
+
return unless Thread.current.respond_to?(:name=)
|
38
|
+
Thread.current.name = "puma #{name}"
|
39
|
+
end
|
40
|
+
|
41
|
+
unless HAS_SSL
|
42
|
+
module MiniSSL
|
43
|
+
# this class is defined so that it exists when Puma is compiled
|
44
|
+
# without ssl support, as Server and Reactor use it in rescue statements.
|
45
|
+
class SSLError < StandardError ; end
|
46
|
+
end
|
47
|
+
end
|
25
48
|
end
|
data/lib/puma/app/status.rb
CHANGED
@@ -1,32 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'json'
|
4
|
-
|
5
3
|
module Puma
|
6
4
|
module App
|
7
5
|
# Check out {#call}'s source code to see what actions this web application
|
8
6
|
# can respond to.
|
9
7
|
class Status
|
10
|
-
def initialize(cli)
|
11
|
-
@cli = cli
|
12
|
-
@auth_token = nil
|
13
|
-
end
|
14
8
|
OK_STATUS = '{ "status": "ok" }'.freeze
|
15
9
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
return true unless @auth_token
|
20
|
-
env['QUERY_STRING'].to_s.split(/&;/).include?("token=#{@auth_token}")
|
21
|
-
end
|
22
|
-
|
23
|
-
def rack_response(status, body, content_type='application/json')
|
24
|
-
headers = {
|
25
|
-
'Content-Type' => content_type,
|
26
|
-
'Content-Length' => body.bytesize.to_s
|
27
|
-
}
|
28
|
-
|
29
|
-
[status, headers, [body]]
|
10
|
+
def initialize(cli, token = nil)
|
11
|
+
@cli = cli
|
12
|
+
@auth_token = token
|
30
13
|
end
|
31
14
|
|
32
15
|
def call(env)
|
@@ -34,46 +17,79 @@ module Puma
|
|
34
17
|
return rack_response(403, 'Invalid auth token', 'text/plain')
|
35
18
|
end
|
36
19
|
|
20
|
+
if env['PATH_INFO'] =~ /\/(gc-stats|stats|thread-backtraces)$/
|
21
|
+
require 'json'
|
22
|
+
end
|
23
|
+
|
37
24
|
case env['PATH_INFO']
|
38
25
|
when /\/stop$/
|
39
26
|
@cli.stop
|
40
|
-
|
27
|
+
rack_response(200, OK_STATUS)
|
41
28
|
|
42
29
|
when /\/halt$/
|
43
30
|
@cli.halt
|
44
|
-
|
31
|
+
rack_response(200, OK_STATUS)
|
45
32
|
|
46
33
|
when /\/restart$/
|
47
34
|
@cli.restart
|
48
|
-
|
35
|
+
rack_response(200, OK_STATUS)
|
49
36
|
|
50
37
|
when /\/phased-restart$/
|
51
38
|
if !@cli.phased_restart
|
52
|
-
|
39
|
+
rack_response(404, '{ "error": "phased restart not available" }')
|
53
40
|
else
|
54
|
-
|
41
|
+
rack_response(200, OK_STATUS)
|
55
42
|
end
|
56
43
|
|
57
44
|
when /\/reload-worker-directory$/
|
58
45
|
if !@cli.send(:reload_worker_directory)
|
59
|
-
|
46
|
+
rack_response(404, '{ "error": "reload_worker_directory not available" }')
|
60
47
|
else
|
61
|
-
|
48
|
+
rack_response(200, OK_STATUS)
|
62
49
|
end
|
63
50
|
|
64
51
|
when /\/gc$/
|
65
52
|
GC.start
|
66
|
-
|
53
|
+
rack_response(200, OK_STATUS)
|
67
54
|
|
68
55
|
when /\/gc-stats$/
|
69
|
-
|
56
|
+
rack_response(200, GC.stat.to_json)
|
70
57
|
|
71
58
|
when /\/stats$/
|
72
|
-
|
59
|
+
rack_response(200, @cli.stats.to_json)
|
60
|
+
|
61
|
+
when /\/thread-backtraces$/
|
62
|
+
backtraces = []
|
63
|
+
@cli.thread_status do |name, backtrace|
|
64
|
+
backtraces << { name: name, backtrace: backtrace }
|
65
|
+
end
|
66
|
+
|
67
|
+
rack_response(200, backtraces.to_json)
|
68
|
+
|
69
|
+
when /\/refork$/
|
70
|
+
Process.kill "SIGURG", $$
|
71
|
+
rack_response(200, OK_STATUS)
|
72
|
+
|
73
73
|
else
|
74
74
|
rack_response 404, "Unsupported action", 'text/plain'
|
75
75
|
end
|
76
76
|
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def authenticate(env)
|
81
|
+
return true unless @auth_token
|
82
|
+
env['QUERY_STRING'].to_s.split(/&;/).include?("token=#{@auth_token}")
|
83
|
+
end
|
84
|
+
|
85
|
+
def rack_response(status, body, content_type='application/json')
|
86
|
+
headers = {
|
87
|
+
'Content-Type' => content_type,
|
88
|
+
'Content-Length' => body.bytesize.to_s
|
89
|
+
}
|
90
|
+
|
91
|
+
[status, headers, [body]]
|
92
|
+
end
|
77
93
|
end
|
78
94
|
end
|
79
95
|
end
|
data/lib/puma/binder.rb
CHANGED
@@ -5,14 +5,22 @@ require 'socket'
|
|
5
5
|
|
6
6
|
require 'puma/const'
|
7
7
|
require 'puma/util'
|
8
|
+
require 'puma/configuration'
|
8
9
|
|
9
10
|
module Puma
|
11
|
+
|
12
|
+
if HAS_SSL
|
13
|
+
require 'puma/minissl'
|
14
|
+
require 'puma/minissl/context_builder'
|
15
|
+
require 'puma/accept_nonblock'
|
16
|
+
end
|
17
|
+
|
10
18
|
class Binder
|
11
19
|
include Puma::Const
|
12
20
|
|
13
|
-
RACK_VERSION = [1,
|
21
|
+
RACK_VERSION = [1,6].freeze
|
14
22
|
|
15
|
-
def initialize(events)
|
23
|
+
def initialize(events, conf = Configuration.new)
|
16
24
|
@events = events
|
17
25
|
@listeners = []
|
18
26
|
@inherited_fds = {}
|
@@ -22,8 +30,8 @@ module Puma
|
|
22
30
|
@proto_env = {
|
23
31
|
"rack.version".freeze => RACK_VERSION,
|
24
32
|
"rack.errors".freeze => events.stderr,
|
25
|
-
"rack.multithread".freeze =>
|
26
|
-
"rack.multiprocess".freeze =>
|
33
|
+
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
34
|
+
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
27
35
|
"rack.run_once".freeze => false,
|
28
36
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
29
37
|
|
@@ -42,7 +50,13 @@ module Puma
|
|
42
50
|
@ios = []
|
43
51
|
end
|
44
52
|
|
45
|
-
attr_reader :
|
53
|
+
attr_reader :ios
|
54
|
+
|
55
|
+
# @version 5.0.0
|
56
|
+
attr_reader :activated_sockets, :envs, :inherited_fds, :listeners, :proto_env, :unix_paths
|
57
|
+
|
58
|
+
# @version 5.0.0
|
59
|
+
attr_writer :ios, :listeners
|
46
60
|
|
47
61
|
def env(sock)
|
48
62
|
@envs.fetch(sock, @proto_env)
|
@@ -50,50 +64,45 @@ module Puma
|
|
50
64
|
|
51
65
|
def close
|
52
66
|
@ios.each { |i| i.close }
|
53
|
-
@unix_paths.each do |i|
|
54
|
-
# Errno::ENOENT is intermittently raised
|
55
|
-
begin
|
56
|
-
unix_socket = UNIXSocket.new i
|
57
|
-
unix_socket.close
|
58
|
-
rescue Errno::ENOENT
|
59
|
-
end
|
60
|
-
end
|
61
67
|
end
|
62
68
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
if k =~ /PUMA_INHERIT_\d+/
|
68
|
-
fd, url = v.split(":", 2)
|
69
|
-
@inherited_fds[url] = fd.to_i
|
70
|
-
remove << k
|
71
|
-
elsif k == 'LISTEN_FDS' && ENV['LISTEN_PID'].to_i == $$
|
72
|
-
v.to_i.times do |num|
|
73
|
-
fd = num + 3
|
74
|
-
sock = TCPServer.for_fd(fd)
|
75
|
-
begin
|
76
|
-
key = [ :unix, Socket.unpack_sockaddr_un(sock.getsockname) ]
|
77
|
-
rescue ArgumentError
|
78
|
-
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
79
|
-
if addr =~ /\:/
|
80
|
-
addr = "[#{addr}]"
|
81
|
-
end
|
82
|
-
key = [ :tcp, addr, port ]
|
83
|
-
end
|
84
|
-
@activated_sockets[key] = sock
|
85
|
-
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
86
|
-
end
|
87
|
-
remove << k << 'LISTEN_PID'
|
88
|
-
end
|
89
|
-
end
|
69
|
+
# @version 5.0.0
|
70
|
+
def connected_ports
|
71
|
+
ios.map { |io| io.addr[1] }.uniq
|
72
|
+
end
|
90
73
|
|
91
|
-
|
92
|
-
|
74
|
+
# @version 5.0.0
|
75
|
+
def create_inherited_fds(env_hash)
|
76
|
+
env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
|
77
|
+
fd, url = v.split(":", 2)
|
78
|
+
@inherited_fds[url] = fd.to_i
|
79
|
+
end.keys # pass keys back for removal
|
80
|
+
end
|
81
|
+
|
82
|
+
# systemd socket activation.
|
83
|
+
# LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
|
84
|
+
# LISTEN_PID = PID of the service process, aka us
|
85
|
+
# @see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
|
86
|
+
# @version 5.0.0
|
87
|
+
#
|
88
|
+
def create_activated_fds(env_hash)
|
89
|
+
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
90
|
+
env_hash['LISTEN_FDS'].to_i.times do |index|
|
91
|
+
sock = TCPServer.for_fd(socket_activation_fd(index))
|
92
|
+
key = begin # Try to parse as a path
|
93
|
+
[:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
|
94
|
+
rescue ArgumentError # Try to parse as a port/ip
|
95
|
+
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
96
|
+
addr = "[#{addr}]" if addr =~ /\:/
|
97
|
+
[:tcp, addr, port]
|
98
|
+
end
|
99
|
+
@activated_sockets[key] = sock
|
100
|
+
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
93
101
|
end
|
102
|
+
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
94
103
|
end
|
95
104
|
|
96
|
-
def parse(binds, logger)
|
105
|
+
def parse(binds, logger, log_msg = 'Listening')
|
97
106
|
binds.each do |str|
|
98
107
|
uri = URI.parse str
|
99
108
|
case uri.scheme
|
@@ -111,7 +120,17 @@ module Puma
|
|
111
120
|
bak = params.fetch('backlog', 1024).to_i
|
112
121
|
|
113
122
|
io = add_tcp_listener uri.host, uri.port, opt, bak
|
114
|
-
|
123
|
+
|
124
|
+
@ios.each do |i|
|
125
|
+
next unless TCPServer === i
|
126
|
+
addr = if i.local_address.ipv6?
|
127
|
+
"[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
|
128
|
+
else
|
129
|
+
i.local_address.ip_unpack.join(':')
|
130
|
+
end
|
131
|
+
|
132
|
+
logger.log "* #{log_msg} on http://#{addr}"
|
133
|
+
end
|
115
134
|
end
|
116
135
|
|
117
136
|
@listeners << [str, io] if io
|
@@ -146,70 +165,16 @@ module Puma
|
|
146
165
|
end
|
147
166
|
|
148
167
|
io = add_unix_listener path, umask, mode, backlog
|
149
|
-
logger.log "*
|
168
|
+
logger.log "* #{log_msg} on #{str}"
|
150
169
|
end
|
151
170
|
|
152
171
|
@listeners << [str, io]
|
153
172
|
when "ssl"
|
154
|
-
params = Util.parse_query uri.query
|
155
|
-
require 'puma/minissl'
|
156
|
-
|
157
|
-
MiniSSL.check
|
158
|
-
|
159
|
-
ctx = MiniSSL::Context.new
|
160
|
-
|
161
|
-
if defined?(JRUBY_VERSION)
|
162
|
-
unless params['keystore']
|
163
|
-
@events.error "Please specify the Java keystore via 'keystore='"
|
164
|
-
end
|
165
|
-
|
166
|
-
ctx.keystore = params['keystore']
|
167
|
-
|
168
|
-
unless params['keystore-pass']
|
169
|
-
@events.error "Please specify the Java keystore password via 'keystore-pass='"
|
170
|
-
end
|
171
|
-
|
172
|
-
ctx.keystore_pass = params['keystore-pass']
|
173
|
-
ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
|
174
|
-
else
|
175
|
-
unless params['key']
|
176
|
-
@events.error "Please specify the SSL key via 'key='"
|
177
|
-
end
|
178
|
-
|
179
|
-
ctx.key = params['key']
|
180
|
-
|
181
|
-
unless params['cert']
|
182
|
-
@events.error "Please specify the SSL cert via 'cert='"
|
183
|
-
end
|
184
173
|
|
185
|
-
|
174
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
186
175
|
|
187
|
-
|
188
|
-
|
189
|
-
@events.error "Please specify the SSL ca via 'ca='"
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
ctx.ca = params['ca'] if params['ca']
|
194
|
-
ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
|
195
|
-
end
|
196
|
-
|
197
|
-
ctx.no_tlsv1 = true if params['no_tlsv1'] == 'true'
|
198
|
-
ctx.no_tlsv1_1 = true if params['no_tlsv1_1'] == 'true'
|
199
|
-
|
200
|
-
if params['verify_mode']
|
201
|
-
ctx.verify_mode = case params['verify_mode']
|
202
|
-
when "peer"
|
203
|
-
MiniSSL::VERIFY_PEER
|
204
|
-
when "force_peer"
|
205
|
-
MiniSSL::VERIFY_PEER | MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
206
|
-
when "none"
|
207
|
-
MiniSSL::VERIFY_NONE
|
208
|
-
else
|
209
|
-
@events.error "Please specify a valid verify_mode="
|
210
|
-
MiniSSL::VERIFY_NONE
|
211
|
-
end
|
212
|
-
end
|
176
|
+
params = Util.parse_query uri.query
|
177
|
+
ctx = MiniSSL::ContextBuilder.new(params, @events).context
|
213
178
|
|
214
179
|
if fd = @inherited_fds.delete(str)
|
215
180
|
logger.log "* Inherited #{str}"
|
@@ -258,12 +223,6 @@ module Puma
|
|
258
223
|
end
|
259
224
|
end
|
260
225
|
|
261
|
-
def loopback_addresses
|
262
|
-
Socket.ip_address_list.select do |addrinfo|
|
263
|
-
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
264
|
-
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
265
|
-
end
|
266
|
-
|
267
226
|
# Tell the server to listen on host +host+, port +port+.
|
268
227
|
# If +optimize_for_latency+ is true (the default) then clients connecting
|
269
228
|
# will be optimized for latency over throughput.
|
@@ -280,20 +239,17 @@ module Puma
|
|
280
239
|
end
|
281
240
|
|
282
241
|
host = host[1..-2] if host and host[0..0] == '['
|
283
|
-
|
242
|
+
tcp_server = TCPServer.new(host, port)
|
284
243
|
if optimize_for_latency
|
285
|
-
|
244
|
+
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
286
245
|
end
|
287
|
-
|
288
|
-
|
289
|
-
@connected_port = s.addr[1]
|
246
|
+
tcp_server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
247
|
+
tcp_server.listen backlog
|
290
248
|
|
291
|
-
@ios <<
|
292
|
-
|
249
|
+
@ios << tcp_server
|
250
|
+
tcp_server
|
293
251
|
end
|
294
252
|
|
295
|
-
attr_reader :connected_port
|
296
|
-
|
297
253
|
def inherit_tcp_listener(host, port, fd)
|
298
254
|
if fd.kind_of? TCPServer
|
299
255
|
s = fd
|
@@ -307,9 +263,8 @@ module Puma
|
|
307
263
|
|
308
264
|
def add_ssl_listener(host, port, ctx,
|
309
265
|
optimize_for_latency=true, backlog=1024)
|
310
|
-
require 'puma/minissl'
|
311
266
|
|
312
|
-
|
267
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
313
268
|
|
314
269
|
if host == "localhost"
|
315
270
|
loopback_addresses.each do |addr|
|
@@ -326,7 +281,6 @@ module Puma
|
|
326
281
|
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
327
282
|
s.listen backlog
|
328
283
|
|
329
|
-
|
330
284
|
ssl = MiniSSL::Server.new s, ctx
|
331
285
|
env = @proto_env.dup
|
332
286
|
env[HTTPS_KEY] = HTTPS
|
@@ -337,8 +291,7 @@ module Puma
|
|
337
291
|
end
|
338
292
|
|
339
293
|
def inherit_ssl_listener(fd, ctx)
|
340
|
-
|
341
|
-
MiniSSL.check
|
294
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
342
295
|
|
343
296
|
if fd.kind_of? TCPServer
|
344
297
|
s = fd
|
@@ -359,7 +312,7 @@ module Puma
|
|
359
312
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
360
313
|
#
|
361
314
|
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
362
|
-
@unix_paths << path
|
315
|
+
@unix_paths << path unless File.exist? path
|
363
316
|
|
364
317
|
# Let anyone connect by default
|
365
318
|
umask ||= 0
|
@@ -397,7 +350,7 @@ module Puma
|
|
397
350
|
end
|
398
351
|
|
399
352
|
def inherit_unix_listener(path, fd)
|
400
|
-
@unix_paths << path
|
353
|
+
@unix_paths << path unless File.exist? path
|
401
354
|
|
402
355
|
if fd.kind_of? TCPServer
|
403
356
|
s = fd
|
@@ -413,5 +366,40 @@ module Puma
|
|
413
366
|
s
|
414
367
|
end
|
415
368
|
|
369
|
+
def close_listeners
|
370
|
+
listeners.each do |l, io|
|
371
|
+
io.close unless io.closed? # Ruby 2.2 issue
|
372
|
+
uri = URI.parse(l)
|
373
|
+
next unless uri.scheme == 'unix'
|
374
|
+
unix_path = "#{uri.host}#{uri.path}"
|
375
|
+
File.unlink unix_path if unix_paths.include? unix_path
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
def redirects_for_restart
|
380
|
+
redirects = listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
381
|
+
redirects[:close_others] = true
|
382
|
+
redirects
|
383
|
+
end
|
384
|
+
|
385
|
+
# @version 5.0.0
|
386
|
+
def redirects_for_restart_env
|
387
|
+
listeners.each_with_object({}).with_index do |(listen, memo), i|
|
388
|
+
memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
private
|
393
|
+
|
394
|
+
def loopback_addresses
|
395
|
+
Socket.ip_address_list.select do |addrinfo|
|
396
|
+
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
397
|
+
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
398
|
+
end
|
399
|
+
|
400
|
+
# @version 5.0.0
|
401
|
+
def socket_activation_fd(int)
|
402
|
+
int + 3 # 3 is the magic number you add to follow the SA protocol
|
403
|
+
end
|
416
404
|
end
|
417
405
|
end
|