puma 4.2.0 → 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 +4 -4
- data/History.md +25 -1
- data/README.md +5 -23
- data/docs/plugins.md +20 -10
- data/docs/tcp_mode.md +96 -0
- data/ext/puma_http11/extconf.rb +5 -0
- data/ext/puma_http11/http11_parser.java.rl +21 -37
- data/ext/puma_http11/org/jruby/puma/Http11.java +106 -114
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +91 -106
- data/ext/puma_http11/puma_http11.c +2 -0
- data/lib/puma/binder.rb +7 -61
- data/lib/puma/cli.rb +1 -1
- data/lib/puma/client.rb +3 -3
- data/lib/puma/cluster.rb +0 -1
- data/lib/puma/const.rb +9 -2
- data/lib/puma/control_cli.rb +11 -3
- data/lib/puma/dsl.rb +5 -3
- data/lib/puma/launcher.rb +1 -4
- data/lib/puma/minissl/context_builder.rb +76 -0
- data/lib/puma/reactor.rb +2 -1
- data/lib/puma/runner.rb +7 -0
- data/lib/puma/server.rb +18 -7
- data/lib/puma/single.rb +0 -1
- data/lib/puma/thread_pool.rb +1 -1
- metadata +4 -4
- data/lib/puma/convenient.rb +0 -25
- data/lib/puma/delegation.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c3e9fdfe5225baf88ff5ee9ec6f58201a220a482d72e5217309964a22b7ccc0
|
4
|
+
data.tar.gz: 541ad3a7311662ca31e3de234870b03d6aac3eb925c8ad04e7a857bac097edc8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8dc3a1d604212819aa8dc549b767cf00e3c237c95f0053ec61f6750666097980a1fbd2c350b1c47e59a1047d1a0bb35451914629f2829a66ded20a930939a60e
|
7
|
+
data.tar.gz: f906230de96dd55841faaf6968939b4a096acb9933e91b34c8aada201e1c5e259c567ce8dce744490093072f6cb9d9553b7d83752e07ec7502973c72990563d4
|
data/History.md
CHANGED
@@ -6,11 +6,35 @@
|
|
6
6
|
* Bugfixes
|
7
7
|
* Your bugfix goes here (#Github Number)
|
8
8
|
|
9
|
+
## 4.3.1 and 3.12.2 / 2019-12-05
|
10
|
+
|
11
|
+
* Security
|
12
|
+
* Fix: a poorly-behaved client could use keepalive requests to monopolize Puma's reactor and create a denial of service attack. CVE-2019-16770.
|
13
|
+
|
14
|
+
## 4.3.0 / 2019-11-07
|
15
|
+
|
16
|
+
* Features
|
17
|
+
* Strip whitespace at end of HTTP headers (#2010)
|
18
|
+
* Optimize HTTP parser for JRuby (#2012)
|
19
|
+
* Add SSL support for the control app and cli (#2046, #2052)
|
20
|
+
|
21
|
+
* Bugfixes
|
22
|
+
* Fix Errno::EINVAL when SSL is enabled and browser rejects cert (#1564)
|
23
|
+
* Fix pumactl defaulting puma to development if an environment was not specified (#2035)
|
24
|
+
* Fix closing file stream when reading pid from pidfile (#2048)
|
25
|
+
* Fix a typo in configuration option `--extra_runtime_dependencies` (#2050)
|
26
|
+
|
27
|
+
## 4.2.1 / 2019-10-07
|
28
|
+
|
29
|
+
* 3 bugfixes
|
30
|
+
* Fix socket activation of systemd (pre-existing) unix binder files (#1842, #1988)
|
31
|
+
* Deal with multiple calls to bind correctly (#1986, #1994, #2006)
|
32
|
+
* Accepts symbols for `verify_mode` (#1222)
|
9
33
|
|
10
34
|
## 4.2.0 / 2019-09-23
|
11
35
|
|
12
36
|
* 6 features
|
13
|
-
* Pumactl has a new -e environment option and reads config/puma/<environment>.rb config files (#1885)
|
37
|
+
* Pumactl has a new -e environment option and reads `config/puma/<environment>.rb` config files (#1885)
|
14
38
|
* Semicolons are now allowed in URL paths (MRI only), useful for Angular or Redmine (#1934)
|
15
39
|
* Allow extra dependencies to be defined when using prune_bundler (#1105)
|
16
40
|
* Puma now reports the correct port when binding to port 0, also reports other listeners when binding to localhost (#1786)
|
data/README.md
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
# Puma: A Ruby Web Server Built For Concurrency
|
6
6
|
|
7
7
|
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/puma/puma?utm\_source=badge&utm\_medium=badge&utm\_campaign=pr-badge)
|
8
|
+
[![Actions Build Status](https://github.com/puma/puma/workflows/Puma/badge.svg)](https://github.com/puma/puma/actions)
|
8
9
|
[![Travis Build Status](https://travis-ci.org/puma/puma.svg?branch=master)](https://travis-ci.org/puma/puma)
|
9
10
|
|
10
11
|
[![Code Climate](https://codeclimate.com/github/puma/puma.svg)](https://codeclimate.com/github/puma/puma)
|
@@ -227,6 +228,8 @@ If you want to prevent Puma from looking for a configuration file in those locat
|
|
227
228
|
$ puma -C "-"
|
228
229
|
```
|
229
230
|
|
231
|
+
The other side-effects of setting the environment are whether to show stack traces (in `development` or `test`), and setting RACK_ENV may potentially affect middleware looking for this value to change their behavior. The default puma RACK_ENV value is `development`. You can see all config default values [here](https://github.com/puma/puma/blob/12d1706ddc71b89ed2ee26275e31c788e94ff541/lib/puma/configuration.rb#L170).
|
232
|
+
|
230
233
|
Check out [dsl.rb](https://github.com/puma/puma/blob/master/lib/puma/dsl.rb) to see all available options.
|
231
234
|
|
232
235
|
## Restart
|
@@ -279,30 +282,9 @@ reliability in production environments:
|
|
279
282
|
|
280
283
|
## Contributing
|
281
284
|
|
282
|
-
|
283
|
-
|
284
|
-
```bash
|
285
|
-
$ bundle install
|
286
|
-
$ bundle exec rake
|
287
|
-
```
|
288
|
-
|
289
|
-
To run a single test file, run only that file:
|
290
|
-
|
291
|
-
```bash
|
292
|
-
$ ruby -Ilib test/test_integration.rb
|
293
|
-
```
|
294
|
-
|
295
|
-
Or use [`m`](https://github.com/qrush/m):
|
285
|
+
Find details for contributing in the [contribution guide].
|
296
286
|
|
297
|
-
|
298
|
-
$ bundle exec m test/test_binder.rb
|
299
|
-
```
|
300
|
-
|
301
|
-
Which can also be used to run a single test case:
|
302
|
-
|
303
|
-
```
|
304
|
-
$ bundle exec m test/test_binder.rb:37
|
305
|
-
```
|
287
|
+
[contribution guide]: https://github.com/puma/puma/blob/master/CONTRIBUTING.md
|
306
288
|
|
307
289
|
## License
|
308
290
|
|
data/docs/plugins.md
CHANGED
@@ -1,15 +1,22 @@
|
|
1
1
|
## Plugins
|
2
2
|
|
3
|
-
Puma 3.0 added support for plugins that can augment configuration and service
|
3
|
+
Puma 3.0 added support for plugins that can augment configuration and service
|
4
|
+
operations.
|
4
5
|
|
5
6
|
2 canonical plugins to look to aid in development of further plugins:
|
6
7
|
|
7
|
-
* [tmp\_restart](https://github.com/puma/puma/blob/master/lib/puma/plugin/tmp_restart.rb):
|
8
|
-
|
8
|
+
* [tmp\_restart](https://github.com/puma/puma/blob/master/lib/puma/plugin/tmp_restart.rb):
|
9
|
+
Restarts the server if the file `tmp/restart.txt` is touched
|
10
|
+
* [heroku](https://github.com/puma/puma-heroku/blob/master/lib/puma/plugin/heroku.rb):
|
11
|
+
Packages up the default configuration used by puma on Heroku
|
9
12
|
|
10
|
-
Plugins are activated in a puma configuration file (such as `config/puma.rb'`)
|
13
|
+
Plugins are activated in a puma configuration file (such as `config/puma.rb'`)
|
14
|
+
by adding `plugin "name"`, such as `plugin "heroku"`.
|
11
15
|
|
12
|
-
Plugins are activated based simply on path requirements so, activating the
|
16
|
+
Plugins are activated based simply on path requirements so, activating the
|
17
|
+
`heroku` plugin will simply be doing `require "puma/plugin/heroku"`. This
|
18
|
+
allows gems to provide multiple plugins (as well as unrelated gems to provide
|
19
|
+
puma plugins).
|
13
20
|
|
14
21
|
The `tmp_restart` plugin is bundled with puma, so it can always be used.
|
15
22
|
|
@@ -17,12 +24,15 @@ To use the `heroku` plugin, add `puma-heroku` to your Gemfile or install it.
|
|
17
24
|
|
18
25
|
### API
|
19
26
|
|
20
|
-
|
27
|
+
## Server-wide hooks
|
21
28
|
|
22
|
-
|
29
|
+
Plugins can use a couple of hooks at server level: `start` and `config`.
|
23
30
|
|
24
|
-
`
|
31
|
+
`start` runs when the server has started and allows the plugin to start other
|
32
|
+
functionality to augment puma.
|
25
33
|
|
26
|
-
|
34
|
+
`config` runs when the server is being configured and is passed a `Puma::DSL`
|
35
|
+
object that can be used to add additional configuration.
|
27
36
|
|
28
|
-
|
37
|
+
Any public methods in `Puma::Plugin` are the public API that any plugin may
|
38
|
+
use.
|
data/docs/tcp_mode.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# TCP mode
|
2
|
+
|
3
|
+
Puma also could be used as a TCP server to process incoming TCP
|
4
|
+
connections.
|
5
|
+
|
6
|
+
|
7
|
+
## Configuration
|
8
|
+
|
9
|
+
TCP mode can be enabled with CLI option `--tcp-mode`:
|
10
|
+
|
11
|
+
```
|
12
|
+
$ puma --tcp-mode
|
13
|
+
```
|
14
|
+
|
15
|
+
Default ip and port to listen to are `0.0.0.0` and `9292`. You can configure
|
16
|
+
them with `--port` and `--bind` options:
|
17
|
+
|
18
|
+
```
|
19
|
+
$ puma --tcp-mode --bind tcp://127.0.0.1:9293
|
20
|
+
$ puma --tcp-mode --port 9293
|
21
|
+
```
|
22
|
+
|
23
|
+
TCP mode could be set with a configuration file as well with `tcp_mode`
|
24
|
+
and `tcp_mode!` methods:
|
25
|
+
|
26
|
+
```
|
27
|
+
# config/puma.rb
|
28
|
+
tcp_mode
|
29
|
+
```
|
30
|
+
|
31
|
+
When Puma starts in the TCP mode it prints the corresponding message:
|
32
|
+
|
33
|
+
```
|
34
|
+
puma --tcp-mode
|
35
|
+
Puma starting in single mode...
|
36
|
+
...
|
37
|
+
* Mode: Lopez Express (tcp)
|
38
|
+
```
|
39
|
+
|
40
|
+
|
41
|
+
## How to declare an application
|
42
|
+
|
43
|
+
An application to process TCP connections should be declared as a
|
44
|
+
callable object which accepts `env` and `socket` arguments.
|
45
|
+
|
46
|
+
`env` argument is a Hash with following structure:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
{ "thread" => {}, "REMOTE_ADDR" => "127.0.0.1:51133", "log" => "#<Proc:0x000..." }
|
50
|
+
```
|
51
|
+
|
52
|
+
It consists of:
|
53
|
+
* `thread` - a Hash for each thread in the thread pool that could be
|
54
|
+
used to store information between requests
|
55
|
+
* `REMOTE_ADDR` - a client ip address
|
56
|
+
* `log` - a proc object to write something down
|
57
|
+
|
58
|
+
`log` object could be used this way:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
env['log'].call('message to log')
|
62
|
+
#> 19/Oct/2019 20:28:53 - 127.0.0.1:51266 - message to log
|
63
|
+
```
|
64
|
+
|
65
|
+
|
66
|
+
## Example of an application
|
67
|
+
|
68
|
+
Let's look at an example of a simple application which just echoes
|
69
|
+
incoming string:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
# config/puma.rb
|
73
|
+
app do |env, socket|
|
74
|
+
s = socket.gets
|
75
|
+
socket.puts "Echo #{s}"
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
We can easily access the TCP server with `telnet` command and receive an
|
80
|
+
echo:
|
81
|
+
|
82
|
+
```shell
|
83
|
+
telnet 0.0.0.0 9293
|
84
|
+
Trying 0.0.0.0...
|
85
|
+
Connected to 0.0.0.0.
|
86
|
+
Escape character is '^]'.
|
87
|
+
sssss
|
88
|
+
Echo sssss
|
89
|
+
^CConnection closed by foreign host.
|
90
|
+
```
|
91
|
+
|
92
|
+
|
93
|
+
## Socket management
|
94
|
+
|
95
|
+
After the application finishes, Puma closes the socket. In order to
|
96
|
+
prevent this, the application should set `env['detach'] = true`.
|
data/ext/puma_http11/extconf.rb
CHANGED
@@ -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();
|
@@ -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;
|