puma 3.12.0 → 4.3.8

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.

Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +164 -0
  3. data/README.md +76 -48
  4. data/docs/architecture.md +1 -0
  5. data/docs/deployment.md +24 -4
  6. data/docs/plugins.md +20 -10
  7. data/docs/restart.md +4 -2
  8. data/docs/systemd.md +27 -9
  9. data/docs/tcp_mode.md +96 -0
  10. data/ext/puma_http11/PumaHttp11Service.java +2 -0
  11. data/ext/puma_http11/extconf.rb +13 -0
  12. data/ext/puma_http11/http11_parser.c +40 -63
  13. data/ext/puma_http11/http11_parser.java.rl +21 -37
  14. data/ext/puma_http11/http11_parser.rl +3 -1
  15. data/ext/puma_http11/http11_parser_common.rl +3 -3
  16. data/ext/puma_http11/mini_ssl.c +86 -4
  17. data/ext/puma_http11/org/jruby/puma/Http11.java +106 -114
  18. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +91 -106
  19. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
  20. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -4
  21. data/ext/puma_http11/puma_http11.c +3 -0
  22. data/lib/puma.rb +8 -0
  23. data/lib/puma/accept_nonblock.rb +7 -1
  24. data/lib/puma/app/status.rb +37 -29
  25. data/lib/puma/binder.rb +47 -68
  26. data/lib/puma/cli.rb +6 -0
  27. data/lib/puma/client.rb +244 -199
  28. data/lib/puma/cluster.rb +55 -30
  29. data/lib/puma/commonlogger.rb +2 -0
  30. data/lib/puma/configuration.rb +6 -3
  31. data/lib/puma/const.rb +32 -18
  32. data/lib/puma/control_cli.rb +41 -14
  33. data/lib/puma/detect.rb +2 -0
  34. data/lib/puma/dsl.rb +311 -77
  35. data/lib/puma/events.rb +6 -1
  36. data/lib/puma/io_buffer.rb +3 -6
  37. data/lib/puma/jruby_restart.rb +2 -0
  38. data/lib/puma/launcher.rb +99 -55
  39. data/lib/puma/minissl.rb +37 -17
  40. data/lib/puma/minissl/context_builder.rb +76 -0
  41. data/lib/puma/null_io.rb +2 -0
  42. data/lib/puma/plugin.rb +7 -2
  43. data/lib/puma/plugin/tmp_restart.rb +2 -0
  44. data/lib/puma/rack/builder.rb +4 -1
  45. data/lib/puma/rack/urlmap.rb +2 -0
  46. data/lib/puma/rack_default.rb +2 -0
  47. data/lib/puma/reactor.rb +112 -57
  48. data/lib/puma/runner.rb +13 -3
  49. data/lib/puma/server.rb +119 -48
  50. data/lib/puma/single.rb +5 -3
  51. data/lib/puma/state_file.rb +2 -0
  52. data/lib/puma/tcp_logger.rb +2 -0
  53. data/lib/puma/thread_pool.rb +17 -33
  54. data/lib/puma/util.rb +2 -6
  55. data/lib/rack/handler/puma.rb +6 -3
  56. data/tools/docker/Dockerfile +16 -0
  57. data/tools/jungle/init.d/puma +6 -6
  58. data/tools/trickletest.rb +0 -1
  59. metadata +26 -14
  60. data/lib/puma/compat.rb +0 -14
  61. data/lib/puma/convenient.rb +0 -23
  62. data/lib/puma/daemon_ext.rb +0 -31
  63. data/lib/puma/delegation.rb +0 -11
  64. data/lib/puma/java_io_buffer.rb +0 -45
  65. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
@@ -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
- if(parser.http_field != null) {
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
- if(parser.request_method != null)
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
- if(parser.request_uri != null)
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
- if(parser.fragment != null)
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
- if(parser.query_string != null)
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
- if(parser.http_version != null)
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
- if(parser.request_path != null)
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
- if(parser.header_done != null)
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(Object data, int at, int length);
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(Object data, int field, int flen, int value, int vlen);
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
- Object data;
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();
@@ -12,12 +12,14 @@
12
12
 
13
13
  /*
14
14
  * capitalizes all lower-case ASCII characters,
15
- * converts dashes to underscores.
15
+ * converts dashes to underscores, and underscores to commas.
16
16
  */
17
17
  static void snake_upcase_char(char *c)
18
18
  {
19
19
  if (*c >= 'a' && *c <= 'z')
20
20
  *c &= ~0x20;
21
+ else if (*c == '_')
22
+ *c = ',';
21
23
  else if (*c == '-')
22
24
  *c = '_';
23
25
  }
@@ -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 (";" params)? ) ("?" %start_query query)?;
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;
@@ -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
 
@@ -164,7 +165,17 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
164
165
  ID sym_ssl_cipher_filter = rb_intern("ssl_cipher_filter");
165
166
  VALUE ssl_cipher_filter = rb_funcall(mini_ssl_ctx, sym_ssl_cipher_filter, 0);
166
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
167
177
  ctx = SSL_CTX_new(SSLv23_server_method());
178
+ #endif
168
179
  conn->ctx = ctx;
169
180
 
170
181
  SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert));
@@ -175,7 +186,36 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
175
186
  SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
176
187
  }
177
188
 
178
- SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION);
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
+
179
219
  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
180
220
 
181
221
  if (!NIL_P(ssl_cipher_filter)) {
@@ -189,12 +229,18 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
189
229
  DH *dh = get_dh1024();
190
230
  SSL_CTX_set_tmp_dh(ctx, dh);
191
231
 
192
- #ifndef OPENSSL_NO_ECDH
193
- EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_secp521r1);
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);
194
236
  if (ecdh) {
195
237
  SSL_CTX_set_tmp_ecdh(ctx, ecdh);
196
238
  EC_KEY_free(ecdh);
197
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);
198
244
  #endif
199
245
 
200
246
  ssl = SSL_new(ctx);
@@ -216,8 +262,11 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
216
262
  VALUE engine_init_client(VALUE klass) {
217
263
  VALUE obj;
218
264
  ms_conn* conn = engine_alloc(klass, &obj);
219
-
265
+ #ifdef HAVE_DTLS_METHOD
266
+ conn->ctx = SSL_CTX_new(DTLS_method());
267
+ #else
220
268
  conn->ctx = SSL_CTX_new(DTLSv1_method());
269
+ #endif
221
270
  conn->ssl = SSL_new(conn->ctx);
222
271
  SSL_set_app_data(conn->ssl, NULL);
223
272
  SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
@@ -433,6 +482,39 @@ void Init_mini_ssl(VALUE puma) {
433
482
  mod = rb_define_module_under(puma, "MiniSSL");
434
483
  eng = rb_define_class_under(mod, "Engine", rb_cObject);
435
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
+
436
518
  rb_define_singleton_method(mod, "check", noop, 0);
437
519
 
438
520
  eError = 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 new RaiseException(runtime, eHttpParserError, msg, true);
78
+ throw newHTTPParserError(runtime, msg);
79
79
  }
80
80
  }
81
81
 
82
- private Http11Parser.FieldCB http_field = new Http11Parser.FieldCB() {
83
- public void call(Object data, int field, int flen, int value, int vlen) {
84
- RubyHash req = (RubyHash)data;
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 Http11Parser.ElementCB request_method = new Http11Parser.ElementCB() {
121
- public void call(Object data, int at, int length) {
122
- RubyHash req = (RubyHash)data;
123
- RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
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
- private Http11Parser.ElementCB http_version = new Http11Parser.ElementCB() {
165
- public void call(Object data, int at, int length) {
166
- RubyHash req = (RubyHash)data;
167
- RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
168
- req.op_aset(req.getRuntime().getCurrentContext(), runtime.newString("HTTP_VERSION"),val);
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
- private Http11Parser.ElementCB header_done = new Http11Parser.ElementCB() {
173
- public void call(Object data, int at, int length) {
174
- body = RubyString.newString(runtime, new ByteList(hp.parser.buffer, at, length));
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 = 0;
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 new RaiseException(runtime, eHttpParserError, "Requested start is after data buffer end.", true);
188
+ throw newHTTPParserError(runtime, "Requested start is after data buffer end.");
203
189
  } else {
204
- this.hp.parser.data = req_hash;
205
- this.hp.execute(d,from);
206
- validateMaxLength(this.hp.parser.nread,MAX_HEADER_LENGTH, MAX_HEADER_LENGTH_ERR);
207
- if(this.hp.has_error()) {
208
- throw new RaiseException(runtime, eHttpParserError, "Invalid HTTP format, parsing fails.", true);
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(this.hp.parser.nread);
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;