Module: Ksef::HTTP::Connection
- Defined in:
- lib/ksef/http/connection.rb
Overview
Builds the single Faraday connection a Client owns.
One connection per client, shared across threads — safe with the default net_http
adapter (DESIGN.md §5.2). The adapter is kept swappable via configuration.
Constant Summary collapse
- JSON_CONTENT_TYPE =
application/problem+jsonis the current error content type, so the response parser must match it as well as plainapplication/json(docs/REFERENCE.md §5.1). /\bjson\b/
Class Method Summary collapse
- .build(config) ⇒ Faraday::Connection
-
.storage(config) ⇒ Faraday::Connection
A second connection for pre-signed storage links, which are not KSeF API routes (docs/REFERENCE.md §14.2).
Class Method Details
.build(config) ⇒ Faraday::Connection
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/ksef/http/connection.rb', line 20 def build(config) Faraday.new(url: config.base_url, headers: default_headers(config)) do |f| f.request :json # Registration order is load-bearing, in two directions. Faraday runs # `on_complete` callbacks innermost-first, so the JSON parser must be registered # *after* the error handler for the handler to see a decoded body rather than a # raw string. And {Retry} must sit *outside* the error handler — so it catches # typed exceptions rather than re-deciding what a status means — while staying # *inside* `request :json`, so a retry re-sends the encoded body instead of # encoding it twice. f.use Retry, policy: config.retry_policy, logger: config.logger f.use ErrorHandler f.use SystemWarning, logger: config.logger # `parser_options` carries the decoder; see {JsonDecoder} for why one exists. # # **The hash must stay a fresh literal here.** Faraday's middleware reads the # decoder with `@parser_options&.delete(:decoder)` — destructively — so hoisting # this into a frozen constant raises `FrozenError` on the first request, and # sharing one hash across two connections leaves the second silently falling back # to `::JSON.parse` and failing again. `build` is called per connection, so the # literal is already correct; the point is not to "tidy" it into a constant. Both # failures measured 2026-09-07. f.response :json, content_type: JSON_CONTENT_TYPE, parser_options: { decoder: [JsonDecoder, :call] } (f, config) f.adapter config.adapter end end |
.storage(config) ⇒ Faraday::Connection
A second connection for pre-signed storage links, which are not KSeF API routes (docs/REFERENCE.md §14.2).
Its whole purpose is what it leaves out. A downloadUrl is a pre-signed Azure
Blob URI carrying its own authorisation in the query string, and the contract says
outright not to send the access token to it — doing so would hand a live KSeF
credential to third-party storage. Keeping those requests on a connection that has
no bearer, and no base_url to accidentally resolve against, makes that structural
rather than a rule someone has to remember.
Also omitted: JSON encoding and parsing, since a UPO is XML and must be kept as the exact bytes received (§12 — it is XAdES-signed, and re-serialising it risks invalidating the signature); and SystemWarning, whose header is an API concern. Retained: TLS settings, timeouts, proxy and the error handler.
67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/ksef/http/connection.rb', line 67 def storage(config) Faraday.new(headers: { "User-Agent" => config.user_agent }) do |f| # Retried on the same terms: a UPO download is a GET, and this path is unmetered, # so a transient storage failure should not cost the caller their proof of # receipt. Still GET/HEAD-only — the policy decides, not this connection. f.use Retry, policy: config.retry_policy, logger: config.logger f.use ErrorHandler (f, config) f.adapter config.adapter end end |