http 0.6.4 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of http might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop.yml +38 -93
- data/.travis.yml +5 -5
- data/.yardopts +2 -0
- data/CHANGES.md +21 -5
- data/Gemfile +12 -9
- data/Guardfile +3 -4
- data/LICENSE.txt +1 -1
- data/README.md +76 -53
- data/Rakefile +1 -1
- data/examples/parallel_requests_with_celluloid.rb +1 -1
- data/http.gemspec +6 -5
- data/lib/http.rb +0 -1
- data/lib/http/chainable.rb +54 -20
- data/lib/http/client.rb +14 -12
- data/lib/http/headers.rb +20 -17
- data/lib/http/mime_type/adapter.rb +1 -1
- data/lib/http/options.rb +1 -1
- data/lib/http/request.rb +23 -16
- data/lib/http/request/writer.rb +2 -7
- data/lib/http/response.rb +47 -76
- data/lib/http/response/body.rb +2 -3
- data/lib/http/response/status.rb +122 -0
- data/lib/http/response/status/reasons.rb +72 -0
- data/lib/http/version.rb +1 -1
- data/logo.png +0 -0
- data/spec/http/client_spec.rb +13 -47
- data/spec/http/content_type_spec.rb +15 -15
- data/spec/http/headers/mixin_spec.rb +1 -1
- data/spec/http/headers_spec.rb +42 -38
- data/spec/http/options/body_spec.rb +1 -1
- data/spec/http/options/form_spec.rb +1 -1
- data/spec/http/options/headers_spec.rb +2 -2
- data/spec/http/options/json_spec.rb +1 -1
- data/spec/http/options/merge_spec.rb +1 -1
- data/spec/http/options/new_spec.rb +2 -2
- data/spec/http/options/proxy_spec.rb +1 -1
- data/spec/http/options_spec.rb +1 -1
- data/spec/http/redirector_spec.rb +1 -1
- data/spec/http/request/writer_spec.rb +72 -24
- data/spec/http/request_spec.rb +31 -35
- data/spec/http/response/body_spec.rb +1 -1
- data/spec/http/response/status_spec.rb +139 -0
- data/spec/http/response_spec.rb +7 -7
- data/spec/http_spec.rb +41 -37
- data/spec/spec_helper.rb +2 -10
- data/spec/support/example_server.rb +14 -86
- data/spec/support/example_server/servlet.rb +102 -0
- metadata +46 -21
- data/lib/http/authorization_header.rb +0 -37
- data/lib/http/authorization_header/basic_auth.rb +0 -24
- data/lib/http/authorization_header/bearer_token.rb +0 -28
- data/lib/http/backports.rb +0 -2
- data/lib/http/backports/base64.rb +0 -6
- data/lib/http/backports/uri.rb +0 -131
- data/spec/http/authorization_header/basic_auth_spec.rb +0 -29
- data/spec/http/authorization_header/bearer_token_spec.rb +0 -36
- data/spec/http/authorization_header_spec.rb +0 -41
- data/spec/http/backports/base64_spec.rb +0 -13
- data/spec/http/backports/uri_spec.rb +0 -9
- data/spec/support/black_hole.rb +0 -5
- data/spec/support/create_certs.rb +0 -57
- data/spec/support/dummy_server.rb +0 -52
- data/spec/support/dummy_server/servlet.rb +0 -30
- data/spec/support/servers/config.rb +0 -13
- data/spec/support/servers/runner.rb +0 -17
data/lib/http/backports.rb
DELETED
data/lib/http/backports/uri.rb
DELETED
@@ -1,131 +0,0 @@
|
|
1
|
-
# Taken from Ruby 1.9's uri/common.rb
|
2
|
-
# By Akira Yamada <akira@ruby-lang.org>
|
3
|
-
# License:
|
4
|
-
# You can redistribute it and/or modify it under the same term as Ruby.
|
5
|
-
|
6
|
-
require 'uri'
|
7
|
-
|
8
|
-
# Backport Ruby 1.9's form encoding/decoding functionality
|
9
|
-
module URI
|
10
|
-
TBLENCWWWCOMP_ = {} # :nodoc:
|
11
|
-
256.times do |i|
|
12
|
-
TBLENCWWWCOMP_[i.chr] = format('%%%02X', i)
|
13
|
-
end
|
14
|
-
TBLENCWWWCOMP_[' '] = '+'
|
15
|
-
TBLENCWWWCOMP_.freeze
|
16
|
-
TBLDECWWWCOMP_ = {} # :nodoc:
|
17
|
-
256.times do |i|
|
18
|
-
h, l = i >> 4, i & 15
|
19
|
-
TBLDECWWWCOMP_[format('%%%X%X', h, l)] = i.chr
|
20
|
-
TBLDECWWWCOMP_[format('%%%x%X', h, l)] = i.chr
|
21
|
-
TBLDECWWWCOMP_[format('%%%X%x', h, l)] = i.chr
|
22
|
-
TBLDECWWWCOMP_[format('%%%x%x', h, l)] = i.chr
|
23
|
-
end
|
24
|
-
TBLDECWWWCOMP_['+'] = ' '
|
25
|
-
TBLDECWWWCOMP_.freeze
|
26
|
-
|
27
|
-
# Encode given +str+ to URL-encoded form data.
|
28
|
-
#
|
29
|
-
# This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP
|
30
|
-
# (ASCII space) to + and converts others to %XX.
|
31
|
-
#
|
32
|
-
# This is an implementation of
|
33
|
-
# http://www.w3.org/TR/html5/association-of-controls-and-forms.html#url-encoded-form-data
|
34
|
-
#
|
35
|
-
# See URI.decode_www_form_component, URI.encode_www_form
|
36
|
-
def self.encode_www_form_component(str)
|
37
|
-
str.to_s.gsub(/[^*\-.0-9A-Z_a-z]/) { |chr| TBLENCWWWCOMP_[chr] }
|
38
|
-
end
|
39
|
-
|
40
|
-
# Decode given +str+ of URL-encoded form data.
|
41
|
-
#
|
42
|
-
# This decods + to SP.
|
43
|
-
#
|
44
|
-
# See URI.encode_www_form_component, URI.decode_www_form
|
45
|
-
def self.decode_www_form_component(str)
|
46
|
-
fail(ArgumentError, "invalid %-encoding (#{str})") unless /\A[^%]*(?:%\h\h[^%]*)*\z/ =~ str
|
47
|
-
str.gsub(/\+|%\h\h/) { |chr| TBLDECWWWCOMP_[chr] }
|
48
|
-
end
|
49
|
-
|
50
|
-
# Generate URL-encoded form data from given +enum+.
|
51
|
-
#
|
52
|
-
# This generates application/x-www-form-urlencoded data defined in HTML5
|
53
|
-
# from given an Enumerable object.
|
54
|
-
#
|
55
|
-
# This internally uses URI.encode_www_form_component(str).
|
56
|
-
#
|
57
|
-
# This method doesn't convert the encoding of given items, so convert them
|
58
|
-
# before call this method if you want to send data as other than original
|
59
|
-
# encoding or mixed encoding data. (Strings which are encoded in an HTML5
|
60
|
-
# ASCII incompatible encoding are converted to UTF-8.)
|
61
|
-
#
|
62
|
-
# This method doesn't handle files. When you send a file, use
|
63
|
-
# multipart/form-data.
|
64
|
-
#
|
65
|
-
# This is an implementation of
|
66
|
-
# http://www.w3.org/TR/html5/forms.html#url-encoded-form-data
|
67
|
-
#
|
68
|
-
# URI.encode_www_form([["q", "ruby"], ["lang", "en"]])
|
69
|
-
# #=> "q=ruby&lang=en"
|
70
|
-
# URI.encode_www_form("q" => "ruby", "lang" => "en")
|
71
|
-
# #=> "q=ruby&lang=en"
|
72
|
-
# URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en")
|
73
|
-
# #=> "q=ruby&q=perl&lang=en"
|
74
|
-
# URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]])
|
75
|
-
# #=> "q=ruby&q=perl&lang=en"
|
76
|
-
#
|
77
|
-
# See URI.encode_www_form_component, URI.decode_www_form
|
78
|
-
def self.encode_www_form(enum)
|
79
|
-
enum.map do |k, v|
|
80
|
-
if v.nil?
|
81
|
-
encode_www_form_component(k)
|
82
|
-
elsif v.respond_to?(:to_ary)
|
83
|
-
v.to_ary.map do |w|
|
84
|
-
next unless w
|
85
|
-
|
86
|
-
str = encode_www_form_component(k)
|
87
|
-
str << '='
|
88
|
-
str << encode_www_form_component(w)
|
89
|
-
end.join('&')
|
90
|
-
else
|
91
|
-
str = encode_www_form_component(k)
|
92
|
-
str << '='
|
93
|
-
str << encode_www_form_component(v)
|
94
|
-
end
|
95
|
-
end.join('&')
|
96
|
-
end
|
97
|
-
|
98
|
-
WFKV_ = '(?:[^%#=;&]*(?:%\h\h[^%#=;&]*)*)' # :nodoc:
|
99
|
-
|
100
|
-
# Decode URL-encoded form data from given +str+.
|
101
|
-
#
|
102
|
-
# This decodes application/x-www-form-urlencoded data
|
103
|
-
# and returns array of key-value array.
|
104
|
-
# This internally uses URI.decode_www_form_component.
|
105
|
-
#
|
106
|
-
# _charset_ hack is not supported now because the mapping from given charset
|
107
|
-
# to Ruby's encoding is not clear yet.
|
108
|
-
# see also http://www.w3.org/TR/html5/syntax.html#character-encodings-0
|
109
|
-
#
|
110
|
-
# This refers http://www.w3.org/TR/html5/forms.html#url-encoded-form-data
|
111
|
-
#
|
112
|
-
# ary = URI.decode_www_form("a=1&a=2&b=3")
|
113
|
-
# p ary #=> [['a', '1'], ['a', '2'], ['b', '3']]
|
114
|
-
# p ary.assoc('a').last #=> '1'
|
115
|
-
# p ary.assoc('b').last #=> '3'
|
116
|
-
# p ary.rassoc('a').last #=> '2'
|
117
|
-
# p Hash[ary] # => {"a"=>"2", "b"=>"3"}
|
118
|
-
#
|
119
|
-
# See URI.decode_www_form_component, URI.encode_www_form
|
120
|
-
def self.decode_www_form(str)
|
121
|
-
return [] if str.empty?
|
122
|
-
unless /\A#{WFKV_}=#{WFKV_}(?:[;&]#{WFKV_}=#{WFKV_})*\z/o =~ str
|
123
|
-
fail(ArgumentError, "invalid data of application/x-www-form-urlencoded (#{str})")
|
124
|
-
end
|
125
|
-
ary = []
|
126
|
-
$&.scan(/([^=;&]+)=([^;&]*)/) do
|
127
|
-
ary << [decode_www_form_component(Regexp.last_match[1]), decode_www_form_component(Regexp.last_match[2])]
|
128
|
-
end
|
129
|
-
ary
|
130
|
-
end
|
131
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe HTTP::AuthorizationHeader::BasicAuth do
|
4
|
-
describe '.new' do
|
5
|
-
it 'fails when options is not a Hash' do
|
6
|
-
expect { described_class.new '[FOOBAR]' }.to raise_error
|
7
|
-
end
|
8
|
-
|
9
|
-
it 'fails when :pass is not given' do
|
10
|
-
expect { described_class.new :user => '[USER]' }.to raise_error
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'fails when :user is not given' do
|
14
|
-
expect { described_class.new :pass => '[PASS]' }.to raise_error
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
describe '#to_s' do
|
19
|
-
let(:user) { 'foo' }
|
20
|
-
let(:pass) { 'bar' * 100 }
|
21
|
-
let(:user_n_pass) { user + ':' + pass }
|
22
|
-
let(:builder) { described_class.new :user => user, :pass => pass }
|
23
|
-
|
24
|
-
subject { builder.to_s }
|
25
|
-
|
26
|
-
it { should eq "Basic #{Base64.strict_encode64 user_n_pass}" }
|
27
|
-
it { should match(/^Basic [^\s]+$/) }
|
28
|
-
end
|
29
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe HTTP::AuthorizationHeader::BearerToken do
|
4
|
-
describe '.new' do
|
5
|
-
it 'fails when options is not a Hash' do
|
6
|
-
expect { described_class.new '[TOKEN]' }.to raise_error
|
7
|
-
end
|
8
|
-
|
9
|
-
it 'fails when :token is not given' do
|
10
|
-
expect { described_class.new :encode => true }.to raise_error
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
describe '#to_s' do
|
15
|
-
let(:token) { 'foobar' * 100 }
|
16
|
-
let(:builder) { described_class.new options.merge :token => token }
|
17
|
-
|
18
|
-
subject { builder.to_s }
|
19
|
-
|
20
|
-
context 'when :encode => true' do
|
21
|
-
let(:options) { {:encode => true} }
|
22
|
-
it { should eq "Bearer #{Base64.strict_encode64 token}" }
|
23
|
-
it { should match(/^Bearer [^\s]+$/) }
|
24
|
-
end
|
25
|
-
|
26
|
-
context 'when :encode => false' do
|
27
|
-
let(:options) { {:encode => false} }
|
28
|
-
it { should eq "Bearer #{token}" }
|
29
|
-
end
|
30
|
-
|
31
|
-
context 'when :encode not specified' do
|
32
|
-
let(:options) { {} }
|
33
|
-
it { should eq "Bearer #{token}" }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe HTTP::AuthorizationHeader do
|
4
|
-
describe '.build' do
|
5
|
-
context 'with unkown type' do
|
6
|
-
let(:type) { :foobar }
|
7
|
-
let(:opts) { {:foo => :bar} }
|
8
|
-
|
9
|
-
it 'fails' do
|
10
|
-
expect { described_class.build type, opts }.to raise_error
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
context 'with :basic type' do
|
15
|
-
let(:type) { :basic }
|
16
|
-
let(:opts) { {:user => 'user', :pass => 'pass'} }
|
17
|
-
|
18
|
-
it 'passes options to BasicAuth' do
|
19
|
-
expect(described_class::BasicAuth).to receive(:new).with(opts)
|
20
|
-
described_class.build type, opts
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
context 'with :bearer type' do
|
25
|
-
let(:type) { :bearer }
|
26
|
-
let(:opts) { {:token => 'token', :encode => true} }
|
27
|
-
|
28
|
-
it 'passes options to BearerToken' do
|
29
|
-
expect(described_class::BearerToken).to receive(:new).with(opts)
|
30
|
-
described_class.build type, opts
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
describe '.register' do
|
36
|
-
it 'registers given klass in builders registry' do
|
37
|
-
described_class.register :dummy, Class.new { def initialize(*); end }
|
38
|
-
expect { described_class.build(:dummy, 'foobar') }.to_not raise_error
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Base64 do
|
4
|
-
specify { expect(Base64).to respond_to :strict_encode64 }
|
5
|
-
|
6
|
-
describe '.strict_encode64' do
|
7
|
-
let(:long_string) { (0...256).map { ('a'..'z').to_a[rand(26)] }.join }
|
8
|
-
|
9
|
-
it 'returns a String without whitespaces' do
|
10
|
-
expect(Base64.strict_encode64 long_string).to_not match(/\s/)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
data/spec/support/black_hole.rb
DELETED
@@ -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
|
@@ -1,52 +0,0 @@
|
|
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
|
@@ -1,30 +0,0 @@
|
|
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
|
-
def do_#{method.upcase}(req, res)
|
18
|
-
handler = self.class.handlers["#{method}:\#{req.path}"]
|
19
|
-
return instance_exec(req, res, &handler) if handler
|
20
|
-
not_found
|
21
|
-
end
|
22
|
-
RUBY
|
23
|
-
end
|
24
|
-
|
25
|
-
get '/' do |_req, res|
|
26
|
-
res.status = 200
|
27
|
-
res.body = '<!doctype html>'
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,17 +0,0 @@
|
|
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 }
|