httpx 0.0.3 → 0.0.4
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/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
|