lib/async/http/protocol/http1/server.rb in async-http-0.76.0 vs lib/async/http/protocol/http1/server.rb in async-http-0.77.0

- old
+ new

@@ -4,13 +4,15 @@ # Copyright, 2018-2024, by Samuel Williams. # Copyright, 2020, by Igor Sidorov. # Copyright, 2023, by Thomas Morgan. # Copyright, 2024, by Anton Zhuravsky. -require_relative 'connection' -require 'console/event/failure' +require_relative "connection" +require_relative "../../body/finishable" +require "console/event/failure" + module Async module HTTP module Protocol module HTTP1 class Server < Connection @@ -18,11 +20,11 @@ @persistent = false write_response(@version, status, {}) write_body(@version, nil) rescue => error # At this point, there is very little we can do to recover: - Console::Event::Failure.for(error).emit(self, "Failed to write failure response.", severity: :debug) + Console::Event::Failure.for(error).emit(self, "Failed to write failure response!", severity: :debug) end def next_request # The default is true. return unless @persistent @@ -33,22 +35,28 @@ unless persistent?(request.version, request.method, request.headers) @persistent = false end return request - rescue ::Protocol::HTTP1::BadRequest + rescue ::Protocol::HTTP1::BadRequest => error fail_request(400) # Conceivably we could retry here, but we don't really know how bad the error is, so it's better to just fail: raise end # Server loop. def each(task: Task.current) task.annotate("Reading #{self.version} requests for #{self.class}.") while request = next_request + if body = request.body + finishable = Body::Finishable.new(body) + request.body = finishable + end + response = yield(request, self) + version = request.version body = response&.body if hijacked? body&.close return @@ -75,11 +83,11 @@ return body.call(stream) elsif response.status == 101 # This code path is to support legacy behavior where the response status is set to 101, but the protocol is not upgraded. This may not be a valid use case, but it is supported for compatibility. We expect the response headers to contain the `upgrade` header. write_response(@version, response.status, response.headers) - stream = write_tunnel_body(request.version) + stream = write_tunnel_body(version) # Same as above: request = nil response = nil @@ -87,39 +95,39 @@ return body&.call(stream) else write_response(@version, response.status, response.headers) if request.connect? and response.success? - stream = write_tunnel_body(request.version) + stream = write_tunnel_body(version) # Same as above: request = nil response = nil # We must return here as no further request processing can be done: return body.call(stream) else head = request.head? - version = request.version # Same as above: - request = nil unless request.body + request = nil response = nil write_body(version, body, head, trailer) end end - # We are done with the body, you shouldn't need to call close on it: + # We are done with the body: body = nil else # If the request failed to generate a response, it was an internal server error: write_response(@version, 500, {}) - write_body(request.version, nil) + write_body(version, nil) + + request&.finish end - # Gracefully finish reading the request body if it was not already done so. - request&.each{} + finishable&.wait # This ensures we yield at least once every iteration of the loop and allow other fibers to execute. task.yield rescue => error raise