puma 5.6.4-java → 6.0.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 +4 -4
- data/History.md +136 -3
- data/README.md +21 -17
- data/bin/puma-wild +1 -1
- data/docs/compile_options.md +34 -0
- data/docs/fork_worker.md +1 -3
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/extconf.rb +18 -10
- data/ext/puma_http11/http11_parser.c +1 -1
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +2 -2
- data/ext/puma_http11/http11_parser.rl +2 -2
- data/ext/puma_http11/http11_parser_common.rl +2 -2
- data/ext/puma_http11/mini_ssl.c +63 -24
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +166 -65
- data/ext/puma_http11/puma_http11.c +17 -9
- data/lib/puma/app/status.rb +6 -3
- data/lib/puma/binder.rb +37 -43
- data/lib/puma/cli.rb +11 -17
- data/lib/puma/client.rb +22 -12
- data/lib/puma/cluster/worker.rb +13 -11
- data/lib/puma/cluster/worker_handle.rb +4 -1
- data/lib/puma/cluster.rb +28 -25
- data/lib/puma/configuration.rb +74 -58
- data/lib/puma/const.rb +14 -18
- data/lib/puma/control_cli.rb +21 -18
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +94 -49
- data/lib/puma/error_logger.rb +17 -9
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +29 -4
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +107 -156
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +23 -12
- data/lib/puma/minissl.rb +91 -15
- data/lib/puma/null_io.rb +5 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/rack/builder.rb +4 -4
- data/lib/puma/rack_default.rb +1 -1
- data/lib/puma/reactor.rb +3 -3
- data/lib/puma/request.rb +291 -156
- data/lib/puma/runner.rb +41 -20
- data/lib/puma/server.rb +53 -64
- data/lib/puma/single.rb +10 -10
- data/lib/puma/state_file.rb +2 -4
- data/lib/puma/systemd.rb +3 -2
- data/lib/puma/thread_pool.rb +16 -13
- data/lib/puma/util.rb +12 -14
- data/lib/puma.rb +11 -8
- data/lib/rack/handler/puma.rb +9 -9
- metadata +7 -3
- data/lib/puma/queue_close.rb +0 -26
data/lib/puma/request.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Puma
|
4
|
+
#———————————————————————— DO NOT USE — this class is for internal use only ———
|
5
|
+
|
4
6
|
|
5
7
|
# The methods here are included in Server, but are separated into this file.
|
6
8
|
# All the methods here pertain to passing the request to the app, then
|
@@ -10,7 +12,21 @@ module Puma
|
|
10
12
|
# #handle_request, which is called in Server#process_client.
|
11
13
|
# @version 5.0.3
|
12
14
|
#
|
13
|
-
module Request
|
15
|
+
module Request # :nodoc:
|
16
|
+
|
17
|
+
# determines whether to write body to io_buffer first, or straight to socket
|
18
|
+
# also fixes max size of chunked body read when bosy is an IO.
|
19
|
+
BODY_LEN_MAX = 1_024 * 256
|
20
|
+
|
21
|
+
# size divide for using copy_stream on body
|
22
|
+
IO_BODY_MAX = 1_024 * 64
|
23
|
+
|
24
|
+
# max size for io_buffer, force write when exceeded
|
25
|
+
IO_BUFFER_LEN_MAX = 1_024 * 1_024 * 4
|
26
|
+
|
27
|
+
SOCKET_WRITE_ERR_MSG = "Socket timeout writing data"
|
28
|
+
|
29
|
+
CUSTOM_STAT = 'CUSTOM'
|
14
30
|
|
15
31
|
include Puma::Const
|
16
32
|
|
@@ -25,40 +41,36 @@ module Puma
|
|
25
41
|
#
|
26
42
|
# Finally, it'll return +true+ on keep-alive connections.
|
27
43
|
# @param client [Puma::Client]
|
28
|
-
# @param
|
44
|
+
# @param io_buffer [Puma::IOBuffer]
|
29
45
|
# @param requests [Integer]
|
30
46
|
# @return [Boolean,:async]
|
31
47
|
#
|
32
|
-
def handle_request(client,
|
48
|
+
def handle_request(client, io_buffer, requests)
|
33
49
|
env = client.env
|
34
|
-
|
50
|
+
socket = client.io # io may be a MiniSSL::Socket
|
35
51
|
|
36
|
-
return false if closed_socket?(
|
52
|
+
return false if closed_socket?(socket)
|
37
53
|
|
38
54
|
normalize_env env, client
|
39
55
|
|
40
|
-
env[PUMA_SOCKET] =
|
56
|
+
env[PUMA_SOCKET] = socket
|
41
57
|
|
42
|
-
if env[HTTPS_KEY] &&
|
43
|
-
env[PUMA_PEERCERT] =
|
58
|
+
if env[HTTPS_KEY] && socket.peercert
|
59
|
+
env[PUMA_PEERCERT] = socket.peercert
|
44
60
|
end
|
45
61
|
|
46
62
|
env[HIJACK_P] = true
|
47
63
|
env[HIJACK] = client
|
48
64
|
|
49
|
-
|
50
|
-
|
51
|
-
head = env[REQUEST_METHOD] == HEAD
|
52
|
-
|
53
|
-
env[RACK_INPUT] = body
|
65
|
+
env[RACK_INPUT] = client.body
|
54
66
|
env[RACK_URL_SCHEME] ||= default_server_port(env) == PORT_443 ? HTTPS : HTTP
|
55
67
|
|
56
68
|
if @early_hints
|
57
69
|
env[EARLY_HINTS] = lambda { |headers|
|
58
70
|
begin
|
59
|
-
|
71
|
+
fast_write_str socket, str_early_hints(headers)
|
60
72
|
rescue ConnectionError => e
|
61
|
-
@
|
73
|
+
@log_writer.debug_error e
|
62
74
|
# noop, if we lost the socket we just won't send the early hints
|
63
75
|
end
|
64
76
|
}
|
@@ -69,119 +81,147 @@ module Puma
|
|
69
81
|
# A rack extension. If the app writes #call'ables to this
|
70
82
|
# array, we will invoke them when the request is done.
|
71
83
|
#
|
72
|
-
|
84
|
+
env[RACK_AFTER_REPLY] ||= []
|
73
85
|
|
74
86
|
begin
|
75
|
-
|
87
|
+
if SUPPORTED_HTTP_METHODS.include?(env[REQUEST_METHOD])
|
76
88
|
status, headers, res_body = @thread_pool.with_force_shutdown do
|
77
89
|
@app.call(env)
|
78
90
|
end
|
91
|
+
else
|
92
|
+
@log_writer.log "Unsupported HTTP method used: #{env[REQUEST_METHOD]}"
|
93
|
+
status, headers, res_body = [501, {}, ["#{env[REQUEST_METHOD]} method is not supported"]]
|
94
|
+
end
|
79
95
|
|
80
|
-
|
81
|
-
|
82
|
-
status = status.to_i
|
96
|
+
return :async if client.hijacked
|
83
97
|
|
84
|
-
|
85
|
-
unless headers.empty? and res_body == []
|
86
|
-
raise "async response must have empty headers and body"
|
87
|
-
end
|
98
|
+
status = status.to_i
|
88
99
|
|
89
|
-
|
100
|
+
if status == -1
|
101
|
+
unless headers.empty? and res_body == []
|
102
|
+
raise "async response must have empty headers and body"
|
90
103
|
end
|
91
|
-
rescue ThreadPool::ForceShutdown => e
|
92
|
-
@events.unknown_error e, client, "Rack app"
|
93
|
-
@events.log "Detected force shutdown of a thread"
|
94
104
|
|
95
|
-
|
96
|
-
rescue Exception => e
|
97
|
-
@events.unknown_error e, client, "Rack app"
|
98
|
-
|
99
|
-
status, headers, res_body = lowlevel_error(e, env, 500)
|
105
|
+
return :async
|
100
106
|
end
|
107
|
+
rescue ThreadPool::ForceShutdown => e
|
108
|
+
@log_writer.unknown_error e, client, "Rack app"
|
109
|
+
@log_writer.log "Detected force shutdown of a thread"
|
110
|
+
|
111
|
+
status, headers, res_body = lowlevel_error(e, env, 503)
|
112
|
+
rescue Exception => e
|
113
|
+
@log_writer.unknown_error e, client, "Rack app"
|
101
114
|
|
102
|
-
|
103
|
-
|
104
|
-
|
115
|
+
status, headers, res_body = lowlevel_error(e, env, 500)
|
116
|
+
end
|
117
|
+
prepare_response(status, headers, res_body, io_buffer, requests, client)
|
118
|
+
end
|
105
119
|
|
106
|
-
|
107
|
-
|
120
|
+
# Assembles the headers and prepares the body for actually sending the
|
121
|
+
# response via #fast_write_response.
|
122
|
+
#
|
123
|
+
# @param status [Integer] the status returned by the Rack application
|
124
|
+
# @param headers [Hash] the headers returned by the Rack application
|
125
|
+
# @param app_body [Array] the body returned by the Rack application or
|
126
|
+
# a call to `lowlevel_error`
|
127
|
+
# @param io_buffer [Puma::IOBuffer] modified in place
|
128
|
+
# @param requests [Integer] number of inline requests handled
|
129
|
+
# @param client [Puma::Client]
|
130
|
+
# @return [Boolean,:async]
|
131
|
+
def prepare_response(status, headers, app_body, io_buffer, requests, client)
|
132
|
+
env = client.env
|
133
|
+
socket = client.io
|
134
|
+
|
135
|
+
after_reply = env[RACK_AFTER_REPLY] || []
|
136
|
+
|
137
|
+
return false if closed_socket?(socket)
|
138
|
+
|
139
|
+
resp_info = str_headers(env, status, headers, app_body, io_buffer, requests, client)
|
140
|
+
|
141
|
+
# below converts app_body into body, dependent on app_body's characteristics, and
|
142
|
+
# resp_info[:content_length] will be set if it can be determined
|
143
|
+
if !resp_info[:content_length] && !resp_info[:transfer_encoding] && status != 204
|
144
|
+
if app_body.respond_to?(:to_ary)
|
145
|
+
length = 0
|
146
|
+
if array_body = app_body.to_ary
|
147
|
+
body = array_body.map { |part| length += part.bytesize; part }
|
148
|
+
elsif app_body.is_a?(::File) && app_body.respond_to?(:size)
|
149
|
+
length = app_body.size
|
150
|
+
elsif app_body.respond_to?(:each)
|
151
|
+
body = []
|
152
|
+
app_body.each { |part| length += part.bytesize; body << part }
|
153
|
+
end
|
154
|
+
resp_info[:content_length] = length
|
155
|
+
elsif app_body.is_a?(File) && app_body.respond_to?(:size)
|
156
|
+
resp_info[:content_length] = app_body.size
|
157
|
+
body = app_body
|
158
|
+
elsif app_body.respond_to?(:to_path) && app_body.respond_to?(:each) &&
|
159
|
+
File.readable?(fn = app_body.to_path)
|
160
|
+
body = File.open fn, 'rb'
|
161
|
+
resp_info[:content_length] = body.size
|
108
162
|
else
|
109
|
-
|
163
|
+
body = app_body
|
110
164
|
end
|
165
|
+
elsif !app_body.is_a?(::File) && app_body.respond_to?(:to_path) && app_body.respond_to?(:each) &&
|
166
|
+
File.readable?(fn = app_body.to_path)
|
167
|
+
body = File.open fn, 'rb'
|
168
|
+
resp_info[:content_length] = body.size
|
169
|
+
else
|
170
|
+
body = app_body
|
171
|
+
end
|
111
172
|
|
112
|
-
|
113
|
-
|
114
|
-
str_headers(env, status, headers, res_info, lines, requests, client)
|
115
|
-
|
116
|
-
line_ending = LINE_END
|
173
|
+
line_ending = LINE_END
|
117
174
|
|
118
|
-
|
119
|
-
|
175
|
+
content_length = resp_info[:content_length]
|
176
|
+
keep_alive = resp_info[:keep_alive]
|
120
177
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
178
|
+
if app_body && !app_body.respond_to?(:each)
|
179
|
+
response_hijack = app_body
|
180
|
+
else
|
181
|
+
response_hijack = resp_info[:response_hijack]
|
182
|
+
end
|
125
183
|
|
126
|
-
|
127
|
-
fast_write io, lines.to_s
|
128
|
-
return res_info[:keep_alive]
|
129
|
-
end
|
184
|
+
cork_socket socket
|
130
185
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
elsif !response_hijack and res_info[:allow_chunked]
|
135
|
-
lines << TRANSFER_ENCODING_CHUNKED
|
136
|
-
chunked = true
|
186
|
+
if resp_info[:no_body]
|
187
|
+
if content_length and status != 204
|
188
|
+
io_buffer.append CONTENT_LENGTH_S, content_length.to_s, line_ending
|
137
189
|
end
|
138
190
|
|
139
|
-
|
140
|
-
|
141
|
-
|
191
|
+
io_buffer << LINE_END
|
192
|
+
fast_write_str socket, io_buffer.to_s
|
193
|
+
return keep_alive
|
194
|
+
end
|
195
|
+
if content_length
|
196
|
+
io_buffer.append CONTENT_LENGTH_S, content_length.to_s, line_ending
|
197
|
+
chunked = false
|
198
|
+
elsif !response_hijack and resp_info[:allow_chunked]
|
199
|
+
io_buffer << TRANSFER_ENCODING_CHUNKED
|
200
|
+
chunked = true
|
201
|
+
end
|
142
202
|
|
143
|
-
|
144
|
-
response_hijack.call io
|
145
|
-
return :async
|
146
|
-
end
|
203
|
+
io_buffer << line_ending
|
147
204
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
fast_write io, part # part may have different encoding
|
154
|
-
fast_write io, line_ending
|
155
|
-
else
|
156
|
-
fast_write io, part
|
157
|
-
end
|
158
|
-
io.flush
|
159
|
-
end
|
205
|
+
if response_hijack
|
206
|
+
fast_write_str socket, io_buffer.to_s
|
207
|
+
response_hijack.call socket
|
208
|
+
return :async
|
209
|
+
end
|
160
210
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
ensure
|
170
|
-
begin
|
171
|
-
uncork_socket io
|
172
|
-
|
173
|
-
body.close
|
174
|
-
client.tempfile.unlink if client.tempfile
|
175
|
-
ensure
|
176
|
-
# Whatever happens, we MUST call `close` on the response body.
|
177
|
-
# Otherwise Rack::BodyProxy callbacks may not fire and lead to various state leaks
|
178
|
-
res_body.close if res_body.respond_to? :close
|
179
|
-
end
|
211
|
+
fast_write_response socket, body, io_buffer, chunked, content_length.to_i
|
212
|
+
keep_alive
|
213
|
+
ensure
|
214
|
+
io_buffer.reset
|
215
|
+
resp_info = nil
|
216
|
+
uncork_socket socket
|
217
|
+
app_body.close if app_body.respond_to? :close
|
218
|
+
client.tempfile&.unlink
|
180
219
|
|
220
|
+
begin
|
181
221
|
after_reply.each { |o| o.call }
|
182
|
-
|
183
|
-
|
184
|
-
|
222
|
+
rescue StandardError => e
|
223
|
+
@log_writer.debug_error e
|
224
|
+
end unless after_reply.empty?
|
185
225
|
end
|
186
226
|
|
187
227
|
# @param env [Hash] see Puma::Client#env, from request
|
@@ -195,45 +235,107 @@ module Puma
|
|
195
235
|
end
|
196
236
|
end
|
197
237
|
|
198
|
-
#
|
199
|
-
#
|
238
|
+
# Used to write 'early hints', 'no body' responses, 'hijacked' responses,
|
239
|
+
# and body segments (called by `fast_write_response`).
|
240
|
+
# Writes a string to an io (normally `Client#io`) using `write_nonblock`.
|
241
|
+
# Large strings may not be written in one pass, especially if `io` is a
|
242
|
+
# `MiniSSL::Socket`.
|
243
|
+
# @param io [#write_nonblock] the io to write to
|
200
244
|
# @param str [String] the string written to the io
|
201
245
|
# @raise [ConnectionError]
|
202
246
|
#
|
203
|
-
def
|
247
|
+
def fast_write_str(socket, str)
|
204
248
|
n = 0
|
205
|
-
|
249
|
+
byte_size = str.bytesize
|
250
|
+
while n < byte_size
|
206
251
|
begin
|
207
|
-
n
|
252
|
+
n += socket.syswrite(n.zero? ? str : str.byteslice(n..-1))
|
208
253
|
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
|
209
|
-
unless
|
210
|
-
raise ConnectionError,
|
254
|
+
unless socket.wait_writable WRITE_TIMEOUT
|
255
|
+
raise ConnectionError, SOCKET_WRITE_ERR_MSG
|
211
256
|
end
|
212
|
-
|
213
257
|
retry
|
214
258
|
rescue Errno::EPIPE, SystemCallError, IOError
|
215
|
-
raise ConnectionError,
|
259
|
+
raise ConnectionError, SOCKET_WRITE_ERR_MSG
|
216
260
|
end
|
217
|
-
|
218
|
-
return if n == str.bytesize
|
219
|
-
str = str.byteslice(n..-1)
|
220
261
|
end
|
221
262
|
end
|
222
|
-
private :fast_write
|
223
263
|
|
224
|
-
#
|
225
|
-
#
|
264
|
+
# Used to write headers and body.
|
265
|
+
# Writes to a socket (normally `Client#io`) using `#fast_write_str`.
|
266
|
+
# Accumulates `body` items into `io_buffer`, then writes to socket.
|
267
|
+
# @param socket [#write] the response socket
|
268
|
+
# @param body [Enumerable, File] the body object
|
269
|
+
# @param io_buffer [Puma::IOBuffer] contains headers
|
270
|
+
# @param chunk [Boolean]
|
271
|
+
# @raise [ConnectionError]
|
226
272
|
#
|
227
|
-
def
|
228
|
-
|
273
|
+
def fast_write_response(socket, body, io_buffer, chunked, content_length)
|
274
|
+
if body.is_a?(::File) || body.respond_to?(:read) || body.respond_to?(:readpartial)
|
275
|
+
if chunked # would this ever happen?
|
276
|
+
while part = body.read(BODY_LEN_MAX)
|
277
|
+
io_buffer.append part.bytesize.to_s(16), LINE_END, part, LINE_END
|
278
|
+
end
|
279
|
+
io_buffer << CLOSE_CHUNKED
|
280
|
+
fast_write_str socket, io_buffer.to_s
|
281
|
+
else
|
282
|
+
if content_length <= IO_BODY_MAX
|
283
|
+
io_buffer.write body.sysread(content_length)
|
284
|
+
fast_write_str socket, io_buffer.to_s
|
285
|
+
else
|
286
|
+
fast_write_str socket, io_buffer.to_s
|
287
|
+
IO.copy_stream body, socket
|
288
|
+
end
|
289
|
+
end
|
290
|
+
body.close
|
291
|
+
elsif body.is_a?(::Array) && body.length == 1
|
292
|
+
body_first = body.first
|
293
|
+
if body_first.is_a?(::String) && body_first.bytesize >= BODY_LEN_MAX
|
294
|
+
# large body, write both header & body to socket
|
295
|
+
fast_write_str socket, io_buffer.to_s
|
296
|
+
fast_write_str socket, body_first
|
297
|
+
else
|
298
|
+
# smaller body, write to stream first
|
299
|
+
io_buffer.write body_first
|
300
|
+
fast_write_str socket, io_buffer.to_s
|
301
|
+
end
|
302
|
+
else
|
303
|
+
# for array bodies, flush io_buffer to socket when size is greater than
|
304
|
+
# IO_BUFFER_LEN_MAX
|
305
|
+
if chunked
|
306
|
+
body.each do |part|
|
307
|
+
next if (byte_size = part.bytesize).zero?
|
308
|
+
io_buffer.append byte_size.to_s(16), LINE_END, part, LINE_END
|
309
|
+
if io_buffer.length > IO_BUFFER_LEN_MAX
|
310
|
+
fast_write_str socket, io_buffer.to_s
|
311
|
+
io_buffer.reset
|
312
|
+
end
|
313
|
+
end
|
314
|
+
io_buffer.write CLOSE_CHUNKED
|
315
|
+
else
|
316
|
+
body.each do |part|
|
317
|
+
next if part.bytesize.zero?
|
318
|
+
io_buffer.write part
|
319
|
+
if io_buffer.length > IO_BUFFER_LEN_MAX
|
320
|
+
fast_write_str socket, io_buffer.to_s
|
321
|
+
io_buffer.reset
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
fast_write_str(socket, io_buffer.to_s) unless io_buffer.length.zero?
|
326
|
+
end
|
327
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
|
328
|
+
raise ConnectionError, SOCKET_WRITE_ERR_MSG
|
329
|
+
rescue Errno::EPIPE, SystemCallError, IOError
|
330
|
+
raise ConnectionError, SOCKET_WRITE_ERR_MSG
|
229
331
|
end
|
230
|
-
|
332
|
+
|
333
|
+
private :fast_write_str, :fast_write_response
|
231
334
|
|
232
335
|
# Given a Hash +env+ for the request read from +client+, add
|
233
336
|
# and fixup keys to comply with Rack's env guidelines.
|
234
337
|
# @param env [Hash] see Puma::Client#env, from request
|
235
338
|
# @param client [Puma::Client] only needed for Client#peerip
|
236
|
-
# @todo make private in 6.0.0
|
237
339
|
#
|
238
340
|
def normalize_env(env, client)
|
239
341
|
if host = env[HTTP_HOST]
|
@@ -258,14 +360,12 @@ module Puma
|
|
258
360
|
uri = URI.parse(env[REQUEST_URI])
|
259
361
|
env[REQUEST_PATH] = uri.path
|
260
362
|
|
261
|
-
raise "No REQUEST PATH" unless env[REQUEST_PATH]
|
262
|
-
|
263
363
|
# A nil env value will cause a LintError (and fatal errors elsewhere),
|
264
364
|
# so only set the env value if there actually is a value.
|
265
365
|
env[QUERY_STRING] = uri.query if uri.query
|
266
366
|
end
|
267
367
|
|
268
|
-
env[PATH_INFO] = env[REQUEST_PATH]
|
368
|
+
env[PATH_INFO] = env[REQUEST_PATH].to_s # #to_s in case it's nil
|
269
369
|
|
270
370
|
# From https://www.ietf.org/rfc/rfc3875 :
|
271
371
|
# "Script authors should be aware that the REMOTE_ADDR and
|
@@ -281,17 +381,31 @@ module Puma
|
|
281
381
|
addr = client.peerip
|
282
382
|
rescue Errno::ENOTCONN
|
283
383
|
# Client disconnects can result in an inability to get the
|
284
|
-
# peeraddr from the socket; default to
|
285
|
-
|
384
|
+
# peeraddr from the socket; default to unspec.
|
385
|
+
if client.peer_family == Socket::AF_INET6
|
386
|
+
addr = UNSPECIFIED_IPV6
|
387
|
+
else
|
388
|
+
addr = UNSPECIFIED_IPV4
|
389
|
+
end
|
286
390
|
end
|
287
391
|
|
288
392
|
# Set unix socket addrs to localhost
|
289
|
-
|
393
|
+
if addr.empty?
|
394
|
+
if client.peer_family == Socket::AF_INET6
|
395
|
+
addr = LOCALHOST_IPV6
|
396
|
+
else
|
397
|
+
addr = LOCALHOST_IPV4
|
398
|
+
end
|
399
|
+
end
|
290
400
|
|
291
401
|
env[REMOTE_ADDR] = addr
|
292
402
|
end
|
403
|
+
|
404
|
+
# The legacy HTTP_VERSION header can be sent as a client header.
|
405
|
+
# Rack v4 may remove using HTTP_VERSION. If so, remove this line.
|
406
|
+
env[HTTP_VERSION] = env[SERVER_PROTOCOL]
|
293
407
|
end
|
294
|
-
|
408
|
+
private :normalize_env
|
295
409
|
|
296
410
|
# @param header_key [#to_s]
|
297
411
|
# @return [Boolean]
|
@@ -350,7 +464,7 @@ module Puma
|
|
350
464
|
# @version 5.0.3
|
351
465
|
#
|
352
466
|
def str_early_hints(headers)
|
353
|
-
eh_str = "HTTP/1.1 103 Early Hints\r\n"
|
467
|
+
eh_str = +"HTTP/1.1 103 Early Hints\r\n"
|
354
468
|
headers.each_pair do |k, vs|
|
355
469
|
next if illegal_header_key?(k)
|
356
470
|
|
@@ -367,66 +481,78 @@ module Puma
|
|
367
481
|
end
|
368
482
|
private :str_early_hints
|
369
483
|
|
484
|
+
# @param status [Integer] status from the app
|
485
|
+
# @return [String] the text description from Puma::HTTP_STATUS_CODES
|
486
|
+
#
|
487
|
+
def fetch_status_code(status)
|
488
|
+
HTTP_STATUS_CODES.fetch(status) { CUSTOM_STAT }
|
489
|
+
end
|
490
|
+
private :fetch_status_code
|
491
|
+
|
370
492
|
# Processes and write headers to the IOBuffer.
|
371
493
|
# @param env [Hash] see Puma::Client#env, from request
|
372
494
|
# @param status [Integer] the status returned by the Rack application
|
373
495
|
# @param headers [Hash] the headers returned by the Rack application
|
374
|
-
# @param
|
375
|
-
#
|
496
|
+
# @param content_length [Integer,nil] content length if it can be determined from the
|
497
|
+
# response body
|
498
|
+
# @param io_buffer [Puma::IOBuffer] modified inn place
|
376
499
|
# @param requests [Integer] number of inline requests handled
|
377
500
|
# @param client [Puma::Client]
|
501
|
+
# @return [Hash] resp_info
|
378
502
|
# @version 5.0.3
|
379
503
|
#
|
380
|
-
def str_headers(env, status, headers,
|
504
|
+
def str_headers(env, status, headers, res_body, io_buffer, requests, client)
|
381
505
|
line_ending = LINE_END
|
382
506
|
colon = COLON
|
383
507
|
|
384
|
-
|
508
|
+
resp_info = {}
|
509
|
+
resp_info[:no_body] = env[REQUEST_METHOD] == HEAD
|
510
|
+
|
511
|
+
http_11 = env[SERVER_PROTOCOL] == HTTP_11
|
385
512
|
if http_11
|
386
|
-
|
387
|
-
|
513
|
+
resp_info[:allow_chunked] = true
|
514
|
+
resp_info[:keep_alive] = env.fetch(HTTP_CONNECTION, "").downcase != CLOSE
|
388
515
|
|
389
516
|
# An optimization. The most common response is 200, so we can
|
390
517
|
# reply with the proper 200 status without having to compute
|
391
518
|
# the response header.
|
392
519
|
#
|
393
520
|
if status == 200
|
394
|
-
|
521
|
+
io_buffer << HTTP_11_200
|
395
522
|
else
|
396
|
-
|
397
|
-
fetch_status_code(status), line_ending
|
523
|
+
io_buffer.append "#{HTTP_11} #{status} ", fetch_status_code(status), line_ending
|
398
524
|
|
399
|
-
|
525
|
+
resp_info[:no_body] ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
|
400
526
|
end
|
401
527
|
else
|
402
|
-
|
403
|
-
|
528
|
+
resp_info[:allow_chunked] = false
|
529
|
+
resp_info[:keep_alive] = env.fetch(HTTP_CONNECTION, "").downcase == KEEP_ALIVE
|
404
530
|
|
405
531
|
# Same optimization as above for HTTP/1.1
|
406
532
|
#
|
407
533
|
if status == 200
|
408
|
-
|
534
|
+
io_buffer << HTTP_10_200
|
409
535
|
else
|
410
|
-
|
536
|
+
io_buffer.append "HTTP/1.0 #{status} ",
|
411
537
|
fetch_status_code(status), line_ending
|
412
538
|
|
413
|
-
|
539
|
+
resp_info[:no_body] ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
|
414
540
|
end
|
415
541
|
end
|
416
542
|
|
417
543
|
# regardless of what the client wants, we always close the connection
|
418
544
|
# if running without request queueing
|
419
|
-
|
545
|
+
resp_info[:keep_alive] &&= @queue_requests
|
420
546
|
|
421
547
|
# Close the connection after a reasonable number of inline requests
|
422
548
|
# if the server is at capacity and the listener has a new connection ready.
|
423
549
|
# This allows Puma to service connections fairly when the number
|
424
550
|
# of concurrent connections exceeds the size of the threadpool.
|
425
|
-
|
551
|
+
resp_info[:keep_alive] &&= requests < @max_fast_inline ||
|
426
552
|
@thread_pool.busy_threads < @max_threads ||
|
427
553
|
!client.listener.to_io.wait_readable(0)
|
428
554
|
|
429
|
-
|
555
|
+
resp_info[:response_hijack] = nil
|
430
556
|
|
431
557
|
headers.each do |k, vs|
|
432
558
|
next if illegal_header_key?(k)
|
@@ -434,25 +560,33 @@ module Puma
|
|
434
560
|
case k.downcase
|
435
561
|
when CONTENT_LENGTH2
|
436
562
|
next if illegal_header_value?(vs)
|
437
|
-
|
563
|
+
resp_info[:content_length] = vs
|
438
564
|
next
|
439
565
|
when TRANSFER_ENCODING
|
440
|
-
|
441
|
-
|
566
|
+
resp_info[:allow_chunked] = false
|
567
|
+
resp_info[:content_length] = nil
|
568
|
+
resp_info[:transfer_encoding] = vs
|
442
569
|
when HIJACK
|
443
|
-
|
570
|
+
resp_info[:response_hijack] = vs
|
444
571
|
next
|
445
572
|
when BANNED_HEADER_KEY
|
446
573
|
next
|
447
574
|
end
|
448
575
|
|
449
|
-
if vs.
|
450
|
-
vs
|
576
|
+
ary = if vs.is_a?(::Array) && !vs.empty?
|
577
|
+
vs
|
578
|
+
elsif vs.respond_to?(:to_s) && !vs.to_s.empty?
|
579
|
+
vs.to_s.split NEWLINE
|
580
|
+
else
|
581
|
+
nil
|
582
|
+
end
|
583
|
+
if ary
|
584
|
+
ary.each do |v|
|
451
585
|
next if illegal_header_value?(v)
|
452
|
-
|
586
|
+
io_buffer.append k, colon, v, line_ending
|
453
587
|
end
|
454
588
|
else
|
455
|
-
|
589
|
+
io_buffer.append k, colon, line_ending
|
456
590
|
end
|
457
591
|
end
|
458
592
|
|
@@ -462,10 +596,11 @@ module Puma
|
|
462
596
|
# Only set the header if we're doing something which is not the default
|
463
597
|
# for this protocol version
|
464
598
|
if http_11
|
465
|
-
|
599
|
+
io_buffer << CONNECTION_CLOSE if !resp_info[:keep_alive]
|
466
600
|
else
|
467
|
-
|
601
|
+
io_buffer << CONNECTION_KEEP_ALIVE if resp_info[:keep_alive]
|
468
602
|
end
|
603
|
+
resp_info
|
469
604
|
end
|
470
605
|
private :str_headers
|
471
606
|
end
|