httpx 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -2
- data/lib/httpx/channel.rb +13 -2
- data/lib/httpx/channel/http1.rb +22 -19
- data/lib/httpx/channel/http2.rb +14 -16
- data/lib/httpx/client.rb +4 -2
- data/lib/httpx/errors.rb +8 -4
- data/lib/httpx/headers.rb +5 -5
- data/lib/httpx/io.rb +1 -1
- data/lib/httpx/loggable.rb +15 -2
- data/lib/httpx/plugins/cookies.rb +2 -2
- data/lib/httpx/plugins/proxy/socks4.rb +1 -1
- data/lib/httpx/plugins/proxy/socks5.rb +1 -1
- data/lib/httpx/plugins/push_promise.rb +1 -1
- data/lib/httpx/response.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6dac1e1e43e2c1935f8fd0c1807d1d29bf782f27
|
4
|
+
data.tar.gz: e5786862d65ee9807f31e9ca847d8ca9125dce29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a5f4106511049f7dc22c6a44aee01cf560827b553b05aede40e5ff11fb20d52213d754488cec4419a895c9a7eef9c77517242f4553ee0d1b797cfe1cbc4726c
|
7
|
+
data.tar.gz: a2390e810c8d6738a9d13fce36bc0cbfcd9e4a7eeb88e5d7a9919a8311dc61404cb3328569836eff86cbabe33b63daef9107d9fe3938136af06b6c78b57f953d
|
data/README.md
CHANGED
@@ -92,14 +92,15 @@ It also means that it ships with the minimum amount of dependencies.
|
|
92
92
|
|
93
93
|
## Easy to test
|
94
94
|
|
95
|
-
The test suite runs against [httpbin proxied over nghttp2](https://nghttp2.org/httpbin/), so there
|
95
|
+
The test suite runs against [httpbin proxied over nghttp2](https://nghttp2.org/httpbin/), so there are no mocking/stubbing false positives. The test suite uses [minitest](https://github.com/seattlerb/minitest), but its matchers usage is (almost) limited to `#assert` (`assert` is all you need).
|
96
96
|
|
97
97
|
## Supported Rubies
|
98
98
|
|
99
99
|
All Rubies greater or equal to 2.1, and always latest JRuby.
|
100
100
|
|
101
101
|
## Resources
|
102
|
-
|
102
|
+
| | |
|
103
|
+
| ------------- | --------------------------------------------------- |
|
103
104
|
| Website | https://honeyryderchuck.gitlab.io/httpx/ |
|
104
105
|
| Documentation | https://honeyryderchuck.gitlab.io/httpx/rdoc/ |
|
105
106
|
| Wiki | https://gitlab.com/honeyryderchuck/httpx/wikis/home |
|
data/lib/httpx/channel.rb
CHANGED
@@ -196,6 +196,14 @@ module HTTPX
|
|
196
196
|
parser.on(:close) do
|
197
197
|
transition(:closing)
|
198
198
|
end
|
199
|
+
parser.on(:reset) do
|
200
|
+
transition(:closing)
|
201
|
+
unless parser.empty?
|
202
|
+
transition(:closed)
|
203
|
+
transition(:idle)
|
204
|
+
transition(:open)
|
205
|
+
end
|
206
|
+
end
|
199
207
|
parser
|
200
208
|
end
|
201
209
|
|
@@ -218,7 +226,10 @@ module HTTPX
|
|
218
226
|
end
|
219
227
|
@state = nextstate
|
220
228
|
rescue Errno::ECONNREFUSED,
|
221
|
-
Errno::
|
229
|
+
Errno::ENETUNREACH,
|
230
|
+
Errno::EADDRNOTAVAIL,
|
231
|
+
OpenSSL::SSL::SSLError => e
|
232
|
+
# connect errors, exit gracefully
|
222
233
|
emit_error(e)
|
223
234
|
@state = :closed
|
224
235
|
emit(:close)
|
@@ -226,7 +237,7 @@ module HTTPX
|
|
226
237
|
|
227
238
|
def emit_error(e)
|
228
239
|
response = ErrorResponse.new(e, 0, @options)
|
229
|
-
@pending.each do |request, _|
|
240
|
+
@pending.each do |request, _|
|
230
241
|
emit(:response, request, response)
|
231
242
|
end
|
232
243
|
end
|
data/lib/httpx/channel/http1.rb
CHANGED
@@ -71,7 +71,7 @@ module HTTPX
|
|
71
71
|
# must be public methods, or else they won't be reachable
|
72
72
|
|
73
73
|
def on_message_begin
|
74
|
-
log(2) { "parsing begins" }
|
74
|
+
log(level: 2) { "parsing begins" }
|
75
75
|
end
|
76
76
|
|
77
77
|
def on_headers_complete(h)
|
@@ -81,14 +81,14 @@ module HTTPX
|
|
81
81
|
request = @requests.first
|
82
82
|
return if request.response
|
83
83
|
|
84
|
-
log(2) { "headers received" }
|
84
|
+
log(level: 2) { "headers received" }
|
85
85
|
headers = @options.headers_class.new(h)
|
86
86
|
response = @options.response_class.new(@requests.last,
|
87
87
|
@parser.status_code,
|
88
88
|
@parser.http_version.join("."),
|
89
89
|
headers, @options)
|
90
|
-
log { "-> HEADLINE: #{response.status} HTTP/#{@parser.http_version.join(".")}" }
|
91
|
-
log { response.headers.each.map { |f, v| "-> HEADER: #{f}: #{v}" }.join("\n") }
|
90
|
+
log(color: :yellow) { "-> HEADLINE: #{response.status} HTTP/#{@parser.http_version.join(".")}" }
|
91
|
+
log(color: :yellow) { response.headers.each.map { |f, v| "-> HEADER: #{f}: #{v}" }.join("\n") }
|
92
92
|
|
93
93
|
request.response = response
|
94
94
|
|
@@ -96,8 +96,8 @@ module HTTPX
|
|
96
96
|
end
|
97
97
|
|
98
98
|
def on_body(chunk)
|
99
|
-
log { "-> DATA: #{chunk.bytesize} bytes..." }
|
100
|
-
log(2) { "-> #{chunk.inspect}" }
|
99
|
+
log(color: :green) { "-> DATA: #{chunk.bytesize} bytes..." }
|
100
|
+
log(level: 2, color: :green) { "-> #{chunk.inspect}" }
|
101
101
|
response = @requests.first.response
|
102
102
|
|
103
103
|
response << chunk
|
@@ -106,7 +106,7 @@ module HTTPX
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def on_message_complete
|
109
|
-
log(2) { "parsing complete" }
|
109
|
+
log(level: 2) { "parsing complete" }
|
110
110
|
request = @requests.first
|
111
111
|
response = request.response
|
112
112
|
|
@@ -144,18 +144,21 @@ module HTTPX
|
|
144
144
|
close
|
145
145
|
send(@pending.shift) unless @pending.empty?
|
146
146
|
return unless response.headers["connection"] == "close"
|
147
|
-
|
148
|
-
|
149
|
-
# server doesn't handle pipelining, and probably
|
150
|
-
# doesn't support keep-alive. Fallback to send only
|
151
|
-
# 1 keep alive request.
|
152
|
-
@max_concurrent_requests = 1
|
153
|
-
end
|
154
|
-
emit(:close)
|
147
|
+
disable_concurrency
|
148
|
+
emit(:reset)
|
155
149
|
end
|
156
150
|
|
157
151
|
private
|
158
152
|
|
153
|
+
def disable_concurrency
|
154
|
+
return if @requests.empty?
|
155
|
+
@requests.each { |r| r.transition(:idle) }
|
156
|
+
# server doesn't handle pipelining, and probably
|
157
|
+
# doesn't support keep-alive. Fallback to send only
|
158
|
+
# 1 keep alive request.
|
159
|
+
@max_concurrent_requests = 1
|
160
|
+
end
|
161
|
+
|
159
162
|
def set_request_headers(request)
|
160
163
|
request.headers["host"] ||= request.authority
|
161
164
|
request.headers["connection"] ||= "keep-alive"
|
@@ -184,12 +187,12 @@ module HTTPX
|
|
184
187
|
def join_headers(request)
|
185
188
|
buffer = +""
|
186
189
|
buffer << "#{request.verb.to_s.upcase} #{headline_uri(request)} HTTP/#{@version.join(".")}" << CRLF
|
187
|
-
log { "<- HEADLINE: #{buffer.chomp.inspect}" }
|
190
|
+
log(color: :yellow) { "<- HEADLINE: #{buffer.chomp.inspect}" }
|
188
191
|
@buffer << buffer
|
189
192
|
buffer.clear
|
190
193
|
request.headers.each do |field, value|
|
191
194
|
buffer << "#{capitalized(field)}: #{value}" << CRLF
|
192
|
-
log { "<- HEADER: #{buffer.chomp}" }
|
195
|
+
log(color: :yellow) { "<- HEADER: #{buffer.chomp}" }
|
193
196
|
@buffer << buffer
|
194
197
|
buffer.clear
|
195
198
|
end
|
@@ -200,8 +203,8 @@ module HTTPX
|
|
200
203
|
def join_body(request)
|
201
204
|
return if request.empty?
|
202
205
|
while (chunk = request.drain_body)
|
203
|
-
log { "<- DATA: #{chunk.bytesize} bytes..." }
|
204
|
-
log(2) { "<- #{chunk.inspect}" }
|
206
|
+
log(color: :green) { "<- DATA: #{chunk.bytesize} bytes..." }
|
207
|
+
log(level: 2, color: :green) { "<- #{chunk.inspect}" }
|
205
208
|
@buffer << chunk
|
206
209
|
throw(:buffer_full, request) if @buffer.full?
|
207
210
|
end
|
data/lib/httpx/channel/http2.rb
CHANGED
@@ -92,7 +92,7 @@ module HTTPX
|
|
92
92
|
def handle_stream(stream, request)
|
93
93
|
stream.on(:close, &method(:on_stream_close).curry[stream, request])
|
94
94
|
stream.on(:half_close) do
|
95
|
-
log(2, "#{stream.id}: ") { "waiting for response..." }
|
95
|
+
log(level: 2, label: "#{stream.id}: ") { "waiting for response..." }
|
96
96
|
end
|
97
97
|
# stream.on(:altsvc)
|
98
98
|
stream.on(:headers, &method(:on_stream_headers).curry[stream, request])
|
@@ -107,7 +107,7 @@ module HTTPX
|
|
107
107
|
headers[":path"] = headline_uri(request)
|
108
108
|
headers[":authority"] = request.authority
|
109
109
|
headers = headers.merge(request.headers)
|
110
|
-
log(1, "#{stream.id}: ") do
|
110
|
+
log(level: 1, label: "#{stream.id}: ", color: :yellow) do
|
111
111
|
headers.map { |k, v| "-> HEADER: #{k}: #{v}" }.join("\n")
|
112
112
|
end
|
113
113
|
stream.headers(headers, end_stream: request.empty?)
|
@@ -117,8 +117,8 @@ module HTTPX
|
|
117
117
|
chunk = @drains.delete(request) || request.drain_body
|
118
118
|
while chunk
|
119
119
|
next_chunk = request.drain_body
|
120
|
-
log(1, "#{stream.id}: ") { "-> DATA: #{chunk.bytesize} bytes..." }
|
121
|
-
log(2, "#{stream.id}: ") { "-> #{chunk.inspect}" }
|
120
|
+
log(level: 1, label: "#{stream.id}: ", color: :green) { "-> DATA: #{chunk.bytesize} bytes..." }
|
121
|
+
log(level: 2, label: "#{stream.id}: ", color: :green) { "-> #{chunk.inspect}" }
|
122
122
|
stream.data(chunk, end_stream: !next_chunk)
|
123
123
|
if next_chunk && @buffer.full?
|
124
124
|
@drains[request] = next_chunk
|
@@ -133,7 +133,7 @@ module HTTPX
|
|
133
133
|
######
|
134
134
|
|
135
135
|
def on_stream_headers(stream, request, h)
|
136
|
-
log(stream.id) do
|
136
|
+
log(label: "#{stream.id}:", color: :yellow) do
|
137
137
|
h.map { |k, v| "<- HEADER: #{k}: #{v}" }.join("\n")
|
138
138
|
end
|
139
139
|
_, status = h.shift
|
@@ -144,8 +144,8 @@ module HTTPX
|
|
144
144
|
end
|
145
145
|
|
146
146
|
def on_stream_data(stream, request, data)
|
147
|
-
log(1, "#{stream.id}: ") { "<- DATA: #{data.bytesize} bytes..." }
|
148
|
-
log(2, "#{stream.id}: ") { "<- #{data.inspect}" }
|
147
|
+
log(level: 1, label: "#{stream.id}: ", color: :green) { "<- DATA: #{data.bytesize} bytes..." }
|
148
|
+
log(level: 2, label: "#{stream.id}: ", color: :green) { "<- #{data.inspect}" }
|
149
149
|
request.response << data
|
150
150
|
end
|
151
151
|
|
@@ -153,7 +153,7 @@ module HTTPX
|
|
153
153
|
return handle(request, stream) if request.expects?
|
154
154
|
response = request.response || ErrorResponse.new(Error.new(error), @retries, @options)
|
155
155
|
emit(:response, request, response)
|
156
|
-
log(2, "#{stream.id}: ") { "closing stream" }
|
156
|
+
log(level: 2, label: "#{stream.id}: ") { "closing stream" }
|
157
157
|
|
158
158
|
@streams.delete(request)
|
159
159
|
send(@pending.shift) unless @pending.empty?
|
@@ -174,13 +174,11 @@ module HTTPX
|
|
174
174
|
end
|
175
175
|
|
176
176
|
def on_frame_sent(frame)
|
177
|
-
log(2, "#{frame[:stream]}: ") { "frame was sent!" }
|
178
|
-
log(2, "#{frame[:stream]}: ") do
|
177
|
+
log(level: 2, label: "#{frame[:stream]}: ") { "frame was sent!" }
|
178
|
+
log(level: 2, label: "#{frame[:stream]}: ", color: :blue) do
|
179
179
|
case frame[:type]
|
180
180
|
when :data
|
181
181
|
frame.merge(payload: frame[:payload].bytesize).inspect
|
182
|
-
when :headers
|
183
|
-
"\e[33m#{frame.inspect}\e[0m"
|
184
182
|
else
|
185
183
|
frame.inspect
|
186
184
|
end
|
@@ -188,8 +186,8 @@ module HTTPX
|
|
188
186
|
end
|
189
187
|
|
190
188
|
def on_frame_received(frame)
|
191
|
-
log(2, "#{frame[:stream]}: ") { "frame was received!" }
|
192
|
-
log(2, "#{frame[:stream]}: ") do
|
189
|
+
log(level: 2, label: "#{frame[:stream]}: ") { "frame was received!" }
|
190
|
+
log(level: 2, label: "#{frame[:stream]}: ", color: :magenta) do
|
193
191
|
case frame[:type]
|
194
192
|
when :data
|
195
193
|
frame.merge(payload: frame[:payload].bytesize).inspect
|
@@ -200,8 +198,8 @@ module HTTPX
|
|
200
198
|
end
|
201
199
|
|
202
200
|
def on_altsvc(frame)
|
203
|
-
log(2, "#{frame[:stream]}: ") { "altsvc frame was received" }
|
204
|
-
log(2, "#{frame[:stream]}: ") { frame.inspect }
|
201
|
+
log(level: 2, label: "#{frame[:stream]}: ") { "altsvc frame was received" }
|
202
|
+
log(level: 2, label: "#{frame[:stream]}: ") { frame.inspect }
|
205
203
|
end
|
206
204
|
|
207
205
|
def on_promise(stream)
|
data/lib/httpx/client.rb
CHANGED
@@ -9,16 +9,18 @@ module HTTPX
|
|
9
9
|
@options = self.class.default_options.merge(options)
|
10
10
|
@connection = Connection.new(@options)
|
11
11
|
@responses = {}
|
12
|
+
@keep_open = false
|
12
13
|
wrap(&blk) if block_given?
|
13
14
|
end
|
14
15
|
|
15
16
|
def wrap
|
16
17
|
return unless block_given?
|
17
18
|
begin
|
19
|
+
prev_keep_open = @keep_open
|
18
20
|
@keep_open = true
|
19
21
|
yield self
|
20
22
|
ensure
|
21
|
-
@keep_open =
|
23
|
+
@keep_open = prev_keep_open
|
22
24
|
close
|
23
25
|
end
|
24
26
|
end
|
@@ -43,7 +45,7 @@ module HTTPX
|
|
43
45
|
end
|
44
46
|
|
45
47
|
def on_promise(_, stream)
|
46
|
-
log(2, "#{stream.id}: ") { "refusing stream!" }
|
48
|
+
log(level: 2, label: "#{stream.id}: ") { "refusing stream!" }
|
47
49
|
stream.refuse
|
48
50
|
# TODO: policy for handling promises
|
49
51
|
end
|
data/lib/httpx/errors.rb
CHANGED
@@ -6,11 +6,15 @@ module HTTPX
|
|
6
6
|
TimeoutError = Class.new(Error)
|
7
7
|
|
8
8
|
HTTPError = Class.new(Error) do
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :response
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
@
|
13
|
-
super("HTTP Error: #{status}")
|
11
|
+
def initialize(response)
|
12
|
+
@response = response
|
13
|
+
super("HTTP Error: #{@response.status}")
|
14
|
+
end
|
15
|
+
|
16
|
+
def status
|
17
|
+
@response.status
|
14
18
|
end
|
15
19
|
end
|
16
20
|
end
|
data/lib/httpx/headers.rb
CHANGED
@@ -5,16 +5,16 @@ module HTTPX
|
|
5
5
|
EMPTY = [].freeze # :nodoc:
|
6
6
|
|
7
7
|
class << self
|
8
|
-
def new(
|
9
|
-
return
|
8
|
+
def new(headers = nil)
|
9
|
+
return headers if headers.is_a?(self)
|
10
10
|
super
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
def initialize(
|
14
|
+
def initialize(headers = nil)
|
15
15
|
@headers = {}
|
16
|
-
return unless
|
17
|
-
|
16
|
+
return unless headers
|
17
|
+
headers.each do |field, value|
|
18
18
|
array_value(value).each do |v|
|
19
19
|
add(downcased(field), v)
|
20
20
|
end
|
data/lib/httpx/io.rb
CHANGED
data/lib/httpx/loggable.rb
CHANGED
@@ -2,10 +2,23 @@
|
|
2
2
|
|
3
3
|
module HTTPX
|
4
4
|
module Loggable
|
5
|
-
|
5
|
+
COLORS = {
|
6
|
+
black: 30,
|
7
|
+
red: 31,
|
8
|
+
green: 32,
|
9
|
+
yellow: 33,
|
10
|
+
blue: 34,
|
11
|
+
magenta: 35,
|
12
|
+
cyan: 36,
|
13
|
+
white: 37,
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def log(level: @options.debug_level, label: "", color: nil, &msg)
|
6
17
|
return unless @options.debug
|
7
18
|
return unless @options.debug_level >= level
|
8
|
-
|
19
|
+
message = (+label << msg.call << "\n")
|
20
|
+
message = "\e[#{COLORS[color]}m#{message}\e[0m" if color && @options.debug.isatty
|
21
|
+
@options.debug << message
|
9
22
|
end
|
10
23
|
end
|
11
24
|
end
|
@@ -37,9 +37,9 @@ module HTTPX
|
|
37
37
|
|
38
38
|
module ResponseMethods
|
39
39
|
def cookie_jar
|
40
|
-
return @
|
40
|
+
return @cookie_jar if defined?(@cookie_jar)
|
41
41
|
return nil unless headers.key?("set-cookie")
|
42
|
-
@
|
42
|
+
@cookie_jar ||= begin
|
43
43
|
jar = HTTP::CookieJar.new
|
44
44
|
jar.parse(headers["set-cookie"], @request.uri)
|
45
45
|
jar
|
data/lib/httpx/response.rb
CHANGED
data/lib/httpx/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httpx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Cardoso
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-03-
|
11
|
+
date: 2018-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http-2
|