lib/aws-defaults/defaults_mode_config_resolver.rb



# frozen_string_literal: true

module Aws
  #@api private
  class DefaultsModeConfigResolver

    @@application_region = nil
    @@application_region_mutex = Mutex.new
    @@imds_client = EC2Metadata.new(retries: 0, http_open_timeout: 0.01)

    # mappings from Ruby SDK configuration names to the
    # sdk defaults option names and (optional) scale modifiers
    CFG_OPTIONS = {
      retry_mode: { name: "retryMode" },
      sts_regional_endpoints: { name: "stsRegionalEndpoints" },
      s3_us_east_1_regional_endpoint: { name: "s3UsEast1RegionalEndpoints" },
      http_open_timeout: { name: "connectTimeoutInMillis", scale: 0.001 },
      http_read_timeout: { name: "timeToFirstByteTimeoutInMillis", scale: 0.001 },
      ssl_timeout: { name: "tlsNegotiationTimeoutInMillis", scale: 0.001 }
    }.freeze

    def initialize(sdk_defaults, cfg)
      @sdk_defaults = sdk_defaults
      @cfg = cfg
      @resolved_mode = nil
      @mutex = Mutex.new
    end

    # option_name should be the symbolized ruby name to resolve
    # returns the ruby appropriate value or nil if none are resolved
    def resolve(option_name)
      return unless (std_option = CFG_OPTIONS[option_name])
      mode = resolved_mode.downcase

      return nil if mode == 'legacy'

      value = resolve_for_mode(std_option[:name], mode)
      value = value * std_option[:scale] if value && std_option[:scale]

      value
    end

    private
    def resolved_mode
      @mutex.synchronize do
        return @resolved_mode unless @resolved_mode.nil?

        @resolved_mode = @cfg.defaults_mode == 'auto' ? resolve_auto_mode : @cfg.defaults_mode
      end
    end

    def resolve_auto_mode
      return "mobile" if env_mobile?

      region = application_current_region

      if region
        @cfg.region == region ? "in-region": "cross-region"
      else
        # We don't seem to be mobile, and we couldn't determine whether we're running within an AWS region. Fall back to standard.
        'standard'
      end
    end

    def application_current_region
      resolved_region = @@application_region_mutex.synchronize do
        return @@application_region unless @@application_region.nil?

        region = nil
        if ENV['AWS_EXECUTION_ENV']
          region = ENV['AWS_REGION'] || ENV['AWS_DEFAULT_REGION']
        end

        if region.nil? && ENV['AWS_EC2_METADATA_DISABLED']&.downcase != "true"
          begin
            region = @@imds_client.get('/latest/meta-data/placement/region')
          rescue
            # unable to get region, leave it unset
          end
        end

        # required so that we cache the unknown/nil result
        @@application_region = region || :unknown
      end
      resolved_region == :unknown ? nil : resolved_region
    end

    def resolve_for_mode(name, mode)
      base_value = @sdk_defaults['base'][name]
      mode_value = @sdk_defaults['modes'].fetch(mode, {})[name]

      if mode_value.nil?
        return base_value
      end

      return mode_value['override'] unless mode_value['override'].nil?
      return base_value + mode_value['add'] unless mode_value['add'].nil?
      return base_value * mode_value['multiply'] unless mode_value['multiply'].nil?
      return base_value
    end

    def env_mobile?
      false
    end

  end
end