puma 3.9.1 → 4.3.1
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 +5 -5
- data/History.md +232 -0
- data/README.md +162 -224
- data/docs/architecture.md +37 -0
- data/{DEPLOYMENT.md → docs/deployment.md} +24 -4
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/plugins.md +38 -0
- data/docs/restart.md +41 -0
- data/docs/signals.md +56 -3
- data/docs/systemd.md +130 -37
- data/docs/tcp_mode.md +96 -0
- data/ext/puma_http11/PumaHttp11Service.java +2 -0
- data/ext/puma_http11/extconf.rb +13 -0
- data/ext/puma_http11/http11_parser.c +115 -140
- data/ext/puma_http11/http11_parser.java.rl +21 -37
- data/ext/puma_http11/http11_parser.rl +9 -9
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/ext/puma_http11/mini_ssl.c +104 -8
- data/ext/puma_http11/org/jruby/puma/Http11.java +106 -114
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +90 -108
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +21 -4
- data/ext/puma_http11/puma_http11.c +2 -0
- data/lib/puma.rb +16 -0
- data/lib/puma/accept_nonblock.rb +7 -1
- data/lib/puma/app/status.rb +40 -26
- data/lib/puma/binder.rb +57 -74
- data/lib/puma/cli.rb +26 -7
- data/lib/puma/client.rb +243 -190
- data/lib/puma/cluster.rb +78 -34
- data/lib/puma/commonlogger.rb +2 -0
- data/lib/puma/configuration.rb +24 -16
- data/lib/puma/const.rb +36 -18
- data/lib/puma/control_cli.rb +46 -19
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +329 -68
- data/lib/puma/events.rb +6 -1
- data/lib/puma/io_buffer.rb +3 -6
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/launcher.rb +120 -58
- data/lib/puma/minissl.rb +69 -27
- data/lib/puma/minissl/context_builder.rb +76 -0
- data/lib/puma/null_io.rb +2 -0
- data/lib/puma/plugin.rb +7 -2
- data/lib/puma/plugin/tmp_restart.rb +2 -1
- data/lib/puma/rack/builder.rb +4 -1
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +2 -0
- data/lib/puma/reactor.rb +224 -34
- data/lib/puma/runner.rb +25 -4
- data/lib/puma/server.rb +148 -62
- data/lib/puma/single.rb +16 -5
- data/lib/puma/state_file.rb +2 -0
- data/lib/puma/tcp_logger.rb +2 -0
- data/lib/puma/thread_pool.rb +61 -38
- data/lib/puma/util.rb +2 -6
- data/lib/rack/handler/puma.rb +10 -4
- data/tools/docker/Dockerfile +16 -0
- data/tools/jungle/README.md +12 -2
- data/tools/jungle/init.d/README.md +2 -0
- data/tools/jungle/init.d/puma +8 -8
- data/tools/jungle/init.d/run-puma +1 -1
- data/tools/jungle/rc.d/README.md +74 -0
- data/tools/jungle/rc.d/puma +61 -0
- data/tools/jungle/rc.d/puma.conf +10 -0
- data/tools/trickletest.rb +1 -2
- metadata +29 -56
- data/.github/issue_template.md +0 -20
- data/Gemfile +0 -14
- data/Manifest.txt +0 -78
- data/Rakefile +0 -165
- data/Release.md +0 -9
- data/gemfiles/2.1-Gemfile +0 -12
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/convenient.rb +0 -23
- data/lib/puma/daemon_ext.rb +0 -31
- data/lib/puma/delegation.rb +0 -11
- data/lib/puma/java_io_buffer.rb +0 -45
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
- data/puma.gemspec +0 -20
@@ -1,5 +1,7 @@
|
|
1
1
|
package org.jruby.puma;
|
2
2
|
|
3
|
+
import org.jruby.Ruby;
|
4
|
+
import org.jruby.RubyHash;
|
3
5
|
import org.jruby.util.ByteList;
|
4
6
|
|
5
7
|
public class Http11Parser {
|
@@ -19,44 +21,35 @@ public class Http11Parser {
|
|
19
21
|
}
|
20
22
|
|
21
23
|
action start_value { parser.mark = fpc; }
|
22
|
-
action write_value {
|
23
|
-
|
24
|
-
parser.http_field.call(parser.data, parser.field_start, parser.field_len, parser.mark, fpc-parser.mark);
|
25
|
-
}
|
24
|
+
action write_value {
|
25
|
+
Http11.http_field(runtime, parser.data, parser.buffer, parser.field_start, parser.field_len, parser.mark, fpc-parser.mark);
|
26
26
|
}
|
27
|
-
action request_method {
|
28
|
-
|
29
|
-
parser.request_method.call(parser.data, parser.mark, fpc-parser.mark);
|
27
|
+
action request_method {
|
28
|
+
Http11.request_method(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
|
30
29
|
}
|
31
|
-
action request_uri {
|
32
|
-
|
33
|
-
parser.request_uri.call(parser.data, parser.mark, fpc-parser.mark);
|
30
|
+
action request_uri {
|
31
|
+
Http11.request_uri(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
|
34
32
|
}
|
35
|
-
action fragment {
|
36
|
-
|
37
|
-
parser.fragment.call(parser.data, parser.mark, fpc-parser.mark);
|
33
|
+
action fragment {
|
34
|
+
Http11.fragment(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
|
38
35
|
}
|
39
36
|
|
40
37
|
action start_query {parser.query_start = fpc; }
|
41
|
-
action query_string {
|
42
|
-
|
43
|
-
parser.query_string.call(parser.data, parser.query_start, fpc-parser.query_start);
|
38
|
+
action query_string {
|
39
|
+
Http11.query_string(runtime, parser.data, parser.buffer, parser.query_start, fpc-parser.query_start);
|
44
40
|
}
|
45
41
|
|
46
|
-
action http_version {
|
47
|
-
|
48
|
-
parser.http_version.call(parser.data, parser.mark, fpc-parser.mark);
|
42
|
+
action http_version {
|
43
|
+
Http11.http_version(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
|
49
44
|
}
|
50
45
|
|
51
46
|
action request_path {
|
52
|
-
|
53
|
-
parser.request_path.call(parser.data, parser.mark, fpc-parser.mark);
|
47
|
+
Http11.request_path(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
|
54
48
|
}
|
55
49
|
|
56
50
|
action done {
|
57
|
-
parser.body_start = fpc + 1;
|
58
|
-
|
59
|
-
parser.header_done.call(parser.data, fpc + 1, pe - fpc - 1);
|
51
|
+
parser.body_start = fpc + 1;
|
52
|
+
http.header_done(runtime, parser.data, parser.buffer, fpc + 1, pe - fpc - 1);
|
60
53
|
fbreak;
|
61
54
|
}
|
62
55
|
|
@@ -68,11 +61,11 @@ public class Http11Parser {
|
|
68
61
|
%% write data;
|
69
62
|
|
70
63
|
public static interface ElementCB {
|
71
|
-
public void call(
|
64
|
+
public void call(Ruby runtime, RubyHash data, ByteList buffer, int at, int length);
|
72
65
|
}
|
73
66
|
|
74
67
|
public static interface FieldCB {
|
75
|
-
public void call(
|
68
|
+
public void call(Ruby runtime, RubyHash data, ByteList buffer, int field, int flen, int value, int vlen);
|
76
69
|
}
|
77
70
|
|
78
71
|
public static class HttpParser {
|
@@ -85,18 +78,9 @@ public class Http11Parser {
|
|
85
78
|
int field_len;
|
86
79
|
int query_start;
|
87
80
|
|
88
|
-
|
81
|
+
RubyHash data;
|
89
82
|
ByteList buffer;
|
90
83
|
|
91
|
-
public FieldCB http_field;
|
92
|
-
public ElementCB request_method;
|
93
|
-
public ElementCB request_uri;
|
94
|
-
public ElementCB fragment;
|
95
|
-
public ElementCB request_path;
|
96
|
-
public ElementCB query_string;
|
97
|
-
public ElementCB http_version;
|
98
|
-
public ElementCB header_done;
|
99
|
-
|
100
84
|
public void init() {
|
101
85
|
cs = 0;
|
102
86
|
|
@@ -113,7 +97,7 @@ public class Http11Parser {
|
|
113
97
|
|
114
98
|
public final HttpParser parser = new HttpParser();
|
115
99
|
|
116
|
-
public int execute(ByteList buffer, int off) {
|
100
|
+
public int execute(Ruby runtime, Http11 http, ByteList buffer, int off) {
|
117
101
|
int p, pe;
|
118
102
|
int cs = parser.cs;
|
119
103
|
int len = buffer.length();
|
@@ -29,7 +29,7 @@ static void snake_upcase_char(char *c)
|
|
29
29
|
/** Machine **/
|
30
30
|
|
31
31
|
%%{
|
32
|
-
|
32
|
+
|
33
33
|
machine puma_parser;
|
34
34
|
|
35
35
|
action mark { MARK(mark, fpc); }
|
@@ -37,7 +37,7 @@ static void snake_upcase_char(char *c)
|
|
37
37
|
|
38
38
|
action start_field { MARK(field_start, fpc); }
|
39
39
|
action snake_upcase_field { snake_upcase_char((char *)fpc); }
|
40
|
-
action write_field {
|
40
|
+
action write_field {
|
41
41
|
parser->field_len = LEN(field_start, fpc);
|
42
42
|
}
|
43
43
|
|
@@ -45,10 +45,10 @@ static void snake_upcase_char(char *c)
|
|
45
45
|
action write_value {
|
46
46
|
parser->http_field(parser, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
|
47
47
|
}
|
48
|
-
action request_method {
|
48
|
+
action request_method {
|
49
49
|
parser->request_method(parser, PTR_TO(mark), LEN(mark, fpc));
|
50
50
|
}
|
51
|
-
action request_uri {
|
51
|
+
action request_uri {
|
52
52
|
parser->request_uri(parser, PTR_TO(mark), LEN(mark, fpc));
|
53
53
|
}
|
54
54
|
action fragment {
|
@@ -56,11 +56,11 @@ static void snake_upcase_char(char *c)
|
|
56
56
|
}
|
57
57
|
|
58
58
|
action start_query { MARK(query_start, fpc); }
|
59
|
-
action query_string {
|
59
|
+
action query_string {
|
60
60
|
parser->query_string(parser, PTR_TO(query_start), LEN(query_start, fpc));
|
61
61
|
}
|
62
62
|
|
63
|
-
action http_version {
|
63
|
+
action http_version {
|
64
64
|
parser->http_version(parser, PTR_TO(mark), LEN(mark, fpc));
|
65
65
|
}
|
66
66
|
|
@@ -68,8 +68,8 @@ static void snake_upcase_char(char *c)
|
|
68
68
|
parser->request_path(parser, PTR_TO(mark), LEN(mark,fpc));
|
69
69
|
}
|
70
70
|
|
71
|
-
action done {
|
72
|
-
parser->body_start = fpc - buffer + 1;
|
71
|
+
action done {
|
72
|
+
parser->body_start = fpc - buffer + 1;
|
73
73
|
parser->header_done(parser, fpc + 1, pe - fpc - 1);
|
74
74
|
fbreak;
|
75
75
|
}
|
@@ -109,7 +109,7 @@ size_t puma_parser_execute(puma_parser *parser, const char *buffer, size_t len,
|
|
109
109
|
pe = buffer+len;
|
110
110
|
|
111
111
|
/* assert(*pe == '\0' && "pointer does not end on NUL"); */
|
112
|
-
assert(pe - p == len - off && "pointers aren't same distance");
|
112
|
+
assert((size_t) (pe - p) == len - off && "pointers aren't same distance");
|
113
113
|
|
114
114
|
%% write exec;
|
115
115
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
%%{
|
2
|
-
|
2
|
+
|
3
3
|
machine puma_parser_common;
|
4
4
|
|
5
5
|
#### HTTP PROTOCOL GRAMMAR
|
@@ -16,7 +16,7 @@
|
|
16
16
|
unreserved = (alpha | digit | safe | extra | national);
|
17
17
|
escape = ("%" xdigit xdigit);
|
18
18
|
uchar = (unreserved | escape | "%");
|
19
|
-
pchar = (uchar | ":" | "@" | "&" | "=" | "+");
|
19
|
+
pchar = (uchar | ":" | "@" | "&" | "=" | "+" | ";");
|
20
20
|
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
|
21
21
|
|
22
22
|
# elements
|
@@ -30,7 +30,7 @@
|
|
30
30
|
query = ( uchar | reserved )* %query_string ;
|
31
31
|
param = ( pchar | "/" )* ;
|
32
32
|
params = ( param ( ";" param )* ) ;
|
33
|
-
rel_path = ( path? %request_path
|
33
|
+
rel_path = ( path? %request_path ) ("?" %start_query query)?;
|
34
34
|
absolute_path = ( "/"+ rel_path );
|
35
35
|
|
36
36
|
Request_URI = ( "*" | absolute_uri | absolute_path ) >mark %request_uri;
|
data/ext/puma_http11/mini_ssl.c
CHANGED
@@ -142,6 +142,7 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|
142
142
|
VALUE obj;
|
143
143
|
SSL_CTX* ctx;
|
144
144
|
SSL* ssl;
|
145
|
+
int min, ssl_options;
|
145
146
|
|
146
147
|
ms_conn* conn = engine_alloc(self, &obj);
|
147
148
|
|
@@ -161,7 +162,20 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|
161
162
|
ID sym_verify_mode = rb_intern("verify_mode");
|
162
163
|
VALUE verify_mode = rb_funcall(mini_ssl_ctx, sym_verify_mode, 0);
|
163
164
|
|
165
|
+
ID sym_ssl_cipher_filter = rb_intern("ssl_cipher_filter");
|
166
|
+
VALUE ssl_cipher_filter = rb_funcall(mini_ssl_ctx, sym_ssl_cipher_filter, 0);
|
167
|
+
|
168
|
+
ID sym_no_tlsv1 = rb_intern("no_tlsv1");
|
169
|
+
VALUE no_tlsv1 = rb_funcall(mini_ssl_ctx, sym_no_tlsv1, 0);
|
170
|
+
|
171
|
+
ID sym_no_tlsv1_1 = rb_intern("no_tlsv1_1");
|
172
|
+
VALUE no_tlsv1_1 = rb_funcall(mini_ssl_ctx, sym_no_tlsv1_1, 0);
|
173
|
+
|
174
|
+
#ifdef HAVE_TLS_SERVER_METHOD
|
175
|
+
ctx = SSL_CTX_new(TLS_server_method());
|
176
|
+
#else
|
164
177
|
ctx = SSL_CTX_new(SSLv23_server_method());
|
178
|
+
#endif
|
165
179
|
conn->ctx = ctx;
|
166
180
|
|
167
181
|
SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert));
|
@@ -172,20 +186,61 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|
172
186
|
SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
|
173
187
|
}
|
174
188
|
|
175
|
-
|
189
|
+
ssl_options = SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION;
|
190
|
+
|
191
|
+
#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
|
192
|
+
if (RTEST(no_tlsv1_1)) {
|
193
|
+
min = TLS1_2_VERSION;
|
194
|
+
}
|
195
|
+
else if (RTEST(no_tlsv1)) {
|
196
|
+
min = TLS1_1_VERSION;
|
197
|
+
}
|
198
|
+
else {
|
199
|
+
min = TLS1_VERSION;
|
200
|
+
}
|
201
|
+
|
202
|
+
SSL_CTX_set_min_proto_version(ctx, min);
|
203
|
+
|
204
|
+
SSL_CTX_set_options(ctx, ssl_options);
|
205
|
+
|
206
|
+
#else
|
207
|
+
/* As of 1.0.2f, SSL_OP_SINGLE_DH_USE key use is always on */
|
208
|
+
ssl_options |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE;
|
209
|
+
|
210
|
+
if (RTEST(no_tlsv1)) {
|
211
|
+
ssl_options |= SSL_OP_NO_TLSv1;
|
212
|
+
}
|
213
|
+
if(RTEST(no_tlsv1_1)) {
|
214
|
+
ssl_options |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
|
215
|
+
}
|
216
|
+
SSL_CTX_set_options(ctx, ssl_options);
|
217
|
+
#endif
|
218
|
+
|
176
219
|
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
177
220
|
|
178
|
-
|
221
|
+
if (!NIL_P(ssl_cipher_filter)) {
|
222
|
+
StringValue(ssl_cipher_filter);
|
223
|
+
SSL_CTX_set_cipher_list(ctx, RSTRING_PTR(ssl_cipher_filter));
|
224
|
+
}
|
225
|
+
else {
|
226
|
+
SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
|
227
|
+
}
|
179
228
|
|
180
229
|
DH *dh = get_dh1024();
|
181
230
|
SSL_CTX_set_tmp_dh(ctx, dh);
|
182
231
|
|
183
|
-
#
|
184
|
-
|
232
|
+
#if OPENSSL_VERSION_NUMBER < 0x10002000L
|
233
|
+
// Remove this case if OpenSSL 1.0.1 (now EOL) support is no
|
234
|
+
// longer needed.
|
235
|
+
EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
185
236
|
if (ecdh) {
|
186
237
|
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
|
187
238
|
EC_KEY_free(ecdh);
|
188
239
|
}
|
240
|
+
#elif OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
|
241
|
+
// Prior to OpenSSL 1.1.0, servers must manually enable server-side ECDH
|
242
|
+
// negotiation.
|
243
|
+
SSL_CTX_set_ecdh_auto(ctx, 1);
|
189
244
|
#endif
|
190
245
|
|
191
246
|
ssl = SSL_new(ctx);
|
@@ -207,8 +262,11 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|
207
262
|
VALUE engine_init_client(VALUE klass) {
|
208
263
|
VALUE obj;
|
209
264
|
ms_conn* conn = engine_alloc(klass, &obj);
|
210
|
-
|
265
|
+
#ifdef HAVE_DTLS_METHOD
|
266
|
+
conn->ctx = SSL_CTX_new(DTLS_method());
|
267
|
+
#else
|
211
268
|
conn->ctx = SSL_CTX_new(DTLSv1_method());
|
269
|
+
#endif
|
212
270
|
conn->ssl = SSL_new(conn->ctx);
|
213
271
|
SSL_set_app_data(conn->ssl, NULL);
|
214
272
|
SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
|
@@ -244,7 +302,7 @@ void raise_error(SSL* ssl, int result) {
|
|
244
302
|
const char* err_str;
|
245
303
|
int err = errno;
|
246
304
|
int ssl_err = SSL_get_error(ssl, result);
|
247
|
-
int verify_err = SSL_get_verify_result(ssl);
|
305
|
+
int verify_err = (int) SSL_get_verify_result(ssl);
|
248
306
|
|
249
307
|
if(SSL_ERROR_SYSCALL == ssl_err) {
|
250
308
|
snprintf(msg, sizeof(msg), "System error: %s - %d", strerror(err), err);
|
@@ -257,7 +315,7 @@ void raise_error(SSL* ssl, int result) {
|
|
257
315
|
err_str, verify_err);
|
258
316
|
|
259
317
|
} else {
|
260
|
-
err = ERR_get_error();
|
318
|
+
err = (int) ERR_get_error();
|
261
319
|
ERR_error_string_n(err, buf, sizeof(buf));
|
262
320
|
snprintf(msg, sizeof(msg), "OpenSSL error: %s - %d", buf, err);
|
263
321
|
|
@@ -411,6 +469,11 @@ VALUE noop(VALUE self) {
|
|
411
469
|
void Init_mini_ssl(VALUE puma) {
|
412
470
|
VALUE mod, eng;
|
413
471
|
|
472
|
+
/* Fake operation for documentation (RDoc, YARD) */
|
473
|
+
#if 0 == 1
|
474
|
+
puma = rb_define_module("Puma");
|
475
|
+
#endif
|
476
|
+
|
414
477
|
SSL_library_init();
|
415
478
|
OpenSSL_add_ssl_algorithms();
|
416
479
|
SSL_load_error_strings();
|
@@ -419,6 +482,39 @@ void Init_mini_ssl(VALUE puma) {
|
|
419
482
|
mod = rb_define_module_under(puma, "MiniSSL");
|
420
483
|
eng = rb_define_class_under(mod, "Engine", rb_cObject);
|
421
484
|
|
485
|
+
// OpenSSL Build / Runtime/Load versions
|
486
|
+
|
487
|
+
/* Version of OpenSSL that Puma was compiled with */
|
488
|
+
rb_define_const(mod, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT));
|
489
|
+
|
490
|
+
#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000
|
491
|
+
/* Version of OpenSSL that Puma loaded with */
|
492
|
+
rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION)));
|
493
|
+
#else
|
494
|
+
rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION)));
|
495
|
+
#endif
|
496
|
+
|
497
|
+
#if defined(OPENSSL_NO_SSL3) || defined(OPENSSL_NO_SSL3_METHOD)
|
498
|
+
/* True if SSL3 is not available */
|
499
|
+
rb_define_const(mod, "OPENSSL_NO_SSL3", Qtrue);
|
500
|
+
#else
|
501
|
+
rb_define_const(mod, "OPENSSL_NO_SSL3", Qfalse);
|
502
|
+
#endif
|
503
|
+
|
504
|
+
#if defined(OPENSSL_NO_TLS1) || defined(OPENSSL_NO_TLS1_METHOD)
|
505
|
+
/* True if TLS1 is not available */
|
506
|
+
rb_define_const(mod, "OPENSSL_NO_TLS1", Qtrue);
|
507
|
+
#else
|
508
|
+
rb_define_const(mod, "OPENSSL_NO_TLS1", Qfalse);
|
509
|
+
#endif
|
510
|
+
|
511
|
+
#if defined(OPENSSL_NO_TLS1_1) || defined(OPENSSL_NO_TLS1_1_METHOD)
|
512
|
+
/* True if TLS1_1 is not available */
|
513
|
+
rb_define_const(mod, "OPENSSL_NO_TLS1_1", Qtrue);
|
514
|
+
#else
|
515
|
+
rb_define_const(mod, "OPENSSL_NO_TLS1_1", Qfalse);
|
516
|
+
#endif
|
517
|
+
|
422
518
|
rb_define_singleton_method(mod, "check", noop, 0);
|
423
519
|
|
424
520
|
eError = rb_define_class_under(mod, "SSLError", rb_eStandardError);
|
@@ -447,7 +543,7 @@ VALUE raise_error(VALUE self) {
|
|
447
543
|
}
|
448
544
|
|
449
545
|
void Init_mini_ssl(VALUE puma) {
|
450
|
-
VALUE mod
|
546
|
+
VALUE mod;
|
451
547
|
|
452
548
|
mod = rb_define_module_under(puma, "MiniSSL");
|
453
549
|
rb_define_class_under(mod, "SSLError", rb_eStandardError);
|
@@ -11,7 +11,6 @@ import org.jruby.RubyString;
|
|
11
11
|
import org.jruby.anno.JRubyMethod;
|
12
12
|
|
13
13
|
import org.jruby.runtime.ObjectAllocator;
|
14
|
-
import org.jruby.runtime.ThreadContext;
|
15
14
|
import org.jruby.runtime.builtin.IRubyObject;
|
16
15
|
|
17
16
|
import org.jruby.exceptions.RaiseException;
|
@@ -20,6 +19,7 @@ import org.jruby.util.ByteList;
|
|
20
19
|
|
21
20
|
/**
|
22
21
|
* @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
|
22
|
+
* @author <a href="mailto:headius@headius.com">Charles Oliver Nutter</a>
|
23
23
|
*/
|
24
24
|
public class Http11 extends RubyObject {
|
25
25
|
public final static int MAX_FIELD_NAME_LENGTH = 256;
|
@@ -37,6 +37,16 @@ public class Http11 extends RubyObject {
|
|
37
37
|
public final static int MAX_HEADER_LENGTH = 1024 * (80 + 32);
|
38
38
|
public final static String MAX_HEADER_LENGTH_ERR = "HTTP element HEADER is longer than the 114688 allowed length.";
|
39
39
|
|
40
|
+
public static final ByteList CONTENT_TYPE_BYTELIST = new ByteList(ByteList.plain("CONTENT_TYPE"));
|
41
|
+
public static final ByteList CONTENT_LENGTH_BYTELIST = new ByteList(ByteList.plain("CONTENT_LENGTH"));
|
42
|
+
public static final ByteList HTTP_PREFIX_BYTELIST = new ByteList(ByteList.plain("HTTP_"));
|
43
|
+
public static final ByteList COMMA_SPACE_BYTELIST = new ByteList(ByteList.plain(", "));
|
44
|
+
public static final ByteList REQUEST_METHOD_BYTELIST = new ByteList(ByteList.plain("REQUEST_METHOD"));
|
45
|
+
public static final ByteList REQUEST_URI_BYTELIST = new ByteList(ByteList.plain("REQUEST_URI"));
|
46
|
+
public static final ByteList FRAGMENT_BYTELIST = new ByteList(ByteList.plain("FRAGMENT"));
|
47
|
+
public static final ByteList REQUEST_PATH_BYTELIST = new ByteList(ByteList.plain("REQUEST_PATH"));
|
48
|
+
public static final ByteList QUERY_STRING_BYTELIST = new ByteList(ByteList.plain("QUERY_STRING"));
|
49
|
+
public static final ByteList HTTP_VERSION_BYTELIST = new ByteList(ByteList.plain("HTTP_VERSION"));
|
40
50
|
|
41
51
|
private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
42
52
|
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
@@ -53,127 +63,104 @@ public class Http11 extends RubyObject {
|
|
53
63
|
}
|
54
64
|
|
55
65
|
private Ruby runtime;
|
56
|
-
private RubyClass eHttpParserError;
|
57
66
|
private Http11Parser hp;
|
58
67
|
private RubyString body;
|
59
68
|
|
60
69
|
public Http11(Ruby runtime, RubyClass clazz) {
|
61
70
|
super(runtime,clazz);
|
62
71
|
this.runtime = runtime;
|
63
|
-
this.eHttpParserError = (RubyClass)runtime.getModule("Puma").getConstant("HttpParserError");
|
64
72
|
this.hp = new Http11Parser();
|
65
|
-
this.hp.parser.http_field = http_field;
|
66
|
-
this.hp.parser.request_method = request_method;
|
67
|
-
this.hp.parser.request_uri = request_uri;
|
68
|
-
this.hp.parser.fragment = fragment;
|
69
|
-
this.hp.parser.request_path = request_path;
|
70
|
-
this.hp.parser.query_string = query_string;
|
71
|
-
this.hp.parser.http_version = http_version;
|
72
|
-
this.hp.parser.header_done = header_done;
|
73
73
|
this.hp.parser.init();
|
74
74
|
}
|
75
75
|
|
76
|
-
public void validateMaxLength(int len, int max, String msg) {
|
76
|
+
public static void validateMaxLength(Ruby runtime, int len, int max, String msg) {
|
77
77
|
if(len>max) {
|
78
|
-
throw
|
78
|
+
throw newHTTPParserError(runtime, msg);
|
79
79
|
}
|
80
80
|
}
|
81
81
|
|
82
|
-
private
|
83
|
-
|
84
|
-
|
85
|
-
RubyString f;
|
86
|
-
IRubyObject v;
|
87
|
-
validateMaxLength(flen, MAX_FIELD_NAME_LENGTH, MAX_FIELD_NAME_LENGTH_ERR);
|
88
|
-
validateMaxLength(vlen, MAX_FIELD_VALUE_LENGTH, MAX_FIELD_VALUE_LENGTH_ERR);
|
89
|
-
|
90
|
-
ByteList b = new ByteList(Http11.this.hp.parser.buffer,field,flen);
|
91
|
-
for(int i = 0,j = b.length();i<j;i++) {
|
92
|
-
if((b.get(i) & 0xFF) == '-') {
|
93
|
-
b.set(i, (byte)'_');
|
94
|
-
} else {
|
95
|
-
b.set(i, (byte)Character.toUpperCase((char)b.get(i)));
|
96
|
-
}
|
97
|
-
}
|
98
|
-
|
99
|
-
String as = b.toString();
|
100
|
-
|
101
|
-
if(as.equals("CONTENT_LENGTH") || as.equals("CONTENT_TYPE")) {
|
102
|
-
f = RubyString.newString(runtime, b);
|
103
|
-
} else {
|
104
|
-
f = RubyString.newString(runtime, "HTTP_");
|
105
|
-
f.cat(b);
|
106
|
-
}
|
107
|
-
|
108
|
-
b = new ByteList(Http11.this.hp.parser.buffer, value, vlen);
|
109
|
-
v = req.op_aref(req.getRuntime().getCurrentContext(), f);
|
110
|
-
if (v.isNil()) {
|
111
|
-
req.op_aset(req.getRuntime().getCurrentContext(), f, RubyString.newString(runtime, b));
|
112
|
-
} else {
|
113
|
-
RubyString vs = v.convertToString();
|
114
|
-
vs.cat(RubyString.newString(runtime, ", "));
|
115
|
-
vs.cat(b);
|
116
|
-
}
|
117
|
-
}
|
118
|
-
};
|
82
|
+
private static RaiseException newHTTPParserError(Ruby runtime, String msg) {
|
83
|
+
return runtime.newRaiseException(getHTTPParserError(runtime), msg);
|
84
|
+
}
|
119
85
|
|
120
|
-
private
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
req.op_aset(req.getRuntime().getCurrentContext(), runtime.newString("REQUEST_METHOD"),val);
|
125
|
-
}
|
126
|
-
};
|
127
|
-
|
128
|
-
private Http11Parser.ElementCB request_uri = new Http11Parser.ElementCB() {
|
129
|
-
public void call(Object data, int at, int length) {
|
130
|
-
RubyHash req = (RubyHash)data;
|
131
|
-
validateMaxLength(length, MAX_REQUEST_URI_LENGTH, MAX_REQUEST_URI_LENGTH_ERR);
|
132
|
-
RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
|
133
|
-
req.op_aset(req.getRuntime().getCurrentContext(), runtime.newString("REQUEST_URI"),val);
|
134
|
-
}
|
135
|
-
};
|
136
|
-
|
137
|
-
private Http11Parser.ElementCB fragment = new Http11Parser.ElementCB() {
|
138
|
-
public void call(Object data, int at, int length) {
|
139
|
-
RubyHash req = (RubyHash)data;
|
140
|
-
validateMaxLength(length, MAX_FRAGMENT_LENGTH, MAX_FRAGMENT_LENGTH_ERR);
|
141
|
-
RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
|
142
|
-
req.op_aset(req.getRuntime().getCurrentContext(), runtime.newString("FRAGMENT"),val);
|
143
|
-
}
|
144
|
-
};
|
145
|
-
|
146
|
-
private Http11Parser.ElementCB request_path = new Http11Parser.ElementCB() {
|
147
|
-
public void call(Object data, int at, int length) {
|
148
|
-
RubyHash req = (RubyHash)data;
|
149
|
-
validateMaxLength(length, MAX_REQUEST_PATH_LENGTH, MAX_REQUEST_PATH_LENGTH_ERR);
|
150
|
-
RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
|
151
|
-
req.op_aset(req.getRuntime().getCurrentContext(), runtime.newString("REQUEST_PATH"),val);
|
152
|
-
}
|
153
|
-
};
|
154
|
-
|
155
|
-
private Http11Parser.ElementCB query_string = new Http11Parser.ElementCB() {
|
156
|
-
public void call(Object data, int at, int length) {
|
157
|
-
RubyHash req = (RubyHash)data;
|
158
|
-
validateMaxLength(length, MAX_QUERY_STRING_LENGTH, MAX_QUERY_STRING_LENGTH_ERR);
|
159
|
-
RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
|
160
|
-
req.op_aset(req.getRuntime().getCurrentContext(), runtime.newString("QUERY_STRING"),val);
|
161
|
-
}
|
162
|
-
};
|
86
|
+
private static RubyClass getHTTPParserError(Ruby runtime) {
|
87
|
+
// Cheaper to look this up lazily than cache eagerly and consume a field, since it's rarely encountered
|
88
|
+
return (RubyClass)runtime.getModule("Puma").getConstant("HttpParserError");
|
89
|
+
}
|
163
90
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
91
|
+
public static void http_field(Ruby runtime, RubyHash req, ByteList buffer, int field, int flen, int value, int vlen) {
|
92
|
+
RubyString f;
|
93
|
+
IRubyObject v;
|
94
|
+
validateMaxLength(runtime, flen, MAX_FIELD_NAME_LENGTH, MAX_FIELD_NAME_LENGTH_ERR);
|
95
|
+
validateMaxLength(runtime, vlen, MAX_FIELD_VALUE_LENGTH, MAX_FIELD_VALUE_LENGTH_ERR);
|
96
|
+
|
97
|
+
ByteList b = new ByteList(buffer,field,flen);
|
98
|
+
for(int i = 0,j = b.length();i<j;i++) {
|
99
|
+
int bite = b.get(i) & 0xFF;
|
100
|
+
if(bite == '-') {
|
101
|
+
b.set(i, (byte)'_');
|
102
|
+
} else {
|
103
|
+
b.set(i, (byte)Character.toUpperCase(bite));
|
169
104
|
}
|
170
|
-
}
|
105
|
+
}
|
171
106
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
}
|
107
|
+
while (vlen > 0 && Character.isWhitespace(buffer.get(value + vlen - 1))) vlen--;
|
108
|
+
|
109
|
+
if (b.equals(CONTENT_LENGTH_BYTELIST) || b.equals(CONTENT_TYPE_BYTELIST)) {
|
110
|
+
f = RubyString.newString(runtime, b);
|
111
|
+
} else {
|
112
|
+
f = RubyString.newStringShared(runtime, HTTP_PREFIX_BYTELIST);
|
113
|
+
f.cat(b);
|
114
|
+
}
|
115
|
+
|
116
|
+
b = new ByteList(buffer, value, vlen);
|
117
|
+
v = req.fastARef(f);
|
118
|
+
if (v == null || v.isNil()) {
|
119
|
+
req.fastASet(f, RubyString.newString(runtime, b));
|
120
|
+
} else {
|
121
|
+
RubyString vs = v.convertToString();
|
122
|
+
vs.cat(COMMA_SPACE_BYTELIST);
|
123
|
+
vs.cat(b);
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
public static void request_method(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) {
|
128
|
+
RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length));
|
129
|
+
req.fastASet(RubyString.newStringShared(runtime, REQUEST_METHOD_BYTELIST),val);
|
130
|
+
}
|
131
|
+
|
132
|
+
public static void request_uri(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) {
|
133
|
+
validateMaxLength(runtime, length, MAX_REQUEST_URI_LENGTH, MAX_REQUEST_URI_LENGTH_ERR);
|
134
|
+
RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length));
|
135
|
+
req.fastASet(RubyString.newStringShared(runtime, REQUEST_URI_BYTELIST),val);
|
136
|
+
}
|
137
|
+
|
138
|
+
public static void fragment(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) {
|
139
|
+
validateMaxLength(runtime, length, MAX_FRAGMENT_LENGTH, MAX_FRAGMENT_LENGTH_ERR);
|
140
|
+
RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length));
|
141
|
+
req.fastASet(RubyString.newStringShared(runtime, FRAGMENT_BYTELIST),val);
|
142
|
+
}
|
143
|
+
|
144
|
+
public static void request_path(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) {
|
145
|
+
validateMaxLength(runtime, length, MAX_REQUEST_PATH_LENGTH, MAX_REQUEST_PATH_LENGTH_ERR);
|
146
|
+
RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length));
|
147
|
+
req.fastASet(RubyString.newStringShared(runtime, REQUEST_PATH_BYTELIST),val);
|
148
|
+
}
|
149
|
+
|
150
|
+
public static void query_string(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) {
|
151
|
+
validateMaxLength(runtime, length, MAX_QUERY_STRING_LENGTH, MAX_QUERY_STRING_LENGTH_ERR);
|
152
|
+
RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length));
|
153
|
+
req.fastASet(RubyString.newStringShared(runtime, QUERY_STRING_BYTELIST),val);
|
154
|
+
}
|
155
|
+
|
156
|
+
public static void http_version(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) {
|
157
|
+
RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length));
|
158
|
+
req.fastASet(RubyString.newStringShared(runtime, HTTP_VERSION_BYTELIST),val);
|
159
|
+
}
|
160
|
+
|
161
|
+
public void header_done(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) {
|
162
|
+
body = RubyString.newStringShared(runtime, new ByteList(buffer, at, length));
|
163
|
+
}
|
177
164
|
|
178
165
|
@JRubyMethod
|
179
166
|
public IRubyObject initialize() {
|
@@ -195,19 +182,24 @@ public class Http11 extends RubyObject {
|
|
195
182
|
|
196
183
|
@JRubyMethod
|
197
184
|
public IRubyObject execute(IRubyObject req_hash, IRubyObject data, IRubyObject start) {
|
198
|
-
int from =
|
199
|
-
from = RubyNumeric.fix2int(start);
|
185
|
+
int from = RubyNumeric.fix2int(start);
|
200
186
|
ByteList d = ((RubyString)data).getByteList();
|
201
187
|
if(from >= d.length()) {
|
202
|
-
throw
|
188
|
+
throw newHTTPParserError(runtime, "Requested start is after data buffer end.");
|
203
189
|
} else {
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
190
|
+
Http11Parser hp = this.hp;
|
191
|
+
Http11Parser.HttpParser parser = hp.parser;
|
192
|
+
|
193
|
+
parser.data = (RubyHash) req_hash;
|
194
|
+
|
195
|
+
hp.execute(runtime, this, d,from);
|
196
|
+
|
197
|
+
validateMaxLength(runtime, parser.nread,MAX_HEADER_LENGTH, MAX_HEADER_LENGTH_ERR);
|
198
|
+
|
199
|
+
if(hp.has_error()) {
|
200
|
+
throw newHTTPParserError(runtime, "Invalid HTTP format, parsing fails.");
|
209
201
|
} else {
|
210
|
-
return runtime.newFixnum(
|
202
|
+
return runtime.newFixnum(parser.nread);
|
211
203
|
}
|
212
204
|
}
|
213
205
|
}
|
@@ -226,7 +218,7 @@ public class Http11 extends RubyObject {
|
|
226
218
|
public IRubyObject nread() {
|
227
219
|
return runtime.newFixnum(this.hp.parser.nread);
|
228
220
|
}
|
229
|
-
|
221
|
+
|
230
222
|
@JRubyMethod
|
231
223
|
public IRubyObject body() {
|
232
224
|
return body;
|