http 0.8.0.pre4 → 0.8.0.pre5
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.
- 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
|