puma 2.3.2 → 2.4.0
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.
- data/History.txt +23 -0
- data/README.md +20 -25
- data/ext/puma_http11/http11_parser.c +15 -15
- data/ext/puma_http11/http11_parser.h +11 -11
- data/ext/puma_http11/http11_parser.rl +12 -12
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/puma_http11.c +39 -39
- data/lib/puma/capistrano.rb +35 -4
- data/lib/puma/cli.rb +24 -7
- data/lib/puma/cluster.rb +0 -10
- data/lib/puma/configuration.rb +15 -0
- data/lib/puma/const.rb +2 -2
- data/lib/puma/events.rb +6 -0
- data/lib/puma/jruby_restart.rb +4 -0
- data/lib/puma/rack_patch.rb +21 -1
- data/lib/puma/runner.rb +1 -1
- data/lib/puma/server.rb +34 -2
- data/lib/puma/single.rb +5 -0
- data/lib/puma/thread_pool.rb +3 -3
- data/lib/rack/handler/puma.rb +2 -2
- data/puma.gemspec +2 -2
- data/test/test_thread_pool.rb +10 -0
- metadata +2 -2
data/History.txt
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
=== 2.4.0 / 2013-07-22
|
2
|
+
|
3
|
+
* 5 minor features:
|
4
|
+
* Add PUMA_JRUBY_DAEMON_OPTS to get around agent starting twice
|
5
|
+
* Add ability to drain accept socket on shutdown
|
6
|
+
* Add port to DSL
|
7
|
+
* Adds support for using puma config file in capistrano deploys.
|
8
|
+
* Make phased_restart fallback to restart if not available
|
9
|
+
|
10
|
+
* 10 bug fixes:
|
11
|
+
|
12
|
+
* Be sure to only delete the pid in the master. Fixes #334
|
13
|
+
* Call out -C/--config flags
|
14
|
+
* Change parser symbol names to avoid clash. Fixes #179
|
15
|
+
* Convert thread pool sizes to integers
|
16
|
+
* Detect when the jruby daemon child doesn't start properly
|
17
|
+
* Fix typo in CLI help
|
18
|
+
* Improve the logging output when hijack is used. Fixes #332
|
19
|
+
* Remove unnecessary thread pool size conversions
|
20
|
+
* Setup :worker_boot as an Array. Fixes #317
|
21
|
+
* Use 127.0.0.1 as REMOTE_ADDR of unix client. Fixes #309
|
22
|
+
|
23
|
+
|
1
24
|
=== 2.3.2 / 2013-07-08
|
2
25
|
|
3
26
|
* 1 bug fix:
|
data/README.md
CHANGED
@@ -78,38 +78,37 @@ Puma will automatically scale the number of threads based on how much traffic is
|
|
78
78
|
Puma 2 offers clustered mode, allowing you to use forked processes to handle multiple incoming requests concurrently, in addition to threads already provided. You can tune the number of workers with the `-w` (or `--workers`) flag:
|
79
79
|
|
80
80
|
$ puma -t 8:32 -w 3
|
81
|
-
|
82
|
-
On a ruby implementation that offers native threads, you should tune this number to match the number of cores available.
|
83
|
-
Note that threads are still used in clustered mode, and the `-t` thread flag setting is per worker, so `-w 2 -t 16:16` will be 32 threads.
|
84
81
|
|
85
|
-
|
86
|
-
|
87
|
-
For instance, you could fire a log notification that a worker booted or send something to statsd.
|
88
|
-
This can be called multiple times to add hooks.
|
82
|
+
On a ruby implementation that offers native threads, you should tune this number to match the number of cores available.
|
83
|
+
Note that threads are still used in clustered mode, and the `-t` thread flag setting is per worker, so `-w 2 -t 16:16` will be 32 threads.
|
89
84
|
|
90
85
|
If you're running in Clustered Mode you can optionally choose to preload your application before starting up the workers. To do this simply specify the `--preload` flag in invocation:
|
91
86
|
|
92
87
|
# CLI invocation
|
93
88
|
$ puma -t 8:32 -w 3 --preload
|
94
|
-
|
89
|
+
|
95
90
|
If you're using a configuration file, use the `preload_app!` method, and be sure to specify your config file's location with the `-C` flag:
|
96
91
|
|
97
92
|
$ puma -C config/puma.rb
|
98
|
-
|
93
|
+
|
99
94
|
# config/puma.rb
|
100
95
|
threads 8,32
|
101
96
|
workers 3
|
102
|
-
preload_app!
|
103
|
-
|
97
|
+
preload_app!
|
104
98
|
|
105
|
-
Additionally, you can specify a block in your configuration that will be run on boot of each worker:
|
99
|
+
Additionally, you can specify a block in your configuration file that will be run on boot of each worker:
|
106
100
|
|
107
101
|
# config/puma.rb
|
108
102
|
on_worker_boot do
|
109
103
|
# configuration here
|
110
104
|
end
|
111
|
-
|
112
|
-
|
105
|
+
|
106
|
+
This code can be used to setup the process before booting the application, allowing
|
107
|
+
you to do some puma-specific things that you don't want to embed in your application.
|
108
|
+
For instance, you could fire a log notification that a worker booted or send something to statsd.
|
109
|
+
This can be called multiple times to add hooks.
|
110
|
+
|
111
|
+
If you're preloading your application and using ActiveRecord, it's recommend you setup your connection pool here:
|
113
112
|
|
114
113
|
# config/puma.rb
|
115
114
|
on_worker_boot do
|
@@ -142,15 +141,11 @@ Puma comes with a builtin status/control app that can be used query and control
|
|
142
141
|
|
143
142
|
$ puma --control tcp://127.0.0.1:9293 --control-token foo
|
144
143
|
|
145
|
-
This directs puma to start the control server on localhost port 9293. Additionally, all requests to the control server will need to include `token=foo` as a query parameter. This allows for simple authentication. Check out https://github.com/puma/puma/blob/master/lib/puma/app/status.rb to see what the app has available.
|
144
|
+
This directs puma to start the control server on localhost port 9293. Additionally, all requests to the control server will need to include `token=foo` as a query parameter. This allows for simple authentication. Check out [status.rb](https://github.com/puma/puma/blob/master/lib/puma/app/status.rb) to see what the app has available.
|
146
145
|
|
147
146
|
### Configuration file
|
148
147
|
|
149
|
-
You can also provide a configuration file which puma will use:
|
150
|
-
|
151
|
-
$ puma --config /path/to/config
|
152
|
-
|
153
|
-
or
|
148
|
+
You can also provide a configuration file which puma will use with the `-C` (or `--config`) flag:
|
154
149
|
|
155
150
|
$ puma -C /path/to/config
|
156
151
|
|
@@ -158,7 +153,7 @@ Take the following [sample configuration](https://github.com/puma/puma/blob/mast
|
|
158
153
|
|
159
154
|
## Restart
|
160
155
|
|
161
|
-
Puma includes the ability to restart itself
|
156
|
+
Puma includes the ability to restart itself allowing easy upgrades to new versions. When available (MRI, Rubinius, JRuby), puma performs a "hot restart". This is the same functionality available in *unicorn* and *nginx* which keep the server sockets open between restarts. This makes sure that no pending requests are dropped while the restart is taking place.
|
162
157
|
|
163
158
|
To perform a restart, there are 2 builtin mechanisms:
|
164
159
|
|
@@ -171,11 +166,11 @@ If the new process is unable to load, it will simply exit. You should therefore
|
|
171
166
|
|
172
167
|
### Cleanup Code
|
173
168
|
|
174
|
-
Puma isn't able to understand all the resources that your app may use, so it provides a hook in the configuration file you pass to `-C`
|
169
|
+
Puma isn't able to understand all the resources that your app may use, so it provides a hook in the configuration file you pass to `-C` called `on_restart`. The block passed to `on_restart` will be called, unsurprisingly, just before puma restarts itself.
|
175
170
|
|
176
171
|
You should place code to close global log files, redis connections, etc in this block so that their file descriptors don't leak into the restarted process. Failure to do so will result in slowly running out of descriptors and eventually obscure crashes as the server is restart many times.
|
177
172
|
|
178
|
-
### Platform
|
173
|
+
### Platform Constraints
|
179
174
|
|
180
175
|
Because of various platforms not being implement certain things, the following differences occur when puma is used on different platforms:
|
181
176
|
|
@@ -195,7 +190,7 @@ or
|
|
195
190
|
|
196
191
|
will cause the server to perform a restart. `pumactl` is a simple CLI frontend to the control/status app described above.
|
197
192
|
|
198
|
-
Allowed commands: status
|
193
|
+
Allowed commands: `status`, `restart`, `halt`, `stop`
|
199
194
|
|
200
195
|
## Managing multiple Pumas / init.d / upstart scripts
|
201
196
|
|
@@ -215,7 +210,7 @@ and then
|
|
215
210
|
|
216
211
|
```bash
|
217
212
|
$ bundle exec cap puma:start
|
218
|
-
$ bundle exec cap puma:restart
|
213
|
+
$ bundle exec cap puma:restart
|
219
214
|
$ bundle exec cap puma:stop
|
220
215
|
```
|
221
216
|
|
@@ -36,21 +36,21 @@ static void snake_upcase_char(char *c)
|
|
36
36
|
/** Data **/
|
37
37
|
|
38
38
|
#line 39 "ext/http11/http11_parser.c"
|
39
|
-
static const int
|
40
|
-
static const int
|
41
|
-
static const int
|
39
|
+
static const int puma_parser_start = 1;
|
40
|
+
static const int puma_parser_first_final = 57;
|
41
|
+
static const int puma_parser_error = 0;
|
42
42
|
|
43
|
-
static const int
|
43
|
+
static const int puma_parser_en_main = 1;
|
44
44
|
|
45
45
|
|
46
46
|
#line 82 "ext/http11/http11_parser.rl"
|
47
47
|
|
48
|
-
int
|
48
|
+
int puma_parser_init(puma_parser *parser) {
|
49
49
|
int cs = 0;
|
50
50
|
|
51
51
|
#line 52 "ext/http11/http11_parser.c"
|
52
52
|
{
|
53
|
-
cs =
|
53
|
+
cs = puma_parser_start;
|
54
54
|
}
|
55
55
|
|
56
56
|
#line 86 "ext/http11/http11_parser.rl"
|
@@ -69,7 +69,7 @@ int http_parser_init(http_parser *parser) {
|
|
69
69
|
|
70
70
|
|
71
71
|
/** exec **/
|
72
|
-
size_t
|
72
|
+
size_t puma_parser_execute(puma_parser *parser, const char *buffer, size_t len, size_t off) {
|
73
73
|
const char *p, *pe;
|
74
74
|
int cs = parser->cs;
|
75
75
|
|
@@ -1191,7 +1191,7 @@ case 56:
|
|
1191
1191
|
|
1192
1192
|
#line 114 "ext/http11/http11_parser.rl"
|
1193
1193
|
|
1194
|
-
if (!
|
1194
|
+
if (!puma_parser_has_error(parser))
|
1195
1195
|
parser->cs = cs;
|
1196
1196
|
parser->nread += p - (buffer + off);
|
1197
1197
|
|
@@ -1205,21 +1205,21 @@ case 56:
|
|
1205
1205
|
return(parser->nread);
|
1206
1206
|
}
|
1207
1207
|
|
1208
|
-
int
|
1208
|
+
int puma_parser_finish(puma_parser *parser)
|
1209
1209
|
{
|
1210
|
-
if (
|
1210
|
+
if (puma_parser_has_error(parser) ) {
|
1211
1211
|
return -1;
|
1212
|
-
} else if (
|
1212
|
+
} else if (puma_parser_is_finished(parser) ) {
|
1213
1213
|
return 1;
|
1214
1214
|
} else {
|
1215
1215
|
return 0;
|
1216
1216
|
}
|
1217
1217
|
}
|
1218
1218
|
|
1219
|
-
int
|
1220
|
-
return parser->cs ==
|
1219
|
+
int puma_parser_has_error(puma_parser *parser) {
|
1220
|
+
return parser->cs == puma_parser_error;
|
1221
1221
|
}
|
1222
1222
|
|
1223
|
-
int
|
1224
|
-
return parser->cs >=
|
1223
|
+
int puma_parser_is_finished(puma_parser *parser) {
|
1224
|
+
return parser->cs >= puma_parser_first_final;
|
1225
1225
|
}
|
@@ -16,16 +16,16 @@
|
|
16
16
|
|
17
17
|
#define BUFFER_LEN 1024
|
18
18
|
|
19
|
-
struct
|
19
|
+
struct puma_parser;
|
20
20
|
|
21
|
-
typedef void (*element_cb)(struct
|
21
|
+
typedef void (*element_cb)(struct puma_parser* hp,
|
22
22
|
const char *at, size_t length);
|
23
23
|
|
24
|
-
typedef void (*field_cb)(struct
|
24
|
+
typedef void (*field_cb)(struct puma_parser* hp,
|
25
25
|
const char *field, size_t flen,
|
26
26
|
const char *value, size_t vlen);
|
27
27
|
|
28
|
-
typedef struct
|
28
|
+
typedef struct puma_parser {
|
29
29
|
int cs;
|
30
30
|
size_t body_start;
|
31
31
|
int content_len;
|
@@ -49,15 +49,15 @@ typedef struct http_parser {
|
|
49
49
|
|
50
50
|
char buf[BUFFER_LEN];
|
51
51
|
|
52
|
-
}
|
52
|
+
} puma_parser;
|
53
53
|
|
54
|
-
int
|
55
|
-
int
|
56
|
-
size_t
|
54
|
+
int puma_parser_init(puma_parser *parser);
|
55
|
+
int puma_parser_finish(puma_parser *parser);
|
56
|
+
size_t puma_parser_execute(puma_parser *parser, const char *data,
|
57
57
|
size_t len, size_t off);
|
58
|
-
int
|
59
|
-
int
|
58
|
+
int puma_parser_has_error(puma_parser *parser);
|
59
|
+
int puma_parser_is_finished(puma_parser *parser);
|
60
60
|
|
61
|
-
#define
|
61
|
+
#define puma_parser_nread(parser) (parser)->nread
|
62
62
|
|
63
63
|
#endif
|
@@ -29,7 +29,7 @@ static void snake_upcase_char(char *c)
|
|
29
29
|
|
30
30
|
%%{
|
31
31
|
|
32
|
-
machine
|
32
|
+
machine puma_parser;
|
33
33
|
|
34
34
|
action mark { MARK(mark, fpc); }
|
35
35
|
|
@@ -73,14 +73,14 @@ static void snake_upcase_char(char *c)
|
|
73
73
|
fbreak;
|
74
74
|
}
|
75
75
|
|
76
|
-
include
|
76
|
+
include puma_parser_common "http11_parser_common.rl";
|
77
77
|
|
78
78
|
}%%
|
79
79
|
|
80
80
|
/** Data **/
|
81
81
|
%% write data;
|
82
82
|
|
83
|
-
int
|
83
|
+
int puma_parser_init(puma_parser *parser) {
|
84
84
|
int cs = 0;
|
85
85
|
%% write init;
|
86
86
|
parser->cs = cs;
|
@@ -98,7 +98,7 @@ int http_parser_init(http_parser *parser) {
|
|
98
98
|
|
99
99
|
|
100
100
|
/** exec **/
|
101
|
-
size_t
|
101
|
+
size_t puma_parser_execute(puma_parser *parser, const char *buffer, size_t len, size_t off) {
|
102
102
|
const char *p, *pe;
|
103
103
|
int cs = parser->cs;
|
104
104
|
|
@@ -112,7 +112,7 @@ size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len,
|
|
112
112
|
|
113
113
|
%% write exec;
|
114
114
|
|
115
|
-
if (!
|
115
|
+
if (!puma_parser_has_error(parser))
|
116
116
|
parser->cs = cs;
|
117
117
|
parser->nread += p - (buffer + off);
|
118
118
|
|
@@ -126,21 +126,21 @@ size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len,
|
|
126
126
|
return(parser->nread);
|
127
127
|
}
|
128
128
|
|
129
|
-
int
|
129
|
+
int puma_parser_finish(puma_parser *parser)
|
130
130
|
{
|
131
|
-
if (
|
131
|
+
if (puma_parser_has_error(parser) ) {
|
132
132
|
return -1;
|
133
|
-
} else if (
|
133
|
+
} else if (puma_parser_is_finished(parser) ) {
|
134
134
|
return 1;
|
135
135
|
} else {
|
136
136
|
return 0;
|
137
137
|
}
|
138
138
|
}
|
139
139
|
|
140
|
-
int
|
141
|
-
return parser->cs ==
|
140
|
+
int puma_parser_has_error(puma_parser *parser) {
|
141
|
+
return parser->cs == puma_parser_error;
|
142
142
|
}
|
143
143
|
|
144
|
-
int
|
145
|
-
return parser->cs >=
|
144
|
+
int puma_parser_is_finished(puma_parser *parser) {
|
145
|
+
return parser->cs >= puma_parser_first_final;
|
146
146
|
}
|
@@ -38,10 +38,10 @@ static VALUE global_http_version;
|
|
38
38
|
static VALUE global_request_path;
|
39
39
|
|
40
40
|
/** Defines common length and error messages for input length validation. */
|
41
|
-
#define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " # length " allowed length
|
41
|
+
#define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " # length " allowed length (was %d)"
|
42
42
|
|
43
43
|
/** Validates the max length of given input and throws an HttpParserError exception if over. */
|
44
|
-
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError,
|
44
|
+
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR, len); }
|
45
45
|
|
46
46
|
/** Defines global strings in the init method. */
|
47
47
|
#define DEF_GLOBAL(N, val) global_##N = rb_str_new2(val); rb_global_variable(&global_##N)
|
@@ -173,7 +173,7 @@ static VALUE find_common_field_value(const char *field, size_t flen)
|
|
173
173
|
#endif /* !HAVE_QSORT_BSEARCH */
|
174
174
|
}
|
175
175
|
|
176
|
-
void http_field(
|
176
|
+
void http_field(puma_parser* hp, const char *field, size_t flen,
|
177
177
|
const char *value, size_t vlen)
|
178
178
|
{
|
179
179
|
VALUE v = Qnil;
|
@@ -204,7 +204,7 @@ void http_field(http_parser* hp, const char *field, size_t flen,
|
|
204
204
|
rb_hash_aset(hp->request, f, v);
|
205
205
|
}
|
206
206
|
|
207
|
-
void request_method(
|
207
|
+
void request_method(puma_parser* hp, const char *at, size_t length)
|
208
208
|
{
|
209
209
|
VALUE val = Qnil;
|
210
210
|
|
@@ -212,7 +212,7 @@ void request_method(http_parser* hp, const char *at, size_t length)
|
|
212
212
|
rb_hash_aset(hp->request, global_request_method, val);
|
213
213
|
}
|
214
214
|
|
215
|
-
void request_uri(
|
215
|
+
void request_uri(puma_parser* hp, const char *at, size_t length)
|
216
216
|
{
|
217
217
|
VALUE val = Qnil;
|
218
218
|
|
@@ -222,7 +222,7 @@ void request_uri(http_parser* hp, const char *at, size_t length)
|
|
222
222
|
rb_hash_aset(hp->request, global_request_uri, val);
|
223
223
|
}
|
224
224
|
|
225
|
-
void fragment(
|
225
|
+
void fragment(puma_parser* hp, const char *at, size_t length)
|
226
226
|
{
|
227
227
|
VALUE val = Qnil;
|
228
228
|
|
@@ -232,7 +232,7 @@ void fragment(http_parser* hp, const char *at, size_t length)
|
|
232
232
|
rb_hash_aset(hp->request, global_fragment, val);
|
233
233
|
}
|
234
234
|
|
235
|
-
void request_path(
|
235
|
+
void request_path(puma_parser* hp, const char *at, size_t length)
|
236
236
|
{
|
237
237
|
VALUE val = Qnil;
|
238
238
|
|
@@ -242,7 +242,7 @@ void request_path(http_parser* hp, const char *at, size_t length)
|
|
242
242
|
rb_hash_aset(hp->request, global_request_path, val);
|
243
243
|
}
|
244
244
|
|
245
|
-
void query_string(
|
245
|
+
void query_string(puma_parser* hp, const char *at, size_t length)
|
246
246
|
{
|
247
247
|
VALUE val = Qnil;
|
248
248
|
|
@@ -252,7 +252,7 @@ void query_string(http_parser* hp, const char *at, size_t length)
|
|
252
252
|
rb_hash_aset(hp->request, global_query_string, val);
|
253
253
|
}
|
254
254
|
|
255
|
-
void http_version(
|
255
|
+
void http_version(puma_parser* hp, const char *at, size_t length)
|
256
256
|
{
|
257
257
|
VALUE val = rb_str_new(at, length);
|
258
258
|
rb_hash_aset(hp->request, global_http_version, val);
|
@@ -261,7 +261,7 @@ void http_version(http_parser* hp, const char *at, size_t length)
|
|
261
261
|
/** Finalizes the request header to have a bunch of stuff that's
|
262
262
|
needed. */
|
263
263
|
|
264
|
-
void header_done(
|
264
|
+
void header_done(puma_parser* hp, const char *at, size_t length)
|
265
265
|
{
|
266
266
|
hp->body = rb_str_new(at, length);
|
267
267
|
}
|
@@ -275,14 +275,14 @@ void HttpParser_free(void *data) {
|
|
275
275
|
}
|
276
276
|
}
|
277
277
|
|
278
|
-
void HttpParser_mark(
|
278
|
+
void HttpParser_mark(puma_parser* hp) {
|
279
279
|
if(hp->request) rb_gc_mark(hp->request);
|
280
280
|
if(hp->body) rb_gc_mark(hp->body);
|
281
281
|
}
|
282
282
|
|
283
283
|
VALUE HttpParser_alloc(VALUE klass)
|
284
284
|
{
|
285
|
-
|
285
|
+
puma_parser *hp = ALLOC_N(puma_parser, 1);
|
286
286
|
TRACE();
|
287
287
|
hp->http_field = http_field;
|
288
288
|
hp->request_method = request_method;
|
@@ -294,7 +294,7 @@ VALUE HttpParser_alloc(VALUE klass)
|
|
294
294
|
hp->header_done = header_done;
|
295
295
|
hp->request = Qnil;
|
296
296
|
|
297
|
-
|
297
|
+
puma_parser_init(hp);
|
298
298
|
|
299
299
|
return Data_Wrap_Struct(klass, HttpParser_mark, HttpParser_free, hp);
|
300
300
|
}
|
@@ -307,9 +307,9 @@ VALUE HttpParser_alloc(VALUE klass)
|
|
307
307
|
*/
|
308
308
|
VALUE HttpParser_init(VALUE self)
|
309
309
|
{
|
310
|
-
|
311
|
-
DATA_GET(self,
|
312
|
-
|
310
|
+
puma_parser *http = NULL;
|
311
|
+
DATA_GET(self, puma_parser, http);
|
312
|
+
puma_parser_init(http);
|
313
313
|
|
314
314
|
return self;
|
315
315
|
}
|
@@ -324,9 +324,9 @@ VALUE HttpParser_init(VALUE self)
|
|
324
324
|
*/
|
325
325
|
VALUE HttpParser_reset(VALUE self)
|
326
326
|
{
|
327
|
-
|
328
|
-
DATA_GET(self,
|
329
|
-
|
327
|
+
puma_parser *http = NULL;
|
328
|
+
DATA_GET(self, puma_parser, http);
|
329
|
+
puma_parser_init(http);
|
330
330
|
|
331
331
|
return Qnil;
|
332
332
|
}
|
@@ -341,11 +341,11 @@ VALUE HttpParser_reset(VALUE self)
|
|
341
341
|
*/
|
342
342
|
VALUE HttpParser_finish(VALUE self)
|
343
343
|
{
|
344
|
-
|
345
|
-
DATA_GET(self,
|
346
|
-
|
344
|
+
puma_parser *http = NULL;
|
345
|
+
DATA_GET(self, puma_parser, http);
|
346
|
+
puma_parser_finish(http);
|
347
347
|
|
348
|
-
return
|
348
|
+
return puma_parser_is_finished(http) ? Qtrue : Qfalse;
|
349
349
|
}
|
350
350
|
|
351
351
|
|
@@ -368,12 +368,12 @@ VALUE HttpParser_finish(VALUE self)
|
|
368
368
|
*/
|
369
369
|
VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
|
370
370
|
{
|
371
|
-
|
371
|
+
puma_parser *http = NULL;
|
372
372
|
int from = 0;
|
373
373
|
char *dptr = NULL;
|
374
374
|
long dlen = 0;
|
375
375
|
|
376
|
-
DATA_GET(self,
|
376
|
+
DATA_GET(self, puma_parser, http);
|
377
377
|
|
378
378
|
from = FIX2INT(start);
|
379
379
|
dptr = rb_extract_chars(data, &dlen);
|
@@ -383,15 +383,15 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
|
|
383
383
|
rb_raise(eHttpParserError, "%s", "Requested start is after data buffer end.");
|
384
384
|
} else {
|
385
385
|
http->request = req_hash;
|
386
|
-
|
386
|
+
puma_parser_execute(http, dptr, dlen, from);
|
387
387
|
|
388
388
|
rb_free_chars(dptr);
|
389
|
-
VALIDATE_MAX_LENGTH(
|
389
|
+
VALIDATE_MAX_LENGTH(puma_parser_nread(http), HEADER);
|
390
390
|
|
391
|
-
if(
|
391
|
+
if(puma_parser_has_error(http)) {
|
392
392
|
rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails.");
|
393
393
|
} else {
|
394
|
-
return INT2FIX(
|
394
|
+
return INT2FIX(puma_parser_nread(http));
|
395
395
|
}
|
396
396
|
}
|
397
397
|
}
|
@@ -406,10 +406,10 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
|
|
406
406
|
*/
|
407
407
|
VALUE HttpParser_has_error(VALUE self)
|
408
408
|
{
|
409
|
-
|
410
|
-
DATA_GET(self,
|
409
|
+
puma_parser *http = NULL;
|
410
|
+
DATA_GET(self, puma_parser, http);
|
411
411
|
|
412
|
-
return
|
412
|
+
return puma_parser_has_error(http) ? Qtrue : Qfalse;
|
413
413
|
}
|
414
414
|
|
415
415
|
|
@@ -421,10 +421,10 @@ VALUE HttpParser_has_error(VALUE self)
|
|
421
421
|
*/
|
422
422
|
VALUE HttpParser_is_finished(VALUE self)
|
423
423
|
{
|
424
|
-
|
425
|
-
DATA_GET(self,
|
424
|
+
puma_parser *http = NULL;
|
425
|
+
DATA_GET(self, puma_parser, http);
|
426
426
|
|
427
|
-
return
|
427
|
+
return puma_parser_is_finished(http) ? Qtrue : Qfalse;
|
428
428
|
}
|
429
429
|
|
430
430
|
|
@@ -437,8 +437,8 @@ VALUE HttpParser_is_finished(VALUE self)
|
|
437
437
|
*/
|
438
438
|
VALUE HttpParser_nread(VALUE self)
|
439
439
|
{
|
440
|
-
|
441
|
-
DATA_GET(self,
|
440
|
+
puma_parser *http = NULL;
|
441
|
+
DATA_GET(self, puma_parser, http);
|
442
442
|
|
443
443
|
return INT2FIX(http->nread);
|
444
444
|
}
|
@@ -450,8 +450,8 @@ VALUE HttpParser_nread(VALUE self)
|
|
450
450
|
* If the request included a body, returns it.
|
451
451
|
*/
|
452
452
|
VALUE HttpParser_body(VALUE self) {
|
453
|
-
|
454
|
-
DATA_GET(self,
|
453
|
+
puma_parser *http = NULL;
|
454
|
+
DATA_GET(self, puma_parser, http);
|
455
455
|
|
456
456
|
return http->body;
|
457
457
|
}
|
data/lib/puma/capistrano.rb
CHANGED
@@ -17,18 +17,49 @@ Capistrano::Configuration.instance.load do
|
|
17
17
|
namespace :puma do
|
18
18
|
desc 'Start puma'
|
19
19
|
task :start, :roles => lambda { fetch(:puma_role) }, :on_no_matching_servers => :continue do
|
20
|
-
|
21
|
-
run "cd #{current_path} && #{fetch(:puma_cmd)} -q -d -e #{puma_env} -b '#{fetch(:puma_socket)}' -S #{fetch(:puma_state)} --control 'unix://#{shared_path}/sockets/pumactl.sock'", :pty => false
|
20
|
+
run "cd #{current_path} && #{fetch(:puma_cmd)} #{start_options}", :pty => false
|
22
21
|
end
|
23
22
|
|
24
23
|
desc 'Stop puma'
|
25
24
|
task :stop, :roles => lambda { fetch(:puma_role) }, :on_no_matching_servers => :continue do
|
26
|
-
run "cd #{current_path} && #{fetch(:pumactl_cmd)} -S #{
|
25
|
+
run "cd #{current_path} && #{fetch(:pumactl_cmd)} -S #{state_path} stop"
|
27
26
|
end
|
28
27
|
|
29
28
|
desc 'Restart puma'
|
30
29
|
task :restart, :roles => lambda { fetch(:puma_role) }, :on_no_matching_servers => :continue do
|
31
|
-
run "cd #{current_path} && #{fetch(:pumactl_cmd)} -S #{
|
30
|
+
run "cd #{current_path} && #{fetch(:pumactl_cmd)} -S #{state_path} restart"
|
32
31
|
end
|
33
32
|
end
|
33
|
+
|
34
|
+
def start_options
|
35
|
+
if config_file
|
36
|
+
"-q -d -e #{puma_env} -C #{config_file}"
|
37
|
+
else
|
38
|
+
"-q -d -e #{puma_env} -b '#{fetch(:puma_socket)}' -S #{state_path} --control 'unix://#{shared_path}/sockets/pumactl.sock'"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def config_file
|
43
|
+
@_config_file ||= begin
|
44
|
+
file = fetch(:puma_config_file, nil)
|
45
|
+
file = "./config/puma/#{puma_env}.rb" if !file && File.exists?("./config/puma/#{puma_env}.rb")
|
46
|
+
file
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def puma_env
|
51
|
+
fetch(:rack_env, fetch(:rails_env, 'production'))
|
52
|
+
end
|
53
|
+
|
54
|
+
def state_path
|
55
|
+
(config_file ? configuration.options[:state] : nil) || fetch(:puma_state)
|
56
|
+
end
|
57
|
+
|
58
|
+
def configuration
|
59
|
+
require 'puma/configuration'
|
60
|
+
|
61
|
+
config = Puma::Configuration.new(:config_file => config_file)
|
62
|
+
config.load
|
63
|
+
config
|
64
|
+
end
|
34
65
|
end
|
data/lib/puma/cli.rb
CHANGED
@@ -148,7 +148,7 @@ module Puma
|
|
148
148
|
$LOAD_PATH.unshift(*arg.split(':'))
|
149
149
|
end
|
150
150
|
|
151
|
-
o.on "-p", "--port PORT", "Define
|
151
|
+
o.on "-p", "--port PORT", "Define the TCP port to bind to",
|
152
152
|
"Use -b for more advanced options" do |arg|
|
153
153
|
@options[:binds] << "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
|
154
154
|
end
|
@@ -178,11 +178,11 @@ module Puma
|
|
178
178
|
o.on '-t', '--threads INT', "min:max threads to use (default 0:16)" do |arg|
|
179
179
|
min, max = arg.split(":")
|
180
180
|
if max
|
181
|
-
@options[:min_threads] = min
|
182
|
-
@options[:max_threads] = max
|
181
|
+
@options[:min_threads] = min
|
182
|
+
@options[:max_threads] = max
|
183
183
|
else
|
184
184
|
@options[:min_threads] = 0
|
185
|
-
@options[:max_threads] = arg
|
185
|
+
@options[:max_threads] = arg
|
186
186
|
end
|
187
187
|
end
|
188
188
|
|
@@ -235,7 +235,13 @@ module Puma
|
|
235
235
|
f.puts Process.pid
|
236
236
|
end
|
237
237
|
|
238
|
-
|
238
|
+
cur = Process.pid
|
239
|
+
|
240
|
+
at_exit do
|
241
|
+
if cur == Process.pid
|
242
|
+
delete_pidfile
|
243
|
+
end
|
244
|
+
end
|
239
245
|
end
|
240
246
|
end
|
241
247
|
|
@@ -443,6 +449,9 @@ module Puma
|
|
443
449
|
when :exit
|
444
450
|
# nothing
|
445
451
|
end
|
452
|
+
|
453
|
+
ensure
|
454
|
+
delete_pidfile
|
446
455
|
end
|
447
456
|
|
448
457
|
def setup_signals
|
@@ -454,6 +463,14 @@ module Puma
|
|
454
463
|
log "*** SIGUSR2 not implemented, signal based restart unavailable!"
|
455
464
|
end
|
456
465
|
|
466
|
+
begin
|
467
|
+
Signal.trap "SIGUSR1" do
|
468
|
+
phased_restart
|
469
|
+
end
|
470
|
+
rescue Exception
|
471
|
+
log "*** SIGUSR1 not implemented, signal based restart unavailable!"
|
472
|
+
end
|
473
|
+
|
457
474
|
begin
|
458
475
|
Signal.trap "SIGTERM" do
|
459
476
|
stop
|
@@ -482,8 +499,8 @@ module Puma
|
|
482
499
|
end
|
483
500
|
|
484
501
|
def phased_restart
|
485
|
-
return
|
486
|
-
@runner.phased_restart
|
502
|
+
return restart unless @runner.respond_to? :phased_restart
|
503
|
+
return restart unless @runner.phased_restart
|
487
504
|
end
|
488
505
|
|
489
506
|
def stats
|
data/lib/puma/cluster.rb
CHANGED
@@ -233,16 +233,6 @@ module Puma
|
|
233
233
|
end
|
234
234
|
end
|
235
235
|
|
236
|
-
if preload?
|
237
|
-
Signal.trap "SIGUSR1" do
|
238
|
-
log "App preloaded, phased restart unavailable"
|
239
|
-
end
|
240
|
-
else
|
241
|
-
Signal.trap "SIGUSR1" do
|
242
|
-
phased_restart
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
236
|
# Used by the workers to detect if the master process dies.
|
247
237
|
# If select says that @check_pipe is ready, it's because the
|
248
238
|
# master has exited and @suicide_pipe has been automatically
|
data/lib/puma/configuration.rb
CHANGED
@@ -19,6 +19,7 @@ module Puma
|
|
19
19
|
@options = options
|
20
20
|
@options[:binds] ||= []
|
21
21
|
@options[:on_restart] ||= []
|
22
|
+
@options[:worker_boot] ||= []
|
22
23
|
end
|
23
24
|
|
24
25
|
attr_reader :options
|
@@ -187,12 +188,26 @@ module Puma
|
|
187
188
|
@options[:binds] << url
|
188
189
|
end
|
189
190
|
|
191
|
+
# Define the TCP port to bind to. Use +bind+ for more advanced options.
|
192
|
+
#
|
193
|
+
def port(port)
|
194
|
+
@options[:binds] << "tcp://#{Configuration::DefaultTCPHost}:#{port}"
|
195
|
+
end
|
196
|
+
|
190
197
|
# Daemonize the server into the background. Highly suggest that
|
191
198
|
# this be combined with +pidfile+ and +stdout_redirect+.
|
192
199
|
def daemonize(which=true)
|
193
200
|
@options[:daemon] = which
|
194
201
|
end
|
195
202
|
|
203
|
+
# When shutting down, drain the accept socket of pending
|
204
|
+
# connections and proces them. This loops over the accept
|
205
|
+
# socket until there are no more read events and then stops
|
206
|
+
# looking and waits for the requests to finish.
|
207
|
+
def drain_on_shutdown(which=true)
|
208
|
+
@options[:drain_on_shutdown] = which
|
209
|
+
end
|
210
|
+
|
196
211
|
# Set the environment in which the Rack's app will run.
|
197
212
|
def environment(environment)
|
198
213
|
@options[:environment] = environment
|
data/lib/puma/const.rb
CHANGED
@@ -28,8 +28,8 @@ module Puma
|
|
28
28
|
# too taxing on performance.
|
29
29
|
module Const
|
30
30
|
|
31
|
-
PUMA_VERSION = VERSION = "2.
|
32
|
-
CODE_NAME = "
|
31
|
+
PUMA_VERSION = VERSION = "2.4.0".freeze
|
32
|
+
CODE_NAME = "Crunchy Munchy Lunchy"
|
33
33
|
|
34
34
|
FAST_TRACK_KA_TIMEOUT = 0.2
|
35
35
|
|
data/lib/puma/events.rb
CHANGED
@@ -20,6 +20,8 @@ module Puma
|
|
20
20
|
@stdout.sync = true
|
21
21
|
@stderr.sync = true
|
22
22
|
|
23
|
+
@debug = ENV.key? 'PUMA_DEBUG'
|
24
|
+
|
23
25
|
@on_booted = []
|
24
26
|
end
|
25
27
|
|
@@ -35,6 +37,10 @@ module Puma
|
|
35
37
|
@stdout.write str
|
36
38
|
end
|
37
39
|
|
40
|
+
def debug(str)
|
41
|
+
log("% #{str}") if @debug
|
42
|
+
end
|
43
|
+
|
38
44
|
# Write +str+ to +@stderr+
|
39
45
|
#
|
40
46
|
def error(str)
|
data/lib/puma/jruby_restart.rb
CHANGED
@@ -44,6 +44,10 @@ module Puma
|
|
44
44
|
def self.daemon_start(dir, argv)
|
45
45
|
ENV['PUMA_DAEMON_RESTART'] = Process.pid.to_s
|
46
46
|
|
47
|
+
if k = ENV['PUMA_JRUBY_DAEMON_OPTS']
|
48
|
+
ENV['JRUBY_OPTS'] = k
|
49
|
+
end
|
50
|
+
|
47
51
|
cmd = argv.first
|
48
52
|
argv = ([:string] * argv.size).zip(argv).flatten
|
49
53
|
argv << :string
|
data/lib/puma/rack_patch.rb
CHANGED
@@ -13,7 +13,10 @@ module Rack
|
|
13
13
|
status, header, body = @app.call(env)
|
14
14
|
header = Utils::HeaderHash.new(header)
|
15
15
|
|
16
|
-
|
16
|
+
# If we've been hijacked, then output a special line
|
17
|
+
if env['rack.hijack_io']
|
18
|
+
log_hijacking(env, 'HIJACK', header, began_at)
|
19
|
+
elsif ary = env['rack.after_reply']
|
17
20
|
ary << lambda { log(env, status, header, began_at) }
|
18
21
|
else
|
19
22
|
body = BodyProxy.new(body) { log(env, status, header, began_at) }
|
@@ -21,5 +24,22 @@ module Rack
|
|
21
24
|
|
22
25
|
[status, header, body]
|
23
26
|
end
|
27
|
+
|
28
|
+
HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
|
29
|
+
|
30
|
+
def log_hijacking(env, status, header, began_at)
|
31
|
+
now = Time.now
|
32
|
+
|
33
|
+
logger = @logger || env['rack.errors']
|
34
|
+
logger.write HIJACK_FORMAT % [
|
35
|
+
env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
|
36
|
+
env["REMOTE_USER"] || "-",
|
37
|
+
now.strftime("%d/%b/%Y %H:%M:%S"),
|
38
|
+
env["REQUEST_METHOD"],
|
39
|
+
env["PATH_INFO"],
|
40
|
+
env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
|
41
|
+
env["HTTP_VERSION"],
|
42
|
+
now - began_at ]
|
43
|
+
end
|
24
44
|
end
|
25
45
|
end
|
data/lib/puma/runner.rb
CHANGED
@@ -115,7 +115,7 @@ module Puma
|
|
115
115
|
min_t = @options[:min_threads]
|
116
116
|
max_t = @options[:max_threads]
|
117
117
|
|
118
|
-
server = Puma::Server.new app, @cli.events
|
118
|
+
server = Puma::Server.new app, @cli.events, @options
|
119
119
|
server.min_threads = min_t
|
120
120
|
server.max_threads = max_t
|
121
121
|
server.inherit_binder @cli.binder
|
data/lib/puma/server.rb
CHANGED
@@ -13,6 +13,8 @@ require 'puma/delegation'
|
|
13
13
|
require 'puma/accept_nonblock'
|
14
14
|
require 'puma/util'
|
15
15
|
|
16
|
+
require 'puma/rack_patch'
|
17
|
+
|
16
18
|
require 'puma/puma_http11'
|
17
19
|
|
18
20
|
unless Puma.const_defined? "IOBuffer"
|
@@ -46,7 +48,7 @@ module Puma
|
|
46
48
|
# Server#run returns a thread that you can join on to wait for the server
|
47
49
|
# to do it's work.
|
48
50
|
#
|
49
|
-
def initialize(app, events=Events.stdio)
|
51
|
+
def initialize(app, events=Events.stdio, options={})
|
50
52
|
@app = app
|
51
53
|
@events = events
|
52
54
|
|
@@ -70,6 +72,8 @@ module Puma
|
|
70
72
|
|
71
73
|
@leak_stack_on_error = true
|
72
74
|
|
75
|
+
@options = options
|
76
|
+
|
73
77
|
ENV['RACK_ENV'] ||= "development"
|
74
78
|
end
|
75
79
|
|
@@ -327,7 +331,13 @@ module Puma
|
|
327
331
|
# server; that client may be a proxy, gateway, or other
|
328
332
|
# intermediary acting on behalf of the actual source client."
|
329
333
|
#
|
330
|
-
|
334
|
+
|
335
|
+
addr = client.peeraddr.last
|
336
|
+
|
337
|
+
# Set unix socket addrs to localhost
|
338
|
+
addr = "127.0.0.1" if addr.empty?
|
339
|
+
|
340
|
+
env[REMOTE_ADDR] = addr
|
331
341
|
end
|
332
342
|
|
333
343
|
def default_server_port(env)
|
@@ -594,6 +604,28 @@ module Puma
|
|
594
604
|
# Wait for all outstanding requests to finish.
|
595
605
|
#
|
596
606
|
def graceful_shutdown
|
607
|
+
if @options[:drain_on_shutdown]
|
608
|
+
count = 0
|
609
|
+
|
610
|
+
while true
|
611
|
+
ios = IO.select @binder.ios, nil, nil, 0
|
612
|
+
break unless ios
|
613
|
+
|
614
|
+
ios.first.each do |sock|
|
615
|
+
begin
|
616
|
+
if io = sock.accept_nonblock
|
617
|
+
count += 1
|
618
|
+
c = Client.new io, @binder.env(sock)
|
619
|
+
@thread_pool << c
|
620
|
+
end
|
621
|
+
rescue SystemCallError
|
622
|
+
end
|
623
|
+
end
|
624
|
+
end
|
625
|
+
|
626
|
+
@events.debug "Drained #{count} additional connections."
|
627
|
+
end
|
628
|
+
|
597
629
|
@thread_pool.shutdown if @thread_pool
|
598
630
|
end
|
599
631
|
|
data/lib/puma/single.rb
CHANGED
data/lib/puma/thread_pool.rb
CHANGED
@@ -20,8 +20,8 @@ module Puma
|
|
20
20
|
@spawned = 0
|
21
21
|
@waiting = 0
|
22
22
|
|
23
|
-
@min = min
|
24
|
-
@max = max
|
23
|
+
@min = Integer(min)
|
24
|
+
@max = Integer(max)
|
25
25
|
@block = block
|
26
26
|
@extra = extra
|
27
27
|
|
@@ -34,7 +34,7 @@ module Puma
|
|
34
34
|
@auto_trim = nil
|
35
35
|
|
36
36
|
@mutex.synchronize do
|
37
|
-
min.times { spawn_thread }
|
37
|
+
@min.times { spawn_thread }
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
data/lib/rack/handler/puma.rb
CHANGED
@@ -31,8 +31,8 @@ module Rack
|
|
31
31
|
puts "* Listening on tcp://#{options[:Host]}:#{options[:Port]}"
|
32
32
|
|
33
33
|
server.add_tcp_listener options[:Host], options[:Port]
|
34
|
-
server.min_threads =
|
35
|
-
server.max_threads =
|
34
|
+
server.min_threads = min
|
35
|
+
server.max_threads = max
|
36
36
|
yield server if block_given?
|
37
37
|
|
38
38
|
begin
|
data/puma.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "puma"
|
5
|
-
s.version = "2.
|
5
|
+
s.version = "2.4.0"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Evan Phoenix"]
|
9
|
-
s.date = "2013-07-
|
9
|
+
s.date = "2013-07-22"
|
10
10
|
s.description = "Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications. Puma is intended for use in both development and production environments. In order to get the best throughput, it is highly recommended that you use a Ruby implementation with real threads like Rubinius or JRuby."
|
11
11
|
s.email = ["evan@phx.io"]
|
12
12
|
s.executables = ["puma", "pumactl"]
|
data/test/test_thread_pool.rb
CHANGED
@@ -32,6 +32,16 @@ class TestThreadPool < Test::Unit::TestCase
|
|
32
32
|
assert_equal 1, pool.spawned
|
33
33
|
end
|
34
34
|
|
35
|
+
def test_converts_pool_sizes
|
36
|
+
pool = new_pool('0', '1')
|
37
|
+
|
38
|
+
assert_equal 0, pool.spawned
|
39
|
+
|
40
|
+
pool << 1
|
41
|
+
|
42
|
+
assert_equal 1, pool.spawned
|
43
|
+
end
|
44
|
+
|
35
45
|
def test_append_queues_on_max
|
36
46
|
finish = false
|
37
47
|
pool = new_pool(0, 1) { Thread.pass until finish }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puma
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-07-
|
12
|
+
date: 2013-07-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|