class Aws::Plugins::S3RequestSigner::BucketRegionErrorHandler
regional endpoint.
region, then finally a version 4 signed request against the correct
region. It follows up by making a request to determine the correct
This handler detects when a request fails because of a mismatched bucket
def call(context)
def call(context) response = @handler.call(context) handle_region_errors(response) end
def get_region_and_retry(context)
def get_region_and_retry(context) actual_region = context.http_response.headers['x-amz-bucket-region'] actual_region ||= region_from_body(context.http_response.body_contents) update_bucket_cache(context, actual_region) log_warning(context, actual_region) update_region_header(context, actual_region) @handler.call(context) end
def handle_region_errors(response)
def handle_region_errors(response) if wrong_sigv4_region?(response) get_region_and_retry(response.context) else response end end
def log_warning(context, actual_region)
def log_warning(context, actual_region) msg = "S3 client configured for #{context.config.region.inspect} " + "but the bucket #{context.params[:bucket].inspect} is in " + "#{actual_region.inspect}; Please configure the proper region " + "to avoid multiple unnecessary redirects and signing attempts\n" if logger = context.config.logger logger.warn(msg) else warn(msg) end end
def region_from_body(body)
def region_from_body(body) region = body.match(/<Region>(.+?)<\/Region>/)[1] if region.nil? || region == "" raise "couldn't get region from body: #{body}" else region end end
def update_bucket_cache(context, actual_region)
def update_bucket_cache(context, actual_region) S3::BUCKET_REGIONS[context.params[:bucket]] = actual_region end
def update_region_header(context, region)
def update_region_header(context, region) context.http_response.body.truncate(0) context.http_request.headers.delete('authorization') context.http_request.headers.delete('x-amz-security-token') context.http_request.endpoint.host = new_hostname(context, region) signer = Signers::V4.new(context.config.credentials, 's3', region) signer.sign(context.http_request) end
def wrong_sigv4_region?(resp)
def wrong_sigv4_region?(resp) resp.context.http_response.status_code == 400 && ( resp.context.http_response.headers['x-amz-bucket-region'] || resp.context.http_response.body_contents.match(/<Region>.+?<\/Region>/) ) end