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.
- checksums.yaml +4 -4
- data/History.md +448 -8
- data/README.md +110 -51
- data/docs/5.0-Upgrade.md +98 -0
- data/docs/6.0-Upgrade.md +56 -0
- data/docs/7.0-Upgrade.md +52 -0
- data/docs/8.0-Upgrade.md +100 -0
- data/docs/deployment.md +58 -23
- data/docs/fork_worker.md +11 -1
- data/docs/grpc.md +62 -0
- data/docs/images/favicon.svg +1 -0
- data/docs/images/running-puma.svg +1 -0
- data/docs/images/standard-logo.svg +1 -0
- data/docs/java_options.md +54 -0
- data/docs/jungle/README.md +1 -1
- data/docs/kubernetes.md +11 -16
- data/docs/plugins.md +6 -2
- data/docs/restart.md +2 -2
- data/docs/signals.md +21 -21
- data/docs/stats.md +11 -5
- data/docs/systemd.md +14 -5
- data/ext/puma_http11/extconf.rb +20 -32
- data/ext/puma_http11/http11_parser.java.rl +51 -65
- data/ext/puma_http11/mini_ssl.c +29 -9
- data/ext/puma_http11/org/jruby/puma/EnvKey.java +241 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +194 -101
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +71 -85
- data/ext/puma_http11/puma_http11.c +125 -118
- data/lib/puma/app/status.rb +11 -3
- data/lib/puma/binder.rb +22 -12
- data/lib/puma/cli.rb +11 -9
- data/lib/puma/client.rb +233 -136
- data/lib/puma/client_env.rb +171 -0
- data/lib/puma/cluster/worker.rb +24 -21
- data/lib/puma/cluster/worker_handle.rb +38 -8
- data/lib/puma/cluster.rb +74 -48
- data/lib/puma/cluster_accept_loop_delay.rb +91 -0
- data/lib/puma/commonlogger.rb +3 -3
- data/lib/puma/configuration.rb +197 -64
- data/lib/puma/const.rb +23 -12
- data/lib/puma/control_cli.rb +11 -7
- data/lib/puma/detect.rb +13 -0
- data/lib/puma/dsl.rb +483 -127
- data/lib/puma/error_logger.rb +7 -5
- data/lib/puma/events.rb +25 -10
- data/lib/puma/io_buffer.rb +8 -4
- data/lib/puma/jruby_restart.rb +0 -16
- data/lib/puma/launcher/bundle_pruner.rb +3 -5
- data/lib/puma/launcher.rb +76 -59
- data/lib/puma/log_writer.rb +17 -11
- data/lib/puma/minissl/context_builder.rb +1 -0
- data/lib/puma/minissl.rb +1 -1
- data/lib/puma/null_io.rb +26 -0
- data/lib/puma/plugin/systemd.rb +3 -3
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/reactor.rb +19 -13
- data/lib/puma/{request.rb → response.rb} +57 -209
- data/lib/puma/runner.rb +15 -17
- data/lib/puma/sd_notify.rb +1 -4
- data/lib/puma/server.rb +200 -104
- data/lib/puma/server_plugin_control.rb +32 -0
- data/lib/puma/single.rb +7 -4
- data/lib/puma/state_file.rb +3 -2
- data/lib/puma/thread_pool.rb +179 -96
- data/lib/puma/util.rb +0 -7
- data/lib/puma.rb +10 -0
- data/lib/rack/handler/puma.rb +11 -8
- data/tools/Dockerfile +15 -5
- metadata +26 -16
- data/ext/puma_http11/ext_help.h +0 -15
data/ext/puma_http11/extconf.rb
CHANGED
|
@@ -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 '
|
|
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
|
-
|
|
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
|
-
|
|
41
|
-
have_func "
|
|
42
|
-
have_func "
|
|
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
|
-
|
|
45
|
-
have_func "
|
|
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
|
-
|
|
48
|
-
|
|
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
|
-
|
|
51
|
-
have_func "
|
|
48
|
+
puts "\n──── Below is yes for 1.1.1 & later ────"
|
|
49
|
+
have_func "SSL_CTX_set_ciphersuites(NULL, \"\")" , ssl_h
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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 {
|
|
16
|
+
action mark {this.mark = fpc; }
|
|
16
17
|
|
|
17
|
-
action start_field {
|
|
18
|
-
action snake_upcase_field { /*
|
|
18
|
+
action start_field { this.field_start = fpc; }
|
|
19
|
+
action snake_upcase_field { /* done lazily as needed */ }
|
|
19
20
|
action write_field {
|
|
20
|
-
|
|
21
|
+
this.field_len = fpc-this.field_start;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
action start_value {
|
|
24
|
+
action start_value { this.mark = fpc; }
|
|
24
25
|
action write_value {
|
|
25
|
-
Http11.http_field(runtime,
|
|
26
|
+
Http11.http_field(runtime, envStrings, this, fpc-this.mark);
|
|
26
27
|
}
|
|
27
28
|
action request_method {
|
|
28
|
-
Http11.request_method(runtime,
|
|
29
|
+
Http11.request_method(runtime, envStrings, this, fpc-this.mark);
|
|
29
30
|
}
|
|
30
31
|
action request_uri {
|
|
31
|
-
Http11.request_uri(runtime,
|
|
32
|
+
Http11.request_uri(runtime, envStrings, this, fpc-this.mark);
|
|
32
33
|
}
|
|
33
34
|
action fragment {
|
|
34
|
-
Http11.fragment(runtime,
|
|
35
|
+
Http11.fragment(runtime, envStrings, this, fpc-this.mark);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
action start_query {
|
|
38
|
+
action start_query {this.query_start = fpc; }
|
|
38
39
|
action query_string {
|
|
39
|
-
Http11.query_string(runtime,
|
|
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,
|
|
44
|
+
Http11.server_protocol(runtime, envStrings, this, fpc-this.mark);
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
action request_path {
|
|
47
|
-
Http11.request_path(runtime,
|
|
48
|
+
Http11.request_path(runtime, envStrings, this, fpc-this.mark);
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
action done {
|
|
51
|
-
|
|
52
|
-
http.header_done(runtime,
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
72
|
+
RubyHash data;
|
|
73
|
+
byte[] buffer;
|
|
70
74
|
|
|
71
|
-
public
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
109
|
-
|
|
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
|
-
|
|
116
|
-
|
|
101
|
+
this.cs = cs;
|
|
102
|
+
this.nread += (p - off);
|
|
117
103
|
|
|
118
104
|
assert p <= pe : "buffer overflow after parsing execute";
|
|
119
|
-
assert
|
|
120
|
-
assert
|
|
121
|
-
assert
|
|
122
|
-
assert
|
|
123
|
-
assert
|
|
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
|
|
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
|
|
125
|
+
return this.cs == puma_parser_error;
|
|
140
126
|
}
|
|
141
127
|
|
|
142
128
|
public boolean is_finished() {
|
|
143
|
-
return
|
|
129
|
+
return this.cs == puma_parser_first_final;
|
|
144
130
|
}
|
|
145
131
|
}
|
data/ext/puma_http11/mini_ssl.c
CHANGED
|
@@ -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
|
-
|
|
662
|
-
|
|
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
|
|
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
|
+
}
|