puma 4.3.5 → 6.0.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 +1639 -519
- data/LICENSE +23 -20
- data/README.md +130 -42
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +63 -26
- data/docs/compile_options.md +55 -0
- data/docs/deployment.md +60 -69
- data/docs/fork_worker.md +31 -0
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/jungle/README.md +9 -0
- data/{tools → docs}/jungle/rc.d/README.md +1 -1
- data/{tools → docs}/jungle/rc.d/puma +2 -2
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +66 -0
- data/docs/nginx.md +2 -2
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +46 -23
- data/docs/signals.md +13 -11
- data/docs/stats.md +142 -0
- data/docs/systemd.md +85 -128
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +56 -11
- data/ext/puma_http11/http11_parser.c +69 -58
- data/ext/puma_http11/http11_parser.h +2 -2
- data/ext/puma_http11/http11_parser.java.rl +3 -3
- data/ext/puma_http11/http11_parser.rl +3 -3
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/ext/puma_http11/mini_ssl.c +322 -130
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +6 -6
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +52 -52
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +241 -96
- data/ext/puma_http11/puma_http11.c +47 -57
- data/lib/puma/app/status.rb +53 -37
- data/lib/puma/binder.rb +232 -119
- data/lib/puma/cli.rb +33 -33
- data/lib/puma/client.rb +197 -101
- data/lib/puma/cluster/worker.rb +175 -0
- data/lib/puma/cluster/worker_handle.rb +97 -0
- data/lib/puma/cluster.rb +224 -229
- data/lib/puma/commonlogger.rb +2 -2
- data/lib/puma/configuration.rb +112 -87
- data/lib/puma/const.rb +30 -25
- data/lib/puma/control_cli.rb +99 -79
- data/lib/puma/detect.rb +31 -2
- data/lib/puma/dsl.rb +426 -110
- data/lib/puma/error_logger.rb +112 -0
- data/lib/puma/events.rb +16 -115
- data/lib/puma/io_buffer.rb +44 -2
- data/lib/puma/jruby_restart.rb +2 -59
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +170 -148
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +35 -19
- data/lib/puma/minissl.rb +213 -55
- data/lib/puma/null_io.rb +18 -1
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +3 -12
- data/lib/puma/rack/builder.rb +5 -9
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +1 -1
- data/lib/puma/reactor.rb +85 -369
- data/lib/puma/request.rb +644 -0
- data/lib/puma/runner.rb +83 -77
- data/lib/puma/server.rb +303 -773
- data/lib/puma/single.rb +18 -74
- data/lib/puma/state_file.rb +45 -8
- data/lib/puma/systemd.rb +47 -0
- data/lib/puma/thread_pool.rb +136 -68
- data/lib/puma/util.rb +21 -4
- data/lib/puma.rb +54 -5
- data/lib/rack/handler/puma.rb +11 -12
- data/tools/{docker/Dockerfile → Dockerfile} +1 -1
- data/tools/trickletest.rb +0 -0
- metadata +36 -28
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/accept_nonblock.rb +0 -29
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
- data/tools/jungle/upstart/README.md +0 -61
- data/tools/jungle/upstart/puma-manager.conf +0 -31
- data/tools/jungle/upstart/puma.conf +0 -69
@@ -10,6 +10,7 @@
|
|
10
10
|
#include "ext_help.h"
|
11
11
|
#include <assert.h>
|
12
12
|
#include <string.h>
|
13
|
+
#include <ctype.h>
|
13
14
|
#include "http11_parser.h"
|
14
15
|
|
15
16
|
#ifndef MANAGED_STRINGS
|
@@ -35,11 +36,13 @@ static VALUE global_request_method;
|
|
35
36
|
static VALUE global_request_uri;
|
36
37
|
static VALUE global_fragment;
|
37
38
|
static VALUE global_query_string;
|
38
|
-
static VALUE
|
39
|
+
static VALUE global_server_protocol;
|
39
40
|
static VALUE global_request_path;
|
40
41
|
|
41
42
|
/** Defines common length and error messages for input length validation. */
|
42
|
-
#define
|
43
|
+
#define QUOTE(s) #s
|
44
|
+
#define EXPAND_MAX_LENGTH_VALUE(s) QUOTE(s)
|
45
|
+
#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 " EXPAND_MAX_LENGTH_VALUE(length) " allowed length (was %d)"
|
43
46
|
|
44
47
|
/** Validates the max length of given input and throws an HttpParserError exception if over. */
|
45
48
|
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR, len); }
|
@@ -49,12 +52,24 @@ static VALUE global_request_path;
|
|
49
52
|
|
50
53
|
|
51
54
|
/* Defines the maximum allowed lengths for various input elements.*/
|
55
|
+
#ifndef PUMA_REQUEST_URI_MAX_LENGTH
|
56
|
+
#define PUMA_REQUEST_URI_MAX_LENGTH (1024 * 12)
|
57
|
+
#endif
|
58
|
+
|
59
|
+
#ifndef PUMA_REQUEST_PATH_MAX_LENGTH
|
60
|
+
#define PUMA_REQUEST_PATH_MAX_LENGTH (8192)
|
61
|
+
#endif
|
62
|
+
|
63
|
+
#ifndef PUMA_QUERY_STRING_MAX_LENGTH
|
64
|
+
#define PUMA_QUERY_STRING_MAX_LENGTH (1024 * 10)
|
65
|
+
#endif
|
66
|
+
|
52
67
|
DEF_MAX_LENGTH(FIELD_NAME, 256);
|
53
68
|
DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
|
54
|
-
DEF_MAX_LENGTH(REQUEST_URI,
|
69
|
+
DEF_MAX_LENGTH(REQUEST_URI, PUMA_REQUEST_URI_MAX_LENGTH);
|
55
70
|
DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
|
56
|
-
DEF_MAX_LENGTH(REQUEST_PATH,
|
57
|
-
DEF_MAX_LENGTH(QUERY_STRING,
|
71
|
+
DEF_MAX_LENGTH(REQUEST_PATH, PUMA_REQUEST_PATH_MAX_LENGTH);
|
72
|
+
DEF_MAX_LENGTH(QUERY_STRING, PUMA_QUERY_STRING_MAX_LENGTH);
|
58
73
|
DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
|
59
74
|
|
60
75
|
struct common_field {
|
@@ -111,21 +126,6 @@ static struct common_field common_http_fields[] = {
|
|
111
126
|
# undef f
|
112
127
|
};
|
113
128
|
|
114
|
-
/*
|
115
|
-
* qsort(3) and bsearch(3) improve average performance slightly, but may
|
116
|
-
* not be worth it for lack of portability to certain platforms...
|
117
|
-
*/
|
118
|
-
#if defined(HAVE_QSORT_BSEARCH)
|
119
|
-
/* sort by length, then by name if there's a tie */
|
120
|
-
static int common_field_cmp(const void *a, const void *b)
|
121
|
-
{
|
122
|
-
struct common_field *cfa = (struct common_field *)a;
|
123
|
-
struct common_field *cfb = (struct common_field *)b;
|
124
|
-
signed long diff = cfa->len - cfb->len;
|
125
|
-
return diff ? diff : memcmp(cfa->name, cfb->name, cfa->len);
|
126
|
-
}
|
127
|
-
#endif /* HAVE_QSORT_BSEARCH */
|
128
|
-
|
129
129
|
static void init_common_fields(void)
|
130
130
|
{
|
131
131
|
unsigned i;
|
@@ -142,28 +142,10 @@ static void init_common_fields(void)
|
|
142
142
|
}
|
143
143
|
rb_global_variable(&cf->value);
|
144
144
|
}
|
145
|
-
|
146
|
-
#if defined(HAVE_QSORT_BSEARCH)
|
147
|
-
qsort(common_http_fields,
|
148
|
-
ARRAY_SIZE(common_http_fields),
|
149
|
-
sizeof(struct common_field),
|
150
|
-
common_field_cmp);
|
151
|
-
#endif /* HAVE_QSORT_BSEARCH */
|
152
145
|
}
|
153
146
|
|
154
147
|
static VALUE find_common_field_value(const char *field, size_t flen)
|
155
148
|
{
|
156
|
-
#if defined(HAVE_QSORT_BSEARCH)
|
157
|
-
struct common_field key;
|
158
|
-
struct common_field *found;
|
159
|
-
key.name = field;
|
160
|
-
key.len = (signed long)flen;
|
161
|
-
found = (struct common_field *)bsearch(&key, common_http_fields,
|
162
|
-
ARRAY_SIZE(common_http_fields),
|
163
|
-
sizeof(struct common_field),
|
164
|
-
common_field_cmp);
|
165
|
-
return found ? found->value : Qnil;
|
166
|
-
#else /* !HAVE_QSORT_BSEARCH */
|
167
149
|
unsigned i;
|
168
150
|
struct common_field *cf = common_http_fields;
|
169
151
|
for(i = 0; i < ARRAY_SIZE(common_http_fields); i++, cf++) {
|
@@ -171,7 +153,6 @@ static VALUE find_common_field_value(const char *field, size_t flen)
|
|
171
153
|
return cf->value;
|
172
154
|
}
|
173
155
|
return Qnil;
|
174
|
-
#endif /* !HAVE_QSORT_BSEARCH */
|
175
156
|
}
|
176
157
|
|
177
158
|
void http_field(puma_parser* hp, const char *field, size_t flen,
|
@@ -263,10 +244,10 @@ void query_string(puma_parser* hp, const char *at, size_t length)
|
|
263
244
|
rb_hash_aset(hp->request, global_query_string, val);
|
264
245
|
}
|
265
246
|
|
266
|
-
void
|
247
|
+
void server_protocol(puma_parser* hp, const char *at, size_t length)
|
267
248
|
{
|
268
249
|
VALUE val = rb_str_new(at, length);
|
269
|
-
rb_hash_aset(hp->request,
|
250
|
+
rb_hash_aset(hp->request, global_server_protocol, val);
|
270
251
|
}
|
271
252
|
|
272
253
|
/** Finalizes the request header to have a bunch of stuff that's
|
@@ -286,11 +267,18 @@ void HttpParser_free(void *data) {
|
|
286
267
|
}
|
287
268
|
}
|
288
269
|
|
289
|
-
void HttpParser_mark(
|
270
|
+
void HttpParser_mark(void *ptr) {
|
271
|
+
puma_parser *hp = ptr;
|
290
272
|
if(hp->request) rb_gc_mark(hp->request);
|
291
273
|
if(hp->body) rb_gc_mark(hp->body);
|
292
274
|
}
|
293
275
|
|
276
|
+
const rb_data_type_t HttpParser_data_type = {
|
277
|
+
"HttpParser",
|
278
|
+
{ HttpParser_mark, HttpParser_free, 0 },
|
279
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
|
280
|
+
};
|
281
|
+
|
294
282
|
VALUE HttpParser_alloc(VALUE klass)
|
295
283
|
{
|
296
284
|
puma_parser *hp = ALLOC_N(puma_parser, 1);
|
@@ -301,13 +289,13 @@ VALUE HttpParser_alloc(VALUE klass)
|
|
301
289
|
hp->fragment = fragment;
|
302
290
|
hp->request_path = request_path;
|
303
291
|
hp->query_string = query_string;
|
304
|
-
hp->
|
292
|
+
hp->server_protocol = server_protocol;
|
305
293
|
hp->header_done = header_done;
|
306
294
|
hp->request = Qnil;
|
307
295
|
|
308
296
|
puma_parser_init(hp);
|
309
297
|
|
310
|
-
return
|
298
|
+
return TypedData_Wrap_Struct(klass, &HttpParser_data_type, hp);
|
311
299
|
}
|
312
300
|
|
313
301
|
/**
|
@@ -319,7 +307,7 @@ VALUE HttpParser_alloc(VALUE klass)
|
|
319
307
|
VALUE HttpParser_init(VALUE self)
|
320
308
|
{
|
321
309
|
puma_parser *http = NULL;
|
322
|
-
DATA_GET(self, puma_parser, http);
|
310
|
+
DATA_GET(self, puma_parser, &HttpParser_data_type, http);
|
323
311
|
puma_parser_init(http);
|
324
312
|
|
325
313
|
return self;
|
@@ -336,7 +324,7 @@ VALUE HttpParser_init(VALUE self)
|
|
336
324
|
VALUE HttpParser_reset(VALUE self)
|
337
325
|
{
|
338
326
|
puma_parser *http = NULL;
|
339
|
-
DATA_GET(self, puma_parser, http);
|
327
|
+
DATA_GET(self, puma_parser, &HttpParser_data_type, http);
|
340
328
|
puma_parser_init(http);
|
341
329
|
|
342
330
|
return Qnil;
|
@@ -353,7 +341,7 @@ VALUE HttpParser_reset(VALUE self)
|
|
353
341
|
VALUE HttpParser_finish(VALUE self)
|
354
342
|
{
|
355
343
|
puma_parser *http = NULL;
|
356
|
-
DATA_GET(self, puma_parser, http);
|
344
|
+
DATA_GET(self, puma_parser, &HttpParser_data_type, http);
|
357
345
|
puma_parser_finish(http);
|
358
346
|
|
359
347
|
return puma_parser_is_finished(http) ? Qtrue : Qfalse;
|
@@ -384,7 +372,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
|
|
384
372
|
char *dptr = NULL;
|
385
373
|
long dlen = 0;
|
386
374
|
|
387
|
-
DATA_GET(self, puma_parser, http);
|
375
|
+
DATA_GET(self, puma_parser, &HttpParser_data_type, http);
|
388
376
|
|
389
377
|
from = FIX2INT(start);
|
390
378
|
dptr = rb_extract_chars(data, &dlen);
|
@@ -400,7 +388,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
|
|
400
388
|
VALIDATE_MAX_LENGTH(puma_parser_nread(http), HEADER);
|
401
389
|
|
402
390
|
if(puma_parser_has_error(http)) {
|
403
|
-
rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails.");
|
391
|
+
rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails. Are you trying to open an SSL connection to a non-SSL Puma?");
|
404
392
|
} else {
|
405
393
|
return INT2FIX(puma_parser_nread(http));
|
406
394
|
}
|
@@ -418,7 +406,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
|
|
418
406
|
VALUE HttpParser_has_error(VALUE self)
|
419
407
|
{
|
420
408
|
puma_parser *http = NULL;
|
421
|
-
DATA_GET(self, puma_parser, http);
|
409
|
+
DATA_GET(self, puma_parser, &HttpParser_data_type, http);
|
422
410
|
|
423
411
|
return puma_parser_has_error(http) ? Qtrue : Qfalse;
|
424
412
|
}
|
@@ -433,7 +421,7 @@ VALUE HttpParser_has_error(VALUE self)
|
|
433
421
|
VALUE HttpParser_is_finished(VALUE self)
|
434
422
|
{
|
435
423
|
puma_parser *http = NULL;
|
436
|
-
DATA_GET(self, puma_parser, http);
|
424
|
+
DATA_GET(self, puma_parser, &HttpParser_data_type, http);
|
437
425
|
|
438
426
|
return puma_parser_is_finished(http) ? Qtrue : Qfalse;
|
439
427
|
}
|
@@ -449,7 +437,7 @@ VALUE HttpParser_is_finished(VALUE self)
|
|
449
437
|
VALUE HttpParser_nread(VALUE self)
|
450
438
|
{
|
451
439
|
puma_parser *http = NULL;
|
452
|
-
DATA_GET(self, puma_parser, http);
|
440
|
+
DATA_GET(self, puma_parser, &HttpParser_data_type, http);
|
453
441
|
|
454
442
|
return INT2FIX(http->nread);
|
455
443
|
}
|
@@ -462,15 +450,16 @@ VALUE HttpParser_nread(VALUE self)
|
|
462
450
|
*/
|
463
451
|
VALUE HttpParser_body(VALUE self) {
|
464
452
|
puma_parser *http = NULL;
|
465
|
-
DATA_GET(self, puma_parser, http);
|
453
|
+
DATA_GET(self, puma_parser, &HttpParser_data_type, http);
|
466
454
|
|
467
455
|
return http->body;
|
468
456
|
}
|
469
457
|
|
470
|
-
|
458
|
+
#ifdef HAVE_OPENSSL_BIO_H
|
471
459
|
void Init_mini_ssl(VALUE mod);
|
460
|
+
#endif
|
472
461
|
|
473
|
-
void Init_puma_http11()
|
462
|
+
void Init_puma_http11(void)
|
474
463
|
{
|
475
464
|
|
476
465
|
VALUE mPuma = rb_define_module("Puma");
|
@@ -480,7 +469,7 @@ void Init_puma_http11()
|
|
480
469
|
DEF_GLOBAL(request_uri, "REQUEST_URI");
|
481
470
|
DEF_GLOBAL(fragment, "FRAGMENT");
|
482
471
|
DEF_GLOBAL(query_string, "QUERY_STRING");
|
483
|
-
DEF_GLOBAL(
|
472
|
+
DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
|
484
473
|
DEF_GLOBAL(request_path, "REQUEST_PATH");
|
485
474
|
|
486
475
|
eHttpParserError = rb_define_class_under(mPuma, "HttpParserError", rb_eIOError);
|
@@ -497,6 +486,7 @@ void Init_puma_http11()
|
|
497
486
|
rb_define_method(cHttpParser, "body", HttpParser_body, 0);
|
498
487
|
init_common_fields();
|
499
488
|
|
500
|
-
|
489
|
+
#ifdef HAVE_OPENSSL_BIO_H
|
501
490
|
Init_mini_ssl(mPuma);
|
491
|
+
#endif
|
502
492
|
}
|
data/lib/puma/app/status.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'json'
|
2
|
+
require_relative '../json_serialization'
|
4
3
|
|
5
4
|
module Puma
|
6
5
|
module App
|
@@ -9,54 +8,71 @@ module Puma
|
|
9
8
|
class Status
|
10
9
|
OK_STATUS = '{ "status": "ok" }'.freeze
|
11
10
|
|
12
|
-
|
13
|
-
|
11
|
+
# @param launcher [::Puma::Launcher]
|
12
|
+
# @param token [String, nil] the token used for authentication
|
13
|
+
#
|
14
|
+
def initialize(launcher, token = nil)
|
15
|
+
@launcher = launcher
|
14
16
|
@auth_token = token
|
15
17
|
end
|
16
18
|
|
19
|
+
# most commands call methods in `::Puma::Launcher` based on command in
|
20
|
+
# `env['PATH_INFO']`
|
17
21
|
def call(env)
|
18
22
|
unless authenticate(env)
|
19
23
|
return rack_response(403, 'Invalid auth token', 'text/plain')
|
20
24
|
end
|
21
25
|
|
22
|
-
case
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
# resp_type is processed by following case statement, return
|
27
|
+
# is a number (status) or a string used as the body of a 200 response
|
28
|
+
resp_type =
|
29
|
+
case env['PATH_INFO'][/\/([^\/]+)$/, 1]
|
30
|
+
when 'stop'
|
31
|
+
@launcher.stop ; 200
|
26
32
|
|
27
|
-
|
28
|
-
|
29
|
-
rack_response(200, OK_STATUS)
|
33
|
+
when 'halt'
|
34
|
+
@launcher.halt ; 200
|
30
35
|
|
31
|
-
|
32
|
-
|
33
|
-
rack_response(200, OK_STATUS)
|
36
|
+
when 'restart'
|
37
|
+
@launcher.restart ; 200
|
34
38
|
|
35
|
-
|
36
|
-
|
37
|
-
rack_response(404, '{ "error": "phased restart not available" }')
|
38
|
-
else
|
39
|
-
rack_response(200, OK_STATUS)
|
40
|
-
end
|
39
|
+
when 'phased-restart'
|
40
|
+
@launcher.phased_restart ? 200 : 404
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
42
|
+
when 'refork'
|
43
|
+
@launcher.refork ? 200 : 404
|
44
|
+
|
45
|
+
when 'reload-worker-directory'
|
46
|
+
@launcher.send(:reload_worker_directory) ? 200 : 404
|
48
47
|
|
49
|
-
|
50
|
-
|
51
|
-
rack_response(200, OK_STATUS)
|
48
|
+
when 'gc'
|
49
|
+
GC.start ; 200
|
52
50
|
|
53
|
-
|
54
|
-
|
51
|
+
when 'gc-stats'
|
52
|
+
Puma::JSONSerialization.generate GC.stat
|
53
|
+
|
54
|
+
when 'stats'
|
55
|
+
Puma::JSONSerialization.generate @launcher.stats
|
56
|
+
|
57
|
+
when 'thread-backtraces'
|
58
|
+
backtraces = []
|
59
|
+
@launcher.thread_status do |name, backtrace|
|
60
|
+
backtraces << { name: name, backtrace: backtrace }
|
61
|
+
end
|
62
|
+
Puma::JSONSerialization.generate backtraces
|
63
|
+
|
64
|
+
else
|
65
|
+
return rack_response(404, "Unsupported action", 'text/plain')
|
66
|
+
end
|
55
67
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
68
|
+
case resp_type
|
69
|
+
when String
|
70
|
+
rack_response 200, resp_type
|
71
|
+
when 200
|
72
|
+
rack_response 200, OK_STATUS
|
73
|
+
when 404
|
74
|
+
str = env['PATH_INFO'][/\/(\S+)/, 1].tr '-', '_'
|
75
|
+
rack_response 404, "{ \"error\": \"#{str} not available\" }"
|
60
76
|
end
|
61
77
|
end
|
62
78
|
|
@@ -69,8 +85,8 @@ module Puma
|
|
69
85
|
|
70
86
|
def rack_response(status, body, content_type='application/json')
|
71
87
|
headers = {
|
72
|
-
'
|
73
|
-
'
|
88
|
+
'content-type' => content_type,
|
89
|
+
'content-length' => body.bytesize.to_s
|
74
90
|
}
|
75
91
|
|
76
92
|
[status, headers, [body]]
|