httpi 2.0.0.rc1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +9 -1
- data/Gemfile +0 -1
- data/httpi.gemspec +5 -5
- data/lib/httpi.rb +13 -11
- data/lib/httpi/adapter/curb.rb +3 -0
- data/lib/httpi/adapter/httpclient.rb +3 -0
- data/lib/httpi/adapter/net_http.rb +3 -0
- data/lib/httpi/request.rb +15 -0
- data/lib/httpi/version.rb +1 -1
- data/spec/httpi/adapter/net_http_spec.rb +84 -10
- data/spec/httpi/error_spec.rb +43 -0
- data/spec/httpi/httpi_spec.rb +15 -3
- data/spec/httpi/request_spec.rb +29 -0
- data/spec/integration/curb_spec.rb +99 -0
- data/spec/integration/em_http_spec.rb +81 -0
- data/spec/integration/httpclient_spec.rb +94 -0
- data/spec/integration/net_http_spec.rb +86 -0
- data/spec/integration/support/application.rb +56 -0
- data/spec/integration/support/server.rb +84 -0
- data/spec/spec_helper.rb +7 -3
- data/spec/support/error_helper.rb +26 -0
- metadata +98 -111
- data/spec/integration/fixtures/ca.pem +0 -23
- data/spec/integration/fixtures/htdigest +0 -1
- data/spec/integration/fixtures/htpasswd +0 -2
- data/spec/integration/fixtures/subca.pem +0 -21
- data/spec/integration/request_spec.rb +0 -112
- data/spec/integration/server.rb +0 -39
- data/spec/integration/ssl_server.rb +0 -70
- data/spec/integration/ssl_spec.rb +0 -102
@@ -0,0 +1,81 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "integration/support/server"
|
3
|
+
|
4
|
+
describe HTTPI::Adapter::EmHttpRequest do
|
5
|
+
|
6
|
+
# em_http is not supported on ruby 1.8
|
7
|
+
unless RUBY_VERSION =~ /1\.8/
|
8
|
+
require "em-synchrony"
|
9
|
+
|
10
|
+
subject(:adapter) { :em_http }
|
11
|
+
|
12
|
+
around :each do |example|
|
13
|
+
EM.synchrony do
|
14
|
+
example.run
|
15
|
+
EM.stop
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "http requests" do
|
20
|
+
before :all do
|
21
|
+
# for some reason, these specs don't work with "localhost". [dh, 2012-12-15]
|
22
|
+
@server = IntegrationServer.run(:host => "127.0.0.1")
|
23
|
+
end
|
24
|
+
|
25
|
+
after :all do
|
26
|
+
@server.stop
|
27
|
+
end
|
28
|
+
|
29
|
+
it "sends and receives HTTP headers" do
|
30
|
+
request = HTTPI::Request.new(@server.url + "x-header")
|
31
|
+
request.headers["X-Header"] = "HTTPI"
|
32
|
+
|
33
|
+
response = HTTPI.get(request, adapter)
|
34
|
+
response.body.should include("HTTPI")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "executes GET requests" do
|
38
|
+
response = HTTPI.get(@server.url, adapter)
|
39
|
+
response.body.should eq("get")
|
40
|
+
response.headers["Content-Type"].should eq("text/plain")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "executes POST requests" do
|
44
|
+
response = HTTPI.post(@server.url, "<some>xml</some>", adapter)
|
45
|
+
response.body.should eq("post")
|
46
|
+
response.headers["Content-Type"].should eq("text/plain")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "executes HEAD requests" do
|
50
|
+
response = HTTPI.head(@server.url, adapter)
|
51
|
+
response.code.should == 200
|
52
|
+
response.headers["Content-Type"].should eq("text/plain")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "executes PUT requests" do
|
56
|
+
response = HTTPI.put(@server.url, "<some>xml</some>", adapter)
|
57
|
+
response.body.should eq("put")
|
58
|
+
response.headers["Content-Type"].should eq("text/plain")
|
59
|
+
end
|
60
|
+
|
61
|
+
it "executes DELETE requests" do
|
62
|
+
response = HTTPI.delete(@server.url, adapter)
|
63
|
+
response.body.should eq("delete")
|
64
|
+
response.headers["Content-Type"].should eq("text/plain")
|
65
|
+
end
|
66
|
+
|
67
|
+
it "supports basic authentication" do
|
68
|
+
request = HTTPI::Request.new(@server.url + "basic-auth")
|
69
|
+
request.auth.basic("admin", "secret")
|
70
|
+
|
71
|
+
response = HTTPI.get(request, adapter)
|
72
|
+
response.body.should eq("basic-auth")
|
73
|
+
end
|
74
|
+
|
75
|
+
# it does not support digest authentication
|
76
|
+
end
|
77
|
+
|
78
|
+
# it does not support ssl authentication
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "integration/support/server"
|
3
|
+
|
4
|
+
describe HTTPI::Adapter::HTTPClient do
|
5
|
+
|
6
|
+
subject(:adapter) { :httpclient }
|
7
|
+
|
8
|
+
context "http requests" do
|
9
|
+
before :all do
|
10
|
+
@server = IntegrationServer.run
|
11
|
+
end
|
12
|
+
|
13
|
+
after :all do
|
14
|
+
@server.stop
|
15
|
+
end
|
16
|
+
|
17
|
+
it "sends and receives HTTP headers" do
|
18
|
+
request = HTTPI::Request.new(@server.url + "x-header")
|
19
|
+
request.headers["X-Header"] = "HTTPI"
|
20
|
+
|
21
|
+
response = HTTPI.get(request, adapter)
|
22
|
+
response.body.should include("HTTPI")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "executes GET requests" do
|
26
|
+
response = HTTPI.get(@server.url, adapter)
|
27
|
+
response.body.should eq("get")
|
28
|
+
response.headers["Content-Type"].should eq("text/plain")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "executes POST requests" do
|
32
|
+
response = HTTPI.post(@server.url, "<some>xml</some>", adapter)
|
33
|
+
response.body.should eq("post")
|
34
|
+
response.headers["Content-Type"].should eq("text/plain")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "executes HEAD requests" do
|
38
|
+
response = HTTPI.head(@server.url, adapter)
|
39
|
+
response.code.should == 200
|
40
|
+
response.headers["Content-Type"].should eq("text/plain")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "executes PUT requests" do
|
44
|
+
response = HTTPI.put(@server.url, "<some>xml</some>", adapter)
|
45
|
+
response.body.should eq("put")
|
46
|
+
response.headers["Content-Type"].should eq("text/plain")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "executes DELETE requests" do
|
50
|
+
response = HTTPI.delete(@server.url, adapter)
|
51
|
+
response.body.should eq("delete")
|
52
|
+
response.headers["Content-Type"].should eq("text/plain")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "supports basic authentication" do
|
56
|
+
request = HTTPI::Request.new(@server.url + "basic-auth")
|
57
|
+
request.auth.basic("admin", "secret")
|
58
|
+
|
59
|
+
response = HTTPI.get(request, adapter)
|
60
|
+
response.body.should eq("basic-auth")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "supports digest authentication" do
|
64
|
+
request = HTTPI::Request.new(@server.url + "digest-auth")
|
65
|
+
request.auth.digest("admin", "secret")
|
66
|
+
|
67
|
+
response = HTTPI.get(request, adapter)
|
68
|
+
response.body.should eq("digest-auth")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "https requests" do
|
73
|
+
before :all do
|
74
|
+
@server = IntegrationServer.run(:ssl => true)
|
75
|
+
end
|
76
|
+
|
77
|
+
after :all do
|
78
|
+
@server.stop
|
79
|
+
end
|
80
|
+
|
81
|
+
it "raises when no certificate was set up" do
|
82
|
+
expect { HTTPI.post(@server.url, "", adapter) }.to raise_error(HTTPI::SSLError)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "works when set up properly" do
|
86
|
+
request = HTTPI::Request.new(@server.url)
|
87
|
+
request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
|
88
|
+
|
89
|
+
response = HTTPI.get(request, adapter)
|
90
|
+
expect(response.body).to eq("get")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "integration/support/server"
|
3
|
+
|
4
|
+
describe HTTPI::Adapter::NetHTTP do
|
5
|
+
|
6
|
+
subject(:adapter) { :net_http }
|
7
|
+
|
8
|
+
context "http requests" do
|
9
|
+
before :all do
|
10
|
+
@server = IntegrationServer.run
|
11
|
+
end
|
12
|
+
|
13
|
+
after :all do
|
14
|
+
@server.stop
|
15
|
+
end
|
16
|
+
|
17
|
+
it "sends and receives HTTP headers" do
|
18
|
+
request = HTTPI::Request.new(@server.url + "x-header")
|
19
|
+
request.headers["X-Header"] = "HTTPI"
|
20
|
+
|
21
|
+
response = HTTPI.get(request, adapter)
|
22
|
+
response.body.should include("HTTPI")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "executes GET requests" do
|
26
|
+
response = HTTPI.get(@server.url, adapter)
|
27
|
+
response.body.should eq("get")
|
28
|
+
response.headers["Content-Type"].should eq("text/plain")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "executes POST requests" do
|
32
|
+
response = HTTPI.post(@server.url, "<some>xml</some>", adapter)
|
33
|
+
response.body.should eq("post")
|
34
|
+
response.headers["Content-Type"].should eq("text/plain")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "executes HEAD requests" do
|
38
|
+
response = HTTPI.head(@server.url, adapter)
|
39
|
+
response.code.should == 200
|
40
|
+
response.headers["Content-Type"].should eq("text/plain")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "executes PUT requests" do
|
44
|
+
response = HTTPI.put(@server.url, "<some>xml</some>", adapter)
|
45
|
+
response.body.should eq("put")
|
46
|
+
response.headers["Content-Type"].should eq("text/plain")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "executes DELETE requests" do
|
50
|
+
response = HTTPI.delete(@server.url, adapter)
|
51
|
+
response.body.should eq("delete")
|
52
|
+
response.headers["Content-Type"].should eq("text/plain")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "supports basic authentication" do
|
56
|
+
request = HTTPI::Request.new(@server.url + "basic-auth")
|
57
|
+
request.auth.basic("admin", "secret")
|
58
|
+
|
59
|
+
response = HTTPI.get(request, adapter)
|
60
|
+
response.body.should eq("basic-auth")
|
61
|
+
end
|
62
|
+
|
63
|
+
# it does not support digest authentication
|
64
|
+
end
|
65
|
+
|
66
|
+
context "https requests" do
|
67
|
+
before :all do
|
68
|
+
@server = IntegrationServer.run(:ssl => true)
|
69
|
+
end
|
70
|
+
|
71
|
+
after :all do
|
72
|
+
@server.stop
|
73
|
+
end
|
74
|
+
|
75
|
+
# does not raise when no certificate was set up
|
76
|
+
|
77
|
+
it "works when set up properly" do
|
78
|
+
request = HTTPI::Request.new(@server.url)
|
79
|
+
request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
|
80
|
+
|
81
|
+
response = HTTPI.get(request, adapter)
|
82
|
+
expect(response.body).to eq("get")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "rack/builder"
|
2
|
+
|
3
|
+
class IntegrationServer
|
4
|
+
|
5
|
+
def self.respond_with(body)
|
6
|
+
[200, { "Content-Type" => "text/plain", "Content-Length" => body.size.to_s }, [body]]
|
7
|
+
end
|
8
|
+
|
9
|
+
Application = Rack::Builder.new do
|
10
|
+
|
11
|
+
map "/" do
|
12
|
+
run lambda { |env|
|
13
|
+
IntegrationServer.respond_with env["REQUEST_METHOD"].downcase
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
map "/repeat" do
|
18
|
+
run lambda { |env|
|
19
|
+
IntegrationServer.respond_with :body => env["rack.input"].read
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
map "/x-header" do
|
24
|
+
run lambda { |env|
|
25
|
+
IntegrationServer.respond_with env["HTTP_X_HEADER"]
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
map "/basic-auth" do
|
30
|
+
use Rack::Auth::Basic, "basic-realm" do |username, password|
|
31
|
+
username == "admin" && password == "secret"
|
32
|
+
end
|
33
|
+
|
34
|
+
run lambda { |env|
|
35
|
+
IntegrationServer.respond_with "basic-auth"
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
map "/digest-auth" do
|
40
|
+
unprotected_app = lambda { |env|
|
41
|
+
IntegrationServer.respond_with "digest-auth"
|
42
|
+
}
|
43
|
+
|
44
|
+
realm = 'digest-realm'
|
45
|
+
app = Rack::Auth::Digest::MD5.new(unprotected_app) do |username|
|
46
|
+
username == 'admin' ? Digest::MD5.hexdigest("admin:#{realm}:secret") : nil
|
47
|
+
end
|
48
|
+
app.realm = realm
|
49
|
+
app.opaque = 'this-should-be-secret'
|
50
|
+
app.passwords_hashed = true
|
51
|
+
|
52
|
+
run app
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "puma"
|
2
|
+
require "puma/minissl"
|
3
|
+
|
4
|
+
require "integration/support/application"
|
5
|
+
|
6
|
+
class IntegrationServer
|
7
|
+
|
8
|
+
def self.run(options = {})
|
9
|
+
server = new(options)
|
10
|
+
server.run
|
11
|
+
server
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.ssl_ca_file; integration_fixture("ca_all.pem") end
|
15
|
+
def self.ssl_key_file; integration_fixture("server.key") end
|
16
|
+
def self.ssl_cert_file; integration_fixture("server.cert") end
|
17
|
+
|
18
|
+
def self.integration_fixture(file)
|
19
|
+
file = File.expand_path("../../fixtures/#{file}", __FILE__)
|
20
|
+
raise "No such file '#{file}'" unless File.exist? file
|
21
|
+
file
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(options = {})
|
25
|
+
@app = Application
|
26
|
+
@host = options.fetch(:host, "localhost")
|
27
|
+
@port = options.fetch(:port, 17172)
|
28
|
+
@ssl = options.fetch(:ssl, false)
|
29
|
+
|
30
|
+
@server = Puma::Server.new(app, events)
|
31
|
+
|
32
|
+
if ssl?
|
33
|
+
add_ssl_listener
|
34
|
+
else
|
35
|
+
add_tcp_listener
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :app, :host, :port, :server
|
40
|
+
|
41
|
+
def url(path = nil)
|
42
|
+
protocol = ssl? ? "https" : "http"
|
43
|
+
File.join "#{protocol}://#{host}:#{port}/", path.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def ssl?
|
47
|
+
@ssl
|
48
|
+
end
|
49
|
+
|
50
|
+
def run
|
51
|
+
server.run
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop
|
55
|
+
server.stop(true)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def events
|
61
|
+
Puma::Events.new($stdout, $stderr)
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_tcp_listener
|
65
|
+
server.add_tcp_listener(host, port)
|
66
|
+
rescue Errno::EADDRINUSE
|
67
|
+
raise "Panther is already running at #{url}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_ssl_listener
|
71
|
+
server.add_ssl_listener(host, port, ssl_context)
|
72
|
+
end
|
73
|
+
|
74
|
+
def ssl_context
|
75
|
+
context = Puma::MiniSSL::Context.new
|
76
|
+
|
77
|
+
context.key = IntegrationServer.ssl_key_file
|
78
|
+
context.cert = IntegrationServer.ssl_cert_file
|
79
|
+
context.verify_mode = Puma::MiniSSL::VERIFY_PEER
|
80
|
+
|
81
|
+
context
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
require "bundler"
|
2
|
-
Bundler.
|
2
|
+
Bundler.setup(:default, :development)
|
3
|
+
|
4
|
+
require "httpi"
|
5
|
+
|
6
|
+
require "rspec"
|
7
|
+
require "mocha/api"
|
3
8
|
|
4
9
|
RSpec.configure do |config|
|
5
|
-
config.include WebMock::API
|
6
10
|
config.mock_with :mocha
|
7
11
|
end
|
8
12
|
|
9
|
-
HTTPI.log = false
|
13
|
+
HTTPI.log = false
|
10
14
|
|
11
15
|
require "support/fixture"
|
12
16
|
require "support/matchers"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ErrorHelper
|
2
|
+
|
3
|
+
class Expectation
|
4
|
+
|
5
|
+
def initialize(error, spec)
|
6
|
+
@error = error
|
7
|
+
@spec = spec
|
8
|
+
end
|
9
|
+
|
10
|
+
def to(tag_error)
|
11
|
+
@spec.expect(@error).to @spec.be_a(tag_error)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def expect_error(error_to_raise, message)
|
17
|
+
fake_error(error_to_raise, message)
|
18
|
+
rescue => error
|
19
|
+
Expectation.new(error, self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def be_tagged_with(tag_error)
|
23
|
+
tag_error
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|