puma 3.12.0 → 5.6.8

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1620 -439
  3. data/LICENSE +23 -20
  4. data/README.md +175 -63
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +59 -21
  7. data/docs/compile_options.md +21 -0
  8. data/docs/deployment.md +69 -58
  9. data/docs/fork_worker.md +33 -0
  10. data/docs/jungle/README.md +9 -0
  11. data/{tools → docs}/jungle/rc.d/README.md +1 -1
  12. data/{tools → docs}/jungle/rc.d/puma +2 -2
  13. data/docs/kubernetes.md +66 -0
  14. data/docs/nginx.md +1 -1
  15. data/docs/plugins.md +22 -12
  16. data/docs/rails_dev_mode.md +28 -0
  17. data/docs/restart.md +47 -22
  18. data/docs/signals.md +13 -11
  19. data/docs/stats.md +142 -0
  20. data/docs/systemd.md +95 -120
  21. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  22. data/ext/puma_http11/ext_help.h +1 -1
  23. data/ext/puma_http11/extconf.rb +57 -2
  24. data/ext/puma_http11/http11_parser.c +105 -117
  25. data/ext/puma_http11/http11_parser.h +1 -1
  26. data/ext/puma_http11/http11_parser.java.rl +22 -38
  27. data/ext/puma_http11/http11_parser.rl +4 -2
  28. data/ext/puma_http11/http11_parser_common.rl +4 -4
  29. data/ext/puma_http11/mini_ssl.c +347 -94
  30. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  31. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
  32. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +84 -99
  33. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +124 -71
  34. data/ext/puma_http11/puma_http11.c +35 -51
  35. data/lib/puma/app/status.rb +71 -49
  36. data/lib/puma/binder.rb +241 -143
  37. data/lib/puma/cli.rb +30 -18
  38. data/lib/puma/client.rb +379 -229
  39. data/lib/puma/cluster/worker.rb +173 -0
  40. data/lib/puma/cluster/worker_handle.rb +94 -0
  41. data/lib/puma/cluster.rb +249 -232
  42. data/lib/puma/commonlogger.rb +4 -2
  43. data/lib/puma/configuration.rb +63 -51
  44. data/lib/puma/const.rb +44 -21
  45. data/lib/puma/control_cli.rb +123 -73
  46. data/lib/puma/detect.rb +31 -2
  47. data/lib/puma/dsl.rb +631 -125
  48. data/lib/puma/error_logger.rb +104 -0
  49. data/lib/puma/events.rb +57 -31
  50. data/lib/puma/io_buffer.rb +9 -5
  51. data/lib/puma/jruby_restart.rb +2 -58
  52. data/lib/puma/json_serialization.rb +96 -0
  53. data/lib/puma/launcher.rb +196 -70
  54. data/lib/puma/minissl/context_builder.rb +81 -0
  55. data/lib/puma/minissl.rb +172 -65
  56. data/lib/puma/null_io.rb +20 -1
  57. data/lib/puma/plugin/tmp_restart.rb +2 -0
  58. data/lib/puma/plugin.rb +9 -13
  59. data/lib/puma/queue_close.rb +26 -0
  60. data/lib/puma/rack/builder.rb +5 -6
  61. data/lib/puma/rack/urlmap.rb +2 -0
  62. data/lib/puma/rack_default.rb +2 -0
  63. data/lib/puma/reactor.rb +87 -316
  64. data/lib/puma/request.rb +476 -0
  65. data/lib/puma/runner.rb +50 -55
  66. data/lib/puma/server.rb +307 -695
  67. data/lib/puma/single.rb +13 -67
  68. data/lib/puma/state_file.rb +49 -7
  69. data/lib/puma/systemd.rb +46 -0
  70. data/lib/puma/thread_pool.rb +134 -82
  71. data/lib/puma/util.rb +34 -10
  72. data/lib/puma.rb +56 -0
  73. data/lib/rack/handler/puma.rb +8 -6
  74. data/lib/rack/version_restriction.rb +15 -0
  75. data/tools/Dockerfile +16 -0
  76. data/tools/trickletest.rb +0 -1
  77. metadata +49 -32
  78. data/ext/puma_http11/io_buffer.c +0 -155
  79. data/lib/puma/accept_nonblock.rb +0 -23
  80. data/lib/puma/compat.rb +0 -14
  81. data/lib/puma/convenient.rb +0 -23
  82. data/lib/puma/daemon_ext.rb +0 -31
  83. data/lib/puma/delegation.rb +0 -11
  84. data/lib/puma/java_io_buffer.rb +0 -45
  85. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
  86. data/lib/puma/tcp_logger.rb +0 -39
  87. data/tools/jungle/README.md +0 -19
  88. data/tools/jungle/init.d/README.md +0 -61
  89. data/tools/jungle/init.d/puma +0 -421
  90. data/tools/jungle/init.d/run-puma +0 -18
  91. data/tools/jungle/upstart/README.md +0 -61
  92. data/tools/jungle/upstart/puma-manager.conf +0 -31
  93. data/tools/jungle/upstart/puma.conf +0 -69
  94. /data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
@@ -6,6 +6,7 @@ import org.jruby.RubyModule;
6
6
  import org.jruby.RubyObject;
7
7
  import org.jruby.RubyString;
8
8
  import org.jruby.anno.JRubyMethod;
9
+ import org.jruby.exceptions.RaiseException;
9
10
  import org.jruby.javasupport.JavaEmbedUtils;
10
11
  import org.jruby.runtime.Block;
11
12
  import org.jruby.runtime.ObjectAllocator;
@@ -22,7 +23,9 @@ import javax.net.ssl.SSLException;
22
23
  import javax.net.ssl.SSLPeerUnverifiedException;
23
24
  import javax.net.ssl.SSLSession;
24
25
  import java.io.FileInputStream;
26
+ import java.io.InputStream;
25
27
  import java.io.IOException;
28
+ import java.nio.Buffer;
26
29
  import java.nio.ByteBuffer;
27
30
  import java.security.KeyManagementException;
28
31
  import java.security.KeyStore;
@@ -31,6 +34,8 @@ import java.security.NoSuchAlgorithmException;
31
34
  import java.security.UnrecoverableKeyException;
32
35
  import java.security.cert.CertificateEncodingException;
33
36
  import java.security.cert.CertificateException;
37
+ import java.util.concurrent.ConcurrentHashMap;
38
+ import java.util.Map;
34
39
 
35
40
  import static javax.net.ssl.SSLEngineResult.Status;
36
41
  import static javax.net.ssl.SSLEngineResult.HandshakeStatus;
@@ -65,7 +70,7 @@ public class MiniSSL extends RubyObject {
65
70
 
66
71
  public void clear() { buffer.clear(); }
67
72
  public void compact() { buffer.compact(); }
68
- public void flip() { buffer.flip(); }
73
+ public void flip() { ((Buffer) buffer).flip(); }
69
74
  public boolean hasRemaining() { return buffer.hasRemaining(); }
70
75
  public int position() { return buffer.position(); }
71
76
 
@@ -76,11 +81,11 @@ public class MiniSSL extends RubyObject {
76
81
  /**
77
82
  * Writes bytes to the buffer after ensuring there's room
78
83
  */
79
- public void put(byte[] bytes) {
80
- if (buffer.remaining() < bytes.length) {
81
- resize(buffer.limit() + bytes.length);
84
+ private void put(byte[] bytes, final int offset, final int length) {
85
+ if (buffer.remaining() < length) {
86
+ resize(buffer.limit() + length);
82
87
  }
83
- buffer.put(bytes);
88
+ buffer.put(bytes, offset, length);
84
89
  }
85
90
 
86
91
  /**
@@ -89,7 +94,7 @@ public class MiniSSL extends RubyObject {
89
94
  public void resize(int newCapacity) {
90
95
  if (newCapacity > buffer.capacity()) {
91
96
  ByteBuffer dstTmp = ByteBuffer.allocate(newCapacity);
92
- buffer.flip();
97
+ flip();
93
98
  dstTmp.put(buffer);
94
99
  buffer = dstTmp;
95
100
  } else {
@@ -101,7 +106,7 @@ public class MiniSSL extends RubyObject {
101
106
  * Drains the buffer to a ByteList, or returns null for an empty buffer
102
107
  */
103
108
  public ByteList asByteList() {
104
- buffer.flip();
109
+ flip();
105
110
  if (!buffer.hasRemaining()) {
106
111
  buffer.clear();
107
112
  return null;
@@ -111,7 +116,7 @@ public class MiniSSL extends RubyObject {
111
116
 
112
117
  buffer.get(bss);
113
118
  buffer.clear();
114
- return new ByteList(bss);
119
+ return new ByteList(bss, false);
115
120
  }
116
121
 
117
122
  @Override
@@ -119,6 +124,8 @@ public class MiniSSL extends RubyObject {
119
124
  }
120
125
 
121
126
  private SSLEngine engine;
127
+ private boolean closed;
128
+ private boolean handshake;
122
129
  private MiniSSLBuffer inboundNetData;
123
130
  private MiniSSLBuffer outboundAppData;
124
131
  private MiniSSLBuffer outboundNetData;
@@ -127,10 +134,39 @@ public class MiniSSL extends RubyObject {
127
134
  super(runtime, klass);
128
135
  }
129
136
 
137
+ private static Map<String, KeyManagerFactory> keyManagerFactoryMap = new ConcurrentHashMap<String, KeyManagerFactory>();
138
+ private static Map<String, TrustManagerFactory> trustManagerFactoryMap = new ConcurrentHashMap<String, TrustManagerFactory>();
139
+
130
140
  @JRubyMethod(meta = true)
131
- public static IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext) {
132
- RubyClass klass = (RubyClass) recv;
141
+ public static synchronized IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext)
142
+ throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException {
143
+ // Create the KeyManagerFactory and TrustManagerFactory for this server
144
+ String keystoreFile = miniSSLContext.callMethod(context, "keystore").convertToString().asJavaString();
145
+ char[] password = miniSSLContext.callMethod(context, "keystore_pass").convertToString().asJavaString().toCharArray();
133
146
 
147
+ KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
148
+ InputStream is = new FileInputStream(keystoreFile);
149
+ try {
150
+ ks.load(is, password);
151
+ } finally {
152
+ is.close();
153
+ }
154
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
155
+ kmf.init(ks, password);
156
+ keyManagerFactoryMap.put(keystoreFile, kmf);
157
+
158
+ KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
159
+ is = new FileInputStream(keystoreFile);
160
+ try {
161
+ ts.load(is, password);
162
+ } finally {
163
+ is.close();
164
+ }
165
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
166
+ tmf.init(ts);
167
+ trustManagerFactoryMap.put(keystoreFile, tmf);
168
+
169
+ RubyClass klass = (RubyClass) recv;
134
170
  return klass.newInstance(context,
135
171
  new IRubyObject[] { miniSSLContext },
136
172
  Block.NULL_BLOCK);
@@ -138,31 +174,37 @@ public class MiniSSL extends RubyObject {
138
174
 
139
175
  @JRubyMethod
140
176
  public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLContext)
141
- throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
142
- KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
143
- KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
177
+ throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
144
178
 
145
- char[] password = miniSSLContext.callMethod(threadContext, "keystore_pass").convertToString().asJavaString().toCharArray();
146
179
  String keystoreFile = miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString();
147
- ks.load(new FileInputStream(keystoreFile), password);
148
- ts.load(new FileInputStream(keystoreFile), password);
149
-
150
- KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
151
- kmf.init(ks, password);
152
-
153
- TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
154
- tmf.init(ts);
180
+ KeyManagerFactory kmf = keyManagerFactoryMap.get(keystoreFile);
181
+ TrustManagerFactory tmf = trustManagerFactoryMap.get(keystoreFile);
182
+ if(kmf == null || tmf == null) {
183
+ throw new KeyStoreException("Could not find KeyManagerFactory/TrustManagerFactory for keystore: " + keystoreFile);
184
+ }
155
185
 
156
186
  SSLContext sslCtx = SSLContext.getInstance("TLS");
157
187
 
158
188
  sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
189
+ closed = false;
190
+ handshake = false;
159
191
  engine = sslCtx.createSSLEngine();
160
192
 
161
- String[] protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
193
+ String[] protocols;
194
+ if(miniSSLContext.callMethod(threadContext, "no_tlsv1").isTrue()) {
195
+ protocols = new String[] { "TLSv1.1", "TLSv1.2" };
196
+ } else {
197
+ protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
198
+ }
199
+
200
+ if(miniSSLContext.callMethod(threadContext, "no_tlsv1_1").isTrue()) {
201
+ protocols = new String[] { "TLSv1.2" };
202
+ }
203
+
162
204
  engine.setEnabledProtocols(protocols);
163
205
  engine.setUseClientMode(false);
164
206
 
165
- long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger().getLongValue();
207
+ long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger("to_i").getLongValue();
166
208
  if ((verify_mode & 0x1) != 0) { // 'peer'
167
209
  engine.setWantClientAuth(true);
168
210
  }
@@ -187,14 +229,9 @@ public class MiniSSL extends RubyObject {
187
229
 
188
230
  @JRubyMethod
189
231
  public IRubyObject inject(IRubyObject arg) {
190
- try {
191
- byte[] bytes = arg.convertToString().getBytes();
192
- inboundNetData.put(bytes);
193
- return this;
194
- } catch (Exception e) {
195
- e.printStackTrace();
196
- throw new RuntimeException(e);
197
- }
232
+ ByteList bytes = arg.convertToString().getByteList();
233
+ inboundNetData.put(bytes.unsafeBytes(), bytes.getBegin(), bytes.getRealSize());
234
+ return this;
198
235
  }
199
236
 
200
237
  private enum SSLOperation {
@@ -229,17 +266,16 @@ public class MiniSSL extends RubyObject {
229
266
  // need to wait for more data to come in before we retry
230
267
  retryOp = false;
231
268
  break;
269
+ case CLOSED:
270
+ closed = true;
271
+ retryOp = false;
272
+ break;
232
273
  default:
233
- // other cases are OK and CLOSED. We're done here.
274
+ // other case is OK. We're done here.
234
275
  retryOp = false;
235
276
  }
236
- }
237
-
238
- // after each op, run any delegated tasks if needed
239
- if(engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
240
- Runnable runnable;
241
- while ((runnable = engine.getDelegatedTask()) != null) {
242
- runnable.run();
277
+ if (res.getHandshakeStatus() == HandshakeStatus.FINISHED) {
278
+ handshake = true;
243
279
  }
244
280
  }
245
281
 
@@ -247,7 +283,7 @@ public class MiniSSL extends RubyObject {
247
283
  }
248
284
 
249
285
  @JRubyMethod
250
- public IRubyObject read() throws Exception {
286
+ public IRubyObject read() {
251
287
  try {
252
288
  inboundNetData.flip();
253
289
 
@@ -261,21 +297,30 @@ public class MiniSSL extends RubyObject {
261
297
  HandshakeStatus handshakeStatus = engine.getHandshakeStatus();
262
298
  boolean done = false;
263
299
  while (!done) {
300
+ SSLEngineResult res;
264
301
  switch (handshakeStatus) {
265
302
  case NEED_WRAP:
266
- doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
303
+ res = doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
304
+ handshakeStatus = res.getHandshakeStatus();
267
305
  break;
268
306
  case NEED_UNWRAP:
269
- SSLEngineResult res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
307
+ res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
270
308
  if (res.getStatus() == Status.BUFFER_UNDERFLOW) {
271
309
  // need more data before we can shake more hands
272
310
  done = true;
273
311
  }
312
+ handshakeStatus = res.getHandshakeStatus();
313
+ break;
314
+ case NEED_TASK:
315
+ Runnable runnable;
316
+ while ((runnable = engine.getDelegatedTask()) != null) {
317
+ runnable.run();
318
+ }
319
+ handshakeStatus = engine.getHandshakeStatus();
274
320
  break;
275
321
  default:
276
322
  done = true;
277
323
  }
278
- handshakeStatus = engine.getHandshakeStatus();
279
324
  }
280
325
 
281
326
  if (inboundNetData.hasRemaining()) {
@@ -289,55 +334,46 @@ public class MiniSSL extends RubyObject {
289
334
  return getRuntime().getNil();
290
335
  }
291
336
 
292
- RubyString str = getRuntime().newString("");
293
- str.setValue(appDataByteList);
294
- return str;
295
- } catch (Exception e) {
296
- throw getRuntime().newEOFError(e.getMessage());
337
+ return RubyString.newString(getRuntime(), appDataByteList);
338
+ } catch (SSLException e) {
339
+ RaiseException re = getRuntime().newEOFError(e.getMessage());
340
+ re.initCause(e);
341
+ throw re;
297
342
  }
298
343
  }
299
344
 
300
345
  @JRubyMethod
301
346
  public IRubyObject write(IRubyObject arg) {
302
- try {
303
- byte[] bls = arg.convertToString().getBytes();
304
- outboundAppData = new MiniSSLBuffer(bls);
347
+ byte[] bls = arg.convertToString().getBytes();
348
+ outboundAppData = new MiniSSLBuffer(bls);
305
349
 
306
- return getRuntime().newFixnum(bls.length);
307
- } catch (Exception e) {
308
- e.printStackTrace();
309
- throw new RuntimeException(e);
310
- }
350
+ return getRuntime().newFixnum(bls.length);
311
351
  }
312
352
 
313
353
  @JRubyMethod
314
- public IRubyObject extract() throws SSLException {
354
+ public IRubyObject extract(ThreadContext context) {
315
355
  try {
316
356
  ByteList dataByteList = outboundNetData.asByteList();
317
357
  if (dataByteList != null) {
318
- RubyString str = getRuntime().newString("");
319
- str.setValue(dataByteList);
320
- return str;
358
+ return RubyString.newString(context.runtime, dataByteList);
321
359
  }
322
360
 
323
361
  if (!outboundAppData.hasRemaining()) {
324
- return getRuntime().getNil();
362
+ return context.nil;
325
363
  }
326
364
 
327
365
  outboundNetData.clear();
328
366
  doOp(SSLOperation.WRAP, outboundAppData, outboundNetData);
329
367
  dataByteList = outboundNetData.asByteList();
330
368
  if (dataByteList == null) {
331
- return getRuntime().getNil();
369
+ return context.nil;
332
370
  }
333
371
 
334
- RubyString str = getRuntime().newString("");
335
- str.setValue(dataByteList);
336
-
337
- return str;
338
- } catch (Exception e) {
339
- e.printStackTrace();
340
- throw new RuntimeException(e);
372
+ return RubyString.newString(context.runtime, dataByteList);
373
+ } catch (SSLException e) {
374
+ RaiseException ex = context.runtime.newRuntimeError(e.toString());
375
+ ex.initCause(e);
376
+ throw ex;
341
377
  }
342
378
  }
343
379
 
@@ -345,8 +381,25 @@ public class MiniSSL extends RubyObject {
345
381
  public IRubyObject peercert() throws CertificateEncodingException {
346
382
  try {
347
383
  return JavaEmbedUtils.javaToRuby(getRuntime(), engine.getSession().getPeerCertificates()[0].getEncoded());
348
- } catch (SSLPeerUnverifiedException ex) {
384
+ } catch (SSLPeerUnverifiedException e) {
349
385
  return getRuntime().getNil();
350
386
  }
351
387
  }
388
+
389
+ @JRubyMethod(name = "init?")
390
+ public IRubyObject isInit(ThreadContext context) {
391
+ return handshake ? getRuntime().getFalse() : getRuntime().getTrue();
392
+ }
393
+
394
+ @JRubyMethod
395
+ public IRubyObject shutdown() {
396
+ if (closed || engine.isInboundDone() && engine.isOutboundDone()) {
397
+ if (engine.isOutboundDone()) {
398
+ engine.closeOutbound();
399
+ }
400
+ return getRuntime().getTrue();
401
+ } else {
402
+ return getRuntime().getFalse();
403
+ }
404
+ }
352
405
  }
@@ -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
@@ -39,7 +40,9 @@ static VALUE global_http_version;
39
40
  static VALUE global_request_path;
40
41
 
41
42
  /** Defines common length and error messages for input length validation. */
42
- #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)"
43
+ #define QUOTE(s) #s
44
+ #define EXPLAIN_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 " EXPLAIN_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,16 @@ static VALUE global_request_path;
49
52
 
50
53
 
51
54
  /* Defines the maximum allowed lengths for various input elements.*/
55
+ #ifndef PUMA_QUERY_STRING_MAX_LENGTH
56
+ #define PUMA_QUERY_STRING_MAX_LENGTH (1024 * 10)
57
+ #endif
58
+
52
59
  DEF_MAX_LENGTH(FIELD_NAME, 256);
53
60
  DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
54
61
  DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
55
62
  DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
56
- DEF_MAX_LENGTH(REQUEST_PATH, 2048);
57
- DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
63
+ DEF_MAX_LENGTH(REQUEST_PATH, 8192);
64
+ DEF_MAX_LENGTH(QUERY_STRING, PUMA_QUERY_STRING_MAX_LENGTH);
58
65
  DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
59
66
 
60
67
  struct common_field {
@@ -111,21 +118,6 @@ static struct common_field common_http_fields[] = {
111
118
  # undef f
112
119
  };
113
120
 
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
121
  static void init_common_fields(void)
130
122
  {
131
123
  unsigned i;
@@ -142,28 +134,10 @@ static void init_common_fields(void)
142
134
  }
143
135
  rb_global_variable(&cf->value);
144
136
  }
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
137
  }
153
138
 
154
139
  static VALUE find_common_field_value(const char *field, size_t flen)
155
140
  {
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
141
  unsigned i;
168
142
  struct common_field *cf = common_http_fields;
169
143
  for(i = 0; i < ARRAY_SIZE(common_http_fields); i++, cf++) {
@@ -171,7 +145,6 @@ static VALUE find_common_field_value(const char *field, size_t flen)
171
145
  return cf->value;
172
146
  }
173
147
  return Qnil;
174
- #endif /* !HAVE_QSORT_BSEARCH */
175
148
  }
176
149
 
177
150
  void http_field(puma_parser* hp, const char *field, size_t flen,
@@ -200,6 +173,8 @@ void http_field(puma_parser* hp, const char *field, size_t flen,
200
173
  f = rb_str_new(hp->buf, new_size);
201
174
  }
202
175
 
176
+ while (vlen > 0 && isspace(value[vlen - 1])) vlen--;
177
+
203
178
  /* check for duplicate header */
204
179
  v = rb_hash_aref(hp->request, f);
205
180
 
@@ -284,11 +259,18 @@ void HttpParser_free(void *data) {
284
259
  }
285
260
  }
286
261
 
287
- void HttpParser_mark(puma_parser* hp) {
262
+ void HttpParser_mark(void *ptr) {
263
+ puma_parser *hp = ptr;
288
264
  if(hp->request) rb_gc_mark(hp->request);
289
265
  if(hp->body) rb_gc_mark(hp->body);
290
266
  }
291
267
 
268
+ const rb_data_type_t HttpParser_data_type = {
269
+ "HttpParser",
270
+ { HttpParser_mark, HttpParser_free, 0 },
271
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
272
+ };
273
+
292
274
  VALUE HttpParser_alloc(VALUE klass)
293
275
  {
294
276
  puma_parser *hp = ALLOC_N(puma_parser, 1);
@@ -305,7 +287,7 @@ VALUE HttpParser_alloc(VALUE klass)
305
287
 
306
288
  puma_parser_init(hp);
307
289
 
308
- return Data_Wrap_Struct(klass, HttpParser_mark, HttpParser_free, hp);
290
+ return TypedData_Wrap_Struct(klass, &HttpParser_data_type, hp);
309
291
  }
310
292
 
311
293
  /**
@@ -317,7 +299,7 @@ VALUE HttpParser_alloc(VALUE klass)
317
299
  VALUE HttpParser_init(VALUE self)
318
300
  {
319
301
  puma_parser *http = NULL;
320
- DATA_GET(self, puma_parser, http);
302
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
321
303
  puma_parser_init(http);
322
304
 
323
305
  return self;
@@ -334,7 +316,7 @@ VALUE HttpParser_init(VALUE self)
334
316
  VALUE HttpParser_reset(VALUE self)
335
317
  {
336
318
  puma_parser *http = NULL;
337
- DATA_GET(self, puma_parser, http);
319
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
338
320
  puma_parser_init(http);
339
321
 
340
322
  return Qnil;
@@ -351,7 +333,7 @@ VALUE HttpParser_reset(VALUE self)
351
333
  VALUE HttpParser_finish(VALUE self)
352
334
  {
353
335
  puma_parser *http = NULL;
354
- DATA_GET(self, puma_parser, http);
336
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
355
337
  puma_parser_finish(http);
356
338
 
357
339
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
@@ -382,7 +364,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
382
364
  char *dptr = NULL;
383
365
  long dlen = 0;
384
366
 
385
- DATA_GET(self, puma_parser, http);
367
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
386
368
 
387
369
  from = FIX2INT(start);
388
370
  dptr = rb_extract_chars(data, &dlen);
@@ -398,7 +380,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
398
380
  VALIDATE_MAX_LENGTH(puma_parser_nread(http), HEADER);
399
381
 
400
382
  if(puma_parser_has_error(http)) {
401
- rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails.");
383
+ rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails. Are you trying to open an SSL connection to a non-SSL Puma?");
402
384
  } else {
403
385
  return INT2FIX(puma_parser_nread(http));
404
386
  }
@@ -416,7 +398,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
416
398
  VALUE HttpParser_has_error(VALUE self)
417
399
  {
418
400
  puma_parser *http = NULL;
419
- DATA_GET(self, puma_parser, http);
401
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
420
402
 
421
403
  return puma_parser_has_error(http) ? Qtrue : Qfalse;
422
404
  }
@@ -431,7 +413,7 @@ VALUE HttpParser_has_error(VALUE self)
431
413
  VALUE HttpParser_is_finished(VALUE self)
432
414
  {
433
415
  puma_parser *http = NULL;
434
- DATA_GET(self, puma_parser, http);
416
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
435
417
 
436
418
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
437
419
  }
@@ -447,7 +429,7 @@ VALUE HttpParser_is_finished(VALUE self)
447
429
  VALUE HttpParser_nread(VALUE self)
448
430
  {
449
431
  puma_parser *http = NULL;
450
- DATA_GET(self, puma_parser, http);
432
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
451
433
 
452
434
  return INT2FIX(http->nread);
453
435
  }
@@ -460,15 +442,16 @@ VALUE HttpParser_nread(VALUE self)
460
442
  */
461
443
  VALUE HttpParser_body(VALUE self) {
462
444
  puma_parser *http = NULL;
463
- DATA_GET(self, puma_parser, http);
445
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
464
446
 
465
447
  return http->body;
466
448
  }
467
449
 
468
- void Init_io_buffer(VALUE puma);
450
+ #ifdef HAVE_OPENSSL_BIO_H
469
451
  void Init_mini_ssl(VALUE mod);
452
+ #endif
470
453
 
471
- void Init_puma_http11()
454
+ void Init_puma_http11(void)
472
455
  {
473
456
 
474
457
  VALUE mPuma = rb_define_module("Puma");
@@ -495,6 +478,7 @@ void Init_puma_http11()
495
478
  rb_define_method(cHttpParser, "body", HttpParser_body, 0);
496
479
  init_common_fields();
497
480
 
498
- Init_io_buffer(mPuma);
481
+ #ifdef HAVE_OPENSSL_BIO_H
499
482
  Init_mini_ssl(mPuma);
483
+ #endif
500
484
  }