http 0.7.2 → 0.7.3
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 +1 -1
- data/CHANGES.md +7 -0
- data/Gemfile +1 -0
- data/lib/http/client.rb +7 -2
- data/lib/http/version.rb +1 -1
- data/spec/lib/http/client_spec.rb +36 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/black_hole.rb +5 -0
- data/spec/support/create_certs.rb +57 -0
- data/spec/support/dummy_server.rb +52 -0
- data/spec/support/dummy_server/servlet.rb +100 -0
- data/spec/support/servers/config.rb +13 -0
- data/spec/support/servers/runner.rb +17 -0
- metadata +40 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb95fe4445c50c6c1af546a7bb70ece6eccb1f78
|
4
|
+
data.tar.gz: e5b2eb9695d742302e54f46f222d81c8921f13c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 267033f1a355e9023d4f62c1978a275f26f67313c0702036ceed2332d585e33c42e91f2bf2318c4631b157e8c1f9f2b1cd486d44c1d084e39c8792f06f735893
|
7
|
+
data.tar.gz: d20251b75cc74fec4643f31c27401f833b6eec2ba1e65d47c7edc9326525ccda4c27c8cad34e2d53f88707361446df6bd1eca6f37d6a9f0c6b2ae4ba0b1e6c80
|
data/.rubocop.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 0.7.3 (2015-03-24)
|
2
|
+
|
3
|
+
* SECURITY FIX: http.rb failed to call the #post_connection_check method
|
4
|
+
on SSL connections. This method implements hostname verification, and
|
5
|
+
without it http.rb was vulnerable to MitM attacks. The problem was
|
6
|
+
corrected by calling #post_connection_check (CVE-2015-1828)
|
7
|
+
|
1
8
|
## 0.7.2 (2015-03-02)
|
2
9
|
|
3
10
|
* Swap from `form_data` to `http-form_data` (changed gem name).
|
data/Gemfile
CHANGED
data/lib/http/client.rb
CHANGED
@@ -52,7 +52,7 @@ module HTTP
|
|
52
52
|
|
53
53
|
# TODO: keep-alive support
|
54
54
|
@socket = options[:socket_class].open(req.socket_host, req.socket_port)
|
55
|
-
@socket = start_tls(@socket, options) if uri.is_a?(URI::HTTPS) && !req.using_proxy?
|
55
|
+
@socket = start_tls(@socket, uri.host, options) if uri.is_a?(URI::HTTPS) && !req.using_proxy?
|
56
56
|
|
57
57
|
req.stream @socket
|
58
58
|
|
@@ -90,12 +90,17 @@ module HTTP
|
|
90
90
|
private
|
91
91
|
|
92
92
|
# Initialize TLS connection
|
93
|
-
def start_tls(socket, options)
|
93
|
+
def start_tls(socket, host, options)
|
94
94
|
# TODO: abstract away SSLContexts so we can use other TLS libraries
|
95
95
|
context = options[:ssl_context] || OpenSSL::SSL::SSLContext.new
|
96
96
|
socket = options[:ssl_socket_class].new(socket, context)
|
97
97
|
|
98
98
|
socket.connect
|
99
|
+
|
100
|
+
if context.verify_mode == OpenSSL::SSL::VERIFY_PEER
|
101
|
+
socket.post_connection_check(host)
|
102
|
+
end
|
103
|
+
|
99
104
|
socket
|
100
105
|
end
|
101
106
|
|
data/lib/http/version.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
require 'support/dummy_server'
|
2
|
+
|
1
3
|
RSpec.describe HTTP::Client do
|
2
4
|
let(:test_endpoint) { "http://#{ExampleServer::ADDR}" }
|
5
|
+
run_server(:dummy) { DummyServer.new }
|
6
|
+
run_server(:dummy_ssl) { DummyServer.new(:ssl => true) }
|
3
7
|
|
4
8
|
StubbedClient = Class.new(HTTP::Client) do
|
5
9
|
def perform(request, options)
|
@@ -142,6 +146,38 @@ RSpec.describe HTTP::Client do
|
|
142
146
|
end
|
143
147
|
end
|
144
148
|
|
149
|
+
describe 'SSL' do
|
150
|
+
let(:client) do
|
151
|
+
described_class.new(
|
152
|
+
:ssl_context => OpenSSL::SSL::SSLContext.new.tap do |context|
|
153
|
+
context.options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
|
154
|
+
|
155
|
+
context.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
156
|
+
context.ca_file = File.join(certs_dir, 'ca.crt')
|
157
|
+
context.cert = OpenSSL::X509::Certificate.new(
|
158
|
+
File.read(File.join(certs_dir, 'client.crt'))
|
159
|
+
)
|
160
|
+
context.key = OpenSSL::PKey::RSA.new(
|
161
|
+
File.read(File.join(certs_dir, 'client.key'))
|
162
|
+
)
|
163
|
+
context
|
164
|
+
end
|
165
|
+
)
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'works via SSL' do
|
169
|
+
response = client.get(dummy_ssl.endpoint)
|
170
|
+
expect(response.body.to_s).to eq('<!doctype html>')
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'with a mismatch host' do
|
174
|
+
it 'errors' do
|
175
|
+
expect { client.get(dummy_ssl.endpoint.gsub('127.0.0.1', 'localhost')) }
|
176
|
+
.to raise_error(OpenSSL::SSL::SSLError, /does not match/)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
145
181
|
describe '#perform' do
|
146
182
|
let(:client) { described_class.new }
|
147
183
|
|
data/spec/spec_helper.rb
CHANGED
@@ -19,6 +19,13 @@ require 'support/example_server'
|
|
19
19
|
require 'support/proxy_server'
|
20
20
|
require 'support/capture_warning'
|
21
21
|
|
22
|
+
# Allow testing against a SSL server
|
23
|
+
def certs_dir
|
24
|
+
Pathname.new File.expand_path('../../tmp/certs', __FILE__)
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'support/create_certs'
|
28
|
+
|
22
29
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
23
30
|
RSpec.configure do |config|
|
24
31
|
config.expect_with :rspec do |expectations|
|
@@ -0,0 +1,57 @@
|
|
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
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'webrick'
|
2
|
+
require 'webrick/ssl'
|
3
|
+
|
4
|
+
require 'support/black_hole'
|
5
|
+
require 'support/dummy_server/servlet'
|
6
|
+
require 'support/servers/config'
|
7
|
+
require 'support/servers/runner'
|
8
|
+
|
9
|
+
class DummyServer < WEBrick::HTTPServer
|
10
|
+
include ServerConfig
|
11
|
+
|
12
|
+
CONFIG = {
|
13
|
+
:BindAddress => '127.0.0.1',
|
14
|
+
:Port => 0,
|
15
|
+
:AccessLog => BlackHole,
|
16
|
+
:Logger => BlackHole
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
def initialize(options = {})
|
20
|
+
if options[:ssl]
|
21
|
+
override_config = {
|
22
|
+
:SSLEnable => true,
|
23
|
+
:SSLStartImmediately => true
|
24
|
+
}
|
25
|
+
else
|
26
|
+
override_config = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
super CONFIG.merge(override_config)
|
30
|
+
|
31
|
+
mount('/', Servlet)
|
32
|
+
end
|
33
|
+
|
34
|
+
def endpoint
|
35
|
+
"#{ssl? ? 'https' : 'http'}://#{addr}:#{port}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def ssl_context
|
39
|
+
@ssl_context ||= begin
|
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
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
class DummyServer < WEBrick::HTTPServer
|
2
|
+
class Servlet < WEBrick::HTTPServlet::AbstractServlet
|
3
|
+
def not_found(_req, res)
|
4
|
+
res.status = 404
|
5
|
+
res.body = 'Not Found'
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.handlers
|
9
|
+
@handlers ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
%w(get post head).each do |method|
|
13
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
14
|
+
def self.#{method}(path, &block)
|
15
|
+
handlers["#{method}:\#{path}"] = block
|
16
|
+
end
|
17
|
+
|
18
|
+
def do_#{method.upcase}(req, res)
|
19
|
+
handler = self.class.handlers["#{method}:\#{req.path}"]
|
20
|
+
return instance_exec(req, res, &handler) if handler
|
21
|
+
not_found
|
22
|
+
end
|
23
|
+
RUBY
|
24
|
+
end
|
25
|
+
|
26
|
+
get '/' do |req, res|
|
27
|
+
res.status = 200
|
28
|
+
|
29
|
+
case req['Accept']
|
30
|
+
when 'application/json'
|
31
|
+
res['Content-Type'] = 'application/json'
|
32
|
+
res.body = '{"json": true}'
|
33
|
+
else
|
34
|
+
res['Content-Type'] = 'text/html'
|
35
|
+
res.body = '<!doctype html>'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
get '/params' do |req, res|
|
40
|
+
next not_found unless 'foo=bar' == req.query_string
|
41
|
+
|
42
|
+
res.status = 200
|
43
|
+
res.body = 'Params!'
|
44
|
+
end
|
45
|
+
|
46
|
+
get '/multiple-params' do |req, res|
|
47
|
+
params = CGI.parse req.query_string
|
48
|
+
|
49
|
+
next not_found unless {'foo' => ['bar'], 'baz' => ['quux']} == params
|
50
|
+
|
51
|
+
res.status = 200
|
52
|
+
res.body = 'More Params!'
|
53
|
+
end
|
54
|
+
|
55
|
+
get '/proxy' do |_req, res|
|
56
|
+
res.status = 200
|
57
|
+
res.body = 'Proxy!'
|
58
|
+
end
|
59
|
+
|
60
|
+
get '/not-found' do |_req, res|
|
61
|
+
res.status = 404
|
62
|
+
res.body = 'not found'
|
63
|
+
end
|
64
|
+
|
65
|
+
get '/redirect-301' do |_req, res|
|
66
|
+
res.status = 301
|
67
|
+
res['Location'] = "http://#{@server.config[:BindAddress]}:#{@server.config[:Port]}/"
|
68
|
+
end
|
69
|
+
|
70
|
+
get '/redirect-302' do |_req, res|
|
71
|
+
res.status = 302
|
72
|
+
res['Location'] = "http://#{@server.config[:BindAddress]}:#{@server.config[:Port]}/"
|
73
|
+
end
|
74
|
+
|
75
|
+
post '/form' do |req, res|
|
76
|
+
if 'testing-form' == req.query['example']
|
77
|
+
res.status = 200
|
78
|
+
res.body = 'passed :)'
|
79
|
+
else
|
80
|
+
res.status = 400
|
81
|
+
res.body = 'invalid! >:E'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
post '/body' do |req, res|
|
86
|
+
if 'testing-body' == req.body
|
87
|
+
res.status = 200
|
88
|
+
res.body = 'passed :)'
|
89
|
+
else
|
90
|
+
res.status = 400
|
91
|
+
res.body = 'invalid! >:E'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
head '/' do |_req, res|
|
96
|
+
res.status = 200
|
97
|
+
res['Content-Type'] = 'text/html'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ServerRunner
|
2
|
+
def run_server(name, &block)
|
3
|
+
let! name do
|
4
|
+
server = block.call
|
5
|
+
|
6
|
+
Thread.new { server.start }
|
7
|
+
|
8
|
+
server
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
send(name).shutdown
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
RSpec.configure { |c| c.extend ServerRunner }
|
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.7.
|
4
|
+
version: 0.7.3
|
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-25 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: http_parser.rb
|
@@ -117,10 +117,16 @@ files:
|
|
117
117
|
- spec/lib/http/response_spec.rb
|
118
118
|
- spec/lib/http_spec.rb
|
119
119
|
- spec/spec_helper.rb
|
120
|
+
- spec/support/black_hole.rb
|
120
121
|
- spec/support/capture_warning.rb
|
122
|
+
- spec/support/create_certs.rb
|
123
|
+
- spec/support/dummy_server.rb
|
124
|
+
- spec/support/dummy_server/servlet.rb
|
121
125
|
- spec/support/example_server.rb
|
122
126
|
- spec/support/example_server/servlet.rb
|
123
127
|
- spec/support/proxy_server.rb
|
128
|
+
- spec/support/servers/config.rb
|
129
|
+
- spec/support/servers/runner.rb
|
124
130
|
homepage: https://github.com/httprb/http.rb
|
125
131
|
licenses:
|
126
132
|
- MIT
|
@@ -141,9 +147,39 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
147
|
version: '0'
|
142
148
|
requirements: []
|
143
149
|
rubyforge_project:
|
144
|
-
rubygems_version: 2.
|
150
|
+
rubygems_version: 2.4.6
|
145
151
|
signing_key:
|
146
152
|
specification_version: 4
|
147
153
|
summary: HTTP should be easy
|
148
|
-
test_files:
|
154
|
+
test_files:
|
155
|
+
- spec/lib/http/client_spec.rb
|
156
|
+
- spec/lib/http/content_type_spec.rb
|
157
|
+
- spec/lib/http/headers/mixin_spec.rb
|
158
|
+
- spec/lib/http/headers_spec.rb
|
159
|
+
- spec/lib/http/options/body_spec.rb
|
160
|
+
- spec/lib/http/options/form_spec.rb
|
161
|
+
- spec/lib/http/options/headers_spec.rb
|
162
|
+
- spec/lib/http/options/json_spec.rb
|
163
|
+
- spec/lib/http/options/merge_spec.rb
|
164
|
+
- spec/lib/http/options/new_spec.rb
|
165
|
+
- spec/lib/http/options/proxy_spec.rb
|
166
|
+
- spec/lib/http/options_spec.rb
|
167
|
+
- spec/lib/http/redirector_spec.rb
|
168
|
+
- spec/lib/http/request/writer_spec.rb
|
169
|
+
- spec/lib/http/request_spec.rb
|
170
|
+
- spec/lib/http/response/body_spec.rb
|
171
|
+
- spec/lib/http/response/status_spec.rb
|
172
|
+
- spec/lib/http/response_spec.rb
|
173
|
+
- spec/lib/http_spec.rb
|
174
|
+
- spec/spec_helper.rb
|
175
|
+
- spec/support/black_hole.rb
|
176
|
+
- spec/support/capture_warning.rb
|
177
|
+
- spec/support/create_certs.rb
|
178
|
+
- spec/support/dummy_server.rb
|
179
|
+
- spec/support/dummy_server/servlet.rb
|
180
|
+
- spec/support/example_server.rb
|
181
|
+
- spec/support/example_server/servlet.rb
|
182
|
+
- spec/support/proxy_server.rb
|
183
|
+
- spec/support/servers/config.rb
|
184
|
+
- spec/support/servers/runner.rb
|
149
185
|
has_rdoc:
|