Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Net::HTTP::Persistent adapter #92

Merged
merged 1 commit into from
Jul 3, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ gemspec
gem "jruby-openssl", :platforms => :jruby

# http clients
gem "httpclient", "~> 2.3", :require => false
gem "curb", "~> 0.8", :require => false, :platforms => :ruby
gem 'em-http-request', :require => false, :platforms => [:ruby, :jruby]
gem 'em-synchrony', :require => false, :platforms => [:ruby, :jruby]
gem "httpclient", "~> 2.3", :require => false
gem "curb", "~> 0.8", :require => false, :platforms => :ruby
gem "em-http-request", :require => false, :platforms => [:ruby, :jruby]
gem "em-synchrony", :require => false, :platforms => [:ruby, :jruby]
gem "net-http-persistent", "~> 2.8", :require => false
1 change: 1 addition & 0 deletions lib/httpi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require "httpi/adapter/httpclient"
require "httpi/adapter/curb"
require "httpi/adapter/net_http"
require "httpi/adapter/net_http_persistent"
require "httpi/adapter/em_http"
require "httpi/adapter/rack"

Expand Down
2 changes: 1 addition & 1 deletion lib/httpi/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module Adapter
ADAPTERS = {}
ADAPTER_CLASS_MAP = {}

LOAD_ORDER = [:httpclient, :curb, :em_http, :net_http]
LOAD_ORDER = [:httpclient, :curb, :em_http, :net_http, :net_http_persistent]

class << self

Expand Down
18 changes: 12 additions & 6 deletions lib/httpi/adapter/net_http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,16 @@ def request(method)
unless REQUEST_METHODS.include? method
raise NotSupportedError, "Net::HTTP does not support custom HTTP methods"
end

do_request(method) do |http, http_request|
http_request.body = @request.body
if @request.on_body then
http.request(http_request) do |res|
perform(http, http_request) do |res|
res.read_body do |seg|
@request.on_body.call(seg)
end
end
else
http.request http_request
perform(http, http_request)
end
end
rescue OpenSSL::SSL::SSLError
Expand All @@ -51,23 +50,30 @@ def request(method)

private

def perform(http, http_request, &block)
http.request http_request, &block
end

def create_client
proxy_url = @request.proxy || URI("")
proxy = Net::HTTP::Proxy(proxy_url.host, proxy_url.port, proxy_url.user, proxy_url.password)
proxy.new(@request.url.host, @request.url.port)
end

def do_request(type, &requester)
setup_client
setup_ssl_auth if @request.auth.ssl?

setup
response = @client.start do |http|
negotiate_ntlm_auth(http, &requester) if @request.auth.ntlm?
requester.call(http, request_client(type))
end
respond_with(response)
end

def setup
setup_client
setup_ssl_auth if @request.auth.ssl?
end

def negotiate_ntlm_auth(http, &requester)
# first call request is to authenticate (exchange secret and auth)...
ntlm_message_type1 = Net::NTLM::Message::Type1.new
Expand Down
40 changes: 40 additions & 0 deletions lib/httpi/adapter/net_http_persistent.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require 'net/http/persistent'

module HTTPI
module Adapter

# = HTTPI::Adapter::NetHTTPPersistent
#
# Adapter for the Net::HTTP::Persistent client.
# http://docs.seattlerb.org/net-http-persistent/Net/HTTP/Persistent.html
class NetHTTPPersistent < NetHTTP

register :net_http_persistent, :deps => %w(net/http/persistent)

private

def create_client
Net::HTTP::Persistent.new thread_key
end

def perform(http, http_request, &on_body)
http.request @request.url, http_request, &on_body
end

def do_request(type, &requester)
setup
response = requester.call @client, request_client(type)
respond_with(response)
end

def setup_client
@client.open_timeout = @request.open_timeout if @request.open_timeout
@client.read_timeout = @request.read_timeout if @request.read_timeout
end

def thread_key
@request.url.host.split(/\W/).reject{|p|p == ""}.join('-')
end
end
end
end
241 changes: 241 additions & 0 deletions spec/httpi/adapter/net_http_persistent_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
require "spec_helper"
require "integration/support/server"

describe HTTPI::Adapter::NetHTTPPersistent do

subject(:adapter) { :net_http_persistent }

context "http requests" do
before :all do
@server = IntegrationServer.run
end

after :all do
@server.stop
end

it "sends and receives HTTP headers" do
request = HTTPI::Request.new(@server.url + "x-header")
request.headers["X-Header"] = "HTTPI"

response = HTTPI.get(request, adapter)
response.body.should include("HTTPI")
end

it "executes GET requests" do
response = HTTPI.get(@server.url, adapter)
response.body.should eq("get")
response.headers["Content-Type"].should eq("text/plain")
end

it "executes POST requests" do
response = HTTPI.post(@server.url, "<some>xml</some>", adapter)
response.body.should eq("post")
response.headers["Content-Type"].should eq("text/plain")
end

it "executes HEAD requests" do
response = HTTPI.head(@server.url, adapter)
response.code.should == 200
response.headers["Content-Type"].should eq("text/plain")
end

it "executes PUT requests" do
response = HTTPI.put(@server.url, "<some>xml</some>", adapter)
response.body.should eq("put")
response.headers["Content-Type"].should eq("text/plain")
end

it "executes DELETE requests" do
response = HTTPI.delete(@server.url, adapter)
response.body.should eq("delete")
response.headers["Content-Type"].should eq("text/plain")
end

it "supports basic authentication" do
request = HTTPI::Request.new(@server.url + "basic-auth")
request.auth.basic("admin", "secret")

response = HTTPI.get(request, adapter)
response.body.should eq("basic-auth")
end

it "supports ntlm authentication" do
request = HTTPI::Request.new(@server.url + "ntlm-auth")
request.auth.ntlm("tester", "vReqSoafRe5O")

response = HTTPI.get(request, adapter)
response.body.should eq("ntlm-auth")
end
end

# it does not support digest auth

if RUBY_PLATFORM =~ /java/
pending "Puma Server complains: SSL not supported on JRuby"
else
context "https requests" do
before :all do
@server = IntegrationServer.run(:ssl => true)
end
after :all do
@server.stop
end

# it does not raise when no certificate was set up
it "works when set up properly" do
request = HTTPI::Request.new(@server.url)
request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file

response = HTTPI.get(request, adapter)
expect(response.body).to eq("get")
end
end
end

end


__END__

describe "#request(:get)" do
it "should return a valid HTTPI::Response" do
stub_request(:get, request.url.to_s).to_return(basic_response)
adapter.request(:get).should match_response(:body => Fixture.xml)
end
end

describe "#request(:post)" do
it "should return a valid HTTPI::Response" do
request.body = Fixture.xml
stub_request(:post, request.url.to_s).with(:body => request.body).to_return(basic_response)

adapter.request(:post).should match_response(:body => Fixture.xml)
end
end

describe "#request(:head)" do
it "should return a valid HTTPI::Response" do
stub_request(:head, request.url.to_s).to_return(basic_response)
adapter.request(:head).should match_response(:body => Fixture.xml)
end
end

describe "#request(:put)" do
it "should return a valid HTTPI::Response" do
request.url = "http://example.com"
request.headers = { "Accept-encoding" => "utf-8" }
request.body = Fixture.xml

stub_request(:put, request.url.to_s).with(:body => request.body).to_return(basic_response)

adapter.request(:put).should match_response(:body => Fixture.xml)
end
end

describe "#request(:delete)" do
it "should return a valid HTTPI::Response" do
stub_request(:delete, request.url.to_s).to_return(basic_response)
adapter.request(:delete).should match_response(:body => Fixture.xml)
end
end

describe "#request(:custom)" do
it "raises a NotSupportedError" do
expect { adapter.request(:custom) }.
to raise_error(HTTPI::NotSupportedError, "Net::HTTP does not support custom HTTP methods")
end
end

describe "settings:" do
before { stub_request(:get, request.url.to_s) }

describe "use_ssl" do
it "should be set to false for non-SSL requests" do
net_http.expects(:use_ssl=).with(false)
adapter.request(:get)
end

it "should be set to true for SSL requests" do
request.ssl = true

net_http.expects(:use_ssl=).with(true)
adapter.request(:get)
end
end

describe "open_timeout" do
it "should not be set if not specified" do
net_http.expects(:open_timeout=).never
adapter.request(:get)
end

it "should be set if specified" do
request.open_timeout = 30

net_http.expects(:open_timeout=).with(30)
adapter.request(:get)
end
end

describe "read_timeout" do
it "should not be set if not specified" do
net_http.expects(:read_timeout=).never
adapter.request(:get)
end

it "should be set if specified" do
request.read_timeout = 30

net_http.expects(:read_timeout=).with(30)
adapter.request(:get)
end
end

describe "basic_auth" do
it "should be set for HTTP basic auth" do
request.auth.basic "username", "password"

stub_request(:get, "http://username:password@example.com")
Net::HTTP::Get.any_instance.expects(:basic_auth).with(*request.auth.credentials)
adapter.request(:get)
end
end

context "(for SSL client auth)" do
before do
request.auth.ssl.cert_key_file = "spec/fixtures/client_key.pem"
request.auth.ssl.cert_file = "spec/fixtures/client_cert.pem"
end

it "key, cert and verify_mode should be set" do
net_http.expects(:cert=).with(request.auth.ssl.cert)
net_http.expects(:key=).with(request.auth.ssl.cert_key)
net_http.expects(:verify_mode=).with(request.auth.ssl.openssl_verify_mode)

adapter.request(:get)
end

it "should set the client_ca if specified" do
request.auth.ssl.ca_cert_file = "spec/fixtures/client_cert.pem"
net_http.expects(:ca_file=).with(request.auth.ssl.ca_cert_file)

adapter.request(:get)
end

it 'should set the ssl_version if specified' do
request.auth.ssl.ssl_version = :SSLv3
net_http.expects(:ssl_version=).with(request.auth.ssl.ssl_version)

adapter.request(:get)
end
end
end

def basic_request
request = HTTPI::Request.new "http://example.com"
yield request if block_given?
request
end

end
12 changes: 7 additions & 5 deletions spec/httpi/httpi_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
let(:client) { HTTPI }
let(:httpclient) { HTTPI::Adapter.load(:httpclient) }
let(:net_http) { HTTPI::Adapter.load(:net_http) }
let(:net_http_persistent) { HTTPI::Adapter.load(:net_http_persistent) }

before(:all) do
HTTPI::Adapter::Rack.mount('example.com', IntegrationServer::Application)
Expand Down Expand Up @@ -209,11 +210,12 @@
HTTPI::Adapter::ADAPTERS.each do |adapter, opts|
unless (adapter == :em_http && RUBY_VERSION =~ /1\.8/) || (adapter == :curb && RUBY_PLATFORM =~ /java/)
client_class = {
:httpclient => lambda { HTTPClient },
:curb => lambda { Curl::Easy },
:net_http => lambda { Net::HTTP },
:em_http => lambda { EventMachine::HttpConnection },
:rack => lambda { Rack::MockRequest }
:httpclient => lambda { HTTPClient },
:curb => lambda { Curl::Easy },
:net_http => lambda { Net::HTTP },
:net_http_persistent => lambda { Net::HTTP::Persistent },
:em_http => lambda { EventMachine::HttpConnection },
:rack => lambda { Rack::MockRequest }
}

context "using #{adapter}" do
Expand Down
Loading