puma 2.8.2-java → 2.9.0-java

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4068c1ab9c07c255327a21b7cb9861c9262bf27f
4
- data.tar.gz: 16d7ca45414cecb9d9bb5de419708f93dee10c31
3
+ metadata.gz: 606df9e31f26a18cd3e3a1219615c9f6ac80b634
4
+ data.tar.gz: 0c94555a3f56ed48cece59cadeb49539727f1483
5
5
  SHA512:
6
- metadata.gz: 96995f4f35e661d5520742532357c731678e809fbb8fc04b8b0d39f6cb35abd043178fae312315d76d0862809c98f0d4d82260d5a1b66e411a91561717f38025
7
- data.tar.gz: 2fb7d98cc1acf55d9d9f6981ba7be452defb8eb45b4cbf8c5f6741aed35f48a159d6fe04c8601396cbe663578953cc920223c95ad8fc0c813f9bb34fe988dc28
6
+ metadata.gz: 1f766ef76252ad9271a3523c04287cce012676173511c834b8f73cb8dce29ea07ad414cc48de219b6635123781d2a7a88a56caaaa1f20488709b99ea3685609e
7
+ data.tar.gz: 3b7dd8bc4f8f8102b907009ce1a8dae858a770ddc1c99b584942bb6d63578a893bc6e1763216bcd3a0d9d2817ac435f37e9b00fad587b84fc0dbf403a5b604b7
@@ -42,7 +42,7 @@ Here are some rules of thumb:
42
42
 
43
43
  **How do you know if you're got enough (or too many workers)?**
44
44
 
45
- A good question. Due to MRI's GIL, only one thread can be executing at a time.
45
+ A good question. Due to MRI's GIL, only one thread can be executing Ruby code at a time.
46
46
  But since so many apps are waiting on IO from DBs, etc., they can utilize threads
47
47
  to make better use of the process.
48
48
 
@@ -56,7 +56,7 @@ you've got capacity still but aren't starving threads.
56
56
 
57
57
  ## Daemonizing
58
58
 
59
- I prefer to not daemonize my servers and use something like `runit` or `upstrart` to
59
+ I prefer to not daemonize my servers and use something like `runit` or `upstart` to
60
60
  monitor them as child processes. This gives them fast response to crashes and
61
61
  makes it easy to figure out what is going on. Additionally, unlike `unicorn`,
62
62
  puma does not require daemonization to do zero-downtime restarts.
@@ -1,3 +1,27 @@
1
+ === 2.9.0 / 2014-07-12
2
+
3
+ * 1 minor feature:
4
+ * Add SSL support for JRuby
5
+
6
+ * 3 bug fixes:
7
+ * Typo BUNDLER_GEMFILE -> BUNDLE_GEMFILE
8
+ * Use fast_write because we can't trust syswrite
9
+ * pumactl - do not modify original ARGV
10
+
11
+ * 4 doc fixes:
12
+ * BSD-3-Clause over BSD to avoid confusion
13
+ * Deploy doc: clarification of the GIL
14
+ * Fix typo in DEPLOYMENT.md
15
+ * Update README.md
16
+
17
+ * 6 PRs merged:
18
+ * Merge pull request #520 from misfo/patch-2
19
+ * Merge pull request #530 from looker/jruby-ssl
20
+ * Merge pull request #537 from vlmonk/patch-1
21
+ * Merge pull request #540 from allaire/patch-1
22
+ * Merge pull request #544 from chulkilee/bsd-3-clause
23
+ * Merge pull request #551 from jcxplorer/patch-1
24
+
1
25
  === 2.8.2 / 2014-04-12
2
26
 
3
27
  * 4 bug fixes:
data/README.md CHANGED
@@ -117,7 +117,7 @@ If you're preloading your application and using ActiveRecord, it's recommend you
117
117
  end
118
118
  end
119
119
 
120
- When you use preload_app, your new code goes all in the master process, and is then copied in the workers (meaning it’s only compatible with cluster mode). General rule is to use preload_app when your workers die often and need fast starts. If you don’t have many workers, you should probably don’t use preload_app.
120
+ When you use preload_app, your new code goes all in the master process, and is then copied in the workers (meaning it’s only compatible with cluster mode). General rule is to use preload_app when your workers die often and need fast starts. If you don’t have many workers, you probably should not use preload_app.
121
121
 
122
122
  Note that preload_app can’t be used with phased restart, since phased restart kills and restarts workers one-by-one, and preload_app is all about copying the code of master into the workers.
123
123
 
@@ -248,4 +248,4 @@ $ bundle exec rake
248
248
 
249
249
  ## License
250
250
 
251
- Puma is copyright 2013 Evan Phoenix and contributors. It is licensed under the BSD license. See the include LICENSE file for details.
251
+ Puma is copyright 2013 Evan Phoenix and contributors. It is licensed under the BSD 3-Clause license. See the include LICENSE file for details.
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ HOE = Hoe.spec "puma" do
11
11
  self.readme_file = "README.md"
12
12
  self.urls = %w!http://puma.io https://github.com/puma/puma!
13
13
 
14
- license "BSD"
14
+ license "BSD-3-Clause"
15
15
  developer 'Evan Phoenix', 'evan@phx.io'
16
16
 
17
17
  spec_extras[:extensions] = ["ext/puma_http11/extconf.rb"]
@@ -22,7 +22,7 @@ HOE = Hoe.spec "puma" do
22
22
 
23
23
  dependency "rack", [">= 1.1", "< 2.0"]
24
24
 
25
- extra_dev_deps << ["rake-compiler", "~> 0.8.0"]
25
+ extra_dev_deps << ["rake-compiler", "~> 0.8"]
26
26
  end
27
27
 
28
28
  task :prerelease => [:clobber, :check_manifest, :test]
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'puma/control_cli'
4
4
 
5
- cli = Puma::ControlCLI.new ARGV
5
+ cli = Puma::ControlCLI.new ARGV.dup
6
6
 
7
7
  begin
8
8
  cli.run
@@ -36,15 +36,18 @@ ms_conn* engine_alloc(VALUE klass, VALUE* obj) {
36
36
  return conn;
37
37
  }
38
38
 
39
- VALUE engine_init_server(VALUE self, VALUE key, VALUE cert) {
39
+ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
40
40
  VALUE obj;
41
41
  SSL_CTX* ctx;
42
42
  SSL* ssl;
43
43
 
44
44
  ms_conn* conn = engine_alloc(self, &obj);
45
45
 
46
- StringValue(key);
47
- StringValue(cert);
46
+ ID sym_key = rb_intern("key");
47
+ VALUE key = rb_funcall(mini_ssl_ctx, sym_key, 0);
48
+
49
+ ID sym_cert = rb_intern("cert");
50
+ VALUE cert = rb_funcall(mini_ssl_ctx, sym_cert, 0);
48
51
 
49
52
  ctx = SSL_CTX_new(SSLv23_server_method());
50
53
  conn->ctx = ctx;
@@ -184,7 +187,7 @@ void Init_mini_ssl(VALUE puma) {
184
187
 
185
188
  eError = rb_define_class_under(mod, "SSLError", rb_eStandardError);
186
189
 
187
- rb_define_singleton_method(eng, "server", engine_init_server, 2);
190
+ rb_define_singleton_method(eng, "server", engine_init_server, 1);
188
191
  rb_define_singleton_method(eng, "client", engine_init_client, 0);
189
192
 
190
193
  rb_define_method(eng, "inject", engine_inject, 1);
@@ -2,29 +2,34 @@ package org.jruby.puma;
2
2
 
3
3
  import org.jruby.Ruby;
4
4
  import org.jruby.RubyClass;
5
- import org.jruby.RubyHash;
6
5
  import org.jruby.RubyModule;
7
- import org.jruby.RubyNumeric;
8
6
  import org.jruby.RubyObject;
9
7
  import org.jruby.RubyString;
10
-
11
8
  import org.jruby.anno.JRubyMethod;
12
-
13
9
  import org.jruby.runtime.Block;
14
10
  import org.jruby.runtime.ObjectAllocator;
15
11
  import org.jruby.runtime.ThreadContext;
16
12
  import org.jruby.runtime.builtin.IRubyObject;
17
-
18
- import org.jruby.exceptions.RaiseException;
19
-
20
13
  import org.jruby.util.ByteList;
21
14
 
22
-
23
- import javax.net.ssl.*;
24
- import javax.net.ssl.SSLEngineResult.*;
25
- import java.io.*;
26
- import java.security.*;
27
- import java.nio.*;
15
+ import javax.net.ssl.KeyManagerFactory;
16
+ import javax.net.ssl.SSLContext;
17
+ import javax.net.ssl.SSLEngine;
18
+ import javax.net.ssl.SSLEngineResult;
19
+ import javax.net.ssl.SSLException;
20
+ import javax.net.ssl.SSLSession;
21
+ import java.io.FileInputStream;
22
+ import java.io.IOException;
23
+ import java.nio.ByteBuffer;
24
+ import java.security.KeyManagementException;
25
+ import java.security.KeyStore;
26
+ import java.security.KeyStoreException;
27
+ import java.security.NoSuchAlgorithmException;
28
+ import java.security.UnrecoverableKeyException;
29
+ import java.security.cert.CertificateException;
30
+
31
+ import static javax.net.ssl.SSLEngineResult.Status;
32
+ import static javax.net.ssl.SSLEngineResult.HandshakeStatus;
28
33
 
29
34
  public class MiniSSL extends RubyObject {
30
35
  private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
@@ -33,9 +38,12 @@ public class MiniSSL extends RubyObject {
33
38
  }
34
39
  };
35
40
 
41
+ // set to true to switch on our low-fi trace logging
42
+ private static boolean DEBUG = false;
43
+
36
44
  public static void createMiniSSL(Ruby runtime) {
37
45
  RubyModule mPuma = runtime.defineModule("Puma");
38
- RubyModule ssl = mPuma.defineModuleUnder("MiniSSL");
46
+ RubyModule ssl = mPuma.defineModuleUnder("MiniSSL");
39
47
 
40
48
  mPuma.defineClassUnder("SSLError",
41
49
  runtime.getClass("IOError"),
@@ -45,245 +53,329 @@ public class MiniSSL extends RubyObject {
45
53
  eng.defineAnnotatedMethods(MiniSSL.class);
46
54
  }
47
55
 
48
- private Ruby runtime;
49
- private SSLContext sslc;
56
+ /**
57
+ * Fairly transparent wrapper around {@link java.nio.ByteBuffer} which adds the enhancements we need
58
+ */
59
+ private static class MiniSSLBuffer {
60
+ ByteBuffer buffer;
61
+
62
+ private MiniSSLBuffer(int capacity) { buffer = ByteBuffer.allocate(capacity); }
63
+ private MiniSSLBuffer(byte[] initialContents) { buffer = ByteBuffer.wrap(initialContents); }
64
+
65
+ public void clear() { buffer.clear(); }
66
+ public void compact() { buffer.compact(); }
67
+ public void flip() { buffer.flip(); }
68
+ public boolean hasRemaining() { return buffer.hasRemaining(); }
69
+ public int position() { return buffer.position(); }
70
+
71
+ public ByteBuffer getRawBuffer() {
72
+ return buffer;
73
+ }
74
+
75
+ /**
76
+ * Writes bytes to the buffer after ensuring there's room
77
+ */
78
+ public void put(byte[] bytes) {
79
+ if (buffer.remaining() < bytes.length) {
80
+ resize(buffer.limit() + bytes.length);
81
+ }
82
+ buffer.put(bytes);
83
+ }
84
+
85
+ /**
86
+ * Ensures that newCapacity bytes can be written to this buffer, only re-allocating if necessary
87
+ */
88
+ public void resize(int newCapacity) {
89
+ if (newCapacity > buffer.capacity()) {
90
+ ByteBuffer dstTmp = ByteBuffer.allocate(newCapacity);
91
+ buffer.flip();
92
+ dstTmp.put(buffer);
93
+ buffer = dstTmp;
94
+ } else {
95
+ buffer.limit(newCapacity);
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Drains the buffer to a ByteList, or returns null for an empty buffer
101
+ */
102
+ public ByteList asByteList() {
103
+ buffer.flip();
104
+ if (!buffer.hasRemaining()) {
105
+ buffer.clear();
106
+ return null;
107
+ }
108
+
109
+ byte[] bss = new byte[buffer.limit()];
110
+
111
+ buffer.get(bss);
112
+ buffer.clear();
113
+ return new ByteList(bss);
114
+ }
115
+
116
+ @Override
117
+ public String toString() { return buffer.toString(); }
118
+ }
50
119
 
51
- private SSLEngine engine;
120
+ private SSLEngine engine;
121
+ private MiniSSLBuffer inboundNetData;
122
+ private MiniSSLBuffer outboundAppData;
123
+ private MiniSSLBuffer outboundNetData;
52
124
 
53
- private ByteBuffer peerAppData;
54
- private ByteBuffer peerNetData;
55
- private ByteBuffer netData;
56
- private ByteBuffer dummy;
57
-
58
125
  public MiniSSL(Ruby runtime, RubyClass klass) {
59
126
  super(runtime, klass);
60
-
61
- this.runtime = runtime;
62
127
  }
63
128
 
64
129
  @JRubyMethod(meta = true)
65
- public static IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject key, IRubyObject cert) {
66
- RubyClass klass = (RubyClass) recv;
67
- IRubyObject newInstance = klass.newInstance(context,
68
- new IRubyObject[] { key, cert },
69
- Block.NULL_BLOCK);
130
+ public static IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext) {
131
+ RubyClass klass = (RubyClass) recv;
70
132
 
71
- return newInstance;
133
+ return klass.newInstance(context,
134
+ new IRubyObject[] { miniSSLContext },
135
+ Block.NULL_BLOCK);
72
136
  }
73
137
 
74
138
  @JRubyMethod
75
- public IRubyObject initialize(IRubyObject key, IRubyObject cert)
76
- throws java.security.KeyStoreException,
77
- java.io.FileNotFoundException,
78
- java.io.IOException,
79
- java.io.FileNotFoundException,
80
- java.security.NoSuchAlgorithmException,
81
- java.security.KeyManagementException,
82
- java.security.cert.CertificateException,
83
- java.security.UnrecoverableKeyException
84
- {
139
+ public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLContext)
140
+ throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
85
141
  KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
86
- KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
87
-
88
- char[] pass = "blahblah".toCharArray();
89
142
 
90
- ks.load(new FileInputStream(key.convertToString().asJavaString()),
91
- pass);
92
- ts.load(new FileInputStream(cert.convertToString().asJavaString()),
93
- pass);
143
+ char[] password = miniSSLContext.callMethod(threadContext, "keystore_pass").convertToString().asJavaString().toCharArray();
144
+ ks.load(new FileInputStream(miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString()),
145
+ password);
94
146
 
95
147
  KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
96
- kmf.init(ks, pass);
97
-
98
- TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
99
- tmf.init(ts);
148
+ kmf.init(ks, password);
100
149
 
101
150
  SSLContext sslCtx = SSLContext.getInstance("TLS");
102
151
 
103
- sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
104
-
105
- sslc = sslCtx;
106
-
107
- engine = sslc.createSSLEngine();
152
+ sslCtx.init(kmf.getKeyManagers(), null, null);
153
+ engine = sslCtx.createSSLEngine();
108
154
  engine.setUseClientMode(false);
109
- // engine.setNeedClientAuth(true);
110
155
 
111
156
  SSLSession session = engine.getSession();
112
- peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
113
- peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
114
- netData = ByteBuffer.allocate(session.getPacketBufferSize());
115
- peerNetData.limit(0);
116
- peerAppData.limit(0);
117
- netData.limit(0);
118
-
119
- peerNetData.clear();
120
- peerAppData.clear();
121
- netData.clear();
122
-
123
- dummy = ByteBuffer.allocate(0);
124
-
157
+ inboundNetData = new MiniSSLBuffer(session.getPacketBufferSize());
158
+ outboundAppData = new MiniSSLBuffer(session.getApplicationBufferSize());
159
+ outboundAppData.flip();
160
+ outboundNetData = new MiniSSLBuffer(session.getPacketBufferSize());
161
+
125
162
  return this;
126
163
  }
127
164
 
128
165
  @JRubyMethod
129
166
  public IRubyObject inject(IRubyObject arg) {
130
- byte[] bytes = arg.convertToString().getBytes();
131
-
132
- peerNetData.limit(peerNetData.limit() + bytes.length);
133
-
134
- log("capacity: " + peerNetData.capacity() + " limit: " + peerNetData.limit());
135
-
136
- peerNetData.put(bytes);
137
-
138
- log("netData: " + peerNetData.position() + "/" + peerAppData.limit());
139
- return this;
167
+ try {
168
+ byte[] bytes = arg.convertToString().getBytes();
169
+
170
+ log("Net Data post pre-inject: " + inboundNetData);
171
+ inboundNetData.put(bytes);
172
+ log("Net Data post post-inject: " + inboundNetData);
173
+
174
+ log("inject(): " + bytes.length + " encrypted bytes from request");
175
+ return this;
176
+ } catch (Exception e) {
177
+ e.printStackTrace();
178
+ throw new RuntimeException(e);
179
+ }
140
180
  }
141
181
 
142
- @JRubyMethod
143
- public IRubyObject read() throws javax.net.ssl.SSLException, Exception {
144
- peerAppData.clear();
145
- peerNetData.flip();
146
- SSLEngineResult res;
182
+ private enum SSLOperation {
183
+ WRAP,
184
+ UNWRAP
185
+ }
147
186
 
148
- log("available read: " + peerNetData.position() + "/ " + peerNetData.limit());
187
+ private SSLEngineResult doOp(SSLOperation sslOp, MiniSSLBuffer src, MiniSSLBuffer dst) throws SSLException {
188
+ SSLEngineResult res = null;
189
+ boolean retryOp = true;
190
+ while (retryOp) {
191
+ switch (sslOp) {
192
+ case WRAP:
193
+ res = engine.wrap(src.getRawBuffer(), dst.getRawBuffer());
194
+ break;
195
+ case UNWRAP:
196
+ res = engine.unwrap(src.getRawBuffer(), dst.getRawBuffer());
197
+ break;
198
+ default:
199
+ throw new IllegalStateException("Unknown SSLOperation: " + sslOp);
200
+ }
149
201
 
150
- if(!peerNetData.hasRemaining()) {
151
- return getRuntime().getNil();
202
+ switch (res.getStatus()) {
203
+ case BUFFER_OVERFLOW:
204
+ log("SSLOp#doRun(): overflow");
205
+ log("SSLOp#doRun(): dst data at overflow: " + dst);
206
+ // increase the buffer size to accommodate the overflowing data
207
+ int newSize = Math.max(engine.getSession().getPacketBufferSize(), engine.getSession().getApplicationBufferSize());
208
+ dst.resize(newSize + dst.position());
209
+ // retry the operation
210
+ retryOp = true;
211
+ break;
212
+ case BUFFER_UNDERFLOW:
213
+ log("SSLOp#doRun(): underflow");
214
+ log("SSLOp#doRun(): src data at underflow: " + src);
215
+ // need to wait for more data to come in before we retry
216
+ retryOp = false;
217
+ break;
218
+ default:
219
+ // other cases are OK and CLOSED. We're done here.
220
+ retryOp = false;
221
+ }
152
222
  }
153
223
 
154
- do {
155
- res = engine.unwrap(peerNetData, peerAppData);
156
- } while(res.getStatus() == SSLEngineResult.Status.OK &&
157
- res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP &&
158
- res.bytesProduced() == 0);
159
-
160
- log("read: ", res);
161
-
162
- if(peerNetData.hasRemaining()) {
163
- log("STILL HAD peerNetData!");
224
+ // after each op, run any delegated tasks if needed
225
+ if(engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
226
+ Runnable runnable;
227
+ while ((runnable = engine.getDelegatedTask()) != null) {
228
+ runnable.run();
229
+ }
164
230
  }
165
231
 
166
- peerNetData.position(0);
167
- peerNetData.limit(0);
232
+ return res;
233
+ }
168
234
 
169
- HandshakeStatus hsStatus = runDelegatedTasks(res, engine);
235
+ @JRubyMethod
236
+ public IRubyObject read() throws Exception {
237
+ try {
238
+ inboundNetData.flip();
170
239
 
171
- if(res.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
172
- return getRuntime().getNil();
173
- }
240
+ if(!inboundNetData.hasRemaining()) {
241
+ return getRuntime().getNil();
242
+ }
174
243
 
175
- if(hsStatus == HandshakeStatus.NEED_WRAP) {
176
- netData.clear();
177
- log("netData: " + netData.limit());
178
- engine.wrap(dummy, netData);
179
- return getRuntime().getNil();
180
- }
244
+ log("read(): inboundNetData prepped for read: " + inboundNetData);
245
+
246
+ MiniSSLBuffer inboundAppData = new MiniSSLBuffer(engine.getSession().getApplicationBufferSize());
247
+ SSLEngineResult res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
248
+ log("read(): after initial unwrap", engine, res);
249
+
250
+ log("read(): Net Data post unwrap: " + inboundNetData);
251
+
252
+ HandshakeStatus handshakeStatus = engine.getHandshakeStatus();
253
+ boolean done = false;
254
+ while (!done) {
255
+ switch (handshakeStatus) {
256
+ case NEED_WRAP:
257
+ res = doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
258
+ log("read(): after handshake wrap", engine, res);
259
+ break;
260
+ case NEED_UNWRAP:
261
+ res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
262
+ log("read(): after handshake unwrap", engine, res);
263
+ if (res.getStatus() == Status.BUFFER_UNDERFLOW) {
264
+ // need more data before we can shake more hands
265
+ done = true;
266
+ }
267
+ break;
268
+ default:
269
+ done = true;
270
+ }
271
+ handshakeStatus = engine.getHandshakeStatus();
272
+ }
181
273
 
182
- if(hsStatus == HandshakeStatus.NEED_UNWRAP) {
183
- return getRuntime().getNil();
274
+ if (inboundNetData.hasRemaining()) {
275
+ log("Net Data post pre-compact: " + inboundNetData);
276
+ inboundNetData.compact();
277
+ log("Net Data post post-compact: " + inboundNetData);
278
+ } else {
279
+ log("Net Data post pre-reset: " + inboundNetData);
280
+ inboundNetData.clear();
281
+ log("Net Data post post-reset: " + inboundNetData);
282
+ }
184
283
 
185
- // log("peerNet: " + peerNetData.position() + "/" + peerNetData.limit());
186
- // log("peerApp: " + peerAppData.position() + "/" + peerAppData.limit());
284
+ ByteList appDataByteList = inboundAppData.asByteList();
285
+ if (appDataByteList == null) {
286
+ return getRuntime().getNil();
287
+ }
187
288
 
188
- // peerNetData.compact();
289
+ RubyString str = getRuntime().newString("");
290
+ str.setValue(appDataByteList);
189
291
 
190
- // log("peerNet: " + peerNetData.position() + "/" + peerNetData.limit());
191
- // do {
192
- // res = engine.unwrap(peerNetData, peerAppData);
193
- // } while(res.getStatus() == SSLEngineResult.Status.OK &&
194
- // res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP &&
195
- // res.bytesProduced() == 0);
196
- // return getRuntime().getNil();
292
+ logPlain("\n");
293
+ log("read(): begin dump of request data >>>>\n");
294
+ if (str.asJavaString().getBytes().length < 1000) {
295
+ logPlain(str.asJavaString() + "\n");
296
+ }
297
+ logPlain("Num bytes: " + str.asJavaString().getBytes().length + "\n");
298
+ log("read(): end dump of request data <<<<\n");
299
+ return str;
300
+ } catch (Exception e) {
301
+ e.printStackTrace();
302
+ throw new RuntimeException(e);
197
303
  }
198
-
199
- // if(peerAppData.position() == 0 &&
200
- // res.getStatus() == SSLEngineResult.Status.OK &&
201
- // peerNetData.hasRemaining()) {
202
- // res = engine.unwrap(peerNetData, peerAppData);
203
- // }
204
-
205
- byte[] bss = new byte[peerAppData.limit()];
206
-
207
- peerAppData.get(bss);
208
-
209
- RubyString str = getRuntime().newString("");
210
- str.setValue(new ByteList(bss));
211
-
212
- return str;
213
304
  }
214
305
 
215
- private static HandshakeStatus runDelegatedTasks(SSLEngineResult result,
216
- SSLEngine engine) throws Exception {
217
-
218
- HandshakeStatus hsStatus = result.getHandshakeStatus();
219
-
220
- if(hsStatus == HandshakeStatus.NEED_TASK) {
221
- Runnable runnable;
222
- while ((runnable = engine.getDelegatedTask()) != null) {
223
- log("\trunning delegated task...");
224
- runnable.run();
225
- }
226
- hsStatus = engine.getHandshakeStatus();
227
- if (hsStatus == HandshakeStatus.NEED_TASK) {
228
- throw new Exception(
229
- "handshake shouldn't need additional tasks");
230
- }
231
- log("\tnew HandshakeStatus: " + hsStatus);
306
+ private static void log(String str, SSLEngine engine, SSLEngineResult result) {
307
+ if (DEBUG) {
308
+ log(str + " " + result.getStatus() + "/" + engine.getHandshakeStatus() +
309
+ "---bytes consumed: " + result.bytesConsumed() +
310
+ ", bytes produced: " + result.bytesProduced());
232
311
  }
233
-
234
- return hsStatus;
235
312
  }
236
-
237
-
238
- private static void log(String str, SSLEngineResult result) {
239
- System.out.println("The format of the SSLEngineResult is: \n" +
240
- "\t\"getStatus() / getHandshakeStatus()\" +\n" +
241
- "\t\"bytesConsumed() / bytesProduced()\"\n");
242
-
243
- HandshakeStatus hsStatus = result.getHandshakeStatus();
244
- log(str +
245
- result.getStatus() + "/" + hsStatus + ", " +
246
- result.bytesConsumed() + "/" + result.bytesProduced() +
247
- " bytes");
248
- if (hsStatus == HandshakeStatus.FINISHED) {
249
- log("\t...ready for application data");
313
+
314
+ private static void log(String str) {
315
+ if (DEBUG) {
316
+ System.out.println("MiniSSL.java: " + str);
250
317
  }
251
318
  }
252
319
 
253
- private static void log(String str) {
254
- System.out.println(str);
320
+ private static void logPlain(String str) {
321
+ if (DEBUG) {
322
+ System.out.println(str);
323
+ }
255
324
  }
256
-
257
-
258
325
 
259
326
  @JRubyMethod
260
- public IRubyObject write(IRubyObject arg) throws javax.net.ssl.SSLException {
261
- log("write from: " + netData.position());
262
-
263
- byte[] bls = arg.convertToString().getBytes();
264
- ByteBuffer src = ByteBuffer.wrap(bls);
327
+ public IRubyObject write(IRubyObject arg) {
328
+ try {
329
+ log("write(): begin dump of response data >>>>\n");
330
+ logPlain("\n");
331
+ if (arg.asJavaString().getBytes().length < 1000) {
332
+ logPlain(arg.asJavaString() + "\n");
333
+ }
334
+ logPlain("Num bytes: " + arg.asJavaString().getBytes().length + "\n");
335
+ log("write(): end dump of response data <<<<\n");
265
336
 
266
- SSLEngineResult res = engine.wrap(src, netData);
337
+ byte[] bls = arg.convertToString().getBytes();
338
+ outboundAppData = new MiniSSLBuffer(bls);
267
339
 
268
- return getRuntime().newFixnum(res.bytesConsumed());
340
+ return getRuntime().newFixnum(bls.length);
341
+ } catch (Exception e) {
342
+ e.printStackTrace();
343
+ throw new RuntimeException(e);
344
+ }
269
345
  }
270
346
 
271
347
  @JRubyMethod
272
- public IRubyObject extract() {
273
- netData.flip();
348
+ public IRubyObject extract() throws SSLException {
349
+ try {
350
+ ByteList dataByteList = outboundNetData.asByteList();
351
+ if (dataByteList != null) {
352
+ RubyString str = getRuntime().newString("");
353
+ str.setValue(dataByteList);
354
+ return str;
355
+ }
274
356
 
275
- if(!netData.hasRemaining()) {
276
- return getRuntime().getNil();
277
- }
357
+ if (!outboundAppData.hasRemaining()) {
358
+ return getRuntime().getNil();
359
+ }
278
360
 
279
- byte[] bss = new byte[netData.limit()];
361
+ outboundNetData.clear();
362
+ SSLEngineResult res = doOp(SSLOperation.WRAP, outboundAppData, outboundNetData);
363
+ log("extract(): bytes consumed: " + res.bytesConsumed() + "\n");
364
+ log("extract(): bytes produced: " + res.bytesProduced() + "\n");
365
+ dataByteList = outboundNetData.asByteList();
366
+ if (dataByteList == null) {
367
+ return getRuntime().getNil();
368
+ }
280
369
 
281
- netData.get(bss);
282
- netData.clear();
370
+ RubyString str = getRuntime().newString("");
371
+ str.setValue(dataByteList);
283
372
 
284
- RubyString str = getRuntime().newString("");
285
- str.setValue(new ByteList(bss));
373
+ log("extract(): " + dataByteList.getRealSize() + " encrypted bytes for response");
286
374
 
287
- return str;
375
+ return str;
376
+ } catch (Exception e) {
377
+ e.printStackTrace();
378
+ throw new RuntimeException(e);
379
+ }
288
380
  }
289
381
  }
@@ -121,26 +121,36 @@ module Puma
121
121
 
122
122
  @listeners << [str, io]
123
123
  when "ssl"
124
- if IS_JRUBY
125
- @events.error "SSL not supported on JRuby"
126
- raise UnsupportedOption
127
- end
128
-
129
124
  params = Rack::Utils.parse_query uri.query
130
125
  require 'puma/minissl'
131
126
 
132
127
  ctx = MiniSSL::Context.new
133
- unless params['key']
134
- @events.error "Please specify the SSL key via 'key='"
135
- end
136
128
 
137
- ctx.key = params['key']
129
+ if defined?(JRUBY_VERSION)
130
+ unless params['keystore']
131
+ @events.error "Please specify the Java keystore via 'keystore='"
132
+ end
138
133
 
139
- unless params['cert']
140
- @events.error "Please specify the SSL cert via 'cert='"
141
- end
134
+ ctx.keystore = params['keystore']
135
+
136
+ unless params['keystore-pass']
137
+ @events.error "Please specify the Java keystore password via 'keystore-pass='"
138
+ end
139
+
140
+ ctx.keystore_pass = params['keystore-pass']
141
+ else
142
+ unless params['key']
143
+ @events.error "Please specify the SSL key via 'key='"
144
+ end
145
+
146
+ ctx.key = params['key']
142
147
 
143
- ctx.cert = params['cert']
148
+ unless params['cert']
149
+ @events.error "Please specify the SSL cert via 'cert='"
150
+ end
151
+
152
+ ctx.cert = params['cert']
153
+ end
144
154
 
145
155
  ctx.verify_mode = MiniSSL::VERIFY_NONE
146
156
 
@@ -215,11 +225,6 @@ module Puma
215
225
 
216
226
  def add_ssl_listener(host, port, ctx,
217
227
  optimize_for_latency=true, backlog=1024)
218
- if IS_JRUBY
219
- @events.error "SSL not supported on JRuby"
220
- raise UnsupportedOption
221
- end
222
-
223
228
  require 'puma/minissl'
224
229
 
225
230
  s = TCPServer.new(host, port)
@@ -239,11 +244,6 @@ module Puma
239
244
  end
240
245
 
241
246
  def inherited_ssl_listener(fd, ctx)
242
- if IS_JRUBY
243
- @events.error "SSL not supported on JRuby"
244
- raise UnsupportedOption
245
- end
246
-
247
247
  require 'puma/minissl'
248
248
  s = TCPServer.for_fd(fd)
249
249
  @ios << MiniSSL::Server.new(s, ctx)
@@ -183,7 +183,7 @@ module Puma
183
183
 
184
184
  # If we're not running under a Bundler context, then
185
185
  # report the info about the context we will be using
186
- if !ENV['BUNDLER_GEMFILE'] and File.exist?("Gemfile")
186
+ if !ENV['BUNDLE_GEMFILE'] and File.exist?("Gemfile")
187
187
  log "+ Gemfile in context: #{File.expand_path("Gemfile")}"
188
188
  end
189
189
 
@@ -28,8 +28,8 @@ module Puma
28
28
  # too taxing on performance.
29
29
  module Const
30
30
 
31
- PUMA_VERSION = VERSION = "2.8.2".freeze
32
- CODE_NAME = "Sir Edmund Percival Hillary".freeze
31
+ PUMA_VERSION = VERSION = "2.9.0".freeze
32
+ CODE_NAME = "Team High Five".freeze
33
33
 
34
34
  FAST_TRACK_KA_TIMEOUT = 0.2
35
35
 
@@ -69,7 +69,7 @@ module Puma
69
69
 
70
70
  return data.bytesize if need == 0
71
71
 
72
- data = data[need..-1]
72
+ data = data[wrote..-1]
73
73
  end
74
74
  end
75
75
 
@@ -91,31 +91,35 @@ module Puma
91
91
  class Context
92
92
  attr_accessor :verify_mode
93
93
 
94
- attr_reader :key
95
- attr_reader :cert
94
+ if defined?(JRUBY_VERSION)
95
+ # jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
96
+ attr_reader :keystore
97
+ attr_accessor :keystore_pass
96
98
 
97
- def key=(key)
98
- raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
99
- @key = key
100
- end
99
+ def keystore=(keystore)
100
+ raise ArgumentError, "No such keystore file '#{keystore}'" unless File.exist? keystore
101
+ @keystore = keystore
102
+ end
103
+ else
104
+ # non-jruby Context properties
105
+ attr_reader :key
106
+ attr_reader :cert
107
+
108
+ def key=(key)
109
+ raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
110
+ @key = key
111
+ end
101
112
 
102
- def cert=(cert)
103
- raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
104
- @cert = cert
113
+ def cert=(cert)
114
+ raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
115
+ @cert = cert
116
+ end
105
117
  end
106
118
  end
107
119
 
108
120
  VERIFY_NONE = 0
109
121
  VERIFY_PEER = 1
110
122
 
111
- #if defined?(JRUBY_VERSION)
112
- #class Engine
113
- #def self.server(key, cert)
114
- #new(key, cert)
115
- #end
116
- #end
117
- #end
118
-
119
123
  class Server
120
124
  def initialize(socket, ctx)
121
125
  @socket = socket
@@ -128,14 +132,14 @@ module Puma
128
132
 
129
133
  def accept
130
134
  io = @socket.accept
131
- engine = Engine.server @ctx.key, @ctx.cert
135
+ engine = Engine.server @ctx
132
136
 
133
137
  Socket.new io, engine
134
138
  end
135
139
 
136
140
  def accept_nonblock
137
141
  io = @socket.accept_nonblock
138
- engine = Engine.server @ctx.key, @ctx.cert
142
+ engine = Engine.server @ctx
139
143
 
140
144
  Socket.new io, engine
141
145
  end
Binary file
@@ -614,10 +614,10 @@ module Puma
614
614
  begin
615
615
  res_body.each do |part|
616
616
  if chunked
617
- client.syswrite part.bytesize.to_s(16)
618
- client.syswrite line_ending
617
+ fast_write client, part.bytesize.to_s(16)
618
+ fast_write client, line_ending
619
619
  fast_write client, part
620
- client.syswrite line_ending
620
+ fast_write client, line_ending
621
621
  else
622
622
  fast_write client, part
623
623
  end
@@ -626,7 +626,7 @@ module Puma
626
626
  end
627
627
 
628
628
  if chunked
629
- client.syswrite CLOSE_CHUNKED
629
+ fast_write client, CLOSE_CHUNKED
630
630
  client.flush
631
631
  end
632
632
  rescue SystemCallError, IOError
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
23
23
  s.extensions = ["ext/puma_http11/extconf.rb"]
24
24
  s.files = `git ls-files`.split($/)
25
25
  s.homepage = "http://puma.io"
26
- s.license = "BSD"
26
+ s.license = "BSD-3-Clause"
27
27
  s.rdoc_options = ["--main", "README.md"]
28
28
  s.require_paths = ["lib"]
29
29
  s.required_ruby_version = Gem::Requirement.new(">= 1.8.7")
@@ -1,25 +1,29 @@
1
1
  require 'test/unit'
2
-
3
- unless defined? JRUBY_VERSION
4
-
5
2
  require 'puma'
6
3
  require 'puma/minissl'
7
4
 
8
5
  class TestMiniSSL < Test::Unit::TestCase
9
6
 
10
- def test_raises_with_invalid_key_file
11
- ctx = Puma::MiniSSL::Context.new
7
+ if defined?(JRUBY_VERSION)
8
+ def test_raises_with_invalid_keystore_file
9
+ ctx = Puma::MiniSSL::Context.new
12
10
 
13
- exception = assert_raise(ArgumentError) { ctx.key = "/no/such/key" }
14
- assert_equal("No such key file '/no/such/key'", exception.message)
15
- end
11
+ exception = assert_raise(ArgumentError) { ctx.keystore = "/no/such/keystore" }
12
+ assert_equal("No such keystore file '/no/such/keystore'", exception.message)
13
+ end
14
+ else
15
+ def test_raises_with_invalid_key_file
16
+ ctx = Puma::MiniSSL::Context.new
16
17
 
17
- def test_raises_with_invalid_cert_file
18
- ctx = Puma::MiniSSL::Context.new
18
+ exception = assert_raise(ArgumentError) { ctx.key = "/no/such/key" }
19
+ assert_equal("No such key file '/no/such/key'", exception.message)
20
+ end
19
21
 
20
- exception = assert_raise(ArgumentError) { ctx.cert = "/no/such/cert" }
21
- assert_equal("No such cert file '/no/such/cert'", exception.message)
22
- end
23
- end
22
+ def test_raises_with_invalid_cert_file
23
+ ctx = Puma::MiniSSL::Context.new
24
24
 
25
+ exception = assert_raise(ArgumentError) { ctx.cert = "/no/such/cert" }
26
+ assert_equal("No such cert file '/no/such/cert'", exception.message)
27
+ end
28
+ end
25
29
  end
@@ -18,47 +18,12 @@ class TestPumaServer < Test::Unit::TestCase
18
18
 
19
19
  @events = Puma::Events.new STDOUT, STDERR
20
20
  @server = Puma::Server.new @app, @events
21
-
22
- if defined?(JRUBY_VERSION)
23
- @ssl_key = File.expand_path "../../examples/puma/keystore.jks", __FILE__
24
- @ssl_cert = @ssl_key
25
- else
26
- @ssl_key = File.expand_path "../../examples/puma/puma_keypair.pem", __FILE__
27
- @ssl_cert = File.expand_path "../../examples/puma/cert_puma.pem", __FILE__
28
- end
29
21
  end
30
22
 
31
23
  def teardown
32
24
  @server.stop(true)
33
25
  end
34
26
 
35
- def test_url_scheme_for_https
36
- ctx = Puma::MiniSSL::Context.new
37
-
38
- ctx.key = @ssl_key
39
- ctx.cert = @ssl_cert
40
-
41
- ctx.verify_mode = Puma::MiniSSL::VERIFY_NONE
42
-
43
- @server.add_ssl_listener @host, @port, ctx
44
- @server.run
45
-
46
- http = Net::HTTP.new @host, @port
47
- http.use_ssl = true
48
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
49
-
50
- body = nil
51
- http.start do
52
- req = Net::HTTP::Get.new "/", {}
53
-
54
- http.request(req) do |rep|
55
- body = rep.body
56
- end
57
- end
58
-
59
- assert_equal "https", body
60
- end unless defined? JRUBY_VERSION
61
-
62
27
  def test_proper_stringio_body
63
28
  data = nil
64
29
 
@@ -0,0 +1,91 @@
1
+ require "rbconfig"
2
+ require 'test/unit'
3
+ require 'socket'
4
+ require 'openssl'
5
+
6
+ require 'puma/minissl'
7
+ require 'puma/server'
8
+
9
+ require 'net/https'
10
+
11
+ class TestPumaServerSSL < Test::Unit::TestCase
12
+
13
+ def setup
14
+ @port = 3212
15
+ @host = "127.0.0.1"
16
+
17
+ @app = lambda { |env| [200, {}, [env['rack.url_scheme']]] }
18
+
19
+ ctx = Puma::MiniSSL::Context.new
20
+
21
+ if defined?(JRUBY_VERSION)
22
+ ctx.keystore = File.expand_path "../../examples/puma/keystore.jks", __FILE__
23
+ ctx.keystore_pass = 'blahblah'
24
+ else
25
+ ctx.key = File.expand_path "../../examples/puma/puma_keypair.pem", __FILE__
26
+ ctx.cert = File.expand_path "../../examples/puma/cert_puma.pem", __FILE__
27
+ end
28
+
29
+ ctx.verify_mode = Puma::MiniSSL::VERIFY_NONE
30
+
31
+ @events = Puma::Events.new STDOUT, STDERR
32
+ @server = Puma::Server.new @app, @events
33
+ @server.add_ssl_listener @host, @port, ctx
34
+ @server.run
35
+
36
+ @http = Net::HTTP.new @host, @port
37
+ @http.use_ssl = true
38
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
39
+ end
40
+
41
+ def teardown
42
+ @server.stop(true)
43
+ end
44
+
45
+ def test_url_scheme_for_https
46
+ body = nil
47
+ @http.start do
48
+ req = Net::HTTP::Get.new "/", {}
49
+
50
+ @http.request(req) do |rep|
51
+ body = rep.body
52
+ end
53
+ end
54
+
55
+ assert_equal "https", body
56
+ end
57
+
58
+ def test_very_large_return
59
+ giant = "x" * 2056610
60
+
61
+ @server.app = proc do
62
+ [200, {}, [giant]]
63
+ end
64
+
65
+ body = nil
66
+ @http.start do
67
+ req = Net::HTTP::Get.new "/"
68
+ @http.request(req) do |rep|
69
+ body = rep.body
70
+ end
71
+ end
72
+
73
+ assert_equal giant.bytesize, body.bytesize
74
+ end
75
+
76
+ def test_form_submit
77
+ body = nil
78
+ @http.start do
79
+ req = Net::HTTP::Post.new '/'
80
+ req.set_form_data('a' => '1', 'b' => '2')
81
+
82
+ @http.request(req) do |rep|
83
+ body = rep.body
84
+ end
85
+
86
+ end
87
+
88
+ assert_equal "https", body
89
+ end
90
+
91
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.2
4
+ version: 2.9.0
5
5
  platform: java
6
6
  authors:
7
7
  - Evan Phoenix
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-12 00:00:00.000000000 Z
11
+ date: 2014-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -50,12 +50,12 @@ dependencies:
50
50
  requirements:
51
51
  - - ~>
52
52
  - !ruby/object:Gem::Version
53
- version: 0.8.0
53
+ version: '0.8'
54
54
  requirement: !ruby/object:Gem::Requirement
55
55
  requirements:
56
56
  - - ~>
57
57
  - !ruby/object:Gem::Version
58
- version: 0.8.0
58
+ version: '0.8'
59
59
  prerelease: false
60
60
  type: :development
61
61
  - !ruby/object:Gem::Dependency
@@ -64,12 +64,12 @@ dependencies:
64
64
  requirements:
65
65
  - - ~>
66
66
  - !ruby/object:Gem::Version
67
- version: '3.11'
67
+ version: '3.12'
68
68
  requirement: !ruby/object:Gem::Requirement
69
69
  requirements:
70
70
  - - ~>
71
71
  - !ruby/object:Gem::Version
72
- version: '3.11'
72
+ version: '3.12'
73
73
  prerelease: false
74
74
  type: :development
75
75
  description: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications. Puma is intended for use in both development and production environments. In order to get the best throughput, it is highly recommended that you use a Ruby implementation with real threads like Rubinius or JRuby.
@@ -140,7 +140,6 @@ files:
140
140
  - lib/puma/jruby_restart.rb
141
141
  - lib/puma/minissl.rb
142
142
  - lib/puma/null_io.rb
143
- - lib/puma/puma_http11.jar
144
143
  - lib/puma/rack_default.rb
145
144
  - lib/puma/rack_patch.rb
146
145
  - lib/puma/reactor.rb
@@ -152,6 +151,14 @@ files:
152
151
  - lib/puma/util.rb
153
152
  - lib/rack/handler/puma.rb
154
153
  - puma.gemspec
154
+ - tools/jungle/README.md
155
+ - tools/jungle/init.d/README.md
156
+ - tools/jungle/init.d/puma
157
+ - tools/jungle/init.d/run-puma
158
+ - tools/jungle/upstart/README.md
159
+ - tools/jungle/upstart/puma-manager.conf
160
+ - tools/jungle/upstart/puma.conf
161
+ - tools/trickletest.rb
155
162
  - test/test_app_status.rb
156
163
  - test/test_cli.rb
157
164
  - test/test_config.rb
@@ -163,23 +170,17 @@ files:
163
170
  - test/test_null_io.rb
164
171
  - test/test_persistent.rb
165
172
  - test/test_puma_server.rb
173
+ - test/test_puma_server_ssl.rb
166
174
  - test/test_rack_handler.rb
167
175
  - test/test_rack_server.rb
168
176
  - test/test_tcp_rack.rb
169
177
  - test/test_thread_pool.rb
170
178
  - test/test_unix_socket.rb
171
179
  - test/test_ws.rb
172
- - tools/jungle/README.md
173
- - tools/jungle/init.d/README.md
174
- - tools/jungle/init.d/puma
175
- - tools/jungle/init.d/run-puma
176
- - tools/jungle/upstart/README.md
177
- - tools/jungle/upstart/puma-manager.conf
178
- - tools/jungle/upstart/puma.conf
179
- - tools/trickletest.rb
180
+ - lib/puma/puma_http11.jar
180
181
  homepage: http://puma.io
181
182
  licenses:
182
- - BSD
183
+ - BSD-3-Clause
183
184
  metadata: {}
184
185
  post_install_message:
185
186
  rdoc_options:
@@ -199,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
199
200
  version: '0'
200
201
  requirements: []
201
202
  rubyforge_project:
202
- rubygems_version: 2.2.1
203
+ rubygems_version: 2.1.9
203
204
  signing_key:
204
205
  specification_version: 4
205
206
  summary: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications
@@ -215,6 +216,7 @@ test_files:
215
216
  - test/test_null_io.rb
216
217
  - test/test_persistent.rb
217
218
  - test/test_puma_server.rb
219
+ - test/test_puma_server_ssl.rb
218
220
  - test/test_rack_handler.rb
219
221
  - test/test_rack_server.rb
220
222
  - test/test_tcp_rack.rb