class Ur::ContentType
Ur::ContentType
represents a Content-Type header field. it parses the media type and its components, as well as any parameters.
this class aims to be permissive in what it will parse. it will not raise any error when given a malformed or syntactically invalid Content-Type string. fields and parameters parsed from invalid Content-Type strings are undefined, but this class generally tries to make the most sense of what it's given.
this class is based on RFCs:
-
Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content Section 3.1.1.1. Media Type tools.ietf.org/html/rfc7231#section-3.1.1.1
-
Media Type Specifications and Registration Procedures tools.ietf.org/html/rfc6838
-
Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies. Section 5.1. Syntax of the Content-Type Header Field tools.ietf.org/html/rfc2045#section-5.1
-
Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types tools.ietf.org/html/rfc2046
Constants
- MEDIA_TYPE_REGEXP
the character ranges in this SHOULD be significantly more restrictive, and the /<subtype> construct should not be optional. however, we'll aim to match whatever media type we are given.
example:
MEDIA_TYPE_REGEXP.match('application/vnd.github+json').named_captures => { "media_type" => "application/vnd.github+json", "type" => "application", "subtype" => "vnd.github+json", "facet" => "vnd", "suffix" => "json", }
example of being more permissive than the spec allows:
MEDIA_TYPE_REGEXP.match('where the %$*! am I').named_captures => { "media_type" => "where the %$*! am I", "type" => "where the %$*! am I", "subtype" => nil, "facet" => nil, "suffix" => nil }
- SOME_TEXT_SUBTYPES
Attributes
@return [String, nil] the 'facet' portion of our media type.
e.g. "vnd" in content-type: application/vnd.github+json; charset="utf-8"
@return [String, nil] the media type of this content type.
e.g. "application/vnd.github+json" in content-type: application/vnd.github+json; charset="utf-8"
@return [Hash<String, String>] parameters of this content type.
e.g. {"charset" => "utf-8"} in content-type: application/vnd.github+json; charset="utf-8"
@return [String, nil] the 'subtype' portion of our media type.
e.g. "vnd.github+json" in content-type: application/vnd.github+json; charset="utf-8"
@return [String, nil] the 'suffix' portion of our media type.
e.g. "json" in content-type: application/vnd.github+json; charset="utf-8"
@return [String, nil] the 'type' portion of our media type.
e.g. "application" in content-type: application/vnd.github+json; charset="utf-8"
Public Class Methods
# File lib/ur/content_type.rb, line 68 def initialize(*a) super scanner = StringScanner.new(self) if scanner.scan(MEDIA_TYPE_REGEXP) @media_type = scanner[:media_type].strip.freeze if scanner[:media_type] @type = scanner[:type].strip.freeze if scanner[:type] @subtype = scanner[:subtype].strip.freeze if scanner[:subtype] @facet = scanner[:facet].strip.freeze if scanner[:facet] @suffix = scanner[:suffix].strip.freeze if scanner[:suffix] end @parameters = Hash.new do |h, k| if k.respond_to?(:downcase) && k != k.downcase h[k.downcase] else nil end end while scanner.scan(/(;\s*)+/) key = scanner.scan(/[^;=\"]*/) if key && scanner.scan(/=/) value = String.new until scanner.eos? || scanner.check(/;/) if scanner.scan(/\s+/) ws = scanner[0] # discard trailing whitespace. # other whitespace isn't technically valid but we are permissive so we put it in the value. value << ws unless scanner.eos? || scanner.check(/;/) elsif scanner.scan(/"/) until scanner.eos? || scanner.scan(/"/) if scanner.scan(/\\/) value << scanner.getch unless scanner.eos? end value << scanner.scan(/[^\"\\]*/) end else value << scanner.scan(/[^\s;\"]*/) end end @parameters[key.downcase.freeze] = value.freeze end end @parameters.freeze freeze end
Public Instance Methods
@param unknown [Boolean] return this value when we have no idea whether
our media type is binary or text.
@return [Boolean] does this content type appear to be binary?
this library makes its best guess based on a very incomplete knowledge of which media types indicate binary or text.
# File lib/ur/content_type.rb, line 181 def binary?(unknown: true) return false if type_text? SOME_TEXT_SUBTYPES.each do |cmpsubtype| return false if (suffix ? suffix.casecmp?(cmpsubtype) : subtype ? subtype.casecmp?(cmpsubtype) : false) end # these are generally binary return true if type_image? || type_audio? || type_video? # we're out of ideas return unknown end
@return [Boolean] is this a x-www-form-urlencoded content type?
# File lib/ur/content_type.rb, line 206 def form_urlencoded? suffix ? suffix.casecmp?('x-www-form-urlencoded'): subtype ? subtype.casecmp?('x-www-form-urlencoded') : false end
@return [Boolean] is this a JSON content type?
# File lib/ur/content_type.rb, line 196 def json? suffix ? suffix.casecmp?('json') : subtype ? subtype.casecmp?('json') : false end
@param other_subtype @return [Boolean] is the 'subtype' portion of our media type equal (case-insensitive) to the given other_subtype
# File lib/ur/content_type.rb, line 151 def subtype?(other_subtype) subtype && subtype.casecmp?(other_subtype) end
@param other_suffix @return [Boolean] is the 'suffix' portion of our media type equal (case-insensitive) to the given other_suffix
# File lib/ur/content_type.rb, line 157 def suffix?(other_suffix) suffix && suffix.casecmp?(other_suffix) end
@param other_type @return [Boolean] is the 'type' portion of our media type equal (case-insensitive) to the given other_type
# File lib/ur/content_type.rb, line 145 def type?(other_type) type && type.casecmp?(other_type) end
@return [Boolean] is the 'type' portion of our media type 'application'
# File lib/ur/content_type.rb, line 231 def type_application? type && type.casecmp?('application') end
@return [Boolean] is the 'type' portion of our media type 'audio'
# File lib/ur/content_type.rb, line 221 def type_audio? type && type.casecmp?('audio') end
@return [Boolean] is the 'type' portion of our media type 'image'
# File lib/ur/content_type.rb, line 216 def type_image? type && type.casecmp?('image') end
@return [Boolean] is the 'type' portion of our media type 'message'
# File lib/ur/content_type.rb, line 236 def type_message? type && type.casecmp?('message') end
@return [Boolean] is the 'type' portion of our media type 'multipart'
# File lib/ur/content_type.rb, line 241 def type_multipart? type && type.casecmp?('multipart') end
@return [Boolean] is the 'type' portion of our media type 'text'
# File lib/ur/content_type.rb, line 211 def type_text? type && type.casecmp?('text') end
@return [Boolean] is the 'type' portion of our media type 'video'
# File lib/ur/content_type.rb, line 226 def type_video? type && type.casecmp?('video') end
@return [Boolean] is this an XML content type?
# File lib/ur/content_type.rb, line 201 def xml? suffix ? suffix.casecmp?('xml'): subtype ? subtype.casecmp?('xml') : false end