puma 7.2.0 → 8.0.0
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 +45 -0
- data/README.md +1 -2
- 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/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/signals.md +1 -1
- data/ext/puma_http11/http11_parser.java.rl +51 -65
- data/ext/puma_http11/org/jruby/puma/EnvKey.java +241 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +168 -104
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +71 -85
- data/lib/puma/cli.rb +1 -1
- data/lib/puma/client.rb +90 -66
- data/lib/puma/client_env.rb +171 -0
- data/lib/puma/cluster.rb +1 -1
- data/lib/puma/configuration.rb +69 -7
- data/lib/puma/const.rb +2 -2
- data/lib/puma/control_cli.rb +1 -1
- data/lib/puma/detect.rb +11 -0
- data/lib/puma/dsl.rb +74 -8
- data/lib/puma/launcher.rb +3 -4
- data/lib/puma/{request.rb → response.rb} +15 -186
- data/lib/puma/server.rb +69 -34
- data/lib/puma/server_plugin_control.rb +32 -0
- data/lib/puma/thread_pool.rb +129 -23
- data/lib/rack/handler/puma.rb +1 -1
- metadata +14 -3
|
@@ -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
|
}
|
|
@@ -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
|
+
}
|