puma 7.2.2-java → 8.0.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/puma/minissl.rb CHANGED
@@ -25,9 +25,6 @@ module Puma
25
25
  @engine = engine
26
26
  @peercert = nil
27
27
  @reuse = nil
28
- @write_buffer = +''
29
- @write_owed = 0
30
- @write_owed_data = nil
31
28
  end
32
29
 
33
30
  # @!attribute [r] to_io
@@ -71,8 +68,9 @@ module Puma
71
68
 
72
69
  return output if output
73
70
 
74
- queue_ciphertext
75
- flush_write_buffer
71
+ while neg_data = @engine.extract
72
+ @socket.write neg_data
73
+ end
76
74
  end
77
75
  end
78
76
 
@@ -87,60 +85,52 @@ module Puma
87
85
  def read_nonblock(size, *_)
88
86
  # *_ is to deal with keyword args that were added
89
87
  # at some point (and being used in the wild)
90
- output = engine_read_all
91
- return output if output
92
-
93
- raise IO::EAGAINWaitReadable unless flush_write_buffer_nonblock
94
-
95
- data = @socket.read_nonblock(size, exception: false)
96
- if data == :wait_readable || data == :wait_writable
97
- # It would make more sense to let @socket.read_nonblock raise
98
- # EAGAIN if necessary but it seems like it'll misbehave on Windows.
99
- # I don't have a Windows machine to debug this so I can't explain
100
- # exactly what's happening in that OS. Please let me know if you
101
- # find out!
102
- #
103
- # In the meantime, we can emulate the correct behavior by
104
- # capturing :wait_readable & :wait_writable and raising EAGAIN
105
- # ourselves.
106
- raise IO::EAGAINWaitReadable
107
- elsif data.nil?
108
- raise SSLError.exception "HTTP connection?" if bad_tlsv1_3?
109
- return nil
110
- end
88
+ while true
89
+ output = engine_read_all
90
+ return output if output
91
+
92
+ data = @socket.read_nonblock(size, exception: false)
93
+ if data == :wait_readable || data == :wait_writable
94
+ # It would make more sense to let @socket.read_nonblock raise
95
+ # EAGAIN if necessary but it seems like it'll misbehave on Windows.
96
+ # I don't have a Windows machine to debug this so I can't explain
97
+ # exactly whats happening in that OS. Please let me know if you
98
+ # find out!
99
+ #
100
+ # In the meantime, we can emulate the correct behavior by
101
+ # capturing :wait_readable & :wait_writable and raising EAGAIN
102
+ # ourselves.
103
+ raise IO::EAGAINWaitReadable
104
+ elsif data.nil?
105
+ raise SSLError.exception "HTTP connection?" if bad_tlsv1_3?
106
+ return nil
107
+ end
111
108
 
112
- @engine.inject(data)
113
- output = engine_read_all
114
- return output if output
109
+ @engine.inject(data)
110
+ output = engine_read_all
115
111
 
116
- queue_ciphertext
117
- flush_write_buffer_nonblock
118
- raise IO::EAGAINWaitReadable
119
- end
112
+ return output if output
120
113
 
121
- def write_pending?
122
- !@write_buffer.empty?
114
+ while neg_data = @engine.extract
115
+ @socket.write neg_data
116
+ end
117
+ end
123
118
  end
124
119
 
125
120
  def write(data)
126
121
  return 0 if data.empty?
127
122
 
128
123
  data_size = data.bytesize
129
-
130
- flush_write_buffer
131
- if @write_owed_data && data.byteslice(0, @write_owed_data.bytesize).b == @write_owed_data
132
- data = data.byteslice(@write_owed..-1)
133
- @write_owed = 0
134
- @write_owed_data = nil
135
- return data_size if data.empty?
136
- end
137
-
138
- need = data.bytesize
124
+ need = data_size
139
125
 
140
126
  while true
141
127
  wrote = @engine.write data
142
- queue_ciphertext
143
- flush_write_buffer
128
+
129
+ enc_wr = +''
130
+ while (enc = @engine.extract)
131
+ enc_wr << enc
132
+ end
133
+ @socket.write enc_wr unless enc_wr.empty?
144
134
 
145
135
  need -= wrote
146
136
 
@@ -153,43 +143,34 @@ module Puma
153
143
  alias_method :syswrite, :write
154
144
  alias_method :<<, :write
155
145
 
156
- # As with SSL_write, retry with the same data after IO::EAGAINWaitWritable
157
- def write_nonblock(data, exception: true)
158
- return 0 if data.empty?
159
-
160
- @write_owed = 0 if @write_owed_data && data.byteslice(0, @write_owed_data.bytesize).b != @write_owed_data
146
+ # This is a temporary fix to deal with websockets code using
147
+ # write_nonblock.
161
148
 
162
- if @write_owed == 0
163
- @write_owed = @engine.write data
164
- @write_owed_data = nil
165
- queue_ciphertext
166
- end
167
-
168
- unless flush_write_buffer_nonblock
169
- @write_owed_data ||= data.byteslice(0, @write_owed).b
170
- raise IO::EAGAINWaitWritable if exception
171
- return :wait_writable
172
- end
173
-
174
- wrote = @write_owed
175
- @write_owed = 0
176
- @write_owed_data = nil
177
- wrote
178
- end
179
-
180
- def wait_writable(timeout = nil)
181
- @socket.wait_writable timeout
149
+ # The problem with implementing it properly
150
+ # is that it means we'd have to have the ability to rewind
151
+ # an engine because after we write+extract, the socket
152
+ # write_nonblock call might raise an exception and later
153
+ # code would pass the same data in, but the engine would think
154
+ # it had already written the data in.
155
+ #
156
+ # So for the time being (and since write blocking is quite rare),
157
+ # go ahead and actually block in write_nonblock.
158
+ #
159
+ def write_nonblock(data, *_)
160
+ write data
182
161
  end
183
162
 
184
163
  def flush
185
- flush_write_buffer
186
164
  @socket.flush
187
165
  end
188
166
 
189
167
  def close
190
168
  begin
191
- queue_ciphertext unless @engine.shutdown
192
- flush_write_buffer_nonblock
169
+ unless @engine.shutdown
170
+ while alert_data = @engine.extract
171
+ @socket.write alert_data
172
+ end
173
+ end
193
174
  rescue IOError, SystemCallError
194
175
  # nothing
195
176
  ensure
@@ -197,32 +178,6 @@ module Puma
197
178
  end
198
179
  end
199
180
 
200
- def queue_ciphertext
201
- while (enc = @engine.extract)
202
- @write_buffer << enc
203
- end
204
- end
205
- private :queue_ciphertext
206
-
207
- def flush_write_buffer
208
- return if @write_buffer.empty?
209
-
210
- @socket.write @write_buffer
211
- @write_buffer = +''
212
- end
213
- private :flush_write_buffer
214
-
215
- def flush_write_buffer_nonblock
216
- until @write_buffer.empty?
217
- n = @socket.write_nonblock @write_buffer, exception: false
218
- return false if n == :wait_writable
219
-
220
- @write_buffer = @write_buffer.byteslice(n..-1)
221
- end
222
- true
223
- end
224
- private :flush_write_buffer_nonblock
225
-
226
181
  # @!attribute [r] peeraddr
227
182
  def peeraddr
228
183
  @socket.peeraddr
Binary file
data/lib/puma/reactor.rb CHANGED
@@ -34,7 +34,6 @@ module Puma
34
34
  @selector = ::NIO::Selector.new(NIO::Selector.backends.delete(backend))
35
35
  @input = Queue.new
36
36
  @timeouts = []
37
- @timeout_keys = {}.compare_by_identity
38
37
  @block = block
39
38
  @reactor_size = 0
40
39
  @reactor_max = 0
@@ -78,13 +77,11 @@ module Puma
78
77
  def select_loop
79
78
  begin
80
79
  until @input.closed? && @input.empty?
81
- requeue_moved_timeouts
82
-
83
80
  # Wakeup any registered object that receives incoming data.
84
81
  # Block until the earliest timeout or Selector#wakeup is called.
85
82
  timeout = (earliest = @timeouts.first) && earliest.timeout
86
83
  @selector.select(timeout) do |monitor|
87
- monitor.interests = interests(monitor.value) unless wakeup!(monitor.value)
84
+ wakeup!(monitor.value)
88
85
  end
89
86
 
90
87
  # Wakeup all objects that timed out.
@@ -96,7 +93,7 @@ module Puma
96
93
  client = @input.pop
97
94
  register(client) if client.io_ok?
98
95
  end
99
- @timeouts.sort_by! { |client| @timeout_keys[client] }
96
+ @timeouts.sort_by!(&:timeout_at)
100
97
  end
101
98
  end
102
99
  rescue StandardError => e
@@ -113,19 +110,14 @@ module Puma
113
110
 
114
111
  # Start monitoring the object.
115
112
  def register(client)
116
- @selector.register(client.to_io, interests(client)).value = client
113
+ @selector.register(client.to_io, :r).value = client
117
114
  @reactor_size += 1
118
115
  @reactor_max = @reactor_size if @reactor_max < @reactor_size
119
116
  @timeouts << client
120
- @timeout_keys[client] = client.timeout_at
121
117
  rescue ArgumentError
122
118
  # unreadable clients raise error when processed by NIO
123
119
  end
124
120
 
125
- def interests(client)
126
- client.write_pending? ? :w : :r
127
- end
128
-
129
121
  # 'Wake up' a monitored object by calling the provided block.
130
122
  # Stop monitoring the object if the block returns `true`.
131
123
  def wakeup!(client)
@@ -133,31 +125,7 @@ module Puma
133
125
  @selector.deregister client.to_io
134
126
  @reactor_size -= 1
135
127
  @timeouts.delete client
136
- @timeout_keys.delete client
137
- true
138
- elsif client.timeout_at < @timeout_keys[client]
139
- @timeouts.delete client
140
- requeue client
141
- false
142
- else
143
- false
144
128
  end
145
129
  end
146
-
147
- # `@timeouts` is sorted by the timeout each client had when it was queued.
148
- # Timeouts that moved earlier are requeued in `wakeup!`. Timeouts that moved
149
- # later are requeued here, once the client reaches the front.
150
- def requeue_moved_timeouts
151
- while (client = @timeouts.first) && @timeout_keys[client] < client.timeout_at
152
- @timeouts.shift
153
- requeue client
154
- end
155
- end
156
-
157
- def requeue(client)
158
- key = @timeout_keys[client] = client.timeout_at
159
- index = @timeouts.bsearch_index { |c| @timeout_keys[c] > key } || @timeouts.size
160
- @timeouts.insert index, client
161
- end
162
130
  end
163
131
  end
@@ -12,7 +12,7 @@ module Puma
12
12
  # #handle_request, which is called in Server#process_client.
13
13
  # @version 5.0.3
14
14
  #
15
- module Request # :nodoc:
15
+ module Response # :nodoc:
16
16
 
17
17
  # Single element array body: smaller bodies are written to io_buffer first,
18
18
  # then a single write from io_buffer. Larger sizes are written separately.
@@ -46,10 +46,11 @@ module Puma
46
46
  # elsewhere, i.e. the connection has been hijacked by the Rack application.
47
47
  #
48
48
  # Finally, it'll return +true+ on keep-alive connections.
49
+ # @param processor [Puma::ThreadPool::ProcessorThread]
49
50
  # @param client [Puma::Client]
50
51
  # @param requests [Integer]
51
52
  # @return [:close, :keep_alive, :async]
52
- def handle_request(client, requests)
53
+ def handle_request(processor, client, requests)
53
54
  env = client.env
54
55
  io_buffer = client.io_buffer
55
56
  socket = client.io # io may be a MiniSSL::Socket
@@ -58,26 +59,6 @@ module Puma
58
59
 
59
60
  return :close if closed_socket?(socket)
60
61
 
61
- write_pending_interim socket, client
62
-
63
- if client.http_content_length_limit_exceeded
64
- return prepare_response(413, {}, ["Payload Too Large"], requests, client)
65
- end
66
-
67
- normalize_env env, client
68
-
69
- env[PUMA_SOCKET] = socket
70
-
71
- if env[HTTPS_KEY] && socket.peercert
72
- env[PUMA_PEERCERT] = socket.peercert
73
- end
74
-
75
- env[HIJACK_P] = true
76
- env[HIJACK] = client.method :full_hijack
77
-
78
- env[RACK_INPUT] = client.body
79
- env[RACK_URL_SCHEME] ||= default_server_port(env) == PORT_443 ? HTTPS : HTTP
80
-
81
62
  if @early_hints
82
63
  env[EARLY_HINTS] = lambda { |headers|
83
64
  begin
@@ -91,22 +72,11 @@ module Puma
91
72
  }
92
73
  end
93
74
 
94
- req_env_post_parse env
95
-
96
- # A rack extension. If the app writes #call'ables to this
97
- # array, we will invoke them when the request is done.
98
- #
99
- env[RACK_AFTER_REPLY] ||= []
100
- env[RACK_RESPONSE_FINISHED] ||= []
75
+ env["puma.mark_as_io_bound"] = -> { processor.mark_as_io_thread! }
101
76
 
102
77
  begin
103
- if @supported_http_methods == :any || @supported_http_methods.key?(env[REQUEST_METHOD])
104
- status, headers, app_body = @thread_pool.with_force_shutdown do
105
- @app.call(env)
106
- end
107
- else
108
- @log_writer.log "Unsupported HTTP method used: #{env[REQUEST_METHOD]}"
109
- status, headers, app_body = [501, {}, ["#{env[REQUEST_METHOD]} method is not supported"]]
78
+ status, headers, app_body = @thread_pool.with_force_shutdown do
79
+ @app.call(env)
110
80
  end
111
81
 
112
82
  # app_body needs to always be closed, hold value in case lowlevel_error
@@ -138,7 +108,6 @@ module Puma
138
108
  prepare_response(status, headers, res_body, requests, client)
139
109
  ensure
140
110
  io_buffer.reset
141
- uncork_socket client.io
142
111
  app_body.close if app_body.respond_to? :close
143
112
  client&.tempfile_close
144
113
  if after_reply = env[RACK_AFTER_REPLY]
@@ -179,8 +148,6 @@ module Puma
179
148
 
180
149
  return :close if closed_socket?(socket)
181
150
 
182
- write_pending_interim socket, client
183
-
184
151
  # Close the connection after a reasonable number of inline requests
185
152
  force_keep_alive = @enable_keep_alives && client.requests_served < @max_keep_alive
186
153
 
@@ -249,7 +216,8 @@ module Puma
249
216
 
250
217
  io_buffer << LINE_END
251
218
  fast_write_str socket, io_buffer.read_and_reset
252
- socket.flush
219
+
220
+ uncork_socket socket.flush
253
221
  return keep_alive ? :keep_alive : :close
254
222
  end
255
223
  else
@@ -268,40 +236,18 @@ module Puma
268
236
  # response_hijack.call
269
237
  if response_hijack
270
238
  fast_write_str socket, io_buffer.read_and_reset
271
- uncork_socket socket
239
+ uncork_socket socket.flush
272
240
  response_hijack.call socket
273
241
  return :async
274
242
  end
275
243
 
276
244
  fast_write_response socket, body, io_buffer, chunked, content_length.to_i
277
245
  body.close if close_body
246
+
278
247
  # if we're shutting down, close keep_alive connections
279
248
  !shutting_down? && keep_alive ? :keep_alive : :close
280
249
  end
281
250
 
282
- HTTP_ON_VALUES = { "on" => true, HTTPS => true }
283
- private_constant :HTTP_ON_VALUES
284
-
285
- # @param env [Hash] see Puma::Client#env, from request
286
- # @return [Puma::Const::PORT_443,Puma::Const::PORT_80]
287
- #
288
- def default_server_port(env)
289
- if HTTP_ON_VALUES[env[HTTPS_KEY]] ||
290
- env[HTTP_X_FORWARDED_PROTO]&.start_with?(HTTPS) ||
291
- env[HTTP_X_FORWARDED_SCHEME] == HTTPS ||
292
- env[HTTP_X_FORWARDED_SSL] == "on"
293
- PORT_443
294
- else
295
- PORT_80
296
- end
297
- end
298
-
299
- def write_pending_interim(socket, client)
300
- pending = client.take_pending_write
301
- fast_write_str socket, pending if pending
302
- end
303
- private :write_pending_interim
304
-
305
251
  # Used to write 'early hints', 'no body' responses, 'hijacked' responses,
306
252
  # and body segments (called by `fast_write_response`).
307
253
  # Writes a string to a socket (normally `Client#io`) using `write_nonblock`.
@@ -351,11 +297,7 @@ module Puma
351
297
  fast_write_str socket, io_buffer.read_and_reset
352
298
  else
353
299
  fast_write_str socket, io_buffer.read_and_reset
354
- remaining = content_length
355
- while remaining > 0 && (chunk = body.read([remaining, BODY_LEN_MAX].min))
356
- fast_write_str socket, chunk
357
- remaining -= chunk.bytesize
358
- end
300
+ IO.copy_stream body, socket
359
301
  end
360
302
  end
361
303
  elsif body.is_a?(::Array) && body.length == 1
@@ -420,6 +362,7 @@ module Puma
420
362
  end
421
363
  end
422
364
  socket.flush
365
+ uncork_socket socket
423
366
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
424
367
  raise ConnectionError, SOCKET_WRITE_ERR_MSG
425
368
  rescue Errno::EPIPE, SystemCallError, IOError
@@ -428,85 +371,6 @@ module Puma
428
371
 
429
372
  private :fast_write_str, :fast_write_response
430
373
 
431
- # Given a Hash +env+ for the request read from +client+, add
432
- # and fixup keys to comply with Rack's env guidelines.
433
- # @param env [Hash] see Puma::Client#env, from request
434
- # @param client [Puma::Client] only needed for Client#peerip
435
- #
436
- def normalize_env(env, client)
437
- if host = env[HTTP_HOST]
438
- # host can be a hostname, ipv4 or bracketed ipv6. Followed by an optional port.
439
- if colon = host.rindex("]:") # IPV6 with port
440
- env[SERVER_NAME] = host[0, colon+1]
441
- env[SERVER_PORT] = host[colon+2, host.bytesize]
442
- elsif !host.start_with?("[") && colon = host.index(":") # not hostname or IPV4 with port
443
- env[SERVER_NAME] = host[0, colon]
444
- env[SERVER_PORT] = host[colon+1, host.bytesize]
445
- else
446
- env[SERVER_NAME] = host
447
- env[SERVER_PORT] = default_server_port(env)
448
- end
449
- else
450
- env[SERVER_NAME] = LOCALHOST
451
- env[SERVER_PORT] = default_server_port(env)
452
- end
453
-
454
- unless env[REQUEST_PATH]
455
- # it might be a dumbass full host request header
456
- uri = begin
457
- URI.parse(env[REQUEST_URI])
458
- rescue URI::InvalidURIError
459
- raise Puma::HttpParserError
460
- end
461
- env[REQUEST_PATH] = uri.path
462
-
463
- # A nil env value will cause a LintError (and fatal errors elsewhere),
464
- # so only set the env value if there actually is a value.
465
- env[QUERY_STRING] = uri.query if uri.query
466
- end
467
-
468
- env[PATH_INFO] = env[REQUEST_PATH].to_s # #to_s in case it's nil
469
-
470
- # From https://www.ietf.org/rfc/rfc3875 :
471
- # "Script authors should be aware that the REMOTE_ADDR and
472
- # REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
473
- # may not identify the ultimate source of the request.
474
- # They identify the client for the immediate request to the
475
- # server; that client may be a proxy, gateway, or other
476
- # intermediary acting on behalf of the actual source client."
477
- #
478
-
479
- unless env.key?(REMOTE_ADDR)
480
- begin
481
- addr = client.peerip
482
- rescue Errno::ENOTCONN
483
- # Client disconnects can result in an inability to get the
484
- # peeraddr from the socket; default to unspec.
485
- if client.peer_family == Socket::AF_INET6
486
- addr = UNSPECIFIED_IPV6
487
- else
488
- addr = UNSPECIFIED_IPV4
489
- end
490
- end
491
-
492
- # Set unix socket addrs to localhost
493
- if addr.empty?
494
- if client.peer_family == Socket::AF_INET6
495
- addr = LOCALHOST_IPV6
496
- else
497
- addr = LOCALHOST_IPV4
498
- end
499
- end
500
-
501
- env[REMOTE_ADDR] = addr
502
- end
503
-
504
- # The legacy HTTP_VERSION header can be sent as a client header.
505
- # Rack v4 may remove using HTTP_VERSION. If so, remove this line.
506
- env[HTTP_VERSION] = env[SERVER_PROTOCOL] if @env_set_http_version
507
- end
508
- private :normalize_env
509
-
510
374
  # @param header_key [#to_s]
511
375
  # @return [Boolean]
512
376
  #
@@ -520,56 +384,6 @@ module Puma
520
384
  def illegal_header_value?(header_value)
521
385
  !!(ILLEGAL_HEADER_VALUE_REGEX =~ header_value.to_s)
522
386
  end
523
- private :illegal_header_key?, :illegal_header_value?
524
-
525
- # Fixup any headers with `,` in the name to have `_` now. We emit
526
- # headers with `,` in them during the parse phase to avoid ambiguity
527
- # with the `-` to `_` conversion for critical headers. But here for
528
- # compatibility, we'll convert them back. This code is written to
529
- # avoid allocation in the common case (ie there are no headers
530
- # with `,` in their names), that's why it has the extra conditionals.
531
- #
532
- # @note If a normalized version of a `,` header already exists, we ignore
533
- # the `,` version. This prevents clobbering headers managed by proxies
534
- # but not by clients (Like X-Forwarded-For).
535
- #
536
- # @param env [Hash] see Puma::Client#env, from request, modifies in place
537
- # @version 5.0.3
538
- #
539
- def req_env_post_parse(env)
540
- to_delete = nil
541
- to_add = nil
542
-
543
- env.each do |k,v|
544
- if k.start_with?("HTTP_") && k.include?(",") && !UNMASKABLE_HEADERS.key?(k)
545
- if to_delete
546
- to_delete << k
547
- else
548
- to_delete = [k]
549
- end
550
-
551
- new_k = k.tr(",", "_")
552
- if env.key?(new_k)
553
- next
554
- end
555
-
556
- unless to_add
557
- to_add = {}
558
- end
559
-
560
- to_add[new_k] = v
561
- end
562
- end
563
-
564
- if to_delete # rubocop:disable Style/SafeNavigation
565
- to_delete.each { |k| env.delete(k) }
566
- end
567
-
568
- if to_add
569
- env.merge! to_add
570
- end
571
- end
572
- private :req_env_post_parse
573
387
 
574
388
  # Used in the lambda for env[ `Puma::Const::EARLY_HINTS` ]
575
389
  # @param headers [Hash] the headers returned by the Rack application
@@ -666,7 +480,8 @@ module Puma
666
480
  headers.each do |k, vs|
667
481
  next if illegal_header_key?(k)
668
482
 
669
- case k.downcase
483
+ key = k.downcase
484
+ case key
670
485
  when CONTENT_LENGTH2
671
486
  next if illegal_header_value?(vs)
672
487
  # nil.to_i is 0, nil&.to_i is nil
@@ -693,10 +508,10 @@ module Puma
693
508
  if ary
694
509
  ary.each do |v|
695
510
  next if illegal_header_value?(v)
696
- io_buffer.append k.downcase, colon, v, line_ending
511
+ io_buffer.append key, colon, v, line_ending
697
512
  end
698
513
  else
699
- io_buffer.append k.downcase, colon, line_ending
514
+ io_buffer.append key, colon, line_ending
700
515
  end
701
516
  end
702
517