puma 2.12.3 → 2.16.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8fb289c9463eb0aec389606cde7abca74b3ba737
4
- data.tar.gz: 0f81b87b1bd9c0fa43d9c67e6d8b5c2f4a4b97a9
3
+ metadata.gz: 761911bec0cc543faf03994736a763dc7d87b6eb
4
+ data.tar.gz: 20cd1109cf893a9940549520e1f2e6c875532f2e
5
5
  SHA512:
6
- metadata.gz: bf53fee269a717c206e9031cfb26c6e7924eb346d7d17f5accf57d858813fb6302aecb1832385b2a87a94e68f452fae50f218cfe2f941617df8370c712e20d9f
7
- data.tar.gz: 92daf0ecf12fb7abda0655461ec72bff29f96dbf955f3434049f00e1e4de87b4b2672db36f03fe62ed36b0df0ea28305bd2dab80fa3de90ac3742a5954a42397
6
+ metadata.gz: c655e44ef966f2a4d8acd9b3d96b11a13072993edb1049ca65470cc0af3460bcd2e0d3f57f8afcec203cf6d9caad85cc7f1849454374ed65949b4284bd27ea33
7
+ data.tar.gz: 9b0e3acb94d4b0e3ae1a0f1eb1279dba87d600df67c928b455d8ea3ad47f3f90fb0a6854920776bad809dd46d013441e95385b981472d5f81f88d19ecbf4560e
data/DEPLOYMENT.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Deployment engineering for puma
2
2
 
3
3
  Puma is software that is expected to be run in a deployed environment eventually.
4
- You can centainly use it as your dev server only, but most people look to use
4
+ You can certainly use it as your dev server only, but most people look to use
5
5
  it in their production deployments as well.
6
6
 
7
7
  To that end, this is meant to serve as a foundation of wisdom how to do that
@@ -17,7 +17,7 @@ Welcome back!
17
17
 
18
18
  ## Single vs Cluster mode
19
19
 
20
- Puma was originally concieved as a thread-only webserver, but grew the ability to
20
+ Puma was originally conceived as a thread-only webserver, but grew the ability to
21
21
  also use processes in version 2.
22
22
 
23
23
  Here are some rules of thumb:
@@ -27,7 +27,7 @@ Here are some rules of thumb:
27
27
  * Use cluster mode and set the number of workers to 1.5x the number of cpu cores
28
28
  in the machine, minimum 2.
29
29
  * Set the number of threads to desired concurrent requests / number of workers.
30
- Puma defaults to 8 and thats a decent number.
30
+ Puma defaults to 8 and that's a decent number.
31
31
 
32
32
  #### Migrating from Unicorn
33
33
 
data/History.txt CHANGED
@@ -1,3 +1,167 @@
1
+ === 2.16.0 / 2016-01-27
2
+
3
+ * 7 minor features:
4
+
5
+ * Add 'set_remote_address' config option
6
+ * Allow to run puma in silent mode
7
+ * Expose cli options in DSL
8
+ * Support passing JRuby keystore info in ssl_bind DSL
9
+ * Allow umask for unix:/// style control urls
10
+ * Expose `old_worker_count` in stats url
11
+ * Support TLS client auth (verify_mode) in jruby
12
+
13
+ * 7 bug fixes:
14
+
15
+ * Don't persist before_fork hook in state file
16
+ * Reload bundler before pulling in rack. Fixes #859
17
+ * Remove NEWRELIC_DISPATCHER env variable
18
+ * Cleanup C code
19
+ * Use Timeout.timeout instead of Object.timeout
20
+ * Make phased restarts faster
21
+ * Ignore the case of certain headers, because HTTP
22
+
23
+ * 1 doc changes:
24
+
25
+ * Test against the latest Ruby 2.1, 2.2, 2.3, head and JRuby 9.0.4.0 on Travis
26
+
27
+ * 12 merged PRs
28
+ * Merge pull request #822 from kwugirl/remove_NEWRELIC_DISPATCHER
29
+ * Merge pull request #833 from joemiller/jruby-client-tls-auth
30
+ * Merge pull request #837 from YuriSolovyov/ssl-keystore-jruby
31
+ * Merge pull request #839 from mezuka/master
32
+ * Merge pull request #845 from deepj/timeout-deprecation
33
+ * Merge pull request #846 from sriedel/strip_before_fork
34
+ * Merge pull request #850 from deepj/travis
35
+ * Merge pull request #853 from Jeffrey6052/patch-1
36
+ * Merge pull request #857 from zendesk/faster_phased_restarts
37
+ * Merge pull request #858 from mlarraz/fix_some_warnings
38
+ * Merge pull request #860 from zendesk/expose_old_worker_count
39
+ * Merge pull request #861 from zendesk/allow_control_url_umask
40
+
41
+ === 2.15.3 / 2015-11-07
42
+
43
+ * 1 bug fix:
44
+
45
+ * Fix JRuby parser
46
+
47
+ === 2.15.2 / 2015-11-06
48
+
49
+ * 2 bug fixes:
50
+ * ext/puma_http11: handle duplicate headers as per RFC
51
+ * Only set ctx.ca iff there is a params['ca'] to set with.
52
+
53
+ * 2 PRs merged:
54
+ * Merge pull request #818 from unleashed/support-duplicate-headers
55
+ * Merge pull request #819 from VictorLowther/fix-ca-and-verify_null-exception
56
+
57
+ === 2.15.1 / 2015-11-06
58
+
59
+ * 1 bug fix:
60
+
61
+ * Allow older openssl versions
62
+
63
+ === 2.15.0 / 2015-11-06
64
+
65
+ * 6 minor features:
66
+ * Allow setting ca without setting a verify mode
67
+ * Make jungle for init.d support rbenv
68
+ * Use SSL_CTX_use_certificate_chain_file for full chain
69
+ * cluster: add worker_boot_timeout option
70
+ * configuration: allow empty tags to mean no tag desired
71
+ * puma/cli: support specifying STD{OUT,ERR} redirections and append mode
72
+
73
+ * 5 bug fixes:
74
+ * Disable SSL Compression
75
+ * Fix bug setting worker_directory when using a symlink directory
76
+ * Fix error message in DSL that was slightly inaccurate
77
+ * Pumactl: set correct process name. Fixes #563
78
+ * thread_pool: fix race condition when shutting down workers
79
+
80
+ * 10 doc fixes:
81
+ * Add before_fork explanation in Readme.md
82
+ * Correct spelling in DEPLOYMENT.md
83
+ * Correct spelling in docs/nginx.md
84
+ * Fix spelling errors.
85
+ * Fix typo in deployment description
86
+ * Fix typos (it's -> its) in events.rb and server.rb
87
+ * fixing for typo mentioned in #803
88
+ * Spelling correction for README
89
+ * thread_pool: fix typos in comment
90
+ * More explicit docs for worker_timeout
91
+
92
+ * 18 PRs merged:
93
+ * Merge pull request #768 from nathansamson/patch-1
94
+ * Merge pull request #773 from rossta/spelling_corrections
95
+ * Merge pull request #774 from snow/master
96
+ * Merge pull request #781 from sunsations/fix-typo
97
+ * Merge pull request #791 from unleashed/allow_empty_tags
98
+ * Merge pull request #793 from robdimarco/fix-working-directory-symlink-bug
99
+ * Merge pull request #794 from peterkeen/patch-1
100
+ * Merge pull request #795 from unleashed/redirects-from-cmdline
101
+ * Merge pull request #796 from cschneid/fix_dsl_message
102
+ * Merge pull request #799 from annafw/master
103
+ * Merge pull request #800 from liamseanbrady/fix_typo
104
+ * Merge pull request #801 from scottjg/ssl-chain-file
105
+ * Merge pull request #802 from scottjg/ssl-crimes
106
+ * Merge pull request #804 from burningTyger/patch-2
107
+ * Merge pull request #809 from unleashed/threadpool-fix-race-in-shutdown
108
+ * Merge pull request #810 from vlmonk/fix-pumactl-restart-bug
109
+ * Merge pull request #814 from schneems/schneems/worker_timeout-docs
110
+ * Merge pull request #817 from unleashed/worker-boot-timeout
111
+
112
+ === 2.14.0 / 2015-09-18
113
+
114
+ * 1 minor feature:
115
+ * Make building with SSL support optional
116
+
117
+ * 1 bug fix:
118
+ * Use Rack::Builder if available. Fixes #735
119
+
120
+ === 2.13.4 / 2015-08-16
121
+
122
+ * 1 bug fix:
123
+ * Use the environment possible set by the config early and from
124
+ the config file later (if set).
125
+
126
+ === 2.13.3 / 2015-08-15
127
+
128
+ Seriously, I need to revamp config with tests.
129
+
130
+ * 1 bug fix:
131
+ * Fix preserving options before cleaning for state. Fixes #769
132
+
133
+ === 2.13.2 / 2015-08-15
134
+
135
+ The "clearly I don't have enough tests for the config" release.
136
+
137
+ * 1 bug fix:
138
+ * Fix another place binds wasn't initialized. Fixes #767
139
+
140
+ === 2.13.1 / 2015-08-15
141
+
142
+ * 2 bug fixes:
143
+ * Fix binds being masked in config files. Fixes #765
144
+ * Use options from the config file properly in pumactl. Fixes #764
145
+
146
+ === 2.13.0 / 2015-08-14
147
+
148
+ * 1 minor feature:
149
+ * Add before_fork hooks option.
150
+
151
+ * 3 bug fixes:
152
+ * Check for OPENSSL_NO_ECDH before using ECDH
153
+ * Eliminate logging overhead from JRuby SSL
154
+ * Prefer cli options over config file ones. Fixes #669
155
+
156
+ * 1 deprecation:
157
+ * Add deprecation warning to capistrano.rb. Fixes #673
158
+
159
+ * 4 PRs merged:
160
+ * Merge pull request #668 from kcollignon/patch-1
161
+ * Merge pull request #754 from nathansamson/before_boot
162
+ * Merge pull request #759 from BenV/fix-centos6-build
163
+ * Merge pull request #761 from looker/no-log
164
+
1
165
  === 2.12.3 / 2015-08-03
2
166
 
3
167
  * 8 minor bugs fixed:
data/README.md CHANGED
@@ -117,6 +117,25 @@ If you're preloading your application and using ActiveRecord, it's recommend you
117
117
  end
118
118
  end
119
119
 
120
+ On top of that, you can specify a block in your configuration file that will be run before workers are forked
121
+
122
+ # config/puma.rb
123
+ before_fork do
124
+ # configuration here
125
+ end
126
+
127
+ This code can be used to clean up before forking to clients, allowing
128
+ you to do some Puma-specific things that you don't want to embed in your application.
129
+
130
+ If you're preloading your application and using ActiveRecord, it's recommend you close any connections to the database here to prevent connection leakage:
131
+
132
+ # config/puma.rb
133
+ before_fork do
134
+ ActiveRecord::Base.connection_pool.disconnect!
135
+ end
136
+
137
+ This rule applies to any connections to external services (Redis, databases, memcache, ...) that might be started automatically by the framework.
138
+
120
139
  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
140
 
122
141
  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.
@@ -167,7 +186,7 @@ You can also provide a configuration file which Puma will use with the `-C` (or
167
186
 
168
187
  $ puma -C /path/to/config
169
188
 
170
- By default, if no configuration file is specifed, Puma will look for a configuration file at config/puma.rb. If an environment is specified, either via the `-e` and `--environment` flags, or through the `RACK_ENV` environment variable, the default file location will be config/puma/environment_name.rb.
189
+ By default, if no configuration file is specified, Puma will look for a configuration file at config/puma.rb. If an environment is specified, either via the `-e` and `--environment` flags, or through the `RACK_ENV` environment variable, the default file location will be config/puma/environment_name.rb.
171
190
 
172
191
  If you want to prevent Puma from looking for a configuration file in those locations, provide a dash as the argument to the `-C` (or `--config`) flag:
173
192
 
@@ -248,7 +267,7 @@ and then
248
267
  $ bundle exec cap puma:start
249
268
  $ bundle exec cap puma:restart
250
269
  $ bundle exec cap puma:stop
251
- $ bundle exec cap puma:phased_restart
270
+ $ bundle exec cap puma:phased-restart
252
271
  ```
253
272
 
254
273
  ## Contributing
data/docs/nginx.md CHANGED
@@ -40,7 +40,7 @@ server {
40
40
  }
41
41
 
42
42
  # check for index.html for directory index
43
- # if its there on the filesystem then rewite
43
+ # if it's there on the filesystem then rewrite
44
44
  # the url to add /index.html to the end of it
45
45
  # and then break to send it to the next config rules.
46
46
  if (-f $request_filename/index.html) {
@@ -2,8 +2,12 @@ require 'mkmf'
2
2
 
3
3
  dir_config("puma_http11")
4
4
 
5
- if %w'crypto libeay32'.find {|crypto| have_library(crypto, 'BIO_read')} and
6
- %w'ssl ssleay32'.find {|ssl| have_library(ssl, 'SSL_CTX_new')}
7
-
8
- create_makefile("puma/puma_http11")
5
+ unless ENV["DISABLE_SSL"]
6
+ if %w'crypto libeay32'.find {|crypto| have_library(crypto, 'BIO_read')} and
7
+ %w'ssl ssleay32'.find {|ssl| have_library(ssl, 'SSL_CTX_new')}
8
+
9
+ have_header "openssl/bio.h"
10
+ end
9
11
  end
12
+
13
+ create_makefile("puma/puma_http11")
@@ -1,12 +1,20 @@
1
1
  #define RSTRING_NOT_MODIFIED 1
2
+
2
3
  #include <ruby.h>
3
4
  #include <rubyio.h>
5
+
6
+ #ifdef HAVE_OPENSSL_BIO_H
7
+
4
8
  #include <openssl/bio.h>
5
9
  #include <openssl/ssl.h>
6
10
  #include <openssl/dh.h>
7
11
  #include <openssl/err.h>
8
12
  #include <openssl/x509.h>
9
13
 
14
+ #ifndef SSL_OP_NO_COMPRESSION
15
+ #define SSL_OP_NO_COMPRESSION 0
16
+ #endif
17
+
10
18
  typedef struct {
11
19
  BIO* read;
12
20
  BIO* write;
@@ -132,14 +140,14 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
132
140
  ctx = SSL_CTX_new(SSLv23_server_method());
133
141
  conn->ctx = ctx;
134
142
 
135
- SSL_CTX_use_certificate_file(ctx, RSTRING_PTR(cert), SSL_FILETYPE_PEM);
143
+ SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert));
136
144
  SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM);
137
145
 
138
146
  if (!NIL_P(ca)) {
139
147
  SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
140
148
  }
141
149
 
142
- SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE);
150
+ SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION);
143
151
  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
144
152
 
145
153
  SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
@@ -147,11 +155,13 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
147
155
  DH *dh = get_dh1024();
148
156
  SSL_CTX_set_tmp_dh(ctx, dh);
149
157
 
158
+ #ifndef OPENSSL_NO_ECDH
150
159
  EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_secp521r1);
151
160
  if (ecdh) {
152
161
  SSL_CTX_set_tmp_ecdh(ctx, ecdh);
153
162
  EC_KEY_free(ecdh);
154
163
  }
164
+ #endif
155
165
 
156
166
  ssl = SSL_new(ctx);
157
167
  conn->ssl = ssl;
@@ -238,7 +248,7 @@ void raise_error(SSL* ssl, int result) {
238
248
  VALUE engine_read(VALUE self) {
239
249
  ms_conn* conn;
240
250
  char buf[512];
241
- int bytes, n, error;
251
+ int bytes, error;
242
252
 
243
253
  Data_Get_Struct(self, ms_conn, conn);
244
254
 
@@ -345,6 +355,10 @@ VALUE engine_peercert(VALUE self) {
345
355
  return rb_cert_buf;
346
356
  }
347
357
 
358
+ VALUE noop(VALUE self) {
359
+ return Qnil;
360
+ }
361
+
348
362
  void Init_mini_ssl(VALUE puma) {
349
363
  VALUE mod, eng;
350
364
 
@@ -356,6 +370,8 @@ void Init_mini_ssl(VALUE puma) {
356
370
  mod = rb_define_module_under(puma, "MiniSSL");
357
371
  eng = rb_define_class_under(mod, "Engine", rb_cObject);
358
372
 
373
+ rb_define_singleton_method(mod, "check", noop, 0);
374
+
359
375
  eError = rb_define_class_under(mod, "SSLError", rb_eStandardError);
360
376
 
361
377
  rb_define_singleton_method(eng, "server", engine_init_server, 1);
@@ -369,3 +385,20 @@ void Init_mini_ssl(VALUE puma) {
369
385
 
370
386
  rb_define_method(eng, "peercert", engine_peercert, 0);
371
387
  }
388
+
389
+ #else
390
+
391
+ VALUE raise_error(VALUE self) {
392
+ rb_raise(rb_eStandardError, "SSL not available in this build");
393
+ return Qnil;
394
+ }
395
+
396
+ void Init_mini_ssl(VALUE puma) {
397
+ VALUE mod, eng;
398
+
399
+ mod = rb_define_module_under(puma, "MiniSSL");
400
+ rb_define_class_under(mod, "SSLError", rb_eStandardError);
401
+
402
+ rb_define_singleton_method(mod, "check", raise_error, 0);
403
+ }
404
+ #endif
@@ -82,11 +82,11 @@ public class Http11 extends RubyObject {
82
82
  private Http11Parser.FieldCB http_field = new Http11Parser.FieldCB() {
83
83
  public void call(Object data, int field, int flen, int value, int vlen) {
84
84
  RubyHash req = (RubyHash)data;
85
- RubyString v,f;
85
+ RubyString f;
86
+ IRubyObject v;
86
87
  validateMaxLength(flen, MAX_FIELD_NAME_LENGTH, MAX_FIELD_NAME_LENGTH_ERR);
87
88
  validateMaxLength(vlen, MAX_FIELD_VALUE_LENGTH, MAX_FIELD_VALUE_LENGTH_ERR);
88
89
 
89
- v = RubyString.newString(runtime, new ByteList(Http11.this.hp.parser.buffer,value,vlen));
90
90
  ByteList b = new ByteList(Http11.this.hp.parser.buffer,field,flen);
91
91
  for(int i = 0,j = b.length();i<j;i++) {
92
92
  if((b.get(i) & 0xFF) == '-') {
@@ -104,7 +104,16 @@ public class Http11 extends RubyObject {
104
104
  f = RubyString.newString(runtime, "HTTP_");
105
105
  f.cat(b);
106
106
  }
107
- req.op_aset(req.getRuntime().getCurrentContext(), f,v);
107
+
108
+ b = new ByteList(Http11.this.hp.parser.buffer, value, vlen);
109
+ v = req.op_aref(req.getRuntime().getCurrentContext(), f);
110
+ if (v.isNil()) {
111
+ req.op_aset(req.getRuntime().getCurrentContext(), f, RubyString.newString(runtime, b));
112
+ } else {
113
+ RubyString vs = v.convertToString();
114
+ vs.cat(RubyString.newString(runtime, ", "));
115
+ vs.cat(b);
116
+ }
108
117
  }
109
118
  };
110
119
 
@@ -1,7 +1,6 @@
1
1
  package org.jruby.puma;
2
2
 
3
3
  import org.jruby.Ruby;
4
- import org.jruby.RubyBoolean;
5
4
  import org.jruby.RubyClass;
6
5
  import org.jruby.RubyModule;
7
6
  import org.jruby.RubyObject;
@@ -14,6 +13,7 @@ import org.jruby.runtime.builtin.IRubyObject;
14
13
  import org.jruby.util.ByteList;
15
14
 
16
15
  import javax.net.ssl.KeyManagerFactory;
16
+ import javax.net.ssl.TrustManagerFactory;
17
17
  import javax.net.ssl.SSLContext;
18
18
  import javax.net.ssl.SSLEngine;
19
19
  import javax.net.ssl.SSLEngineResult;
@@ -39,9 +39,6 @@ public class MiniSSL extends RubyObject {
39
39
  }
40
40
  };
41
41
 
42
- // set to true to switch on our low-fi trace logging
43
- private static boolean DEBUG = false;
44
-
45
42
  public static void createMiniSSL(Ruby runtime) {
46
43
  RubyModule mPuma = runtime.defineModule("Puma");
47
44
  RubyModule ssl = mPuma.defineModuleUnder("MiniSSL");
@@ -140,23 +137,36 @@ public class MiniSSL extends RubyObject {
140
137
  public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLContext)
141
138
  throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
142
139
  KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
140
+ KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
143
141
 
144
142
  char[] password = miniSSLContext.callMethod(threadContext, "keystore_pass").convertToString().asJavaString().toCharArray();
145
- ks.load(new FileInputStream(miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString()),
146
- password);
143
+ String keystoreFile = miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString();
144
+ ks.load(new FileInputStream(keystoreFile), password);
145
+ ts.load(new FileInputStream(keystoreFile), password);
147
146
 
148
147
  KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
149
148
  kmf.init(ks, password);
150
149
 
150
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
151
+ tmf.init(ts);
152
+
151
153
  SSLContext sslCtx = SSLContext.getInstance("TLS");
152
154
 
153
- sslCtx.init(kmf.getKeyManagers(), null, null);
155
+ sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
154
156
  engine = sslCtx.createSSLEngine();
155
157
 
156
158
  String[] protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
157
159
  engine.setEnabledProtocols(protocols);
158
160
  engine.setUseClientMode(false);
159
161
 
162
+ long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger().getLongValue();
163
+ if ((verify_mode & 0x1) != 0) { // 'peer'
164
+ engine.setWantClientAuth(true);
165
+ }
166
+ if ((verify_mode & 0x2) != 0) { // 'force_peer'
167
+ engine.setNeedClientAuth(true);
168
+ }
169
+
160
170
  SSLSession session = engine.getSession();
161
171
  inboundNetData = new MiniSSLBuffer(session.getPacketBufferSize());
162
172
  outboundAppData = new MiniSSLBuffer(session.getApplicationBufferSize());
@@ -170,12 +180,7 @@ public class MiniSSL extends RubyObject {
170
180
  public IRubyObject inject(IRubyObject arg) {
171
181
  try {
172
182
  byte[] bytes = arg.convertToString().getBytes();
173
-
174
- log("Net Data post pre-inject: " + inboundNetData);
175
183
  inboundNetData.put(bytes);
176
- log("Net Data post post-inject: " + inboundNetData);
177
-
178
- log("inject(): " + bytes.length + " encrypted bytes from request");
179
184
  return this;
180
185
  } catch (Exception e) {
181
186
  e.printStackTrace();
@@ -205,8 +210,6 @@ public class MiniSSL extends RubyObject {
205
210
 
206
211
  switch (res.getStatus()) {
207
212
  case BUFFER_OVERFLOW:
208
- log("SSLOp#doRun(): overflow");
209
- log("SSLOp#doRun(): dst data at overflow: " + dst);
210
213
  // increase the buffer size to accommodate the overflowing data
211
214
  int newSize = Math.max(engine.getSession().getPacketBufferSize(), engine.getSession().getApplicationBufferSize());
212
215
  dst.resize(newSize + dst.position());
@@ -214,8 +217,6 @@ public class MiniSSL extends RubyObject {
214
217
  retryOp = true;
215
218
  break;
216
219
  case BUFFER_UNDERFLOW:
217
- log("SSLOp#doRun(): underflow");
218
- log("SSLOp#doRun(): src data at underflow: " + src);
219
220
  // need to wait for more data to come in before we retry
220
221
  retryOp = false;
221
222
  break;
@@ -245,25 +246,18 @@ public class MiniSSL extends RubyObject {
245
246
  return getRuntime().getNil();
246
247
  }
247
248
 
248
- log("read(): inboundNetData prepped for read: " + inboundNetData);
249
-
250
249
  MiniSSLBuffer inboundAppData = new MiniSSLBuffer(engine.getSession().getApplicationBufferSize());
251
- SSLEngineResult res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
252
- log("read(): after initial unwrap", engine, res);
253
-
254
- log("read(): Net Data post unwrap: " + inboundNetData);
250
+ doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
255
251
 
256
252
  HandshakeStatus handshakeStatus = engine.getHandshakeStatus();
257
253
  boolean done = false;
258
254
  while (!done) {
259
255
  switch (handshakeStatus) {
260
256
  case NEED_WRAP:
261
- res = doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
262
- log("read(): after handshake wrap", engine, res);
257
+ doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
263
258
  break;
264
259
  case NEED_UNWRAP:
265
- res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
266
- log("read(): after handshake unwrap", engine, res);
260
+ SSLEngineResult res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
267
261
  if (res.getStatus() == Status.BUFFER_UNDERFLOW) {
268
262
  // need more data before we can shake more hands
269
263
  done = true;
@@ -276,13 +270,9 @@ public class MiniSSL extends RubyObject {
276
270
  }
277
271
 
278
272
  if (inboundNetData.hasRemaining()) {
279
- log("Net Data post pre-compact: " + inboundNetData);
280
273
  inboundNetData.compact();
281
- log("Net Data post post-compact: " + inboundNetData);
282
274
  } else {
283
- log("Net Data post pre-reset: " + inboundNetData);
284
275
  inboundNetData.clear();
285
- log("Net Data post post-reset: " + inboundNetData);
286
276
  }
287
277
 
288
278
  ByteList appDataByteList = inboundAppData.asByteList();
@@ -292,54 +282,15 @@ public class MiniSSL extends RubyObject {
292
282
 
293
283
  RubyString str = getRuntime().newString("");
294
284
  str.setValue(appDataByteList);
295
-
296
- logPlain("\n");
297
- log("read(): begin dump of request data >>>>\n");
298
- if (str.asJavaString().getBytes().length < 1000) {
299
- logPlain(str.asJavaString() + "\n");
300
- }
301
- logPlain("Num bytes: " + str.asJavaString().getBytes().length + "\n");
302
- log("read(): end dump of request data <<<<\n");
303
285
  return str;
304
286
  } catch (Exception e) {
305
- if (DEBUG) {
306
- e.printStackTrace();
307
- }
308
287
  throw getRuntime().newEOFError(e.getMessage());
309
288
  }
310
289
  }
311
290
 
312
- private static void log(String str, SSLEngine engine, SSLEngineResult result) {
313
- if (DEBUG) {
314
- log(str + " " + result.getStatus() + "/" + engine.getHandshakeStatus() +
315
- "---bytes consumed: " + result.bytesConsumed() +
316
- ", bytes produced: " + result.bytesProduced());
317
- }
318
- }
319
-
320
- private static void log(String str) {
321
- if (DEBUG) {
322
- System.out.println("MiniSSL.java: " + str);
323
- }
324
- }
325
-
326
- private static void logPlain(String str) {
327
- if (DEBUG) {
328
- System.out.println(str);
329
- }
330
- }
331
-
332
291
  @JRubyMethod
333
292
  public IRubyObject write(IRubyObject arg) {
334
293
  try {
335
- log("write(): begin dump of response data >>>>\n");
336
- logPlain("\n");
337
- if (arg.asJavaString().getBytes().length < 1000) {
338
- logPlain(arg.asJavaString() + "\n");
339
- }
340
- logPlain("Num bytes: " + arg.asJavaString().getBytes().length + "\n");
341
- log("write(): end dump of response data <<<<\n");
342
-
343
294
  byte[] bls = arg.convertToString().getBytes();
344
295
  outboundAppData = new MiniSSLBuffer(bls);
345
296
 
@@ -365,9 +316,7 @@ public class MiniSSL extends RubyObject {
365
316
  }
366
317
 
367
318
  outboundNetData.clear();
368
- SSLEngineResult res = doOp(SSLOperation.WRAP, outboundAppData, outboundNetData);
369
- log("extract(): bytes consumed: " + res.bytesConsumed() + "\n");
370
- log("extract(): bytes produced: " + res.bytesProduced() + "\n");
319
+ doOp(SSLOperation.WRAP, outboundAppData, outboundNetData);
371
320
  dataByteList = outboundNetData.asByteList();
372
321
  if (dataByteList == null) {
373
322
  return getRuntime().getNil();
@@ -376,8 +325,6 @@ public class MiniSSL extends RubyObject {
376
325
  RubyString str = getRuntime().newString("");
377
326
  str.setValue(dataByteList);
378
327
 
379
- log("extract(): " + dataByteList.getRealSize() + " encrypted bytes for response");
380
-
381
328
  return str;
382
329
  } catch (Exception e) {
383
330
  e.printStackTrace();
@@ -176,14 +176,12 @@ static VALUE find_common_field_value(const char *field, size_t flen)
176
176
  void http_field(puma_parser* hp, const char *field, size_t flen,
177
177
  const char *value, size_t vlen)
178
178
  {
179
- VALUE v = Qnil;
180
179
  VALUE f = Qnil;
180
+ VALUE v;
181
181
 
182
182
  VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
183
183
  VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
184
184
 
185
- v = rb_str_new(value, vlen);
186
-
187
185
  f = find_common_field_value(field, flen);
188
186
 
189
187
  if (f == Qnil) {
@@ -201,7 +199,17 @@ void http_field(puma_parser* hp, const char *field, size_t flen,
201
199
  f = rb_str_new(hp->buf, new_size);
202
200
  }
203
201
 
204
- rb_hash_aset(hp->request, f, v);
202
+ /* check for duplicate header */
203
+ v = rb_hash_aref(hp->request, f);
204
+
205
+ if (v == Qnil) {
206
+ v = rb_str_new(value, vlen);
207
+ rb_hash_aset(hp->request, f, v);
208
+ } else {
209
+ /* if duplicate header, normalize to comma-separated values */
210
+ rb_str_cat2(v, ", ");
211
+ rb_str_cat(v, value, vlen);
212
+ }
205
213
  }
206
214
 
207
215
  void request_method(puma_parser* hp, const char *at, size_t length)