Class: Axn::Webhooks::Outbound::Deliver
- Inherits:
-
Object
- Object
- Axn::Webhooks::Outbound::Deliver
- Includes:
- Axn, VendorFacet
- Defined in:
- lib/axn/webhooks/outbound/deliver.rb
Overview
A single delivery attempt + the self-managed retry engine. Built as an Axn: metrics/OTel/ structured logs per attempt come free. Retryable responses reschedule via axn's adapter-agnostic call_async(_async: { wait: }) seam (never branching on adapter type); unexpected exceptions propagate so the async adapter retries the un-acked job (at-least-once).
Constant Summary collapse
- MANAGED_HEADERS =
The headers Deliver adds AFTER the signer's, and therefore the ones a signer must not emit: Ruby Hash keys are case-sensitive so a differently-cased duplicate survives the merge below, but Net::HTTP is case-INSENSITIVE and the later assignment wins — silently replacing the signature. Signer::HmacSigner rejects these at declaration time.
%w[content-type user-agent].freeze
- PLAUSIBLE_FIELD_NAME =
A plausible field-name (
Authorization,content_type-- the documented common case of writing aheadersresolver with a Symbol/String literal key) is a short, simple identifier -- used bykey_descto decide whether a malformed header's key is safe to log as-is (seeadd_custom_header). /\A[A-Za-z_][A-Za-z0-9_]{0,49}\z/- FORBIDDEN_HEADER_VALUE_BYTES =
RFC 7230's
field-valuegrammar forbids every control byte except HTAB (0x09) -- CR/LF (0x0D/0x0A) are the ones Net::HTTP itself raises on, but any OTHER control byte (NUL, BEL, ...) is equally invalid on the wire and unvalidated here would reach the receiver (seeadd_custom_header). /[\x00-\x08\x0A-\x1F\x7F]/
Instance Method Summary collapse
Methods included from VendorFacet
Instance Method Details
#call ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/axn/webhooks/outbound/deliver.rb', line 71 def call # Scoped deliberately to ONLY `post` (not the whole method): a network error talking to # the receiver is a retryable delivery failure, but if `retry_or_exhaust!`'s own # `call_async` raises while ENQUEUING the follow-up job (e.g. a Redis/Sidekiq outage), # that must propagate as a loud exception — not get caught here and misinterpreted as # another delivery network error, which would re-run retry_or_exhaust! a second time in # the same attempt (a duplicate enqueue). Letting it propagate means the current job goes # un-acked and the async adapter's own retry path handles the outage (at-least-once). response = nil begin response = post rescue *Transport::RETRYABLE_NETWORK_ERRORS => e return retry_or_exhaust!(network_error: e) end return if success?(response.status) # 2xx -> done return retry_or_exhaust!(retry_after: header_value(response.headers, "retry-after")) if retryable?(response.status) fail!((response)) end |