Module: Protocol::Rack::Body
- Defined in:
- lib/protocol/rack/body.rb,
lib/protocol/rack/body/streaming.rb,
lib/protocol/rack/body/enumerable.rb,
lib/protocol/rack/body/input_wrapper.rb
Overview
The Body module provides functionality for handling Rack response bodies. It includes methods for wrapping different types of response bodies and handling completion callbacks.
Defined Under Namespace
Classes: Enumerable, InputWrapper
Constant Summary collapse
- CONTENT_LENGTH =
The ‘content-length` header key.
"content-length"- Streaming =
::Protocol::HTTP::Body::Streamable::ResponseBody
Class Method Summary collapse
-
.completion_callback(response_finished, env, status, headers) ⇒ Object
Create a completion callback for response finished handlers.
-
.no_content?(status) ⇒ Boolean
Check if the given status code indicates no content should be returned.
-
.wrap(env, status, headers, body, input = nil, head = false) ⇒ Object
Wrap a Rack response body into a HTTP::Body instance.
Class Method Details
.completion_callback(response_finished, env, status, headers) ⇒ Object
Create a completion callback for response finished handlers. The callback is called with any error that occurred during response processing.
Callbacks are invoked in reverse order of registration, as specified by the Rack specification. If a callback raises an exception, it is caught and logged, but does not prevent other callbacks from being invoked.
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/protocol/rack/body.rb', line 117 def self.completion_callback(response_finished, env, status, headers) proc do |error| # Invoke callbacks in reverse order of registration, as specified by the Rack specification. response_finished.reverse_each do |callback| begin callback.call(env, status, headers, error) rescue => callback_error # If a callback raises an exception, log it but continue invoking other callbacks. # The Rack specification states that callbacks should not raise exceptions, but we handle # this gracefully to prevent one misbehaving callback from breaking others. Console.error(self, "Error occurred during response finished callback:", callback_error) end end end end |
.no_content?(status) ⇒ Boolean
Check if the given status code indicates no content should be returned. Status codes 204 (No Content), 205 (Reset Content), and 304 (Not Modified) should not include a response body.
27 28 29 |
# File 'lib/protocol/rack/body.rb', line 27 def self.no_content?(status) status == 204 or status == 205 or status == 304 end |
.wrap(env, status, headers, body, input = nil, head = false) ⇒ Object
Wrap a Rack response body into a HTTP::Body instance. Handles different types of response bodies:
-
HTTP::Body::Readable instances are returned as-is.
-
Bodies that respond to ‘to_path` are wrapped in HTTP::Body::File.
-
Enumerable bodies are wrapped in Enumerable.
-
Other bodies are wrapped in Streaming.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/protocol/rack/body.rb', line 45 def self.wrap(env, status, headers, body, input = nil, head = false) # In no circumstance do we want this header propagating out: if length = headers.delete(CONTENT_LENGTH) # We don't really trust the user to provide the right length to the transport. length = Integer(length) end # If we have an Async::HTTP body, we return it directly: if body.is_a?(::Protocol::HTTP::Body::Readable) # Ignore. elsif status == 200 and body.respond_to?(:to_path) begin # Don't mangle partial responses (206) body = ::Protocol::HTTP::Body::File.open(body.to_path).tap do body.close if body.respond_to?(:close) # Close the original body. end rescue Errno::ENOENT # If the file is not available, ignore. end elsif body.respond_to?(:each) body = Body::Enumerable.wrap(body, length) elsif body body = Body::Streaming.new(body, input) else Console.warn(self, "Rack response body was nil, ignoring!") end if body and no_content?(status) unless body.empty? Console.warn(self, "Rack response body was not empty, and status code indicates no content!", body: body, status: status) end body.close body = nil end response_finished = env[RACK_RESPONSE_FINISHED] if response_finished&.any? if body body = ::Protocol::HTTP::Body::Completable.new(body, completion_callback(response_finished, env, status, headers)) else completion_callback(response_finished, env, status, headers).call(nil) end end # There are two main situations we need to handle: # 1. The application has the `Rack::Head` middleware in the stack, which means we should not return a body, and the application is also responsible for setting the content-length header. `Rack::Head` will result in an empty enumerable body. # 2. The application does not have `Rack::Head`, in which case it will return a body and we need to extract the length. # In both cases, we need to ensure that the body is wrapped correctly. If there is no body and we don't know the length, we also just return `nil`. if head if body body = ::Protocol::HTTP::Body::Head.for(body) elsif length body = ::Protocol::HTTP::Body::Head.new(length) end # Otherwise, body is `nil` and we don't know the length either. end return body end |