Class: PatientLLM::AwsRequestSigner
- Inherits:
-
Object
- Object
- PatientLLM::AwsRequestSigner
- Defined in:
- lib/patient_llm/aws_request_signer.rb
Overview
Signs outgoing requests with an AWS SigV4 signature. Instances are callable, so one can be registered directly as a PatientHttp request preprocessor for providers that require request signing (AWS Bedrock):
The credentials can be given as a credential chain (any object responding
to resolve, e.g. Aws::CredentialProviderChain), a credentials provider
(responding to credentials), or a static credentials object (responding
to access_key_id and secret_access_key, e.g. Aws::Credentials).
A chain is resolved lazily on the first request and the resolved provider
is reused thereafter.
The signing service and region can be passed explicitly; when omitted they
are derived from each request's URL host for standard
<service>.<region>.<dns-suffix> endpoints, including dual-stack
api.aws hosts (e.g. bedrock-runtime.us-east-1.amazonaws.com signs as
service "bedrock" and bedrock-mantle.us-east-1.api.aws as
"bedrock-mantle", both in region "us-east-1").
Only a whitelist of request headers is signed (see DEFAULT_SIGNED_HEADERS; override with signed_headers:) because intermediaries commonly rewrite other headers in transit — the Bedrock Mantle gateway, for one, replaces x-request-id before validating the signature. The signature also covers a host header derived from the request URL, but the host header itself is never set on the request — the HTTP client generates its own from the URL, and sending both would invalidate the signature.
The aws-sigv4 gem must be loaded for this class to work, but it is not a dependency of this gem; add it to your bundle (it is included with the aws-sdk-core gem).
Constant Summary collapse
- REGION_PATTERN =
Matches AWS region host labels like "us-east-1" or "us-gov-west-1".
/\A[a-z]{2}(?:-gov|-iso[a-z]?)?-[a-z]+-\d+\z/- AWS_DNS_SUFFIXES =
DNS suffixes used by AWS endpoint hosts: the standard and China suffixes plus their dual-stack equivalents.
[ ".amazonaws.com", ".api.aws", ".amazonaws.com.cn", ".api.amazonwebservices.com.cn" ].freeze
- SIGNING_NAME_ALIASES =
Endpoint host labels whose SigV4 signing name differs from the label. Note that bedrock-mantle is intentionally absent: the Mantle endpoint signs under its own "bedrock-mantle" service name.
{ "bedrock-runtime" => "bedrock", "bedrock-agent-runtime" => "bedrock" }.freeze
- DEFAULT_SIGNED_HEADERS =
Request headers included in the signature by default. Signing is whitelist-based because proxies and gateways commonly rewrite other headers in transit (the Bedrock Mantle gateway replaces x-request-id with its own id before validating the signature), which would invalidate any signature that covers them. The signer itself always adds and signs host, x-amz-date, x-amz-content-sha256, and x-amz-security-token.
["content-type", "anthropic-version"].freeze
Instance Method Summary collapse
-
#call(request) ⇒ void
Sign the outgoing request, adding the SigV4 signature headers to it.
-
#initialize(credentials:, service: nil, region: nil, signed_headers: DEFAULT_SIGNED_HEADERS) ⇒ AwsRequestSigner
constructor
A new instance of AwsRequestSigner.
Constructor Details
#initialize(credentials:, service: nil, region: nil, signed_headers: DEFAULT_SIGNED_HEADERS) ⇒ AwsRequestSigner
Returns a new instance of AwsRequestSigner.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/patient_llm/aws_request_signer.rb', line 83 def initialize(credentials:, service: nil, region: nil, signed_headers: DEFAULT_SIGNED_HEADERS) unless defined?(Aws::Sigv4::Signer) raise LoadError, "#{self.class.name} requires the aws-sigv4 gem, which is not a dependency of patient_llm. Add `gem \"aws-sigv4\"` to your Gemfile and require \"aws-sigv4\" (the gem is included with aws-sdk-core)." end supported = credentials.respond_to?(:resolve) || credentials.respond_to?(:credentials) || (credentials.respond_to?(:access_key_id) && credentials.respond_to?(:secret_access_key)) unless supported raise ArgumentError, "credentials must be a credential chain (responds to resolve), a credentials provider (responds to credentials), or a credentials object (responds to access_key_id and secret_access_key)" end @credentials = credentials @service = service @region = region @signed_headers = signed_headers.map { |name| name.to_s.downcase } @signer = nil @resolved_provider = nil @mutex = Mutex.new end |
Instance Method Details
#call(request) ⇒ void
This method returns an undefined value.
Sign the outgoing request, adding the SigV4 signature headers to it.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/patient_llm/aws_request_signer.rb', line 110 def call(request) signature = signer(request.url).sign_request( http_method: request.http_method.to_s.upcase, url: request.url, headers: request.headers.to_h.slice(*@signed_headers), body: request.body.to_s ) # The signature covers a host header derived from the URL, but it must # not be set on the request: the HTTP client generates its own Host # header from the URL, and sending both makes the server canonicalize a # doubled value that can never match the signature. signature.headers.each do |name, value| request.headers[name] = value unless name == "host" end end |