puma 3.12.6 → 6.3.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.

Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1806 -451
  3. data/LICENSE +23 -20
  4. data/README.md +217 -65
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +59 -21
  7. data/docs/compile_options.md +55 -0
  8. data/docs/deployment.md +69 -58
  9. data/docs/fork_worker.md +31 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +9 -0
  14. data/{tools → docs}/jungle/rc.d/README.md +1 -1
  15. data/{tools → docs}/jungle/rc.d/puma +2 -2
  16. data/docs/kubernetes.md +66 -0
  17. data/docs/nginx.md +2 -2
  18. data/docs/plugins.md +22 -12
  19. data/docs/rails_dev_mode.md +28 -0
  20. data/docs/restart.md +47 -22
  21. data/docs/signals.md +13 -11
  22. data/docs/stats.md +142 -0
  23. data/docs/systemd.md +94 -120
  24. data/docs/testing_benchmarks_local_files.md +150 -0
  25. data/docs/testing_test_rackup_ci_files.md +36 -0
  26. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  27. data/ext/puma_http11/ext_help.h +1 -1
  28. data/ext/puma_http11/extconf.rb +61 -3
  29. data/ext/puma_http11/http11_parser.c +103 -117
  30. data/ext/puma_http11/http11_parser.h +2 -2
  31. data/ext/puma_http11/http11_parser.java.rl +22 -38
  32. data/ext/puma_http11/http11_parser.rl +3 -3
  33. data/ext/puma_http11/http11_parser_common.rl +6 -6
  34. data/ext/puma_http11/mini_ssl.c +389 -99
  35. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  36. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
  37. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +84 -99
  38. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +248 -92
  39. data/ext/puma_http11/puma_http11.c +49 -57
  40. data/lib/puma/app/status.rb +71 -49
  41. data/lib/puma/binder.rb +244 -150
  42. data/lib/puma/cli.rb +38 -34
  43. data/lib/puma/client.rb +388 -244
  44. data/lib/puma/cluster/worker.rb +180 -0
  45. data/lib/puma/cluster/worker_handle.rb +97 -0
  46. data/lib/puma/cluster.rb +261 -243
  47. data/lib/puma/commonlogger.rb +21 -14
  48. data/lib/puma/configuration.rb +116 -88
  49. data/lib/puma/const.rb +154 -104
  50. data/lib/puma/control_cli.rb +115 -70
  51. data/lib/puma/detect.rb +33 -2
  52. data/lib/puma/dsl.rb +764 -134
  53. data/lib/puma/error_logger.rb +113 -0
  54. data/lib/puma/events.rb +16 -112
  55. data/lib/puma/io_buffer.rb +42 -5
  56. data/lib/puma/jruby_restart.rb +2 -59
  57. data/lib/puma/json_serialization.rb +96 -0
  58. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  59. data/lib/puma/launcher.rb +184 -133
  60. data/lib/puma/log_writer.rb +147 -0
  61. data/lib/puma/minissl/context_builder.rb +93 -0
  62. data/lib/puma/minissl.rb +263 -70
  63. data/lib/puma/null_io.rb +18 -1
  64. data/lib/puma/plugin/systemd.rb +90 -0
  65. data/lib/puma/plugin/tmp_restart.rb +3 -1
  66. data/lib/puma/plugin.rb +7 -13
  67. data/lib/puma/rack/builder.rb +9 -11
  68. data/lib/puma/rack/urlmap.rb +2 -0
  69. data/lib/puma/rack_default.rb +21 -4
  70. data/lib/puma/reactor.rb +93 -315
  71. data/lib/puma/request.rb +671 -0
  72. data/lib/puma/runner.rb +94 -69
  73. data/lib/puma/sd_notify.rb +149 -0
  74. data/lib/puma/server.rb +327 -772
  75. data/lib/puma/single.rb +20 -74
  76. data/lib/puma/state_file.rb +45 -8
  77. data/lib/puma/thread_pool.rb +146 -92
  78. data/lib/puma/util.rb +22 -10
  79. data/lib/puma.rb +60 -5
  80. data/lib/rack/handler/puma.rb +116 -90
  81. data/tools/Dockerfile +16 -0
  82. data/tools/trickletest.rb +0 -1
  83. metadata +54 -32
  84. data/ext/puma_http11/io_buffer.c +0 -155
  85. data/lib/puma/accept_nonblock.rb +0 -23
  86. data/lib/puma/compat.rb +0 -14
  87. data/lib/puma/convenient.rb +0 -25
  88. data/lib/puma/daemon_ext.rb +0 -33
  89. data/lib/puma/delegation.rb +0 -13
  90. data/lib/puma/java_io_buffer.rb +0 -47
  91. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
  92. data/lib/puma/tcp_logger.rb +0 -41
  93. data/tools/jungle/README.md +0 -19
  94. data/tools/jungle/init.d/README.md +0 -61
  95. data/tools/jungle/init.d/puma +0 -421
  96. data/tools/jungle/init.d/run-puma +0 -18
  97. data/tools/jungle/upstart/README.md +0 -61
  98. data/tools/jungle/upstart/puma-manager.conf +0 -31
  99. data/tools/jungle/upstart/puma.conf +0 -69
  100. /data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
@@ -0,0 +1,15 @@
1
+ package puma;
2
+
3
+ import java.io.IOException;
4
+
5
+ import org.jruby.Ruby;
6
+ import org.jruby.runtime.load.BasicLibraryService;
7
+
8
+ import org.jruby.puma.Http11;
9
+
10
+ public class PumaHttp11Service implements BasicLibraryService {
11
+ public boolean basicLoad(final Ruby runtime) throws IOException {
12
+ Http11.createHttp11(runtime);
13
+ return true;
14
+ }
15
+ }
@@ -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;
@@ -30,13 +30,23 @@ public class Http11 extends RubyObject {
30
30
  public final static String MAX_REQUEST_URI_LENGTH_ERR = "HTTP element REQUEST_URI is longer than the 12288 allowed length.";
31
31
  public final static int MAX_FRAGMENT_LENGTH = 1024;
32
32
  public final static String MAX_FRAGMENT_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 1024 allowed length.";
33
- public final static int MAX_REQUEST_PATH_LENGTH = 2048;
34
- public final static String MAX_REQUEST_PATH_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 2048 allowed length.";
33
+ public final static int MAX_REQUEST_PATH_LENGTH = 8192;
34
+ public final static String MAX_REQUEST_PATH_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 8192 allowed length.";
35
35
  public final static int MAX_QUERY_STRING_LENGTH = 1024 * 10;
36
36
  public final static String MAX_QUERY_STRING_LENGTH_ERR = "HTTP element QUERY_STRING is longer than the 10240 allowed length.";
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 SERVER_PROTOCOL_BYTELIST = new ByteList(ByteList.plain("SERVER_PROTOCOL"));
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 new RaiseException(runtime, eHttpParserError, msg, true);
78
+ throw newHTTPParserError(runtime, msg);
79
79
  }
80
80
  }
81
81
 
82
- private Http11Parser.FieldCB http_field = new Http11Parser.FieldCB() {
83
- public void call(Object data, int field, int flen, int value, int vlen) {
84
- RubyHash req = (RubyHash)data;
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 Http11Parser.ElementCB request_method = new Http11Parser.ElementCB() {
121
- public void call(Object data, int at, int length) {
122
- RubyHash req = (RubyHash)data;
123
- RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
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
- private Http11Parser.ElementCB http_version = new Http11Parser.ElementCB() {
165
- public void call(Object data, int at, int length) {
166
- RubyHash req = (RubyHash)data;
167
- RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
168
- req.op_aset(req.getRuntime().getCurrentContext(), runtime.newString("HTTP_VERSION"),val);
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
- private Http11Parser.ElementCB header_done = new Http11Parser.ElementCB() {
173
- public void call(Object data, int at, int length) {
174
- body = RubyString.newString(runtime, new ByteList(hp.parser.buffer, at, length));
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 server_protocol(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, SERVER_PROTOCOL_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 = 0;
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 new RaiseException(runtime, eHttpParserError, "Requested start is after data buffer end.", true);
188
+ throw newHTTPParserError(runtime, "Requested start is after data buffer end.");
203
189
  } else {
204
- this.hp.parser.data = req_hash;
205
- this.hp.execute(d,from);
206
- validateMaxLength(this.hp.parser.nread,MAX_HEADER_LENGTH, MAX_HEADER_LENGTH_ERR);
207
- if(this.hp.has_error()) {
208
- throw new RaiseException(runtime, eHttpParserError, "Invalid HTTP format, parsing fails.", true);
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. Are you trying to open an SSL connection to a non-SSL Puma?");
209
201
  } else {
210
- return runtime.newFixnum(this.hp.parser.nread);
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;