puma 6.4.3 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +448 -8
  3. data/README.md +110 -51
  4. data/docs/5.0-Upgrade.md +98 -0
  5. data/docs/6.0-Upgrade.md +56 -0
  6. data/docs/7.0-Upgrade.md +52 -0
  7. data/docs/8.0-Upgrade.md +100 -0
  8. data/docs/deployment.md +58 -23
  9. data/docs/fork_worker.md +11 -1
  10. data/docs/grpc.md +62 -0
  11. data/docs/images/favicon.svg +1 -0
  12. data/docs/images/running-puma.svg +1 -0
  13. data/docs/images/standard-logo.svg +1 -0
  14. data/docs/java_options.md +54 -0
  15. data/docs/jungle/README.md +1 -1
  16. data/docs/kubernetes.md +11 -16
  17. data/docs/plugins.md +6 -2
  18. data/docs/restart.md +2 -2
  19. data/docs/signals.md +21 -21
  20. data/docs/stats.md +11 -5
  21. data/docs/systemd.md +14 -5
  22. data/ext/puma_http11/extconf.rb +20 -32
  23. data/ext/puma_http11/http11_parser.java.rl +51 -65
  24. data/ext/puma_http11/mini_ssl.c +29 -9
  25. data/ext/puma_http11/org/jruby/puma/EnvKey.java +241 -0
  26. data/ext/puma_http11/org/jruby/puma/Http11.java +194 -101
  27. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +71 -85
  28. data/ext/puma_http11/puma_http11.c +125 -118
  29. data/lib/puma/app/status.rb +11 -3
  30. data/lib/puma/binder.rb +22 -12
  31. data/lib/puma/cli.rb +11 -9
  32. data/lib/puma/client.rb +233 -136
  33. data/lib/puma/client_env.rb +171 -0
  34. data/lib/puma/cluster/worker.rb +24 -21
  35. data/lib/puma/cluster/worker_handle.rb +38 -8
  36. data/lib/puma/cluster.rb +74 -48
  37. data/lib/puma/cluster_accept_loop_delay.rb +91 -0
  38. data/lib/puma/commonlogger.rb +3 -3
  39. data/lib/puma/configuration.rb +197 -64
  40. data/lib/puma/const.rb +23 -12
  41. data/lib/puma/control_cli.rb +11 -7
  42. data/lib/puma/detect.rb +13 -0
  43. data/lib/puma/dsl.rb +483 -127
  44. data/lib/puma/error_logger.rb +7 -5
  45. data/lib/puma/events.rb +25 -10
  46. data/lib/puma/io_buffer.rb +8 -4
  47. data/lib/puma/jruby_restart.rb +0 -16
  48. data/lib/puma/launcher/bundle_pruner.rb +3 -5
  49. data/lib/puma/launcher.rb +76 -59
  50. data/lib/puma/log_writer.rb +17 -11
  51. data/lib/puma/minissl/context_builder.rb +1 -0
  52. data/lib/puma/minissl.rb +1 -1
  53. data/lib/puma/null_io.rb +26 -0
  54. data/lib/puma/plugin/systemd.rb +3 -3
  55. data/lib/puma/rack/urlmap.rb +1 -1
  56. data/lib/puma/reactor.rb +19 -13
  57. data/lib/puma/{request.rb → response.rb} +57 -209
  58. data/lib/puma/runner.rb +15 -17
  59. data/lib/puma/sd_notify.rb +1 -4
  60. data/lib/puma/server.rb +200 -104
  61. data/lib/puma/server_plugin_control.rb +32 -0
  62. data/lib/puma/single.rb +7 -4
  63. data/lib/puma/state_file.rb +3 -2
  64. data/lib/puma/thread_pool.rb +179 -96
  65. data/lib/puma/util.rb +0 -7
  66. data/lib/puma.rb +10 -0
  67. data/lib/rack/handler/puma.rb +11 -8
  68. data/tools/Dockerfile +15 -5
  69. metadata +26 -16
  70. data/ext/puma_http11/ext_help.h +0 -15
@@ -10,15 +10,13 @@ end
10
10
 
11
11
  unless ENV["PUMA_DISABLE_SSL"]
12
12
  # don't use pkg_config('openssl') if '--with-openssl-dir' is used
13
- # also looks within the Ruby build for directory info
14
13
  has_openssl_dir = dir_config('openssl').any? ||
15
- RbConfig::CONFIG['configure_args']&.include?('openssl') ||
16
- Dir.exist?("#{RbConfig::TOPDIR}/src/main/c/openssl") # TruffleRuby
14
+ RbConfig::CONFIG['configure_args']&.include?('openssl')
17
15
 
18
16
  found_pkg_config = !has_openssl_dir && pkg_config('openssl')
19
17
 
20
18
  found_ssl = if !$mingw && found_pkg_config
21
- puts 'using OpenSSL pkgconfig (openssl.pc)'
19
+ puts '──── Using OpenSSL pkgconfig (openssl.pc) ────'
22
20
  true
23
21
  elsif have_library('libcrypto', 'BIO_read') && have_library('libssl', 'SSL_CTX_new')
24
22
  true
@@ -33,45 +31,35 @@ unless ENV["PUMA_DISABLE_SSL"]
33
31
  if found_ssl
34
32
  have_header "openssl/bio.h"
35
33
 
36
- # below is yes for 1.0.2 & later
37
- have_func "DTLS_method" , "openssl/ssl.h"
38
- have_func "SSL_CTX_set_session_cache_mode(NULL, 0)", "openssl/ssl.h"
34
+ ssl_h = "openssl/ssl.h".freeze
39
35
 
40
- # below are yes for 1.1.0 & later
41
- have_func "TLS_server_method" , "openssl/ssl.h"
42
- have_func "SSL_CTX_set_min_proto_version(NULL, 0)" , "openssl/ssl.h"
36
+ puts "\n──── Below are yes for 1.0.2 & later ────"
37
+ have_func "DTLS_method" , ssl_h
38
+ have_func "SSL_CTX_set_session_cache_mode(NULL, 0)", ssl_h
43
39
 
44
- have_func "X509_STORE_up_ref"
45
- have_func "SSL_CTX_set_ecdh_auto(NULL, 0)" , "openssl/ssl.h"
40
+ puts "\n──── Below are yes for 1.1.0 & later ────"
41
+ have_func "TLS_server_method" , ssl_h
42
+ have_func "SSL_CTX_set_min_proto_version(NULL, 0)" , ssl_h
46
43
 
47
- # below exists in 1.1.0 and later, but isn't documented until 3.0.0
48
- have_func "SSL_CTX_set_dh_auto(NULL, 0)" , "openssl/ssl.h"
44
+ puts "\n──── Below is yes for 1.1.0 and later, but isn't documented until 3.0.0 ────"
45
+ # https://github.com/openssl/openssl/blob/OpenSSL_1_1_0/include/openssl/ssl.h#L1159
46
+ have_func "SSL_CTX_set_dh_auto(NULL, 0)" , ssl_h
49
47
 
50
- # below is yes for 3.0.0 & later
51
- have_func "SSL_get1_peer_certificate" , "openssl/ssl.h"
48
+ puts "\n──── Below is yes for 1.1.1 & later ────"
49
+ have_func "SSL_CTX_set_ciphersuites(NULL, \"\")" , ssl_h
52
50
 
53
- # Random.bytes available in Ruby 2.5 and later, Random::DEFAULT deprecated in 3.0
54
- if Random.respond_to?(:bytes)
55
- $defs.push "-DHAVE_RANDOM_BYTES"
56
- puts "checking for Random.bytes... yes"
57
- else
58
- puts "checking for Random.bytes... no"
59
- end
51
+ puts "\n──── Below is yes for 3.0.0 & later ────"
52
+ have_func "SSL_get1_peer_certificate" , ssl_h
53
+
54
+ puts ''
60
55
  end
61
56
  end
62
57
 
63
58
  if ENV["PUMA_MAKE_WARNINGS_INTO_ERRORS"]
64
59
  # Make all warnings into errors
65
60
  # Except `implicit-fallthrough` since most failures comes from ragel state machine generated code
66
- if respond_to?(:append_cflags, true) # Ruby 2.5 and later
67
- append_cflags(config_string('WERRORFLAG') || '-Werror')
68
- append_cflags '-Wno-implicit-fallthrough'
69
- else
70
- # flag may not exist on some platforms, -Werror may not be defined on some platforms, but
71
- # works with all in current CI
72
- $CFLAGS << " #{config_string('WERRORFLAG') || '-Werror'}"
73
- $CFLAGS << ' -Wno-implicit-fallthrough'
74
- end
61
+ append_cflags(config_string('WERRORFLAG') || '-Werror')
62
+ append_cflags '-Wno-implicit-fallthrough'
75
63
  end
76
64
 
77
65
  create_makefile("puma/puma_http11")
@@ -2,6 +2,7 @@ package org.jruby.puma;
2
2
 
3
3
  import org.jruby.Ruby;
4
4
  import org.jruby.RubyHash;
5
+ import org.jruby.RubyString;
5
6
  import org.jruby.util.ByteList;
6
7
 
7
8
  public class Http11Parser {
@@ -12,44 +13,44 @@ public class Http11Parser {
12
13
 
13
14
  machine puma_parser;
14
15
 
15
- action mark {parser.mark = fpc; }
16
+ action mark {this.mark = fpc; }
16
17
 
17
- action start_field { parser.field_start = fpc; }
18
- action snake_upcase_field { /* FIXME stub */ }
18
+ action start_field { this.field_start = fpc; }
19
+ action snake_upcase_field { /* done lazily as needed */ }
19
20
  action write_field {
20
- parser.field_len = fpc-parser.field_start;
21
+ this.field_len = fpc-this.field_start;
21
22
  }
22
23
 
23
- action start_value { parser.mark = fpc; }
24
+ action start_value { this.mark = fpc; }
24
25
  action write_value {
25
- Http11.http_field(runtime, parser.data, parser.buffer, parser.field_start, parser.field_len, parser.mark, fpc-parser.mark);
26
+ Http11.http_field(runtime, envStrings, this, fpc-this.mark);
26
27
  }
27
28
  action request_method {
28
- Http11.request_method(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
29
+ Http11.request_method(runtime, envStrings, this, fpc-this.mark);
29
30
  }
30
31
  action request_uri {
31
- Http11.request_uri(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
32
+ Http11.request_uri(runtime, envStrings, this, fpc-this.mark);
32
33
  }
33
34
  action fragment {
34
- Http11.fragment(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
35
+ Http11.fragment(runtime, envStrings, this, fpc-this.mark);
35
36
  }
36
37
 
37
- action start_query {parser.query_start = fpc; }
38
+ action start_query {this.query_start = fpc; }
38
39
  action query_string {
39
- Http11.query_string(runtime, parser.data, parser.buffer, parser.query_start, fpc-parser.query_start);
40
+ Http11.query_string(runtime, envStrings, this, fpc-this.query_start);
40
41
  }
41
42
 
42
43
  action server_protocol {
43
- Http11.server_protocol(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
44
+ Http11.server_protocol(runtime, envStrings, this, fpc-this.mark);
44
45
  }
45
46
 
46
47
  action request_path {
47
- Http11.request_path(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
48
+ Http11.request_path(runtime, envStrings, this, fpc-this.mark);
48
49
  }
49
50
 
50
51
  action done {
51
- parser.body_start = fpc + 1;
52
- http.header_done(runtime, parser.data, parser.buffer, fpc + 1, pe - fpc - 1);
52
+ this.body_start = fpc + 1;
53
+ http.header_done(runtime, this, fpc + 1, pe - fpc - 1);
53
54
  fbreak;
54
55
  }
55
56
 
@@ -60,69 +61,54 @@ public class Http11Parser {
60
61
  /** Data **/
61
62
  %% write data noentry;
62
63
 
63
- public static interface ElementCB {
64
- public void call(Ruby runtime, RubyHash data, ByteList buffer, int at, int length);
65
- }
64
+ int cs;
65
+ int body_start;
66
+ int nread;
67
+ int mark;
68
+ int field_start;
69
+ int field_len;
70
+ int query_start;
66
71
 
67
- public static interface FieldCB {
68
- public void call(Ruby runtime, RubyHash data, ByteList buffer, int field, int flen, int value, int vlen);
69
- }
72
+ RubyHash data;
73
+ byte[] buffer;
70
74
 
71
- public static class HttpParser {
72
- int cs;
73
- int body_start;
74
- int content_len;
75
- int nread;
76
- int mark;
77
- int field_start;
78
- int field_len;
79
- int query_start;
80
-
81
- RubyHash data;
82
- ByteList buffer;
83
-
84
- public void init() {
85
- cs = 0;
86
-
87
- %% write init;
88
-
89
- body_start = 0;
90
- content_len = 0;
91
- mark = 0;
92
- nread = 0;
93
- field_len = 0;
94
- field_start = 0;
95
- }
96
- }
75
+ public void init() {
97
76
 
98
- public final HttpParser parser = new HttpParser();
77
+ %% write init;
78
+
79
+ body_start = 0;
80
+ mark = 0;
81
+ nread = 0;
82
+ field_len = 0;
83
+ field_start = 0;
84
+ }
99
85
 
100
86
  public int execute(Ruby runtime, Http11 http, ByteList buffer, int off) {
101
87
  int p, pe;
102
- int cs = parser.cs;
88
+ int cs = this.cs;
103
89
  int len = buffer.length();
90
+ int beg = buffer.begin();
91
+ RubyString[] envStrings = http.envStrings;
104
92
  assert off<=len : "offset past end of buffer";
105
93
 
106
- p = off;
107
- pe = len;
108
- // get a copy of the bytes, since it may not start at 0
109
- // FIXME: figure out how to just use the bytes in-place
110
- byte[] data = buffer.bytes();
111
- parser.buffer = buffer;
94
+ p = beg + off;
95
+ pe = beg + len;
96
+ byte[] data = buffer.unsafeBytes();
97
+ this.buffer = data;
112
98
 
113
99
  %% write exec;
114
100
 
115
- parser.cs = cs;
116
- parser.nread += (p - off);
101
+ this.cs = cs;
102
+ this.nread += (p - off);
117
103
 
118
104
  assert p <= pe : "buffer overflow after parsing execute";
119
- assert parser.nread <= len : "nread longer than length";
120
- assert parser.body_start <= len : "body starts after buffer end";
121
- assert parser.mark < len : "mark is after buffer end";
122
- assert parser.field_len <= len : "field has length longer than whole buffer";
123
- assert parser.field_start < len : "field starts after buffer end";
105
+ assert this.nread <= len : "nread longer than length";
106
+ assert this.body_start <= len : "body starts after buffer end";
107
+ assert this.mark < len : "mark is after buffer end";
108
+ assert this.field_len <= len : "field has length longer than whole buffer";
109
+ assert this.field_start < len : "field starts after buffer end";
124
110
 
125
- return parser.nread;
111
+ return this.nread;
126
112
  }
127
113
 
128
114
  public int finish() {
@@ -136,10 +122,10 @@ public class Http11Parser {
136
122
  }
137
123
 
138
124
  public boolean has_error() {
139
- return parser.cs == puma_parser_error;
125
+ return this.cs == puma_parser_error;
140
126
  }
141
127
 
142
128
  public boolean is_finished() {
143
- return parser.cs == puma_parser_first_final;
129
+ return this.cs == puma_parser_first_final;
144
130
  }
145
131
  }
@@ -229,7 +229,7 @@ VALUE
229
229
  sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
230
230
  SSL_CTX* ctx;
231
231
  int ssl_options;
232
- VALUE key, cert, ca, verify_mode, ssl_cipher_filter, no_tlsv1, no_tlsv1_1,
232
+ VALUE key, cert, ca, verify_mode, ssl_cipher_filter, ssl_ciphersuites, no_tlsv1, no_tlsv1_1,
233
233
  verification_flags, session_id_bytes, cert_pem, key_pem, key_password_command, key_password;
234
234
  BIO *bio;
235
235
  X509 *x509 = NULL;
@@ -269,6 +269,8 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
269
269
 
270
270
  ssl_cipher_filter = rb_funcall(mini_ssl_ctx, rb_intern_const("ssl_cipher_filter"), 0);
271
271
 
272
+ ssl_ciphersuites = rb_funcall(mini_ssl_ctx, rb_intern_const("ssl_ciphersuites"), 0);
273
+
272
274
  no_tlsv1 = rb_funcall(mini_ssl_ctx, rb_intern_const("no_tlsv1"), 0);
273
275
 
274
276
  no_tlsv1_1 = rb_funcall(mini_ssl_ctx, rb_intern_const("no_tlsv1_1"), 0);
@@ -444,6 +446,14 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
444
446
  SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
445
447
  }
446
448
 
449
+ #if HAVE_SSL_CTX_SET_CIPHERSUITES
450
+ // Only override OpenSSL default ciphersuites if config option is supplied.
451
+ if (!NIL_P(ssl_ciphersuites)) {
452
+ StringValue(ssl_ciphersuites);
453
+ SSL_CTX_set_ciphersuites(ctx, RSTRING_PTR(ssl_ciphersuites));
454
+ }
455
+ #endif
456
+
447
457
  #if OPENSSL_VERSION_NUMBER < 0x10002000L
448
458
  // Remove this case if OpenSSL 1.0.1 (now EOL) support is no longer needed.
449
459
  ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
@@ -461,13 +471,8 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
461
471
  SSL_CTX_set_verify(ctx, NUM2INT(verify_mode), engine_verify_callback);
462
472
  }
463
473
 
464
- // Random.bytes available in Ruby 2.5 and later, Random::DEFAULT deprecated in 3.0
465
474
  session_id_bytes = rb_funcall(
466
- #ifdef HAVE_RANDOM_BYTES
467
475
  rb_cRandom,
468
- #else
469
- rb_const_get(rb_cRandom, rb_intern_const("DEFAULT")),
470
- #endif
471
476
  rb_intern_const("bytes"),
472
477
  1, ULL2NUM(SSL_MAX_SSL_SESSION_ID_LENGTH));
473
478
 
@@ -655,14 +660,29 @@ VALUE engine_shutdown(VALUE self) {
655
660
 
656
661
  TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
657
662
 
663
+ if (SSL_in_init(conn->ssl)) {
664
+ // Avoid "shutdown while in init" error
665
+ // See https://github.com/openssl/openssl/blob/openssl-3.5.2/ssl/ssl_lib.c#L2827-L2828
666
+ return Qtrue;
667
+ }
668
+
658
669
  ERR_clear_error();
659
670
 
660
671
  ok = SSL_shutdown(conn->ssl);
661
- if (ok == 0) {
662
- return Qfalse;
672
+ // See https://github.com/openssl/openssl/blob/openssl-3.5.2/ssl/ssl_lib.c#L2792-L2797
673
+ // for description of SSL_shutdown return values.
674
+ switch (ok) {
675
+ case 0:
676
+ // "close notify" alert is sent by us.
677
+ return Qfalse;
678
+ case 1:
679
+ // "close notify" alert was received from peer.
680
+ return Qtrue;
681
+ default:
682
+ raise_error(conn->ssl, ok);
663
683
  }
664
684
 
665
- return Qtrue;
685
+ return Qnil;
666
686
  }
667
687
 
668
688
  VALUE engine_init(VALUE self) {
@@ -0,0 +1,241 @@
1
+ package org.jruby.puma;
2
+
3
+ import org.jruby.util.ByteList;
4
+
5
+ import java.nio.charset.StandardCharsets;
6
+
7
+ /**
8
+ * A set of keys representing HTTP/1.1 header fields and CGI env keys.
9
+ *
10
+ * The set of header fields is derived from RFC 2616 with some additions
11
+ * common to mainstream browsers.
12
+ *
13
+ * This enum is used by {@link Http11 }to access pre-allocated strings for
14
+ * Puma's version of these keys and includes a perfect hash function to quickly
15
+ * find the key for a given range of bytes.
16
+ */
17
+ public enum EnvKey {
18
+ ACCEPT,
19
+ ACCEPT_CHARSET,
20
+ ACCEPT_ENCODING,
21
+ ACCEPT_LANGUAGE,
22
+ ACCEPT_RANGES,
23
+ AGE,
24
+ ALLOW,
25
+ AUTHORIZATION,
26
+ CACHE_CONTROL,
27
+ CONNECTION,
28
+ CONTENT_ENCODING,
29
+ CONTENT_LANGUAGE,
30
+ CONTENT_LENGTH(true),
31
+ CONTENT_LOCATION,
32
+ CONTENT_MD5,
33
+ CONTENT_RANGE,
34
+ CONTENT_TYPE(true),
35
+ DATE,
36
+ ETAG,
37
+ EXPECT,
38
+ EXPIRES,
39
+ FRAGMENT(true),
40
+ FROM,
41
+ HOST,
42
+ IF_MATCH,
43
+ IF_MODIFIED_SINCE,
44
+ IF_NONE_MATCH,
45
+ IF_RANGE,
46
+ IF_UNMODIFIED_SINCE,
47
+ KEEP_ALIVE,
48
+ LAST_MODIFIED,
49
+ LOCATION,
50
+ MAX_FORWARDS,
51
+ PRAGMA,
52
+ PROXY_AUTHENTICATE,
53
+ PROXY_AUTHORIZATION,
54
+ QUERY_STRING(true),
55
+ RANGE,
56
+ REFERER,
57
+ REQUEST_METHOD(true),
58
+ REQUEST_PATH(true),
59
+ REQUEST_URI(true),
60
+ RETRY_AFTER,
61
+ SERVER,
62
+ SERVER_PROTOCOL(true),
63
+ TE,
64
+ TRAILER,
65
+ TRANSFER_ENCODING,
66
+ UPGRADE,
67
+ USER_AGENT,
68
+ VARY,
69
+ VIA,
70
+ X_FORWARDED_FOR,
71
+ X_REAL_IP,
72
+ WARNING,
73
+ WWW_AUTHENTICATE;
74
+
75
+ final ByteList httpName;
76
+
77
+ EnvKey() {
78
+ this(false);
79
+ }
80
+
81
+ EnvKey(boolean raw) {
82
+ String httpName = raw ? name() : "HTTP_" + name();
83
+ this.httpName = new ByteList(ByteList.plain(httpName), false);
84
+ }
85
+
86
+ /**
87
+ * Using a perfect hash, find the EnvKey for a given field name, or null if none matches.
88
+ *
89
+ * Generated by the gperf tool as C and adapted to Java. Must be regenerated if the set of keys changes.
90
+ *
91
+ * @param buffer the buffer containing the field name
92
+ * @param start the starting offset of the field name
93
+ * @param len the length of the field name
94
+ * @return the matching EnvKey, or else null
95
+ */
96
+ public static EnvKey keyForField(byte[] buffer, int start, int len) {
97
+ int key = hash(buffer, start, len);
98
+
99
+ if (key <= MAX_HASH_VALUE) {
100
+ EnvKey match = keyList[key];
101
+
102
+ if (match != null && equalsIgnoreCase(buffer, start, len, match.name())) {
103
+ return match;
104
+ }
105
+ }
106
+ return null;
107
+ }
108
+
109
+ private static boolean equalsIgnoreCase(byte[] buf, int start, int len, String key) {
110
+ int slen = key.length();
111
+ if (len != slen) return false;
112
+ for (int i = 0; i < slen; i++) {
113
+ byte b1 = buf[start + i];
114
+ byte b2 = (byte) key.charAt(i);
115
+ if (Http11.snakeUpcase(b1) != b2) return false;
116
+ }
117
+ return true;
118
+ }
119
+
120
+ private static int hash(byte[] buffer, int start, int len) {
121
+ return len
122
+ + asso_values[Byte.toUnsignedInt(Http11.snakeUpcase(buffer[start + len - 1]))]
123
+ + asso_values[Byte.toUnsignedInt(Http11.snakeUpcase(buffer[start]))];
124
+ }
125
+
126
+ private static final int MAX_HASH_VALUE = 94;
127
+
128
+ private final static int[] asso_values = {
129
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
130
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
131
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
132
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
133
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
134
+ 95, 95, 95, 10, 95, 95, 95, 95, 95, 95,
135
+ 95, 95, 95, 95, 95, 5, 95, 15, 40, 0,
136
+ 0, 10, 10, 15, 95, 0, 20, 5, 25, 95,
137
+ 50, 40, 0, 40, 10, 35, 5, 35, 0, 10,
138
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
139
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
140
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
141
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
142
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
143
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
144
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
145
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
146
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
147
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
148
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
149
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
150
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
151
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
152
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
153
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
154
+ 95, 95, 95, 95, 95, 95
155
+ };
156
+
157
+ private static final EnvKey[] keyList = {
158
+ null, null, null, null, null,
159
+ EnvKey.RANGE,
160
+ null,
161
+ EnvKey.REFERER,
162
+ EnvKey.AGE,
163
+ EnvKey.FROM,
164
+ EnvKey.KEEP_ALIVE,
165
+ EnvKey.RETRY_AFTER,
166
+ EnvKey.TE,
167
+ EnvKey.VIA,
168
+ EnvKey.ETAG,
169
+ EnvKey.X_FORWARDED_FOR,
170
+ EnvKey.EXPECT,
171
+ EnvKey.TRAILER,
172
+ EnvKey.FRAGMENT,
173
+ EnvKey.VARY,
174
+ EnvKey.ACCEPT_LANGUAGE,
175
+ EnvKey.ACCEPT,
176
+ EnvKey.REQUEST_PATH,
177
+ EnvKey.IF_RANGE,
178
+ EnvKey.HOST,
179
+ null,
180
+ EnvKey.REQUEST_URI,
181
+ EnvKey.CONTENT_TYPE,
182
+ EnvKey.CONTENT_RANGE,
183
+ EnvKey.ACCEPT_CHARSET,
184
+ EnvKey.ACCEPT_ENCODING,
185
+ EnvKey.CONTENT_LANGUAGE,
186
+ EnvKey.IF_MODIFIED_SINCE,
187
+ EnvKey.IF_MATCH,
188
+ EnvKey.IF_UNMODIFIED_SINCE,
189
+ null,
190
+ EnvKey.CONTENT_MD5,
191
+ EnvKey.TRANSFER_ENCODING,
192
+ EnvKey.IF_NONE_MATCH,
193
+ EnvKey.CONTENT_LENGTH,
194
+ null,
195
+ EnvKey.CONTENT_ENCODING,
196
+ EnvKey.UPGRADE,
197
+ EnvKey.AUTHORIZATION,
198
+ EnvKey.DATE,
199
+ EnvKey.ALLOW,
200
+ EnvKey.SERVER,
201
+ EnvKey.EXPIRES,
202
+ EnvKey.CACHE_CONTROL,
203
+ null,
204
+ EnvKey.CONNECTION,
205
+ EnvKey.WWW_AUTHENTICATE,
206
+ EnvKey.WARNING,
207
+ EnvKey.LOCATION,
208
+ EnvKey.REQUEST_METHOD,
209
+ EnvKey.USER_AGENT,
210
+ EnvKey.CONTENT_LOCATION,
211
+ EnvKey.MAX_FORWARDS,
212
+ EnvKey.ACCEPT_RANGES,
213
+ EnvKey.X_REAL_IP,
214
+ null,
215
+ EnvKey.PRAGMA,
216
+ EnvKey.QUERY_STRING,
217
+ null, null, null, null, null,
218
+ EnvKey.PROXY_AUTHENTICATE,
219
+ null, null, null, null,
220
+ EnvKey.LAST_MODIFIED,
221
+ null,
222
+ EnvKey.SERVER_PROTOCOL,
223
+ null, null, null, null, null, null, null, null, null,
224
+ null, null, null, null, null, null, null, null, null,
225
+ EnvKey.PROXY_AUTHORIZATION
226
+ };
227
+
228
+ static {
229
+ // The perfect hash function must be regenerated if the list above changes
230
+ for (EnvKey key : values()) {
231
+ EnvKey found;
232
+ assert (found = keyForKey(key)) == key : key + " was not matched by perfect hash and found " + found;
233
+ }
234
+ }
235
+
236
+ // Only used for verification of the perfect hash, at boot with asserts enabled.
237
+ private static EnvKey keyForKey(EnvKey key) {
238
+ byte[] bytes = key.name().getBytes(StandardCharsets.ISO_8859_1);
239
+ return keyList[hash(bytes, 0, bytes.length)];
240
+ }
241
+ }