Skip to content

Instantly share code, notes, and snippets.

@kuboon
Created September 5, 2025 08:28
Show Gist options
  • Select an option

  • Save kuboon/165aae18033c274548dcb1d4d3318852 to your computer and use it in GitHub Desktop.

Select an option

Save kuboon/165aae18033c274548dcb1d4d3318852 to your computer and use it in GitHub Desktop.
s3 presign 用に作ったけど aws-sdk-s3 が内部で uri encode するので事前に encode してはダメだったと判明し供養
module HttpUtil
class PathSegment < String
MATCHER = Regexp.new("\\A#{URI::RFC3986_Parser::SEG_NC}+\\Z")
def self.encode_from(str, allow_slash: false)
return str if str.is_a?(PathSegment)
raise 'should not include / in element or use allow_slash option to encode' if !allow_slash && str.include?('/')
new(URI.encode_uri_component(str.unicode_normalize(:nfkc)))
end
def initialize(str)
raise "invalid char: #{str}" unless MATCHER.match?(str)
super.freeze
end
def decode
URI.decode_uri_component(self)
end
end
class Path < Array
def self.build(str_or_arr, allow_slash: false)
case str_or_arr
when Path
str_or_arr
when String
build(str_or_arr.split('/'))
when Array
raise 'should not include blank path segment' if str_or_arr.any?(&:blank?)
Path[*str_or_arr.map { PathSegment.encode_from(_1, allow_slash:) }].freeze
else
raise 'invalid type'
end
end
def to_s
join('/')
end
end
module_function
def content_disposition_value(type, filename)
raise 'type should be attachment or inline' unless type.in? %i[attachment inline]
encoded = PathSegment.encode_from(filename)
"#{type}; filename*=UTF-8''#{encoded.decode}"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment