puma 3.5.2 → 3.6.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: 684ca59ad11f10e3bebb95bba60b96af418f4cd1
4
- data.tar.gz: 59375b16ad00a91016a1bea17066adcefd894e5b
3
+ metadata.gz: 59a23e7e6ba37bf306a2ae94c314daa0abf2f592
4
+ data.tar.gz: c9228af1cebabc34902fd222965a4c851efd84e0
5
5
  SHA512:
6
- metadata.gz: a8a5471623bdd37a9251198a9f3ac3dfb7ec1da3295b7102ae5f12ab8b89c07faea39e45061ed37e369f89a6612bd85c3992eb459350508ff7ac2e18ffe114d7
7
- data.tar.gz: d8c35d43256a6b3a1b21414afeb3b7033ea11b489156ad4dc429474a24a13789f7037a2d9c22a0f60377108844b20003f4fe7deca4301a2c21426dc98b549a0c
6
+ metadata.gz: 91433445f3ec432dfe5dd29361c06d8726e6c253351d2a66906442329edc6fe7e6b4dc47f120786a7a1adc11aae6b650ce7f0f6e829f63979ee8aa738d33e0ca
7
+ data.tar.gz: 2aea9f4334fdf09dd1ff5ff8b6a71a61ad0ef5527f3e08d98a73b17cf940d85c56e5bae6221ea4ddc09d8b12cc368359227462b3fc93a372da0327041300c397
@@ -1,3 +1,19 @@
1
+ === 3.6.0 / 2016-07-24
2
+
3
+ * 12 bug fixes:
4
+ * Add ability to detect a shutting down server. Fixes #932
5
+ * Add support for Expect: 100-continue. Fixes #519
6
+ * Check SSLContext better. Fixes #828
7
+ * Clarify behavior of '-t <num>'. Fixes #984
8
+ * Don't default to VERIFY_PEER. Fixes #1028
9
+ * Don't use ENV['PWD'] on windows. Fixes #1023
10
+ * Enlarge the scope of catching app exceptions. Fixes #1027
11
+ * Execute background hooks after daemonizing. Fixes #925
12
+ * Handle HUP as a stop unless there is IO redirection. Fixes #911
13
+ * Implement chunked request handling. Fixes #620
14
+ * Just rescue exception to return a 500. Fixes #1027
15
+ * Redirect IO in the jruby daemon mode. Fixes #778
16
+
1
17
  === 3.5.2 / 2016-07-20
2
18
 
3
19
  * 1 bug fix:
@@ -134,9 +134,13 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
134
134
  ID sym_key = rb_intern("key");
135
135
  VALUE key = rb_funcall(mini_ssl_ctx, sym_key, 0);
136
136
 
137
+ StringValue(key);
138
+
137
139
  ID sym_cert = rb_intern("cert");
138
140
  VALUE cert = rb_funcall(mini_ssl_ctx, sym_cert, 0);
139
141
 
142
+ StringValue(cert);
143
+
140
144
  ID sym_ca = rb_intern("ca");
141
145
  VALUE ca = rb_funcall(mini_ssl_ctx, sym_ca, 0);
142
146
 
@@ -150,6 +154,7 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
150
154
  SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM);
151
155
 
152
156
  if (!NIL_P(ca)) {
157
+ StringValue(ca);
153
158
  SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
154
159
  }
155
160
 
@@ -181,7 +181,7 @@ module Puma
181
181
  ctx.ca = params['ca'] if params['ca']
182
182
  end
183
183
 
184
- if params['verify_mode']
184
+ if params['verify_mode']
185
185
  ctx.verify_mode = case params['verify_mode']
186
186
  when "peer"
187
187
  MiniSSL::VERIFY_PEER
@@ -193,8 +193,6 @@ module Puma
193
193
  @events.error "Please specify a valid verify_mode="
194
194
  MiniSSL::VERIFY_NONE
195
195
  end
196
- else
197
- ctx.verify_mode = MiniSSL::VERIFY_PEER
198
196
  end
199
197
 
200
198
  if fd = @inherited_fds.delete(str)
@@ -170,7 +170,7 @@ module Puma
170
170
  if max
171
171
  c.threads min, max
172
172
  else
173
- c.threads 0, max
173
+ c.threads min, min
174
174
  end
175
175
  end
176
176
 
@@ -8,6 +8,7 @@ end
8
8
 
9
9
  require 'puma/detect'
10
10
  require 'puma/delegation'
11
+ require 'tempfile'
11
12
 
12
13
  if Puma::IS_JRUBY
13
14
  # We have to work around some OpenSSL buffer/io-readiness bugs
@@ -117,9 +118,116 @@ module Puma
117
118
  # no body share this one object since it has no state.
118
119
  EmptyBody = NullIO.new
119
120
 
121
+ def setup_chunked_body(body)
122
+ @chunked_body = true
123
+ @partial_part_left = 0
124
+ @prev_chunk = ""
125
+
126
+ @body = Tempfile.new(Const::PUMA_TMP_BASE)
127
+ @body.binmode
128
+ @tempfile = @body
129
+
130
+ return decode_chunk(body)
131
+ end
132
+
133
+ def decode_chunk(chunk)
134
+ if @partial_part_left > 0
135
+ if @partial_part_left <= chunk.size
136
+ @body << chunk[0..(@partial_part_left-3)] # skip the \r\n
137
+ chunk = chunk[@partial_part_left..-1]
138
+ else
139
+ @body << chunk
140
+ @partial_part_left -= chunk.size
141
+ return false
142
+ end
143
+ end
144
+
145
+ if @prev_chunk.empty?
146
+ io = StringIO.new(chunk)
147
+ else
148
+ io = StringIO.new(@prev_chunk+chunk)
149
+ @prev_chunk = ""
150
+ end
151
+
152
+ while !io.eof?
153
+ line = io.gets
154
+ if line.end_with?("\r\n")
155
+ len = line.strip.to_i(16)
156
+ if len == 0
157
+ @body.rewind
158
+ @buffer = nil
159
+ @requests_served += 1
160
+ @ready = true
161
+ return true
162
+ end
163
+
164
+ len += 2
165
+
166
+ part = io.read(len)
167
+
168
+ unless part
169
+ @partial_part_left = len
170
+ next
171
+ end
172
+
173
+ got = part.size
174
+
175
+ case
176
+ when got == len
177
+ @body << part[0..-3] # to skip the ending \r\n
178
+ when got <= len - 2
179
+ @body << part
180
+ @partial_part_left = len - part.size
181
+ when got == len - 1 # edge where we get just \r but not \n
182
+ @body << part[0..-2]
183
+ @partial_part_left = len - part.size
184
+ end
185
+ else
186
+ @prev_chunk = line
187
+ return false
188
+ end
189
+ end
190
+
191
+ return false
192
+ end
193
+
194
+ def read_chunked_body
195
+ while true
196
+ begin
197
+ chunk = @io.read_nonblock(4096)
198
+ rescue Errno::EAGAIN
199
+ return false
200
+ rescue SystemCallError, IOError
201
+ raise ConnectionError, "Connection error detected during read"
202
+ end
203
+
204
+ # No chunk means a closed socket
205
+ unless chunk
206
+ @body.close
207
+ @buffer = nil
208
+ @requests_served += 1
209
+ @ready = true
210
+ raise EOFError
211
+ end
212
+
213
+ return true if decode_chunk(chunk)
214
+ end
215
+ end
216
+
120
217
  def setup_body
121
218
  @in_data_phase = true
219
+ @read_header = false
220
+
122
221
  body = @parser.body
222
+
223
+ te = @env[TRANSFER_ENCODING2]
224
+
225
+ if te == CHUNKED
226
+ return setup_chunked_body(body)
227
+ end
228
+
229
+ @chunked_body = false
230
+
123
231
  cl = @env[CONTENT_LENGTH]
124
232
 
125
233
  unless cl
@@ -154,8 +262,6 @@ module Puma
154
262
 
155
263
  @body_remain = remain
156
264
 
157
- @read_header = false
158
-
159
265
  return false
160
266
  end
161
267
 
@@ -246,6 +352,10 @@ module Puma
246
352
  end
247
353
 
248
354
  def read_body
355
+ if @chunked_body
356
+ return read_chunked_body
357
+ end
358
+
249
359
  # Read an odd sized chunk so we can read even sized ones
250
360
  # after this
251
361
  remain = @body_remain
@@ -413,10 +413,12 @@ module Puma
413
413
 
414
414
  redirect_io
415
415
 
416
- start_control
416
+ Plugins.fire_background
417
417
 
418
418
  @launcher.write_state
419
419
 
420
+ start_control
421
+
420
422
  @master_read, @worker_write = read, @wakeup
421
423
 
422
424
  @launcher.config.run_hooks :before_fork, nil
@@ -100,8 +100,8 @@ module Puma
100
100
  # too taxing on performance.
101
101
  module Const
102
102
 
103
- PUMA_VERSION = VERSION = "3.5.2".freeze
104
- CODE_NAME = "Amateur Raccoon Rocketry".freeze
103
+ PUMA_VERSION = VERSION = "3.6.0".freeze
104
+ CODE_NAME = "Sleepy Sunday Serenity".freeze
105
105
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
106
106
 
107
107
  FAST_TRACK_KA_TIMEOUT = 0.2
@@ -219,7 +219,10 @@ module Puma
219
219
 
220
220
  HTTP_VERSION = "HTTP_VERSION".freeze
221
221
  HTTP_CONNECTION = "HTTP_CONNECTION".freeze
222
+ HTTP_EXPECT = "HTTP_EXPECT".freeze
223
+ CONTINUE = "100-continue".freeze
222
224
 
225
+ HTTP_11_100 = "HTTP/1.1 100 Continue\r\n\r\n".freeze
223
226
  HTTP_11_200 = "HTTP/1.1 200 OK\r\n".freeze
224
227
  HTTP_10_200 = "HTTP/1.0 200 OK\r\n".freeze
225
228
 
@@ -229,6 +232,7 @@ module Puma
229
232
  CONTENT_LENGTH2 = "content-length".freeze
230
233
  CONTENT_LENGTH_S = "Content-Length: ".freeze
231
234
  TRANSFER_ENCODING = "transfer-encoding".freeze
235
+ TRANSFER_ENCODING2 = "HTTP_TRANSFER_ENCODING".freeze
232
236
 
233
237
  CONNECTION_CLOSE = "Connection: close\r\n".freeze
234
238
  CONNECTION_KEEP_ALIVE = "Connection: Keep-Alive\r\n".freeze
@@ -236,6 +240,8 @@ module Puma
236
240
  TRANSFER_ENCODING_CHUNKED = "Transfer-Encoding: chunked\r\n".freeze
237
241
  CLOSE_CHUNKED = "0\r\n\r\n".freeze
238
242
 
243
+ CHUNKED = "chunked".freeze
244
+
239
245
  COLON = ": ".freeze
240
246
 
241
247
  NEWLINE = "\n".freeze
@@ -6,6 +6,6 @@ module Puma
6
6
  end
7
7
 
8
8
  def self.windows?
9
- RUBY_PLATFORM =~ /mswin32|ming32/
9
+ RUBY_PLATFORM =~ /mswin|ming|cygwin/
10
10
  end
11
11
  end
@@ -316,6 +316,11 @@ module Puma
316
316
  if dir = @options[:directory]
317
317
  @restart_dir = dir
318
318
 
319
+ elsif Puma.windows?
320
+ # I guess the value of PWD is garbage on windows so don't bother
321
+ # using it.
322
+ @restart_dir = Dir.pwd
323
+
319
324
  # Use the same trick as unicorn, namely favor PWD because
320
325
  # it will contain an unresolved symlink, useful for when
321
326
  # the pwd is /data/releases/current.
@@ -382,7 +387,11 @@ module Puma
382
387
 
383
388
  begin
384
389
  Signal.trap "SIGHUP" do
385
- @runner.redirect_io
390
+ if @runner.redirected_io?
391
+ @runner.redirect_io
392
+ else
393
+ stop
394
+ end
386
395
  end
387
396
  rescue Exception
388
397
  log "*** SIGHUP not implemented, signal based logs reopening unavailable!"
@@ -118,6 +118,11 @@ module Puma
118
118
  raise ArgumentError, "No such keystore file '#{keystore}'" unless File.exist? keystore
119
119
  @keystore = keystore
120
120
  end
121
+
122
+ def check
123
+ raise "Keystore not configured" unless @keystore
124
+ end
125
+
121
126
  else
122
127
  # non-jruby Context properties
123
128
  attr_reader :key
@@ -138,6 +143,11 @@ module Puma
138
143
  raise ArgumentError, "No such ca file '#{ca}'" unless File.exist? ca
139
144
  @ca = ca
140
145
  end
146
+
147
+ def check
148
+ raise "Key not configured" unless @key
149
+ raise "Cert not configured" unless @cert
150
+ end
141
151
  end
142
152
  end
143
153
 
@@ -156,6 +166,7 @@ module Puma
156
166
  end
157
167
 
158
168
  def accept
169
+ @ctx.check
159
170
  io = @socket.accept
160
171
  engine = Engine.server @ctx
161
172
 
@@ -163,6 +174,7 @@ module Puma
163
174
  end
164
175
 
165
176
  def accept_nonblock
177
+ @ctx.check
166
178
  io = @socket.accept_nonblock
167
179
  engine = Engine.server @ctx
168
180
 
@@ -28,6 +28,7 @@ module Puma
28
28
  class PluginRegistry
29
29
  def initialize
30
30
  @plugins = {}
31
+ @background = []
31
32
  end
32
33
 
33
34
  def register(name, cls)
@@ -53,6 +54,16 @@ module Puma
53
54
 
54
55
  raise UnknownPlugin, "file failed to register a plugin"
55
56
  end
57
+
58
+ def add_background(blk)
59
+ @background << blk
60
+ end
61
+
62
+ def fire_background
63
+ @background.each do |b|
64
+ Thread.new(&b)
65
+ end
66
+ end
56
67
  end
57
68
 
58
69
  Plugins = PluginRegistry.new
@@ -93,7 +104,7 @@ module Puma
93
104
  end
94
105
 
95
106
  def in_background(&blk)
96
- Thread.new(&blk)
107
+ Plugins.add_background blk
97
108
  end
98
109
 
99
110
  def workers_supported?
@@ -94,6 +94,10 @@ module Puma
94
94
  end
95
95
  end
96
96
 
97
+ def redirected_io?
98
+ @options[:redirect_stdout] || @options[:redirect_stderr]
99
+ end
100
+
97
101
  def redirect_io
98
102
  stdout = @options[:redirect_stdout]
99
103
  stderr = @options[:redirect_stderr]
@@ -242,6 +242,10 @@ module Puma
242
242
  @thread_pool = ThreadPool.new(@min_threads,
243
243
  @max_threads,
244
244
  IOBuffer) do |client, buffer|
245
+
246
+ # Advertise this server into the thread
247
+ Thread.current[ThreadLocalKey] = self
248
+
245
249
  process_now = false
246
250
 
247
251
  begin
@@ -399,6 +403,11 @@ module Puma
399
403
  #
400
404
  def process_client(client, buffer)
401
405
  begin
406
+
407
+ if client.env[HTTP_EXPECT] == CONTINUE
408
+ client.io << HTTP_11_100
409
+ end
410
+
402
411
  clean_thread_locals = @options[:clean_thread_locals]
403
412
  close_socket = true
404
413
 
@@ -587,7 +596,7 @@ module Puma
587
596
  headers = {}
588
597
  res_body = ["Request was internally terminated early\n"]
589
598
 
590
- rescue StandardError => e
599
+ rescue Exception => e
591
600
  @events.unknown_error self, e, "Rack app", env
592
601
 
593
602
  status, headers, res_body = lowlevel_error(e, env)
@@ -911,5 +920,15 @@ module Puma
911
920
  end
912
921
  end
913
922
  private :fast_write
923
+
924
+ ThreadLocalKey = :puma_server
925
+
926
+ def self.current
927
+ Thread.current[ThreadLocalKey]
928
+ end
929
+
930
+ def shutting_down?
931
+ @status == :stop || @status == :restart
932
+ end
914
933
  end
915
934
  end
@@ -44,6 +44,7 @@ module Puma
44
44
  if JRubyRestart.daemon?
45
45
  # load and bind before redirecting IO so errors show up on stdout/stderr
46
46
  load_and_bind
47
+ redirect_io
47
48
  end
48
49
 
49
50
  already_daemon = JRubyRestart.daemon_init
@@ -84,6 +85,8 @@ module Puma
84
85
  load_and_bind
85
86
  end
86
87
 
88
+ Plugins.fire_background
89
+
87
90
  @launcher.write_state
88
91
 
89
92
  start_control
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: 3.5.2
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-20 00:00:00.000000000 Z
11
+ date: 2016-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdoc