httpi 2.0.0.rc1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|