Class: Axn::Webhooks::Response
- Inherits:
-
Object
- Object
- Axn::Webhooks::Response
- Defined in:
- lib/axn/webhooks/response.rb
Overview
A Rails-agnostic HTTP response value: status + body + headers. Produced by
Endpoint#to_response/#challenge_response from the pipeline's Axn::Result. #to_rack
renders it as the [status, headers, body] triple Endpoint#call(env) returns.
Instance Attribute Summary collapse
-
#body ⇒ Object
readonly
Returns the value of attribute body.
-
#headers ⇒ Object
readonly
Returns the value of attribute headers.
-
#status ⇒ Object
readonly
Returns the value of attribute status.
Class Method Summary collapse
- .ack(status: 200, headers: {}) ⇒ Object
-
.json(body, status: 200, headers: {}) ⇒ Object
A Hash/Array body is JSON-encoded; a String is assumed pre-serialized and passed through.
- .service_unavailable(retry_after: nil) ⇒ Object
- .text(body, status: 200, headers: {}) ⇒ Object
-
.valid_status?(value) ⇒ Boolean
A plausible HTTP status: an Integer inside the range HTTP defines.
- .xml(body, status: 200, headers: {}) ⇒ Object
Instance Method Summary collapse
- #==(other) ⇒ Object
-
#initialize(status: 200, body: "", headers: {}) ⇒ Response
constructor
A new instance of Response.
-
#to_rack ⇒ Object
[status, headers, body] — the Rack app return contract.
-
#with_status(status) ⇒ Object
The same body and headers under a different status.
Constructor Details
#initialize(status: 200, body: "", headers: {}) ⇒ Response
Returns a new instance of Response.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/axn/webhooks/response.rb', line 13 def initialize(status: 200, body: "", headers: {}) @status = status # deep_freeze (not `.freeze`) so a caller-owned String body isn't frozen in place — # `String#to_s` returns self, so `.freeze` would mutate the handler's own string. @body = deep_freeze(body.to_s) # Keys are lower-cased (Rack 3's SPEC forbids uppercase in response header keys, and # Rack::Lint rejects them). Keys AND values are frozen deeply (Array multi-value headers # freeze their elements too) so a caller's mutable value can't mutate this rendered-later value. # Values carrying CR/LF (or any other byte RFC 7230 forbids) are DROPPED, not rendered: # a `respond`/`static_respond`/`unauthorized_headers` declaration that echoes request data # into a header would otherwise let a sender inject headers or split the response. Dropped # rather than raised so a rendering mistake degrades to a missing header instead of a 500, # matching what the outbound half already does with a subscriber's custom headers. @headers = headers.each_with_object({}) do |(key, value), frozen| safe = sanitize_header(key, value) next if safe.nil? frozen[key.to_s.downcase.freeze] = deep_freeze(safe) end.freeze freeze end |
Instance Attribute Details
#body ⇒ Object (readonly)
Returns the value of attribute body.
11 12 13 |
# File 'lib/axn/webhooks/response.rb', line 11 def body @body end |
#headers ⇒ Object (readonly)
Returns the value of attribute headers.
11 12 13 |
# File 'lib/axn/webhooks/response.rb', line 11 def headers @headers end |
#status ⇒ Object (readonly)
Returns the value of attribute status.
11 12 13 |
# File 'lib/axn/webhooks/response.rb', line 11 def status @status end |
Class Method Details
.ack(status: 200, headers: {}) ⇒ Object
61 |
# File 'lib/axn/webhooks/response.rb', line 61 def self.ack(status: 200, headers: {}) = new(status:, headers:) |
.json(body, status: 200, headers: {}) ⇒ Object
A Hash/Array body is JSON-encoded; a String is assumed pre-serialized and passed through.
72 73 74 75 |
# File 'lib/axn/webhooks/response.rb', line 72 def self.json(body, status: 200, headers: {}) body = JSON.generate(body) unless body.is_a?(String) new(status:, body:, headers: { "content-type" => "application/json" }.merge(headers)) end |
.service_unavailable(retry_after: nil) ⇒ Object
82 83 84 85 |
# File 'lib/axn/webhooks/response.rb', line 82 def self.service_unavailable(retry_after: nil) headers = retry_after ? { "retry-after" => retry_after.to_s } : {} new(status: 503, headers:) end |
.text(body, status: 200, headers: {}) ⇒ Object
63 64 65 |
# File 'lib/axn/webhooks/response.rb', line 63 def self.text(body, status: 200, headers: {}) new(status:, body:, headers: { "content-type" => "text/plain" }.merge(headers)) end |
.valid_status?(value) ⇒ Boolean
A plausible HTTP status: an Integer inside the range HTTP defines. Shared by the
unparseable_status config setting and the per-endpoint dispatch unparseable_status:, so
both reject the same values against the same bound.
80 |
# File 'lib/axn/webhooks/response.rb', line 80 def self.valid_status?(value) = value.is_a?(Integer) && (200..599).cover?(value) |
.xml(body, status: 200, headers: {}) ⇒ Object
67 68 69 |
# File 'lib/axn/webhooks/response.rb', line 67 def self.xml(body, status: 200, headers: {}) new(status:, body:, headers: { "content-type" => "application/xml" }.merge(headers)) end |
Instance Method Details
#==(other) ⇒ Object
92 93 94 |
# File 'lib/axn/webhooks/response.rb', line 92 def ==(other) other.is_a?(self.class) && status == other.status && body == other.body && headers == other.headers end |
#to_rack ⇒ Object
[status, headers, body] — the Rack app return contract. Headers are already lower-cased (see #initialize); body is wrapped in an Array, Rack's documented minimal body contract. Return a mutable copy of headers so Rails middleware can add headers (e.g., ETag). Array header values (multi-value headers like Set-Cookie) are duped to be mutable so middleware like Rack::Utils.set_cookie_header! can append; String values pass through. Rack 3 requires Array headers, not newline-joined Strings.
102 |
# File 'lib/axn/webhooks/response.rb', line 102 def to_rack = [status, headers.transform_values { |value| value.is_a?(Array) ? value.dup : value }, [body]] |
#with_status(status) ⇒ Object
The same body and headers under a different status. The unparseable-body mapping needs it: a
declared static_respond block picked its status for the success path, but the gem owns the
outcome->status mapping, so the body a vendor keys on survives and only the status is restamped.
90 |
# File 'lib/axn/webhooks/response.rb', line 90 def with_status(status) = self.class.new(status:, body:, headers:) |