lib/aws-sdk-core/rest/response/header_list_parser.rb



# frozen_string_literal: true

require 'strscan'

module Aws
  module Rest
    module Response
      # @api private
      module HeaderListParser

        class << self
          # parse a list of possibly quoted and escaped string values
          # Follows:
          # # [RFC-7230's specification of header values](https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6).
          def parse_string_list(value)
            buffer = StringScanner.new(value)
            parsed = []

            parsed << read_value(buffer) until buffer.eos?

            parsed
          end

          def parse_timestamp_list(value, ref)
            # timestamp lists use an http-date by default and are unescaped
            # eg: Mon, 16 Dec 2019 23:48:18 GMT, Mon, 16 Dec 2019 23:48:18 GMT
            case ref['timestampFormat'] || ref.shape['timestampFormat']
            when 'unixTimestamp'
              value.split(', ').map { |v| Time.at(v.to_f) }
            when 'iso8601' then value.split(', ').map { |v| Time.parse(v) }
            else
              # header default to rfc822/http-date, which has a comma after day
              value.split(',').each_slice(2).map { |v| Time.parse(v[0] + v[1])}
            end
          end

          private

          def read_value(buffer)
            until buffer.eos?
              case buffer.peek(1)
              when ' ', "\t"
                # drop leading whitespace
                buffer.getch
                next
              when '"'
                buffer.getch # drop the quote and advance
                return read_quoted_value(buffer)
              else
                return read_unquoted_value(buffer)
              end
            end
            # buffer is only whitespace
            nil
          end

          def read_unquoted_value(buffer)
            # there cannot be any escaped values
            value = buffer.scan_until(/,|$/)
            # drop the comma if we matched it
            buffer.matched == ',' ? value.chop : value
          end

          def read_quoted_value(buffer)
            # scan until we have an unescaped double quote
            value = buffer.scan_until(/[^\\]"/)
            raise ArgumentError, 'Invalid String list: No closing quote found' unless value

            # drop any remaining whitespace/commas
            buffer.scan_until(/[\s,]*/)
            # the last character will always be the closing quote.
            # Add a starting quote  and then unescape (undump)
            "\"#{value}".undump
          end
        end
      end
    end
  end
end