aws-sdk-http-async - A Ruby gem that replaces the AWS SDK for Ruby Net::HTTP
send handler with async-http to enable fiber-based concurrency (Falcon/Async).
It includes a global patcher and a configurable Net::HTTP fallback so
CLI/test/console use cases "just work".
All architecture docs and decisions live in md-docs/. Keep RFCs and plans
there.
lib/async/aws/handler.rb- core Seahorse send handler, streaming + fallback logic.lib/async/aws/client_cache.rb- per-reactor async-http client cache, SSL/proxy config.lib/async/aws/http_plugin.rb- plugin options + registration.lib/async/aws/patcher.rb- global patch/unpatch utilities (ObjectSpace scan).spec/- unit + integration tests (including proxy CONNECT harness).md-docs/- RFCs, decisions, roadmap.bin/ci+Rakefile- local CI/format/test tasks.
- Ruby 3.4+ only.
aws-sdk-core >= 3.241.0,async-http >= 0.94.0. - Pure gem (no Rails/ActiveRecord).
- Seahorse handler contract must be preserved:
signal_headers,signal_data,signal_done,signal_error.
- Public methods require YARD docs (
@param,@return). - No destructive git commands (no add/commit/push/reset/checkout).
- Async transport when reactor exists:
Async::Task.current?->async-http. - Fallback when no reactor: default
:net_httpso CLI/tests/console work. - Config options:
Aws.config[:async_http_fallback] = :net_http | :sync | :raise- ENV
AWS_SDK_HTTP_ASYNC_FALLBACKoverrides config. - ENV
AWS_SDK_HTTP_ASYNC_FORCE_NET_HTTPforces Net::HTTP even if reactor exists.
- Event streams (Transcribe/Bedrock streaming) always require an Async reactor and delegate to AWS native HTTP/2 handler.
async_http_streaming_uploadsmodes::auto(default): stream rewindable + known size bodies; buffer otherwise.:force: always stream; raises if retries enabled and body non-rewindable.:off: always buffer.
- Size detection order:
Content-Lengthheader ->#bytesize->#length->#size. - Memory safety:
async_http_max_buffer_bytesdefault 5MB.- Applies to Strings, buffered bodies, and
StreamingBody#buffered. - Unknown-size IOs are read in chunks; if chunked read unsupported and max set -> raises.
- Applies to Strings, buffered bodies, and
Transfer-Encodingis stripped when body is buffered to avoid header/body mismatch.
- Headers normalized to lowercase;
host+content-lengthremoved before request. accept-encodingforced to empty only if absent andasync_http_force_accept_encodingtrue.- Duplicate response headers merged with
,(Net::HTTP parity). Set-CookieandSet-Cookie2are joined with\nto preserve cookie boundaries (RFC 6265).
http_open_timeoutused for endpoint connect.http_read_timeoutenforced per read chunk (not total request deadline).async_http_total_timeoutapplies to the full request lifecycle (upload + headers + body).async_http_idle_timeoutis used as the endpoint timeout whenhttp_open_timeoutis unset.timeout <= 0disables timeouts.
- Per-reactor LRU cache, capped by
async_http_max_cached_clients(default 100). - Cache key includes endpoint, connection limit, open timeout, proxy + SSL settings.
- Reactor binding tracked via
WeakRefto avoid stale clients on GC. - Cold-start guard removed: concurrent cold hits can build duplicate clients; extras are closed. This avoids Async + Mutex deadlocks and keeps logic simple.
clear!/close!are shutdown-only; call inside each reactor to close on owning reactor. If called outside a reactor, it force-closes (may interrupt in-flight requests).
ssl_verify_peerenforces hostname verification (verify_hostname=truewhen supported).- If no CA settings provided, a cached
OpenSSL::X509::Storewithset_default_pathsis used. ssl_cert/ssl_keyaccept OpenSSL objects or file paths (String /#to_path).- Empty strings raise
ArgumentError. - File reads are blocking; prefer preloading at boot and passing OpenSSL objects.
- Empty strings raise
- Cache key uses string paths when provided; OpenSSL objects use
object_id(reuse objects to keep cache stable).
http_proxysupports a full URL (incl. Basic auth).- Uses CONNECT for HTTPS. No env proxy support, PAC, or auto-discovery.
- Proxy auth is Basic derived from
user:pass@(userinfo is percent-decoded). - Proxy credentials are hashed in cache keys to avoid leaking secrets.
require 'aws-sdk-http-async'auto-patches all AWS clients (retroactive via ObjectSpace scan).require 'aws-sdk-http-async/core'loads without auto-patching for explicit plugin usage.Async::Aws::Patcher.patch(:all)andunpatch(:all)available for test isolation.- Unpatch removes only classes tracked by the patcher (custom subclasses may retain plugin).
- ObjectSpace scan is O(N) in loaded classes; require early in boot for best performance.
Commands:
bundle exec rake formatter
bundle exec rspec
bin/cibin/ci runs: bundle check -> rufo -> rubocop -> rspec -> bundler-audit ->
brakeman (skips if no Rails app). Formatting is Rufo + RuboCop (single quotes).
- Specs live in
spec/with unit + integration coverage. - Proxy CONNECT integration test uses an async-native proxy harness (no
Timeout.timeout). - No special RSpec setup required; Net::HTTP fallback covers reactorless contexts.
- Use
Sync { ... }if you want tests to exercise async-http.
- Avoid blocking I/O inside the reactor; use preloaded SSL objects.
- Avoid threads for I/O coordination; prefer fibers / Async primitives.
- Use
rgfor search (nogrep/find). - Keep changes small; add/adjust specs for behavior changes.
require 'aws-sdk-http-async'
client = Aws::DynamoDB::Client.new
client.list_tables