http 0.9.8 → 1.0.0
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/.travis.yml +1 -0
- data/CHANGES.md +143 -92
- data/Gemfile +4 -4
- data/README.md +8 -27
- data/lib/http/chainable.rb +13 -30
- data/lib/http/client.rb +17 -12
- data/lib/http/connection.rb +10 -7
- data/lib/http/errors.rb +3 -0
- data/lib/http/headers.rb +0 -3
- data/lib/http/options.rb +9 -12
- data/lib/http/request/writer.rb +14 -12
- data/lib/http/request.rb +23 -14
- data/lib/http/response/body.rb +7 -6
- data/lib/http/response/parser.rb +2 -1
- data/lib/http/response/status.rb +1 -3
- data/lib/http/response.rb +31 -28
- data/lib/http/timeout/global.rb +2 -1
- data/lib/http/timeout/null.rb +2 -1
- data/lib/http/timeout/per_operation.rb +2 -1
- data/lib/http/version.rb +1 -1
- data/lib/http.rb +0 -2
- data/logo.png +0 -0
- data/spec/lib/http/client_spec.rb +25 -16
- data/spec/lib/http/options/merge_spec.rb +3 -1
- data/spec/lib/http/options_spec.rb +2 -3
- data/spec/lib/http/redirector_spec.rb +39 -34
- data/spec/lib/http/request/writer_spec.rb +3 -3
- data/spec/lib/http/request_spec.rb +41 -7
- data/spec/lib/http/response/body_spec.rb +1 -1
- data/spec/lib/http/response/status_spec.rb +3 -3
- data/spec/lib/http/response_spec.rb +10 -1
- data/spec/lib/http_spec.rb +66 -7
- data/spec/spec_helper.rb +2 -2
- data/spec/support/connection_reuse_shared.rb +1 -1
- data/spec/support/dummy_server/servlet.rb +7 -0
- data/spec/support/http_handling_shared.rb +7 -1
- metadata +3 -4
- data/examples/parallel_requests_with_celluloid.rb +0 -38
data/lib/http/client.rb
CHANGED
|
@@ -32,13 +32,19 @@ module HTTP
|
|
|
32
32
|
body = make_request_body(opts, headers)
|
|
33
33
|
proxy = opts.proxy
|
|
34
34
|
|
|
35
|
-
req = HTTP::Request.new(
|
|
36
|
-
|
|
35
|
+
req = HTTP::Request.new(
|
|
36
|
+
:verb => verb,
|
|
37
|
+
:uri => uri,
|
|
38
|
+
:headers => headers,
|
|
39
|
+
:proxy => proxy,
|
|
40
|
+
:body => body
|
|
41
|
+
)
|
|
37
42
|
|
|
43
|
+
res = perform(req, opts)
|
|
38
44
|
return res unless opts.follow
|
|
39
45
|
|
|
40
|
-
Redirector.new(opts.follow).perform
|
|
41
|
-
perform
|
|
46
|
+
Redirector.new(opts.follow).perform(req, res) do |request|
|
|
47
|
+
perform(request, opts)
|
|
42
48
|
end
|
|
43
49
|
end
|
|
44
50
|
|
|
@@ -61,11 +67,12 @@ module HTTP
|
|
|
61
67
|
end
|
|
62
68
|
|
|
63
69
|
res = Response.new(
|
|
64
|
-
@connection.status_code,
|
|
65
|
-
@connection.http_version,
|
|
66
|
-
@connection.headers,
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
:status => @connection.status_code,
|
|
71
|
+
:version => @connection.http_version,
|
|
72
|
+
:headers => @connection.headers,
|
|
73
|
+
:connection => @connection,
|
|
74
|
+
:encoding => options.encoding,
|
|
75
|
+
:uri => req.uri
|
|
69
76
|
)
|
|
70
77
|
|
|
71
78
|
@connection.finish_response if req.verb == :head
|
|
@@ -73,9 +80,7 @@ module HTTP
|
|
|
73
80
|
|
|
74
81
|
res
|
|
75
82
|
rescue
|
|
76
|
-
|
|
77
|
-
# we don't have conns in a bad state resulting in mixed requests/responses
|
|
78
|
-
close if persistent?
|
|
83
|
+
close
|
|
79
84
|
raise
|
|
80
85
|
end
|
|
81
86
|
|
data/lib/http/connection.rb
CHANGED
|
@@ -20,21 +20,24 @@ module HTTP
|
|
|
20
20
|
|
|
21
21
|
# @param [HTTP::Request] req
|
|
22
22
|
# @param [HTTP::Options] options
|
|
23
|
+
# @raise [HTTP::ConnectionError] when failed to connect
|
|
23
24
|
def initialize(req, options)
|
|
24
25
|
@persistent = options.persistent?
|
|
25
|
-
@keep_alive_timeout = options
|
|
26
|
+
@keep_alive_timeout = options.keep_alive_timeout.to_f
|
|
26
27
|
@pending_request = false
|
|
27
28
|
@pending_response = false
|
|
28
29
|
@failed_proxy_connect = false
|
|
29
30
|
|
|
30
31
|
@parser = Response::Parser.new
|
|
31
32
|
|
|
32
|
-
@socket = options
|
|
33
|
-
@socket.connect(options
|
|
33
|
+
@socket = options.timeout_class.new(options.timeout_options)
|
|
34
|
+
@socket.connect(options.socket_class, req.socket_host, req.socket_port, options.nodelay)
|
|
34
35
|
|
|
35
36
|
send_proxy_connect_request(req)
|
|
36
37
|
start_tls(req, options)
|
|
37
38
|
reset_timer
|
|
39
|
+
rescue SocketError, SystemCallError => e
|
|
40
|
+
raise ConnectionError, "failed to connect: #{e}"
|
|
38
41
|
end
|
|
39
42
|
|
|
40
43
|
# @see (HTTP::Response::Parser#status_code)
|
|
@@ -104,7 +107,7 @@ module HTTP
|
|
|
104
107
|
|
|
105
108
|
set_keep_alive
|
|
106
109
|
rescue IOError, Errno::ECONNRESET, Errno::EPIPE => e
|
|
107
|
-
raise
|
|
110
|
+
raise ConnectionError, "failed to read headers: #{e}"
|
|
108
111
|
end
|
|
109
112
|
|
|
110
113
|
# Callback for when we've reached the end of a response
|
|
@@ -148,14 +151,14 @@ module HTTP
|
|
|
148
151
|
def start_tls(req, options)
|
|
149
152
|
return unless req.uri.https? && !failed_proxy_connect?
|
|
150
153
|
|
|
151
|
-
ssl_context = options
|
|
154
|
+
ssl_context = options.ssl_context
|
|
152
155
|
|
|
153
156
|
unless ssl_context
|
|
154
157
|
ssl_context = OpenSSL::SSL::SSLContext.new
|
|
155
|
-
ssl_context.set_params(options
|
|
158
|
+
ssl_context.set_params(options.ssl || {})
|
|
156
159
|
end
|
|
157
160
|
|
|
158
|
-
@socket.start_tls(req.uri.host, options
|
|
161
|
+
@socket.start_tls(req.uri.host, options.ssl_socket_class, ssl_context)
|
|
159
162
|
end
|
|
160
163
|
|
|
161
164
|
# Open tunnel through proxy
|
data/lib/http/errors.rb
CHANGED
data/lib/http/headers.rb
CHANGED
data/lib/http/options.rb
CHANGED
|
@@ -43,11 +43,13 @@ module HTTP
|
|
|
43
43
|
:timeout_class => self.class.default_timeout_class,
|
|
44
44
|
:timeout_options => {},
|
|
45
45
|
:socket_class => self.class.default_socket_class,
|
|
46
|
+
:nodelay => false,
|
|
46
47
|
:ssl_socket_class => self.class.default_ssl_socket_class,
|
|
47
48
|
:ssl => {},
|
|
48
49
|
:keep_alive_timeout => 5,
|
|
49
50
|
:headers => {},
|
|
50
|
-
:cookies => {}
|
|
51
|
+
:cookies => {},
|
|
52
|
+
:encoding => nil
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
opts_w_defaults = defaults.merge(options)
|
|
@@ -66,9 +68,13 @@ module HTTP
|
|
|
66
68
|
end
|
|
67
69
|
end
|
|
68
70
|
|
|
71
|
+
def_option :encoding do |encoding|
|
|
72
|
+
self.encoding = Encoding.find(encoding)
|
|
73
|
+
end
|
|
74
|
+
|
|
69
75
|
%w(
|
|
70
76
|
proxy params form json body follow response
|
|
71
|
-
socket_class ssl_socket_class ssl_context ssl
|
|
77
|
+
socket_class nodelay ssl_socket_class ssl_context ssl
|
|
72
78
|
persistent keep_alive_timeout timeout_class timeout_options
|
|
73
79
|
).each do |method_name|
|
|
74
80
|
def_option method_name
|
|
@@ -91,15 +97,6 @@ module HTTP
|
|
|
91
97
|
!persistent.nil?
|
|
92
98
|
end
|
|
93
99
|
|
|
94
|
-
# @deprecated
|
|
95
|
-
def [](option)
|
|
96
|
-
send(option)
|
|
97
|
-
rescue
|
|
98
|
-
warn "[DEPRECATED] `HTTP::Options#[:#{option}]` was deprecated. " \
|
|
99
|
-
"Use `HTTP::Options##{option}` instead."
|
|
100
|
-
nil
|
|
101
|
-
end
|
|
102
|
-
|
|
103
100
|
def merge(other)
|
|
104
101
|
h1 = to_hash
|
|
105
102
|
h2 = other.to_hash
|
|
@@ -119,7 +116,7 @@ module HTTP
|
|
|
119
116
|
def to_hash
|
|
120
117
|
hash_pairs = self.class.
|
|
121
118
|
defined_options.
|
|
122
|
-
flat_map { |opt_name| [opt_name,
|
|
119
|
+
flat_map { |opt_name| [opt_name, send(opt_name)] }
|
|
123
120
|
Hash[*hash_pairs]
|
|
124
121
|
end
|
|
125
122
|
|
data/lib/http/request/writer.rb
CHANGED
|
@@ -18,7 +18,7 @@ module HTTP
|
|
|
18
18
|
# Types valid to be used as body source
|
|
19
19
|
VALID_BODY_TYPES = [String, NilClass, Enumerable]
|
|
20
20
|
|
|
21
|
-
def initialize(socket, body, headers, headline)
|
|
21
|
+
def initialize(socket, body, headers, headline)
|
|
22
22
|
@body = body
|
|
23
23
|
@socket = socket
|
|
24
24
|
@headers = headers
|
|
@@ -36,8 +36,9 @@ module HTTP
|
|
|
36
36
|
|
|
37
37
|
# Stream the request to a socket
|
|
38
38
|
def stream
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
add_headers
|
|
40
|
+
add_body_type_headers
|
|
41
|
+
send_request
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
# Send headers needed to connect through proxy
|
|
@@ -64,23 +65,24 @@ module HTTP
|
|
|
64
65
|
@request_header.join(CRLF) + (CRLF) * 2
|
|
65
66
|
end
|
|
66
67
|
|
|
67
|
-
def
|
|
68
|
-
|
|
69
|
-
add_body_type_headers
|
|
68
|
+
def send_request
|
|
69
|
+
headers = join_headers
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
case @body
|
|
72
|
+
when NilClass
|
|
73
|
+
write(headers)
|
|
74
|
+
when String
|
|
75
|
+
write(headers << @body)
|
|
76
|
+
when Enumerable
|
|
77
|
+
write(headers)
|
|
73
78
|
|
|
74
|
-
def send_request_body
|
|
75
|
-
if @body.is_a?(String)
|
|
76
|
-
write(@body)
|
|
77
|
-
elsif @body.is_a?(Enumerable)
|
|
78
79
|
@body.each do |chunk|
|
|
79
80
|
write(chunk.bytesize.to_s(16) << CRLF)
|
|
80
81
|
write(chunk << CRLF)
|
|
81
82
|
end
|
|
82
83
|
|
|
83
84
|
write(CHUNKED_END)
|
|
85
|
+
else fail TypeError, "invalid body type: #{@body.class}"
|
|
84
86
|
end
|
|
85
87
|
end
|
|
86
88
|
|
data/lib/http/request.rb
CHANGED
|
@@ -63,20 +63,24 @@ module HTTP
|
|
|
63
63
|
attr_reader :uri
|
|
64
64
|
attr_reader :proxy, :body, :version
|
|
65
65
|
|
|
66
|
-
# :
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
# @option opts [String] :version
|
|
67
|
+
# @option opts [#to_s] :verb HTTP request method
|
|
68
|
+
# @option opts [HTTP::URI, #to_s] :uri
|
|
69
|
+
# @option opts [Hash] :headers
|
|
70
|
+
# @option opts [Hash] :proxy
|
|
71
|
+
# @option opts [String] :body
|
|
72
|
+
def initialize(opts)
|
|
73
|
+
@verb = opts.fetch(:verb).to_s.downcase.to_sym
|
|
74
|
+
@uri = normalize_uri(opts.fetch :uri)
|
|
75
|
+
@scheme = @uri.scheme.to_s.downcase.to_sym if @uri.scheme
|
|
71
76
|
|
|
72
77
|
fail(UnsupportedMethodError, "unknown method: #{verb}") unless METHODS.include?(@verb)
|
|
73
78
|
fail(UnsupportedSchemeError, "unknown scheme: #{scheme}") unless SCHEMES.include?(@scheme)
|
|
74
79
|
|
|
75
|
-
@proxy = proxy
|
|
76
|
-
@body = body
|
|
77
|
-
@version = version
|
|
78
|
-
|
|
79
|
-
@headers = HTTP::Headers.coerce(headers || {})
|
|
80
|
+
@proxy = opts[:proxy] || {}
|
|
81
|
+
@body = opts[:body]
|
|
82
|
+
@version = opts[:version] || "1.1"
|
|
83
|
+
@headers = HTTP::Headers.coerce(opts[:headers] || {})
|
|
80
84
|
|
|
81
85
|
@headers[Headers::HOST] ||= default_host_header_value
|
|
82
86
|
@headers[Headers::USER_AGENT] ||= USER_AGENT
|
|
@@ -84,7 +88,15 @@ module HTTP
|
|
|
84
88
|
|
|
85
89
|
# Returns new Request with updated uri
|
|
86
90
|
def redirect(uri, verb = @verb)
|
|
87
|
-
req = self.class.new(
|
|
91
|
+
req = self.class.new(
|
|
92
|
+
:verb => verb,
|
|
93
|
+
:uri => @uri.join(uri),
|
|
94
|
+
:headers => headers,
|
|
95
|
+
:proxy => proxy,
|
|
96
|
+
:body => body,
|
|
97
|
+
:version => version
|
|
98
|
+
)
|
|
99
|
+
|
|
88
100
|
req[Headers::HOST] = req.uri.host
|
|
89
101
|
req
|
|
90
102
|
end
|
|
@@ -126,9 +138,6 @@ module HTTP
|
|
|
126
138
|
"#{verb.to_s.upcase} #{request_uri.omit :fragment} HTTP/#{version}"
|
|
127
139
|
end
|
|
128
140
|
|
|
129
|
-
# @deprecated Will be removed in 1.0.0
|
|
130
|
-
alias_method :request_header, :headline
|
|
131
|
-
|
|
132
141
|
# Compute HTTP request header SSL proxy connection
|
|
133
142
|
def proxy_connect_header
|
|
134
143
|
"CONNECT #{host}:#{port} HTTP/#{version}"
|
data/lib/http/response/body.rb
CHANGED
|
@@ -9,10 +9,11 @@ module HTTP
|
|
|
9
9
|
include Enumerable
|
|
10
10
|
def_delegator :to_s, :empty?
|
|
11
11
|
|
|
12
|
-
def initialize(client)
|
|
13
|
-
@client
|
|
14
|
-
@streaming
|
|
15
|
-
@contents
|
|
12
|
+
def initialize(client, encoding = Encoding::BINARY)
|
|
13
|
+
@client = client
|
|
14
|
+
@streaming = nil
|
|
15
|
+
@contents = nil
|
|
16
|
+
@encoding = encoding
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
# (see HTTP::Client#readpartial)
|
|
@@ -36,9 +37,9 @@ module HTTP
|
|
|
36
37
|
|
|
37
38
|
begin
|
|
38
39
|
@streaming = false
|
|
39
|
-
@contents = "".force_encoding(
|
|
40
|
+
@contents = "".force_encoding(@encoding)
|
|
40
41
|
while (chunk = @client.readpartial)
|
|
41
|
-
@contents << chunk.force_encoding(
|
|
42
|
+
@contents << chunk.force_encoding(@encoding)
|
|
42
43
|
end
|
|
43
44
|
rescue
|
|
44
45
|
@contents = nil
|
data/lib/http/response/parser.rb
CHANGED
data/lib/http/response/status.rb
CHANGED
|
@@ -91,9 +91,6 @@ module HTTP
|
|
|
91
91
|
SYMBOLS[code]
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
# @deprecated Will be removed in 1.0.0
|
|
95
|
-
alias_method :symbolize, :to_sym
|
|
96
|
-
|
|
97
94
|
# Printable version of HTTP Status, surrounded by quote marks,
|
|
98
95
|
# with special characters escaped.
|
|
99
96
|
#
|
|
@@ -111,6 +108,7 @@ module HTTP
|
|
|
111
108
|
end
|
|
112
109
|
|
|
113
110
|
def __setobj__(obj)
|
|
111
|
+
fail TypeError, "Expected #{obj.inspect} to respond to #to_i" unless obj.respond_to? :to_i
|
|
114
112
|
@code = obj.to_i
|
|
115
113
|
end
|
|
116
114
|
|
data/lib/http/response.rb
CHANGED
|
@@ -14,13 +14,6 @@ module HTTP
|
|
|
14
14
|
|
|
15
15
|
include HTTP::Headers::Mixin
|
|
16
16
|
|
|
17
|
-
# @deprecated Will be removed in 1.0.0
|
|
18
|
-
# Use Status::REASONS
|
|
19
|
-
STATUS_CODES = Status::REASONS
|
|
20
|
-
|
|
21
|
-
# @deprecated Will be removed in 1.0.0
|
|
22
|
-
SYMBOL_TO_STATUS_CODE = Hash[STATUS_CODES.map { |k, v| [v.downcase.gsub(/\s|-/, "_").to_sym, k] }].freeze
|
|
23
|
-
|
|
24
17
|
# @return [Status]
|
|
25
18
|
attr_reader :status
|
|
26
19
|
|
|
@@ -30,12 +23,29 @@ module HTTP
|
|
|
30
23
|
# @return [URI, nil]
|
|
31
24
|
attr_reader :uri
|
|
32
25
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
# Inits a new instance
|
|
27
|
+
#
|
|
28
|
+
# @option opts [Integer] :status Status code
|
|
29
|
+
# @option opts [String] :version HTTP version
|
|
30
|
+
# @option opts [Hash] :headers
|
|
31
|
+
# @option opts [HTTP::Connection] :connection
|
|
32
|
+
# @option opts [String] :encoding Encoding to use when reading body
|
|
33
|
+
# @option opts [String] :body
|
|
34
|
+
# @option opts [String] :uri
|
|
35
|
+
def initialize(opts)
|
|
36
|
+
@version = opts.fetch(:version)
|
|
37
|
+
@uri = HTTP::URI.parse(opts.fetch :uri) if opts.include? :uri
|
|
38
|
+
@status = HTTP::Response::Status.new(opts.fetch :status)
|
|
39
|
+
@headers = HTTP::Headers.coerce(opts[:headers] || {})
|
|
40
|
+
|
|
41
|
+
if opts.include?(:connection)
|
|
42
|
+
connection = opts.fetch(:connection)
|
|
43
|
+
encoding = opts[:encoding] || charset || Encoding::BINARY
|
|
44
|
+
|
|
45
|
+
@body = Response::Body.new(connection, encoding)
|
|
46
|
+
else
|
|
47
|
+
@body = opts.fetch(:body)
|
|
48
|
+
end
|
|
39
49
|
end
|
|
40
50
|
|
|
41
51
|
# @!method reason
|
|
@@ -46,9 +56,6 @@ module HTTP
|
|
|
46
56
|
# @return (see HTTP::Response::Status#code)
|
|
47
57
|
def_delegator :status, :code
|
|
48
58
|
|
|
49
|
-
# @deprecated Will be removed in 1.0.0
|
|
50
|
-
alias_method :status_code, :code
|
|
51
|
-
|
|
52
59
|
# @!method to_s
|
|
53
60
|
# (see HTTP::Response::Body#to_s)
|
|
54
61
|
def_delegator :body, :to_s
|
|
@@ -80,19 +87,15 @@ module HTTP
|
|
|
80
87
|
@content_type ||= ContentType.parse headers[Headers::CONTENT_TYPE]
|
|
81
88
|
end
|
|
82
89
|
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
|
|
87
|
-
@mime_type ||= content_type.mime_type
|
|
88
|
-
end
|
|
90
|
+
# @!method mime_type
|
|
91
|
+
# MIME type of response (if any)
|
|
92
|
+
# @return [String, nil]
|
|
93
|
+
def_delegator :content_type, :mime_type
|
|
89
94
|
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
|
|
94
|
-
@charset ||= content_type.charset
|
|
95
|
-
end
|
|
95
|
+
# @!method charset
|
|
96
|
+
# Charset of response (if any)
|
|
97
|
+
# @return [String, nil]
|
|
98
|
+
def_delegator :content_type, :charset
|
|
96
99
|
|
|
97
100
|
def cookies
|
|
98
101
|
@cookies ||= headers.each_with_object CookieJar.new do |(k, v), jar|
|
data/lib/http/timeout/global.rb
CHANGED
|
@@ -18,10 +18,11 @@ module HTTP
|
|
|
18
18
|
@total_timeout = time_left
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def connect(socket_class, host, port)
|
|
21
|
+
def connect(socket_class, host, port, nodelay = false)
|
|
22
22
|
reset_timer
|
|
23
23
|
::Timeout.timeout(time_left, TimeoutError) do
|
|
24
24
|
@socket = socket_class.open(host, port)
|
|
25
|
+
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if nodelay
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
log_time
|
data/lib/http/timeout/null.rb
CHANGED
|
@@ -14,8 +14,9 @@ module HTTP
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
# Connects to a socket
|
|
17
|
-
def connect(socket_class, host, port)
|
|
17
|
+
def connect(socket_class, host, port, nodelay = false)
|
|
18
18
|
@socket = socket_class.open(host, port)
|
|
19
|
+
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if nodelay
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
# Starts a SSL connection on a socket
|
|
@@ -19,9 +19,10 @@ module HTTP
|
|
|
19
19
|
@connect_timeout = options.fetch(:connect_timeout, CONNECT_TIMEOUT)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def connect(socket_class, host, port)
|
|
22
|
+
def connect(socket_class, host, port, nodelay = false)
|
|
23
23
|
::Timeout.timeout(connect_timeout, TimeoutError) do
|
|
24
24
|
@socket = socket_class.open(host, port)
|
|
25
|
+
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if nodelay
|
|
25
26
|
end
|
|
26
27
|
end
|
|
27
28
|
|
data/lib/http/version.rb
CHANGED
data/lib/http.rb
CHANGED
data/logo.png
CHANGED
|
Binary file
|
|
@@ -26,11 +26,20 @@ RSpec.describe HTTP::Client do
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def redirect_response(location, status = 302)
|
|
29
|
-
HTTP::Response.new(
|
|
29
|
+
HTTP::Response.new(
|
|
30
|
+
:status => status,
|
|
31
|
+
:version => "1.1",
|
|
32
|
+
:headers => {"Location" => location},
|
|
33
|
+
:body => ""
|
|
34
|
+
)
|
|
30
35
|
end
|
|
31
36
|
|
|
32
37
|
def simple_response(body, status = 200)
|
|
33
|
-
HTTP::Response.new(
|
|
38
|
+
HTTP::Response.new(
|
|
39
|
+
:status => status,
|
|
40
|
+
:version => "1.1",
|
|
41
|
+
:body => body
|
|
42
|
+
)
|
|
34
43
|
end
|
|
35
44
|
|
|
36
45
|
describe "following redirects" do
|
|
@@ -100,48 +109,48 @@ RSpec.describe HTTP::Client do
|
|
|
100
109
|
before { allow(client).to receive :perform }
|
|
101
110
|
|
|
102
111
|
it "accepts params within the provided URL" do
|
|
103
|
-
expect(HTTP::Request).to receive(:new) do |
|
|
104
|
-
expect(CGI.parse uri.query).to eq("foo" => %w(bar))
|
|
112
|
+
expect(HTTP::Request).to receive(:new) do |opts|
|
|
113
|
+
expect(CGI.parse opts[:uri].query).to eq("foo" => %w(bar))
|
|
105
114
|
end
|
|
106
115
|
|
|
107
116
|
client.get("http://example.com/?foo=bar")
|
|
108
117
|
end
|
|
109
118
|
|
|
110
119
|
it "combines GET params from the URI with the passed in params" do
|
|
111
|
-
expect(HTTP::Request).to receive(:new) do |
|
|
112
|
-
expect(CGI.parse uri.query).to eq("foo" => %w(bar), "baz" => %w(quux))
|
|
120
|
+
expect(HTTP::Request).to receive(:new) do |opts|
|
|
121
|
+
expect(CGI.parse opts[:uri].query).to eq("foo" => %w(bar), "baz" => %w(quux))
|
|
113
122
|
end
|
|
114
123
|
|
|
115
124
|
client.get("http://example.com/?foo=bar", :params => {:baz => "quux"})
|
|
116
125
|
end
|
|
117
126
|
|
|
118
127
|
it "merges duplicate values" do
|
|
119
|
-
expect(HTTP::Request).to receive(:new) do |
|
|
120
|
-
expect(uri.query).to match(/^(a=1&a=2|a=2&a=1)$/)
|
|
128
|
+
expect(HTTP::Request).to receive(:new) do |opts|
|
|
129
|
+
expect(opts[:uri].query).to match(/^(a=1&a=2|a=2&a=1)$/)
|
|
121
130
|
end
|
|
122
131
|
|
|
123
132
|
client.get("http://example.com/?a=1", :params => {:a => 2})
|
|
124
133
|
end
|
|
125
134
|
|
|
126
135
|
it "does not modifies query part if no params were given" do
|
|
127
|
-
expect(HTTP::Request).to receive(:new) do |
|
|
128
|
-
expect(uri.query).to eq "deadbeef"
|
|
136
|
+
expect(HTTP::Request).to receive(:new) do |opts|
|
|
137
|
+
expect(opts[:uri].query).to eq "deadbeef"
|
|
129
138
|
end
|
|
130
139
|
|
|
131
140
|
client.get("http://example.com/?deadbeef")
|
|
132
141
|
end
|
|
133
142
|
|
|
134
143
|
it "does not corrupts index-less arrays" do
|
|
135
|
-
expect(HTTP::Request).to receive(:new) do |
|
|
136
|
-
expect(CGI.parse uri.query).to eq "a[]" => %w(b c), "d" => %w(e)
|
|
144
|
+
expect(HTTP::Request).to receive(:new) do |opts|
|
|
145
|
+
expect(CGI.parse opts[:uri].query).to eq "a[]" => %w(b c), "d" => %w(e)
|
|
137
146
|
end
|
|
138
147
|
|
|
139
148
|
client.get("http://example.com/?a[]=b&a[]=c", :params => {:d => "e"})
|
|
140
149
|
end
|
|
141
150
|
|
|
142
151
|
it "properly encodes colons" do
|
|
143
|
-
expect(HTTP::Request).to receive(:new) do |
|
|
144
|
-
expect(uri.query).to eq "t=1970-01-01T00%3A00%3A00Z"
|
|
152
|
+
expect(HTTP::Request).to receive(:new) do |opts|
|
|
153
|
+
expect(opts[:uri].query).to eq "t=1970-01-01T00%3A00%3A00Z"
|
|
145
154
|
end
|
|
146
155
|
|
|
147
156
|
client.get("http://example.com/", :params => {:t => "1970-01-01T00:00:00Z"})
|
|
@@ -153,8 +162,8 @@ RSpec.describe HTTP::Client do
|
|
|
153
162
|
client = HTTP::Client.new
|
|
154
163
|
allow(client).to receive(:perform)
|
|
155
164
|
|
|
156
|
-
expect(HTTP::Request).to receive(:new) do
|
|
157
|
-
expect(
|
|
165
|
+
expect(HTTP::Request).to receive(:new) do |opts|
|
|
166
|
+
expect(opts[:body]).to eq '{"foo":"bar"}'
|
|
158
167
|
end
|
|
159
168
|
|
|
160
169
|
client.get("http://example.com/", :json => {:foo => :bar})
|
|
@@ -53,8 +53,10 @@ RSpec.describe HTTP::Options, "merge" do
|
|
|
53
53
|
:proxy => {:proxy_address => "127.0.0.1", :proxy_port => 8080},
|
|
54
54
|
:follow => nil,
|
|
55
55
|
:socket_class => described_class.default_socket_class,
|
|
56
|
+
:nodelay => false,
|
|
56
57
|
:ssl_socket_class => described_class.default_ssl_socket_class,
|
|
57
58
|
:ssl_context => nil,
|
|
58
|
-
:cookies => {}
|
|
59
|
+
:cookies => {},
|
|
60
|
+
:encoding => nil)
|
|
59
61
|
end
|
|
60
62
|
end
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
RSpec.describe HTTP::Options do
|
|
2
2
|
subject { described_class.new(:response => :body) }
|
|
3
3
|
|
|
4
|
-
it "
|
|
5
|
-
expect(subject
|
|
6
|
-
expect(subject[:nosuchone]).to be nil
|
|
4
|
+
it "has reader methods for attributes" do
|
|
5
|
+
expect(subject.response).to eq(:body)
|
|
7
6
|
end
|
|
8
7
|
|
|
9
8
|
it "coerces to a Hash" do
|