class Pakyow::Connection::MultipartParser

Constants

DEFAULT_MULTIPART_LIMIT

Attributes

values[R]

Public Class Methods

new(params, boundary:) click to toggle source
# File lib/pakyow/connection/multipart_parser.rb, line 20
def initialize(params, boundary:)
  @params, @boundary = params, boundary.to_s.gsub(/[\"\']/, "")
  @reader = ::MultipartParser::Reader.new(@boundary)
  @reader.on_part(&method(:on_part))
  @reader.on_error(&method(:on_error))
  @values = []
  @size = 0
end

Public Instance Methods

parse(input) click to toggle source
# File lib/pakyow/connection/multipart_parser.rb, line 29
def parse(input)
  while data = input.read
    @reader.write(data)
  end

  finalize
  @params
rescue StandardError => error
  ensure_closed
  if error.is_a?(LimitExceeded)
    raise error
  else
    raise ParseError.build(error)
  end
end

Private Instance Methods

add(value) click to toggle source
# File lib/pakyow/connection/multipart_parser.rb, line 53
def add(value)
  @values << value

  if value.is_a?(MultipartInput)
    @size += 1
  end

  if @size > DEFAULT_MULTIPART_LIMIT
    raise LimitExceeded, "multipart limit (#{DEFAULT_MULTIPART_LIMIT}) exceeded"
  end

  value
end
ensure_closed() click to toggle source
# File lib/pakyow/connection/multipart_parser.rb, line 105
def ensure_closed
  @values.each do |value|
    value.close if value.is_a?(MultipartInput)
  end
end
finalize() click to toggle source
# File lib/pakyow/connection/multipart_parser.rb, line 47
def finalize
  @values.select { |value|
    value.is_a?(MultipartInput)
  }.each(&:rewind)
end
on_error(error) click to toggle source
# File lib/pakyow/connection/multipart_parser.rb, line 100
def on_error(error)
  ensure_closed
  raise ParseError, error
end
on_part(part) click to toggle source
# File lib/pakyow/connection/multipart_parser.rb, line 67
def on_part(part)
  headers = Protocol::HTTP::Headers.new(part.headers).to_h
  disposition = QueryParser.new.tap { |parser|
    parser.parse(headers["content-disposition"].to_s)
  }.params
  content_type = QueryParser.new.tap { |parser|
    parser.parse(headers["content-type"].to_s)
  }.params

  if filename = disposition["filename"]
    value = add(MultipartInput.new(filename: filename, headers: headers, type: part.mime))

    part.on_data do |data|
      value << data
    end
  else
    value = add(String.new)
    encoding = if charset = content_type["charset"]
      Encoding.find(charset.gsub(/[^a-zA-Z0-9\-_]/, ""))
    else
      Encoding::UTF_8
    end

    value.force_encoding(encoding)

    part.on_data do |data|
      value << data
    end
  end

  @params.add_value_for_key(value, part.name || disposition["filename"])
end