1
0
Fork 0
mirror of https://github.com/puma/puma.git synced 2022-11-09 13:48:40 -05:00

Swallow connection errors when sending early hints (#1822)

* Swallow connection errors when sending early hints

I've been thinking on how Puma should behave when the early hints
can't be sent because the connection has been lost.

I first thought that it should behave the same way it does when the
connection is lost before main response is sent, I've seen that it
swallows those errors (https://git.io/fjVtg) so perhaps we should do
the same here.

There's one consideration though. Because the early hints are sent at
the very beginning of the app execution, we may want to abort this
request altogether and save resources by throwing an exception and
causing Rails to interrupt execution.

Finally I decided to overlook that case just because I believe it's
least surprising from the Rack app point of view. Also
I haven't seen any other example of the web server interrupting
the apps execution due to an error on the lower layer.

Fixes #1640.

* Adds a test

* Updates test as per @nateberkopec's suggestion
This commit is contained in:
Jesús Burgos Maciá 2019-08-01 15:23:08 -04:00 committed by Nate Berkopec
parent b0c56db7f6
commit 2deaf77a38
2 changed files with 39 additions and 9 deletions

View file

@ -634,19 +634,23 @@ module Puma
if @early_hints
env[EARLY_HINTS] = lambda { |headers|
fast_write client, "HTTP/1.1 103 Early Hints\r\n".freeze
begin
fast_write client, "HTTP/1.1 103 Early Hints\r\n".freeze
headers.each_pair do |k, vs|
if vs.respond_to?(:to_s) && !vs.to_s.empty?
vs.to_s.split(NEWLINE).each do |v|
fast_write client, "#{k}: #{v}\r\n"
headers.each_pair do |k, vs|
if vs.respond_to?(:to_s) && !vs.to_s.empty?
vs.to_s.split(NEWLINE).each do |v|
fast_write client, "#{k}: #{v}\r\n"
end
else
fast_write client, "#{k}: #{vs}\r\n"
end
else
fast_write client, "#{k}: #{vs}\r\n"
end
end
fast_write client, "\r\n".freeze
fast_write client, "\r\n".freeze
rescue ConnectionError
# noop, if we lost the socket we just won't send the early hints
end
}
end

View file

@ -213,6 +213,32 @@ EOF
assert_equal expected_data, data
end
def test_early_hints_are_ignored_if_connection_lost
app = proc { |env|
env['rack.early_hints'].call("Link" => "</script.js>; rel=preload")
[200, { "X-Hello" => "World" }, ["Hello world!"]]
}
events = Puma::Events.strings
server = Puma::Server.new app, events
def server.fast_write(*args)
raise Puma::ConnectionError
end
server.add_tcp_listener @host, @port
server.early_hints = true
server.run
# This request will cause the server to try and send early hints
sock = TCPSocket.new @host, server.connected_port
sock << "HEAD / HTTP/1.0\r\n\r\n"
# Give the server some time to try to write (and fail)
sleep 0.1
# Expect no errors in stderr
assert events.stderr.pos.zero?, "Server didn't swallow the connection error"
end
def test_early_hints_is_off_by_default
@server.app = proc { |env|
assert_nil env['rack.early_hints']