http 0.8.0.pre4 → 0.8.0.pre5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGES.md +1 -1
- data/Gemfile +1 -0
- data/README.md +1 -0
- data/Rakefile +42 -0
- data/lib/http/chainable.rb +1 -1
- data/lib/http/client.rb +17 -10
- data/lib/http/connection.rb +81 -52
- data/lib/http/options.rb +11 -1
- data/lib/http/redirector.rb +60 -32
- data/lib/http/response/status/reasons.rb +13 -7
- data/lib/http/timeout/global.rb +14 -29
- data/lib/http/timeout/null.rb +0 -2
- data/lib/http/timeout/per_operation.rb +16 -68
- data/lib/http/version.rb +1 -1
- data/spec/lib/http/client_spec.rb +24 -28
- data/spec/lib/http/options/merge_spec.rb +2 -0
- data/spec/lib/http/redirector_spec.rb +338 -57
- data/spec/lib/http/response/status_spec.rb +0 -6
- data/spec/lib/http_spec.rb +2 -2
- data/spec/spec_helper.rb +0 -7
- data/spec/support/dummy_server.rb +13 -24
- data/spec/support/http_handling_shared.rb +19 -45
- data/spec/support/servers/config.rb +0 -4
- data/spec/support/ssl_helper.rb +102 -0
- metadata +4 -4
- data/spec/support/create_certs.rb +0 -57
@@ -68,12 +68,6 @@ RSpec.describe HTTP::Response::Status do
|
|
68
68
|
|
69
69
|
# "Bad Request"
|
70
70
|
its([400]) { is_expected.to be :bad_request }
|
71
|
-
|
72
|
-
# "Request-URI Too Long"
|
73
|
-
its([414]) { is_expected.to be :request_uri_too_long }
|
74
|
-
|
75
|
-
# "I'm a Teapot"
|
76
|
-
its([418]) { is_expected.to be :im_a_teapot }
|
77
71
|
end
|
78
72
|
|
79
73
|
described_class::SYMBOLS.each do |code, symbol|
|
data/spec/lib/http_spec.rb
CHANGED
@@ -106,12 +106,12 @@ RSpec.describe HTTP do
|
|
106
106
|
|
107
107
|
context "with redirects" do
|
108
108
|
it "is easy for 301" do
|
109
|
-
response = HTTP.
|
109
|
+
response = HTTP.follow.get("#{dummy.endpoint}/redirect-301")
|
110
110
|
expect(response.to_s).to match(/<!doctype html>/)
|
111
111
|
end
|
112
112
|
|
113
113
|
it "is easy for 302" do
|
114
|
-
response = HTTP.
|
114
|
+
response = HTTP.follow.get("#{dummy.endpoint}/redirect-302")
|
115
115
|
expect(response.to_s).to match(/<!doctype html>/)
|
116
116
|
end
|
117
117
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -17,13 +17,6 @@ require "http"
|
|
17
17
|
require "rspec/its"
|
18
18
|
require "support/capture_warning"
|
19
19
|
|
20
|
-
# Allow testing against a SSL server
|
21
|
-
def certs_dir
|
22
|
-
Pathname.new File.expand_path("../../tmp/certs", __FILE__)
|
23
|
-
end
|
24
|
-
|
25
|
-
require "support/create_certs"
|
26
|
-
|
27
20
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
28
21
|
RSpec.configure do |config|
|
29
22
|
config.expect_with :rspec do |expectations|
|
@@ -5,6 +5,7 @@ require "support/black_hole"
|
|
5
5
|
require "support/dummy_server/servlet"
|
6
6
|
require "support/servers/config"
|
7
7
|
require "support/servers/runner"
|
8
|
+
require "support/ssl_helper"
|
8
9
|
|
9
10
|
class DummyServer < WEBrick::HTTPServer
|
10
11
|
include ServerConfig
|
@@ -16,37 +17,25 @@ class DummyServer < WEBrick::HTTPServer
|
|
16
17
|
:Logger => BlackHole
|
17
18
|
}.freeze
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
:SSLStartImmediately => true
|
24
|
-
}
|
25
|
-
else
|
26
|
-
override_config = {}
|
27
|
-
end
|
28
|
-
|
29
|
-
super CONFIG.merge(override_config)
|
20
|
+
SSL_CONFIG = CONFIG.merge(
|
21
|
+
:SSLEnable => true,
|
22
|
+
:SSLStartImmediately => true
|
23
|
+
).freeze
|
30
24
|
|
25
|
+
def initialize(options = {})
|
26
|
+
super(options[:ssl] ? SSL_CONFIG : CONFIG)
|
31
27
|
mount("/", Servlet)
|
32
28
|
end
|
33
29
|
|
34
30
|
def endpoint
|
35
|
-
"#{
|
31
|
+
"#{scheme}://#{addr}:#{port}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def scheme
|
35
|
+
config[:SSLEnable] ? "https" : "http"
|
36
36
|
end
|
37
37
|
|
38
38
|
def ssl_context
|
39
|
-
@ssl_context ||=
|
40
|
-
context = OpenSSL::SSL::SSLContext.new
|
41
|
-
context.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
42
|
-
context.key = OpenSSL::PKey::RSA.new(
|
43
|
-
File.read(File.join(certs_dir, "server.key"))
|
44
|
-
)
|
45
|
-
context.cert = OpenSSL::X509::Certificate.new(
|
46
|
-
File.read(File.join(certs_dir, "server.crt"))
|
47
|
-
)
|
48
|
-
context.ca_file = File.join(certs_dir, "ca.crt")
|
49
|
-
context
|
50
|
-
end
|
39
|
+
@ssl_context ||= SSLHelper.server_context
|
51
40
|
end
|
52
41
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
RSpec.shared_context "HTTP handling" do
|
1
|
+
RSpec.shared_context "HTTP handling" do
|
2
2
|
describe "timeouts" do
|
3
3
|
let(:conn_timeout) { 1 }
|
4
4
|
let(:read_timeout) { 1 }
|
@@ -73,56 +73,18 @@ RSpec.shared_context "HTTP handling" do |ssl = false|
|
|
73
73
|
|
74
74
|
let(:response) { client.get(server.endpoint).body.to_s }
|
75
75
|
|
76
|
-
context "with localhost" do
|
77
|
-
let(:endpoint) { server.endpoint.sub("127.0.0.1", "localhost") }
|
78
|
-
|
79
|
-
it "errors if DNS takes too long" do
|
80
|
-
# Block the localhost lookup
|
81
|
-
expect(timeout_class::HostResolver)
|
82
|
-
.to receive(:getaddress).with("localhost").and_return(nil)
|
83
|
-
|
84
|
-
# Request
|
85
|
-
expect(Resolv::DNS).to receive(:open).with(:timeout => 1) do |_|
|
86
|
-
sleep 1.25
|
87
|
-
"127.0.0.1"
|
88
|
-
end
|
89
|
-
|
90
|
-
expect { client.get(server.endpoint.sub("127.0.0.1", "localhost")) }
|
91
|
-
.to raise_error(HTTP::TimeoutError, /Timed out/)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
76
|
it "errors if connecting takes too long" do
|
96
|
-
|
97
|
-
|
98
|
-
fake_socket = double(:to_io => socket)
|
99
|
-
expect(fake_socket).to receive(:connect_nonblock) do |*args|
|
77
|
+
expect(TCPSocket).to receive(:open) do
|
100
78
|
sleep 1.25
|
101
|
-
socket.connect_nonblock(*args)
|
102
79
|
end
|
103
80
|
|
104
|
-
|
105
|
-
|
106
|
-
expect { response }.to raise_error(HTTP::TimeoutError, /Timed out/)
|
81
|
+
expect { response }.to raise_error(HTTP::TimeoutError, /execution/)
|
107
82
|
end
|
108
83
|
|
109
84
|
it "errors if reading takes too long" do
|
110
85
|
expect { client.get("#{server.endpoint}/sleep").body.to_s }
|
111
86
|
.to raise_error(HTTP::TimeoutError, /Timed out/)
|
112
87
|
end
|
113
|
-
|
114
|
-
unless ssl
|
115
|
-
it "errors if writing takes too long" do
|
116
|
-
socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
117
|
-
allow_any_instance_of(timeout_class).to receive(:socket).and_return(socket)
|
118
|
-
|
119
|
-
expect(socket).to receive(:<<) do |*|
|
120
|
-
sleep 1.25
|
121
|
-
end
|
122
|
-
|
123
|
-
expect { response }.to raise_error(HTTP::TimeoutError, /Timed out/)
|
124
|
-
end
|
125
|
-
end
|
126
88
|
end
|
127
89
|
end
|
128
90
|
|
@@ -148,6 +110,18 @@ RSpec.shared_context "HTTP handling" do |ssl = false|
|
|
148
110
|
expect(sockets_used.uniq.length).to eq(1)
|
149
111
|
end
|
150
112
|
|
113
|
+
context "on a mixed state" do
|
114
|
+
it "re-opens the connection" do
|
115
|
+
first_socket_id = client.get("#{server.endpoint}/socket/1").body.to_s
|
116
|
+
|
117
|
+
client.instance_variable_set(:@state, :dirty)
|
118
|
+
|
119
|
+
second_socket_id = client.get("#{server.endpoint}/socket/2").body.to_s
|
120
|
+
|
121
|
+
expect(first_socket_id).to_not eq(second_socket_id)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
151
125
|
context "when trying to read a stale body" do
|
152
126
|
it "errors" do
|
153
127
|
client.get("#{server.endpoint}/not-found")
|
@@ -169,8 +143,8 @@ RSpec.shared_context "HTTP handling" do |ssl = false|
|
|
169
143
|
|
170
144
|
context "with a socket issue" do
|
171
145
|
it "transparently reopens" do
|
172
|
-
|
173
|
-
expect(
|
146
|
+
first_socket_id = client.get("#{server.endpoint}/socket").body.to_s
|
147
|
+
expect(first_socket_id).to_not eq("")
|
174
148
|
# Kill off the sockets we used
|
175
149
|
# rubocop:disable Style/RescueModifier
|
176
150
|
DummyServer::Servlet.sockets.each do |socket|
|
@@ -183,8 +157,8 @@ RSpec.shared_context "HTTP handling" do |ssl = false|
|
|
183
157
|
expect { client.get("#{server.endpoint}/socket").body.to_s }.to raise_error(IOError)
|
184
158
|
|
185
159
|
# Should succeed since we create a new socket
|
186
|
-
|
187
|
-
expect(
|
160
|
+
second_socket_id = client.get("#{server.endpoint}/socket").body.to_s
|
161
|
+
expect(second_socket_id).to_not eq(first_socket_id)
|
188
162
|
end
|
189
163
|
end
|
190
164
|
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
require "certificate_authority"
|
4
|
+
|
5
|
+
module SSLHelper
|
6
|
+
CERTS_PATH = Pathname.new File.expand_path("../../../tmp/certs", __FILE__)
|
7
|
+
|
8
|
+
class RootCertificate < ::CertificateAuthority::Certificate
|
9
|
+
EXTENSIONS = {"keyUsage" => {"usage" => %w(critical keyCertSign)}}
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
super()
|
13
|
+
|
14
|
+
subject.common_name = "honestachmed.com"
|
15
|
+
serial_number.number = 1
|
16
|
+
key_material.generate_key
|
17
|
+
|
18
|
+
self.signing_entity = true
|
19
|
+
|
20
|
+
sign!("extensions" => EXTENSIONS)
|
21
|
+
end
|
22
|
+
|
23
|
+
def file
|
24
|
+
return @file if defined? @file
|
25
|
+
|
26
|
+
CERTS_PATH.mkpath
|
27
|
+
|
28
|
+
cert_file = CERTS_PATH.join("ca.crt")
|
29
|
+
cert_file.open("w") { |io| io << to_pem }
|
30
|
+
|
31
|
+
@file = cert_file.to_s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class ChildCertificate < ::CertificateAuthority::Certificate
|
36
|
+
def initialize(parent)
|
37
|
+
super()
|
38
|
+
|
39
|
+
subject.common_name = "127.0.0.1"
|
40
|
+
serial_number.number = 1
|
41
|
+
|
42
|
+
key_material.generate_key
|
43
|
+
|
44
|
+
self.parent = parent
|
45
|
+
|
46
|
+
sign!
|
47
|
+
end
|
48
|
+
|
49
|
+
def cert
|
50
|
+
OpenSSL::X509::Certificate.new to_pem
|
51
|
+
end
|
52
|
+
|
53
|
+
def key
|
54
|
+
OpenSSL::PKey::RSA.new key_material.private_key.to_pem
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class << self
|
59
|
+
def server_context
|
60
|
+
context = OpenSSL::SSL::SSLContext.new
|
61
|
+
|
62
|
+
context.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
63
|
+
context.key = server_cert.key
|
64
|
+
context.cert = server_cert.cert
|
65
|
+
context.ca_file = ca.file
|
66
|
+
|
67
|
+
context
|
68
|
+
end
|
69
|
+
|
70
|
+
def client_context
|
71
|
+
context = OpenSSL::SSL::SSLContext.new
|
72
|
+
|
73
|
+
context.options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
|
74
|
+
context.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
75
|
+
context.key = client_cert.key
|
76
|
+
context.cert = client_cert.cert
|
77
|
+
context.ca_file = ca.file
|
78
|
+
|
79
|
+
context
|
80
|
+
end
|
81
|
+
|
82
|
+
def client_params
|
83
|
+
{
|
84
|
+
:key => client_cert.key,
|
85
|
+
:cert => client_cert.cert,
|
86
|
+
:ca_file => ca.file
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
%w(server client).each do |side|
|
91
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
92
|
+
def #{side}_cert
|
93
|
+
@#{side}_cert ||= ChildCertificate.new ca
|
94
|
+
end
|
95
|
+
RUBY
|
96
|
+
end
|
97
|
+
|
98
|
+
def ca
|
99
|
+
@ca ||= RootCertificate.new
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: http
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.0.
|
4
|
+
version: 0.8.0.pre5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Arcieri
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2015-03-
|
13
|
+
date: 2015-03-31 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: http_parser.rb
|
@@ -138,13 +138,13 @@ files:
|
|
138
138
|
- spec/support/black_hole.rb
|
139
139
|
- spec/support/capture_warning.rb
|
140
140
|
- spec/support/connection_reuse_shared.rb
|
141
|
-
- spec/support/create_certs.rb
|
142
141
|
- spec/support/dummy_server.rb
|
143
142
|
- spec/support/dummy_server/servlet.rb
|
144
143
|
- spec/support/http_handling_shared.rb
|
145
144
|
- spec/support/proxy_server.rb
|
146
145
|
- spec/support/servers/config.rb
|
147
146
|
- spec/support/servers/runner.rb
|
147
|
+
- spec/support/ssl_helper.rb
|
148
148
|
homepage: https://github.com/httprb/http.rb
|
149
149
|
licenses:
|
150
150
|
- MIT
|
@@ -199,11 +199,11 @@ test_files:
|
|
199
199
|
- spec/support/black_hole.rb
|
200
200
|
- spec/support/capture_warning.rb
|
201
201
|
- spec/support/connection_reuse_shared.rb
|
202
|
-
- spec/support/create_certs.rb
|
203
202
|
- spec/support/dummy_server.rb
|
204
203
|
- spec/support/dummy_server/servlet.rb
|
205
204
|
- spec/support/http_handling_shared.rb
|
206
205
|
- spec/support/proxy_server.rb
|
207
206
|
- spec/support/servers/config.rb
|
208
207
|
- spec/support/servers/runner.rb
|
208
|
+
- spec/support/ssl_helper.rb
|
209
209
|
has_rdoc:
|
@@ -1,57 +0,0 @@
|
|
1
|
-
require "fileutils"
|
2
|
-
require "certificate_authority"
|
3
|
-
|
4
|
-
FileUtils.mkdir_p(certs_dir)
|
5
|
-
|
6
|
-
#
|
7
|
-
# Certificate Authority
|
8
|
-
#
|
9
|
-
|
10
|
-
ca = CertificateAuthority::Certificate.new
|
11
|
-
|
12
|
-
ca.subject.common_name = "honestachmed.com"
|
13
|
-
ca.serial_number.number = 1
|
14
|
-
ca.key_material.generate_key
|
15
|
-
ca.signing_entity = true
|
16
|
-
|
17
|
-
ca.sign! "extensions" => {"keyUsage" => {"usage" => %w(critical keyCertSign)}}
|
18
|
-
|
19
|
-
ca_cert_path = File.join(certs_dir, "ca.crt")
|
20
|
-
ca_key_path = File.join(certs_dir, "ca.key")
|
21
|
-
|
22
|
-
File.write ca_cert_path, ca.to_pem
|
23
|
-
File.write ca_key_path, ca.key_material.private_key.to_pem
|
24
|
-
|
25
|
-
#
|
26
|
-
# Server Certificate
|
27
|
-
#
|
28
|
-
|
29
|
-
server_cert = CertificateAuthority::Certificate.new
|
30
|
-
server_cert.subject.common_name = "127.0.0.1"
|
31
|
-
server_cert.serial_number.number = 1
|
32
|
-
server_cert.key_material.generate_key
|
33
|
-
server_cert.parent = ca
|
34
|
-
server_cert.sign!
|
35
|
-
|
36
|
-
server_cert_path = File.join(certs_dir, "server.crt")
|
37
|
-
server_key_path = File.join(certs_dir, "server.key")
|
38
|
-
|
39
|
-
File.write server_cert_path, server_cert.to_pem
|
40
|
-
File.write server_key_path, server_cert.key_material.private_key.to_pem
|
41
|
-
|
42
|
-
#
|
43
|
-
# Client Certificate
|
44
|
-
#
|
45
|
-
|
46
|
-
client_cert = CertificateAuthority::Certificate.new
|
47
|
-
client_cert.subject.common_name = "127.0.0.1"
|
48
|
-
client_cert.serial_number.number = 1
|
49
|
-
client_cert.key_material.generate_key
|
50
|
-
client_cert.parent = ca
|
51
|
-
client_cert.sign!
|
52
|
-
|
53
|
-
client_cert_path = File.join(certs_dir, "client.crt")
|
54
|
-
client_key_path = File.join(certs_dir, "client.key")
|
55
|
-
|
56
|
-
File.write client_cert_path, client_cert.to_pem
|
57
|
-
File.write client_key_path, client_cert.key_material.private_key.to_pem
|