Skip to content

Commit

Permalink
Improve Content-Type parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
ixti committed Jan 26, 2014
1 parent b042dca commit 4760ff8
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Rakefile
Expand Up @@ -22,7 +22,7 @@ end

require 'yardstick/rake/verify'
Yardstick::Rake::Verify.new do |verify|
verify.threshold = 56.3
verify.threshold = 56.8
end

task :default => [:spec, :rubocop, :verify_measurements]
27 changes: 27 additions & 0 deletions lib/http/content_type.rb
@@ -0,0 +1,27 @@
module HTTP
ContentType = Struct.new(:mime_type, :charset) do
MIME_TYPE_RE = %r{^([^/]+/[^;]+)(?:$|;)}
CHARSET_RE = /;\s*charset=([^;]+)/i

class << self
# Parse string and return ContentType struct
def parse(str)
new mime_type(str), charset(str)
end

private

# :nodoc:
def mime_type(str)
md = str.to_s.match MIME_TYPE_RE
md && md[1].to_s.strip.downcase
end

# :nodoc:
def charset(str)
md = str.to_s.match CHARSET_RE
md && md[1].to_s.strip.gsub(/^"|"$/, '')
end
end
end
end
15 changes: 14 additions & 1 deletion lib/http/response.rb
@@ -1,5 +1,6 @@
require 'delegate'
require 'http/header'
require 'http/content_type'

module HTTP
class Response
Expand Down Expand Up @@ -120,10 +121,22 @@ def to_s
end
alias_method :to_str, :to_s

# Parsed Content-Type header
# @return [HTTP::ContentType]
def content_type
@content_type ||= ContentType.parse @headers['Content-Type']
end

# MIME type of response (if any)
# @return [String, nil]
def mime_type
@mime_type ||= @headers['Content-Type'].split(/;\s*/).first
@mime_type ||= content_type.mime_type
end

# Charset of response (if any)
# @return [String, nil]
def charset
@mime_type ||= content_type.charset
end

# Inspect a response
Expand Down
47 changes: 47 additions & 0 deletions spec/http/content_type_spec.rb
@@ -0,0 +1,47 @@
require 'spec_helper'

describe HTTP::ContentType do
describe '.parse' do
context 'with text/plain' do
subject { described_class.parse 'text/plain' }
its(:mime_type) { should eq 'text/plain' }
its(:charset) { should be_nil }
end

context 'with tEXT/plaIN' do
subject { described_class.parse 'tEXT/plaIN' }
its(:mime_type) { should eq 'text/plain' }
its(:charset) { should be_nil }
end

context 'with text/plain; charset=utf-8' do
subject { described_class.parse 'text/plain; charset=utf-8' }
its(:mime_type) { should eq 'text/plain' }
its(:charset) { should eq 'utf-8' }
end

context 'with text/plain; charset="utf-8"' do
subject { described_class.parse 'text/plain; charset="utf-8"' }
its(:mime_type) { should eq 'text/plain' }
its(:charset) { should eq 'utf-8' }
end

context 'with text/plain; charSET=utf-8' do
subject { described_class.parse 'text/plain; charSET=utf-8' }
its(:mime_type) { should eq 'text/plain' }
its(:charset) { should eq 'utf-8' }
end

context 'with text/plain; foo=bar; charset=utf-8' do
subject { described_class.parse 'text/plain; foo=bar; charset=utf-8' }
its(:mime_type) { should eq 'text/plain' }
its(:charset) { should eq 'utf-8' }
end

context 'with text/plain;charset=utf-8;foo=bar' do
subject { described_class.parse 'text/plain;charset=utf-8;foo=bar' }
its(:mime_type) { should eq 'text/plain' }
its(:charset) { should eq 'utf-8' }
end
end
end
38 changes: 34 additions & 4 deletions spec/http/response_spec.rb
Expand Up @@ -25,10 +25,40 @@
end

describe 'mime_type' do
it 'strips out charset' do
headers = {'Content-Type' => 'text/html; charset=utf8'}
response = HTTP::Response.new 200, '1.1', headers, ''
expect(response.mime_type).to eq 'text/html'
subject { HTTP::Response.new(200, '1.1', headers, '').mime_type }

context 'without Content-Type header' do
let(:headers) { {} }
it { should be_nil }
end

context 'with Content-Type: text/html' do
let(:headers) { {'Content-Type' => 'text/html'} }
it { should eq 'text/html' }
end

context 'with Content-Type: text/html; charset=utf-8' do
let(:headers) { {'Content-Type' => 'text/html; charset=utf-8'} }
it { should eq 'text/html' }
end
end

describe 'charset' do
subject { HTTP::Response.new(200, '1.1', headers, '').charset }

context 'without Content-Type header' do
let(:headers) { {} }
it { should be_nil }
end

context 'with Content-Type: text/html' do
let(:headers) { {'Content-Type' => 'text/html'} }
it { should be_nil }
end

context 'with Content-Type: text/html; charset=utf-8' do
let(:headers) { {'Content-Type' => 'text/html; charset=utf-8'} }
it { should eq 'utf-8' }
end
end
end

0 comments on commit 4760ff8

Please sign in to comment.