puma 4.1.1-java → 4.2.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.

@@ -42,7 +42,7 @@ module Puma
42
42
  @ios = []
43
43
  end
44
44
 
45
- attr_reader :listeners, :ios
45
+ attr_reader :ios
46
46
 
47
47
  def env(sock)
48
48
  @envs.fetch(sock, @proto_env)
@@ -50,14 +50,6 @@ module Puma
50
50
 
51
51
  def close
52
52
  @ios.each { |i| i.close }
53
- @unix_paths.each do |i|
54
- # Errno::ENOENT is intermittently raised
55
- begin
56
- unix_socket = UNIXSocket.new i
57
- unix_socket.close
58
- rescue Errno::ENOENT
59
- end
60
- end
61
53
  end
62
54
 
63
55
  def import_from_env
@@ -111,7 +103,16 @@ module Puma
111
103
  bak = params.fetch('backlog', 1024).to_i
112
104
 
113
105
  io = add_tcp_listener uri.host, uri.port, opt, bak
114
- logger.log "* Listening on #{str}"
106
+
107
+ @ios.each do |i|
108
+ addr = if i.local_address.ipv6?
109
+ "[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
110
+ else
111
+ i.local_address.ip_unpack.join(':')
112
+ end
113
+
114
+ logger.log "* Listening on tcp://#{addr}"
115
+ end
115
116
  end
116
117
 
117
118
  @listeners << [str, io] if io
@@ -413,5 +414,26 @@ module Puma
413
414
  s
414
415
  end
415
416
 
417
+ def close_listeners
418
+ @listeners.each do |l, io|
419
+ io.close
420
+ uri = URI.parse(l)
421
+ next unless uri.scheme == 'unix'
422
+ File.unlink("#{uri.host}#{uri.path}")
423
+ end
424
+ end
425
+
426
+ def close_unix_paths
427
+ @unix_paths.each { |up| File.unlink(up) if File.exist? up }
428
+ end
429
+
430
+ def redirects_for_restart
431
+ redirects = {:close_others => true}
432
+ @listeners.each_with_index do |(l, io), i|
433
+ ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
434
+ redirects[io.to_i] = io.to_i
435
+ end
436
+ redirects
437
+ end
416
438
  end
417
439
  end
@@ -161,6 +161,10 @@ module Puma
161
161
  user_config.prune_bundler
162
162
  end
163
163
 
164
+ o.on "--extra-runtime-dependencies GEM1,GEM2", "Defines any extra needed gems when using --prune-bundler" do |arg|
165
+ c.extra_runtime_dependencies arg.split(',')
166
+ end
167
+
164
168
  o.on "-q", "--quiet", "Do not log requests internally (default true)" do
165
169
  user_config.quiet
166
170
  end
@@ -24,11 +24,11 @@ module Puma
24
24
  class ConnectionError < RuntimeError; end
25
25
 
26
26
  # An instance of this class represents a unique request from a client.
27
- # For example a web request from a browser or from CURL. This
27
+ # For example, this could be a web request from a browser or from CURL.
28
28
  #
29
29
  # An instance of `Puma::Client` can be used as if it were an IO object
30
- # by the reactor, that's because the latter is expected to call `#to_io`
31
- # on any non-IO objects it polls. For example nio4r internally calls
30
+ # by the reactor. The reactor is expected to call `#to_io`
31
+ # on any non-IO objects it polls. For example, nio4r internally calls
32
32
  # `IO::try_convert` (which may call `#to_io`) when a new socket is
33
33
  # registered.
34
34
  #
@@ -36,6 +36,10 @@ module Puma
36
36
  # the header and body are fully buffered via the `try_to_finish` method.
37
37
  # They can be used to "time out" a response via the `timeout_at` reader.
38
38
  class Client
39
+ # The object used for a request with no body. All requests with
40
+ # no body share this one object since it has no state.
41
+ EmptyBody = NullIO.new
42
+
39
43
  include Puma::Const
40
44
  extend Puma::Delegation
41
45
 
@@ -144,179 +148,6 @@ module Puma
144
148
  end
145
149
  end
146
150
 
147
- # The object used for a request with no body. All requests with
148
- # no body share this one object since it has no state.
149
- EmptyBody = NullIO.new
150
-
151
- def setup_chunked_body(body)
152
- @chunked_body = true
153
- @partial_part_left = 0
154
- @prev_chunk = ""
155
-
156
- @body = Tempfile.new(Const::PUMA_TMP_BASE)
157
- @body.binmode
158
- @tempfile = @body
159
-
160
- return decode_chunk(body)
161
- end
162
-
163
- def decode_chunk(chunk)
164
- if @partial_part_left > 0
165
- if @partial_part_left <= chunk.size
166
- if @partial_part_left > 2
167
- @body << chunk[0..(@partial_part_left-3)] # skip the \r\n
168
- end
169
- chunk = chunk[@partial_part_left..-1]
170
- @partial_part_left = 0
171
- else
172
- @body << chunk if @partial_part_left > 2 # don't include the last \r\n
173
- @partial_part_left -= chunk.size
174
- return false
175
- end
176
- end
177
-
178
- if @prev_chunk.empty?
179
- io = StringIO.new(chunk)
180
- else
181
- io = StringIO.new(@prev_chunk+chunk)
182
- @prev_chunk = ""
183
- end
184
-
185
- while !io.eof?
186
- line = io.gets
187
- if line.end_with?("\r\n")
188
- len = line.strip.to_i(16)
189
- if len == 0
190
- @in_last_chunk = true
191
- @body.rewind
192
- rest = io.read
193
- last_crlf_size = "\r\n".bytesize
194
- if rest.bytesize < last_crlf_size
195
- @buffer = nil
196
- @partial_part_left = last_crlf_size - rest.bytesize
197
- return false
198
- else
199
- @buffer = rest[last_crlf_size..-1]
200
- @buffer = nil if @buffer.empty?
201
- set_ready
202
- return true
203
- end
204
- end
205
-
206
- len += 2
207
-
208
- part = io.read(len)
209
-
210
- unless part
211
- @partial_part_left = len
212
- next
213
- end
214
-
215
- got = part.size
216
-
217
- case
218
- when got == len
219
- @body << part[0..-3] # to skip the ending \r\n
220
- when got <= len - 2
221
- @body << part
222
- @partial_part_left = len - part.size
223
- when got == len - 1 # edge where we get just \r but not \n
224
- @body << part[0..-2]
225
- @partial_part_left = len - part.size
226
- end
227
- else
228
- @prev_chunk = line
229
- return false
230
- end
231
- end
232
-
233
- if @in_last_chunk
234
- set_ready
235
- true
236
- else
237
- false
238
- end
239
- end
240
-
241
- def read_chunked_body
242
- while true
243
- begin
244
- chunk = @io.read_nonblock(4096)
245
- rescue IO::WaitReadable
246
- return false
247
- rescue SystemCallError, IOError
248
- raise ConnectionError, "Connection error detected during read"
249
- end
250
-
251
- # No chunk means a closed socket
252
- unless chunk
253
- @body.close
254
- @buffer = nil
255
- set_ready
256
- raise EOFError
257
- end
258
-
259
- return true if decode_chunk(chunk)
260
- end
261
- end
262
-
263
- def setup_body
264
- @body_read_start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
265
-
266
- if @env[HTTP_EXPECT] == CONTINUE
267
- # TODO allow a hook here to check the headers before
268
- # going forward
269
- @io << HTTP_11_100
270
- @io.flush
271
- end
272
-
273
- @read_header = false
274
-
275
- body = @parser.body
276
-
277
- te = @env[TRANSFER_ENCODING2]
278
-
279
- if te && CHUNKED.casecmp(te) == 0
280
- return setup_chunked_body(body)
281
- end
282
-
283
- @chunked_body = false
284
-
285
- cl = @env[CONTENT_LENGTH]
286
-
287
- unless cl
288
- @buffer = body.empty? ? nil : body
289
- @body = EmptyBody
290
- set_ready
291
- return true
292
- end
293
-
294
- remain = cl.to_i - body.bytesize
295
-
296
- if remain <= 0
297
- @body = StringIO.new(body)
298
- @buffer = nil
299
- set_ready
300
- return true
301
- end
302
-
303
- if remain > MAX_BODY
304
- @body = Tempfile.new(Const::PUMA_TMP_BASE)
305
- @body.binmode
306
- @tempfile = @body
307
- else
308
- # The body[0,0] trick is to get an empty string in the same
309
- # encoding as body.
310
- @body = StringIO.new body[0,0]
311
- end
312
-
313
- @body.write body
314
-
315
- @body_remain = remain
316
-
317
- return false
318
- end
319
-
320
151
  def try_to_finish
321
152
  return read_body unless @read_header
322
153
 
@@ -417,6 +248,84 @@ module Puma
417
248
  true
418
249
  end
419
250
 
251
+ def write_error(status_code)
252
+ begin
253
+ @io << ERROR_RESPONSE[status_code]
254
+ rescue StandardError
255
+ end
256
+ end
257
+
258
+ def peerip
259
+ return @peerip if @peerip
260
+
261
+ if @remote_addr_header
262
+ hdr = (@env[@remote_addr_header] || LOCALHOST_ADDR).split(/[\s,]/).first
263
+ @peerip = hdr
264
+ return hdr
265
+ end
266
+
267
+ @peerip ||= @io.peeraddr.last
268
+ end
269
+
270
+ private
271
+
272
+ def setup_body
273
+ @body_read_start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
274
+
275
+ if @env[HTTP_EXPECT] == CONTINUE
276
+ # TODO allow a hook here to check the headers before
277
+ # going forward
278
+ @io << HTTP_11_100
279
+ @io.flush
280
+ end
281
+
282
+ @read_header = false
283
+
284
+ body = @parser.body
285
+
286
+ te = @env[TRANSFER_ENCODING2]
287
+
288
+ if te && CHUNKED.casecmp(te) == 0
289
+ return setup_chunked_body(body)
290
+ end
291
+
292
+ @chunked_body = false
293
+
294
+ cl = @env[CONTENT_LENGTH]
295
+
296
+ unless cl
297
+ @buffer = body.empty? ? nil : body
298
+ @body = EmptyBody
299
+ set_ready
300
+ return true
301
+ end
302
+
303
+ remain = cl.to_i - body.bytesize
304
+
305
+ if remain <= 0
306
+ @body = StringIO.new(body)
307
+ @buffer = nil
308
+ set_ready
309
+ return true
310
+ end
311
+
312
+ if remain > MAX_BODY
313
+ @body = Tempfile.new(Const::PUMA_TMP_BASE)
314
+ @body.binmode
315
+ @tempfile = @body
316
+ else
317
+ # The body[0,0] trick is to get an empty string in the same
318
+ # encoding as body.
319
+ @body = StringIO.new body[0,0]
320
+ end
321
+
322
+ @body.write body
323
+
324
+ @body_remain = remain
325
+
326
+ return false
327
+ end
328
+
420
329
  def read_body
421
330
  if @chunked_body
422
331
  return read_chunked_body
@@ -462,45 +371,124 @@ module Puma
462
371
  false
463
372
  end
464
373
 
465
- def set_ready
466
- if @body_read_start
467
- @env['puma.request_body_wait'] = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) - @body_read_start
374
+ def read_chunked_body
375
+ while true
376
+ begin
377
+ chunk = @io.read_nonblock(4096)
378
+ rescue IO::WaitReadable
379
+ return false
380
+ rescue SystemCallError, IOError
381
+ raise ConnectionError, "Connection error detected during read"
382
+ end
383
+
384
+ # No chunk means a closed socket
385
+ unless chunk
386
+ @body.close
387
+ @buffer = nil
388
+ set_ready
389
+ raise EOFError
390
+ end
391
+
392
+ return true if decode_chunk(chunk)
468
393
  end
469
- @requests_served += 1
470
- @ready = true
471
394
  end
472
395
 
473
- def write_400
474
- begin
475
- @io << ERROR_400_RESPONSE
476
- rescue StandardError
477
- end
396
+ def setup_chunked_body(body)
397
+ @chunked_body = true
398
+ @partial_part_left = 0
399
+ @prev_chunk = ""
400
+
401
+ @body = Tempfile.new(Const::PUMA_TMP_BASE)
402
+ @body.binmode
403
+ @tempfile = @body
404
+
405
+ return decode_chunk(body)
478
406
  end
479
407
 
480
- def write_408
481
- begin
482
- @io << ERROR_408_RESPONSE
483
- rescue StandardError
408
+ def decode_chunk(chunk)
409
+ if @partial_part_left > 0
410
+ if @partial_part_left <= chunk.size
411
+ if @partial_part_left > 2
412
+ @body << chunk[0..(@partial_part_left-3)] # skip the \r\n
413
+ end
414
+ chunk = chunk[@partial_part_left..-1]
415
+ @partial_part_left = 0
416
+ else
417
+ @body << chunk if @partial_part_left > 2 # don't include the last \r\n
418
+ @partial_part_left -= chunk.size
419
+ return false
420
+ end
484
421
  end
485
- end
486
422
 
487
- def write_500
488
- begin
489
- @io << ERROR_500_RESPONSE
490
- rescue StandardError
423
+ if @prev_chunk.empty?
424
+ io = StringIO.new(chunk)
425
+ else
426
+ io = StringIO.new(@prev_chunk+chunk)
427
+ @prev_chunk = ""
491
428
  end
492
- end
493
429
 
494
- def peerip
495
- return @peerip if @peerip
430
+ while !io.eof?
431
+ line = io.gets
432
+ if line.end_with?("\r\n")
433
+ len = line.strip.to_i(16)
434
+ if len == 0
435
+ @in_last_chunk = true
436
+ @body.rewind
437
+ rest = io.read
438
+ last_crlf_size = "\r\n".bytesize
439
+ if rest.bytesize < last_crlf_size
440
+ @buffer = nil
441
+ @partial_part_left = last_crlf_size - rest.bytesize
442
+ return false
443
+ else
444
+ @buffer = rest[last_crlf_size..-1]
445
+ @buffer = nil if @buffer.empty?
446
+ set_ready
447
+ return true
448
+ end
449
+ end
496
450
 
497
- if @remote_addr_header
498
- hdr = (@env[@remote_addr_header] || LOCALHOST_ADDR).split(/[\s,]/).first
499
- @peerip = hdr
500
- return hdr
451
+ len += 2
452
+
453
+ part = io.read(len)
454
+
455
+ unless part
456
+ @partial_part_left = len
457
+ next
458
+ end
459
+
460
+ got = part.size
461
+
462
+ case
463
+ when got == len
464
+ @body << part[0..-3] # to skip the ending \r\n
465
+ when got <= len - 2
466
+ @body << part
467
+ @partial_part_left = len - part.size
468
+ when got == len - 1 # edge where we get just \r but not \n
469
+ @body << part[0..-2]
470
+ @partial_part_left = len - part.size
471
+ end
472
+ else
473
+ @prev_chunk = line
474
+ return false
475
+ end
501
476
  end
502
477
 
503
- @peerip ||= @io.peeraddr.last
478
+ if @in_last_chunk
479
+ set_ready
480
+ true
481
+ else
482
+ false
483
+ end
484
+ end
485
+
486
+ def set_ready
487
+ if @body_read_start
488
+ @env['puma.request_body_wait'] = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) - @body_read_start
489
+ end
490
+ @requests_served += 1
491
+ @ready = true
504
492
  end
505
493
  end
506
494
  end