Skip to content

Commit

Permalink
Merge pull request #273 from httprb/Connorhd/add-encoding-option
Browse files Browse the repository at this point in the history
Add encoding option
  • Loading branch information
ixti committed Dec 13, 2015
2 parents 2663da6 + 6de9119 commit 1836ae8
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 40 deletions.
5 changes: 5 additions & 0 deletions lib/http/chainable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ def cookies(cookies)
branch default_options.with_cookies(cookies)
end

# Force a specific encoding for response body
def encoding(encoding)
branch default_options.with_encoding(encoding)
end

# Accept the given MIME type(s)
# @param type
def accept(type)
Expand Down
11 changes: 6 additions & 5 deletions lib/http/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,12 @@ def perform(req, options)
end

res = Response.new(
@connection.status_code,
@connection.http_version,
@connection.headers,
Response::Body.new(@connection),
req.uri
:status => @connection.status_code,
:version => @connection.http_version,
:headers => @connection.headers,
:connection => @connection,
:encoding => options.encoding,
:uri => req.uri
)

@connection.finish_response if req.verb == :head
Expand Down
7 changes: 6 additions & 1 deletion lib/http/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ def initialize(options = {})
:ssl => {},
:keep_alive_timeout => 5,
:headers => {},
:cookies => {}
:cookies => {},
:encoding => nil
}

opts_w_defaults = defaults.merge(options)
Expand All @@ -66,6 +67,10 @@ def initialize(options = {})
end
end

def_option :encoding do |encoding|
self.encoding = Encoding.find(encoding)
end

%w(
proxy params form json body follow response
socket_class ssl_socket_class ssl_context ssl
Expand Down
47 changes: 29 additions & 18 deletions lib/http/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,27 @@ class Response
# @return [URI, nil]
attr_reader :uri

def initialize(status, version, headers, body, uri = nil) # rubocop:disable ParameterLists
@version = version
@body = body
@uri = uri && HTTP::URI.parse(uri)
@status = HTTP::Response::Status.new status
@headers = HTTP::Headers.coerce(headers || {})
# Inits a new instance
#
# @option opts [Integer] :status Status code
# @option opts [String] :version HTTP version
# @option opts [Hash] :headers
# @option opts [HTTP::Connection] :connection
# @option opts [String] :encoding Encoding to use when reading body
# @option opts [String] :body
# @option opts [String] :uri
def initialize(opts)
@version = opts.fetch(:version)
@uri = opts.include?(:uri) && HTTP::URI.parse(opts.fetch(:uri))
@status = HTTP::Response::Status.new opts.fetch(:status)
@headers = HTTP::Headers.coerce(opts.fetch(:headers, {}))

if opts.include?(:connection)
encoding = opts[:encoding] || charset || Encoding::BINARY
@body = Response::Body.new(opts.fetch(:connection), encoding)
else
@body = opts.fetch(:body)
end
end

# @!method reason
Expand Down Expand Up @@ -70,19 +85,15 @@ def content_type
@content_type ||= ContentType.parse headers[Headers::CONTENT_TYPE]
end

# MIME type of response (if any)
#
# @return [String, nil]
def mime_type
@mime_type ||= content_type.mime_type
end
# @!method mime_type
# MIME type of response (if any)
# @return [String, nil]
def_delegator :content_type, :mime_type

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

def cookies
@cookies ||= headers.each_with_object CookieJar.new do |(k, v), jar|
Expand Down
13 changes: 7 additions & 6 deletions lib/http/response/body.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ class Body
include Enumerable
def_delegator :to_s, :empty?

def initialize(client)
@client = client
@streaming = nil
@contents = nil
def initialize(client, encoding)
@client = client
@streaming = nil
@contents = nil
@encoding = encoding
end

# (see HTTP::Client#readpartial)
Expand All @@ -36,9 +37,9 @@ def to_s

begin
@streaming = false
@contents = "".force_encoding(Encoding::UTF_8)
@contents = "".force_encoding(@encoding)
while (chunk = @client.readpartial)
@contents << chunk.force_encoding(Encoding::ASCII_8BIT)
@contents << chunk.force_encoding(@encoding)
end
rescue
@contents = nil
Expand Down
11 changes: 9 additions & 2 deletions spec/lib/http/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,18 @@ def stub(stubs)
end

def redirect_response(location, status = 302)
HTTP::Response.new(status, "1.1", {"Location" => location}, "")
HTTP::Response.new(
:status => status,
:version => "1.1",
:headers => {"Location" => location},
:body => "")
end

def simple_response(body, status = 200)
HTTP::Response.new(status, "1.1", {}, body)
HTTP::Response.new(
:status => status,
:version => "1.1",
:body => body)
end

describe "following redirects" do
Expand Down
3 changes: 2 additions & 1 deletion spec/lib/http/options/merge_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
:socket_class => described_class.default_socket_class,
:ssl_socket_class => described_class.default_ssl_socket_class,
:ssl_context => nil,
:cookies => {})
:cookies => {},
:encoding => nil)
end
end
7 changes: 6 additions & 1 deletion spec/lib/http/redirector_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
RSpec.describe HTTP::Redirector do
def simple_response(status, body = "", headers = {})
HTTP::Response.new(status, "1.1", headers, body)
HTTP::Response.new(
:status => status,
:version => "1.1",
:headers => headers,
:body => body
)
end

def redirect_response(status, location)
Expand Down
2 changes: 1 addition & 1 deletion spec/lib/http/response/body_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

before { allow(client).to receive(:readpartial) { chunks.shift } }

subject(:body) { described_class.new client }
subject(:body) { described_class.new client, Encoding::UTF_8 }

it "streams bodies from responses" do
expect(subject.to_s).to eq "Hello, World!"
Expand Down
11 changes: 10 additions & 1 deletion spec/lib/http/response_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@
let(:body) { "Hello world!" }
let(:uri) { "http://example.com/" }
let(:headers) { {} }
subject(:response) { HTTP::Response.new 200, "1.1", headers, body, uri }

subject(:response) do
HTTP::Response.new(
:status => 200,
:version => "1.1",
:headers => headers,
:body => body,
:uri => uri
)
end

it "includes HTTP::Headers::Mixin" do
expect(described_class).to include HTTP::Headers::Mixin
Expand Down
32 changes: 28 additions & 4 deletions spec/lib/http_spec.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# encoding: UTF-8

require "json"

require "support/dummy_server"
Expand Down Expand Up @@ -160,14 +162,36 @@
context "loading binary data" do
it "is encoded as bytes" do
response = HTTP.get "#{dummy.endpoint}/bytes"
expect(response.to_s.encoding).to eq(Encoding::ASCII_8BIT)
expect(response.to_s.encoding).to eq(Encoding::BINARY)
end
end

context "loading endpoint with charset" do
it "uses charset from headers" do
response = HTTP.get "#{dummy.endpoint}/iso-8859-1"
expect(response.to_s.encoding).to eq(Encoding::ISO8859_1)
expect(response.to_s.encode(Encoding::UTF_8)).to eq("testæ")
end

context "with encoding option" do
it "respects option" do
response = HTTP.get "#{dummy.endpoint}/iso-8859-1", "encoding" => Encoding::BINARY
expect(response.to_s.encoding).to eq(Encoding::BINARY)
end
end
end

context "passing a string encoding type" do
it "finds encoding" do
response = HTTP.get dummy.endpoint, "encoding" => "ascii"
expect(response.to_s.encoding).to eq(Encoding::ASCII)
end
end

context "loading text" do
it "is utf-8 encoded" do
context "loading text with no charset" do
it "is binary encoded" do
response = HTTP.get dummy.endpoint
expect(response.to_s.encoding).to eq(Encoding::UTF_8)
expect(response.to_s.encoding).to eq(Encoding::BINARY)
end
end

Expand Down
7 changes: 7 additions & 0 deletions spec/support/dummy_server/servlet.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# encoding: UTF-8

class DummyServer < WEBrick::HTTPServer
class Servlet < WEBrick::HTTPServlet::AbstractServlet
def self.sockets
Expand Down Expand Up @@ -129,6 +131,11 @@ def do_#{method.upcase}(req, res)
res.body = bytes.pack("c*")
end

get "/iso-8859-1" do |_req, res|
res["Content-Type"] = "text/plain; charset=ISO-8859-1"
res.body = "testæ".encode(Encoding::ISO8859_1)
end

get "/cookies" do |req, res|
res["Set-Cookie"] = "foo=bar"
res.body = req.cookies.map { |c| [c.name, c.value].join ": " }.join("\n")
Expand Down

0 comments on commit 1836ae8

Please sign in to comment.