Class: ReactOnRailsPro::RendererHttpClient
- Inherits:
-
Object
- Object
- ReactOnRailsPro::RendererHttpClient
- Defined in:
- lib/react_on_rails_pro/renderer_http_client.rb
Overview
rubocop:disable Metrics/ClassLength
Defined Under Namespace
Classes: ConnectTimeoutWrapper, ConnectionError, Error, HTTPError, Response, TimeoutError
Constant Summary collapse
- CONNECTION_ERRORS =
[ SocketError, IOError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::EPIPE, Errno::ETIMEDOUT, Protocol::HTTP::RefusedError, # Treat HTTP/2 stream resets as transport failures because the renderer can # abort streams without a usable HTTP response for Request/StreamRequest. Protocol::HTTP2::StreamError ].freeze
- SCHEDULER_CLIENTS_KEY =
Per-scheduler storage for persistent HTTP clients. When an outer Fiber.scheduler exists BEFORE we enter ‘Sync {}`, clients are stored on the scheduler object using this instance variable key. This enables connection reuse across requests within the same long-lived scheduler context (e.g., Falcon, Puma with async scheduler). The hash maps origin URLs to Async::HTTP::Client instances.
IMPORTANT: We only use persistent mode when a scheduler already exists before ‘execute_request` enters `Sync {}`. If `Sync {}` creates an ephemeral scheduler, we use the ephemeral client path to ensure proper cleanup when the block exits.
:@__ror_pro_http_clients__
Class Method Summary collapse
- .build_form_body(form) ⇒ Object
- .build_multipart_body(form, boundary: SecureRandom.hex(24)) ⇒ Object
- .get(url, connect_timeout:, read_timeout:) ⇒ Object
- .split_url(url) ⇒ Object
Instance Method Summary collapse
- #close ⇒ Object
- #get(path) ⇒ Object
-
#initialize(origin:, pool_size:, connect_timeout:, read_timeout:, force_http2: true) ⇒ RendererHttpClient
constructor
A new instance of RendererHttpClient.
- #post(path, form: nil, json: nil, stream: false) ⇒ Object
-
#post_bidi(path, headers:) ⇒ Object
Bidirectional HTTP/2 streaming POST.
Constructor Details
#initialize(origin:, pool_size:, connect_timeout:, read_timeout:, force_http2: true) ⇒ RendererHttpClient
Returns a new instance of RendererHttpClient.
259 260 261 262 263 264 265 |
# File 'lib/react_on_rails_pro/renderer_http_client.rb', line 259 def initialize(origin:, pool_size:, connect_timeout:, read_timeout:, force_http2: true) @origin = origin @pool_size = pool_size @connect_timeout = connect_timeout @read_timeout = read_timeout @force_h2c = force_http2 && URI.parse(origin).scheme == "http" end |
Class Method Details
.build_form_body(form) ⇒ Object
183 184 185 186 187 188 189 190 |
# File 'lib/react_on_rails_pro/renderer_http_client.rb', line 183 def build_form_body(form) return build_multipart_body(form) if form.any? { |_name, value| file_part?(value) } [ [["content-type", "application/x-www-form-urlencoded"]], URI.encode_www_form(flatten_url_encoded_form(form)) ] end |
.build_multipart_body(form, boundary: SecureRandom.hex(24)) ⇒ Object
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/react_on_rails_pro/renderer_http_client.rb', line 166 def build_multipart_body(form, boundary: SecureRandom.hex(24)) raise ArgumentError, "boundary must not contain '--'" if boundary.include?("--") body = +"".b form.each do |name, value| append_multipart_value(body, boundary, name, value) end body << "--#{boundary}--\r\n" [ [["content-type", "multipart/form-data; boundary=#{boundary}"]], body ] end |
.get(url, connect_timeout:, read_timeout:) ⇒ Object
146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/react_on_rails_pro/renderer_http_client.rb', line 146 def get(url, connect_timeout:, read_timeout:) origin, path = split_url(url) new( origin: origin, pool_size: 1, connect_timeout: connect_timeout, read_timeout: read_timeout, force_http2: false ).get(path) end |
.split_url(url) ⇒ Object
158 159 160 161 162 163 164 |
# File 'lib/react_on_rails_pro/renderer_http_client.rb', line 158 def split_url(url) uri = URI.parse(url) port = uri.port unless uri.default_port == uri.port origin = "#{uri.scheme}://#{uri.host}#{":#{port}" if port}" [origin, uri.request_uri] end |
Instance Method Details
#close ⇒ Object
294 295 296 297 298 299 |
# File 'lib/react_on_rails_pro/renderer_http_client.rb', line 294 def close scheduler = Fiber.scheduler return unless scheduler evict_client_from_scheduler(scheduler) end |
#get(path) ⇒ Object
274 275 276 277 278 |
# File 'lib/react_on_rails_pro/renderer_http_client.rb', line 274 def get(path) build_response(stream: false) do |yielder, status_assigner| execute_request(:get, path, [[], nil], yielder, status_assigner) end end |
#post(path, form: nil, json: nil, stream: false) ⇒ Object
267 268 269 270 271 272 |
# File 'lib/react_on_rails_pro/renderer_http_client.rb', line 267 def post(path, form: nil, json: nil, stream: false) headers, body = request_body(form: form, json: json) build_response(stream: stream) do |yielder, status_assigner| execute_request(:post, path, [headers, body], yielder, status_assigner) end end |
#post_bidi(path, headers:) ⇒ Object
Bidirectional HTTP/2 streaming POST. Returns [output, response] where:
-
output is a Protocol::HTTP::Body::Writable::Output (supports << and close)
-
response is a lazy Response whose body is consumed via Response#each
The caller writes NDJSON lines to output while concurrently reading response chunks. Calling output.close sends END_STREAM on the HTTP/2 stream.
286 287 288 289 290 291 292 |
# File 'lib/react_on_rails_pro/renderer_http_client.rb', line 286 def post_bidi(path, headers:) writable = Protocol::HTTP::Body::Writable.new response = build_response(stream: true) do |yielder, status_assigner| execute_request(:post, path, [headers, writable], yielder, status_assigner) end [writable.output, response] end |