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.
- checksums.yaml +4 -4
- data/History.md +1620 -439
- data/LICENSE +23 -20
- data/README.md +175 -63
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +59 -21
- data/docs/compile_options.md +21 -0
- data/docs/deployment.md +69 -58
- data/docs/fork_worker.md +33 -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/docs/kubernetes.md +66 -0
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +22 -12
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +47 -22
- data/docs/signals.md +13 -11
- data/docs/stats.md +142 -0
- data/docs/systemd.md +95 -120
- data/ext/puma_http11/PumaHttp11Service.java +2 -2
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +57 -2
- data/ext/puma_http11/http11_parser.c +105 -117
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +22 -38
- data/ext/puma_http11/http11_parser.rl +4 -2
- data/ext/puma_http11/http11_parser_common.rl +4 -4
- data/ext/puma_http11/mini_ssl.c +347 -94
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +84 -99
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +124 -71
- data/ext/puma_http11/puma_http11.c +35 -51
- data/lib/puma/app/status.rb +71 -49
- data/lib/puma/binder.rb +241 -143
- data/lib/puma/cli.rb +30 -18
- data/lib/puma/client.rb +379 -229
- data/lib/puma/cluster/worker.rb +173 -0
- data/lib/puma/cluster/worker_handle.rb +94 -0
- data/lib/puma/cluster.rb +249 -232
- data/lib/puma/commonlogger.rb +4 -2
- data/lib/puma/configuration.rb +63 -51
- data/lib/puma/const.rb +44 -21
- data/lib/puma/control_cli.rb +123 -73
- data/lib/puma/detect.rb +31 -2
- data/lib/puma/dsl.rb +631 -125
- data/lib/puma/error_logger.rb +104 -0
- data/lib/puma/events.rb +57 -31
- data/lib/puma/io_buffer.rb +9 -5
- data/lib/puma/jruby_restart.rb +2 -58
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher.rb +196 -70
- data/lib/puma/minissl/context_builder.rb +81 -0
- data/lib/puma/minissl.rb +172 -65
- data/lib/puma/null_io.rb +20 -1
- data/lib/puma/plugin/tmp_restart.rb +2 -0
- data/lib/puma/plugin.rb +9 -13
- data/lib/puma/queue_close.rb +26 -0
- data/lib/puma/rack/builder.rb +5 -6
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +2 -0
- data/lib/puma/reactor.rb +87 -316
- data/lib/puma/request.rb +476 -0
- data/lib/puma/runner.rb +50 -55
- data/lib/puma/server.rb +307 -695
- data/lib/puma/single.rb +13 -67
- data/lib/puma/state_file.rb +49 -7
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/thread_pool.rb +134 -82
- data/lib/puma/util.rb +34 -10
- data/lib/puma.rb +56 -0
- data/lib/rack/handler/puma.rb +8 -6
- data/lib/rack/version_restriction.rb +15 -0
- data/tools/Dockerfile +16 -0
- data/tools/trickletest.rb +0 -1
- metadata +49 -32
- data/ext/puma_http11/io_buffer.c +0 -155
- data/lib/puma/accept_nonblock.rb +0 -23
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/convenient.rb +0 -23
- data/lib/puma/daemon_ext.rb +0 -31
- data/lib/puma/delegation.rb +0 -11
- data/lib/puma/java_io_buffer.rb +0 -45
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
- data/lib/puma/tcp_logger.rb +0 -39
- 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
- /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
|
-
|
80
|
-
if (buffer.remaining() <
|
81
|
-
resize(buffer.limit() +
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
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
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
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
|
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
|
-
|
191
|
-
|
192
|
-
|
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
|
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()
|
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
|
-
|
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
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
throw
|
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
|
-
|
303
|
-
|
304
|
-
outboundAppData = new MiniSSLBuffer(bls);
|
347
|
+
byte[] bls = arg.convertToString().getBytes();
|
348
|
+
outboundAppData = new MiniSSLBuffer(bls);
|
305
349
|
|
306
|
-
|
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()
|
354
|
+
public IRubyObject extract(ThreadContext context) {
|
315
355
|
try {
|
316
356
|
ByteList dataByteList = outboundNetData.asByteList();
|
317
357
|
if (dataByteList != null) {
|
318
|
-
RubyString
|
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
|
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
|
369
|
+
return context.nil;
|
332
370
|
}
|
333
371
|
|
334
|
-
RubyString
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
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
|
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
|
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,
|
57
|
-
DEF_MAX_LENGTH(QUERY_STRING,
|
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(
|
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
|
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
|
-
|
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
|
-
|
481
|
+
#ifdef HAVE_OPENSSL_BIO_H
|
499
482
|
Init_mini_ssl(mPuma);
|
483
|
+
#endif
|
500
484
|
}
|