httpx 0.13.0 → 0.14.2
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/doc/release_notes/0_10_1.md +1 -1
- data/doc/release_notes/0_13_0.md +2 -2
- data/doc/release_notes/0_13_1.md +5 -0
- data/doc/release_notes/0_13_2.md +9 -0
- data/doc/release_notes/0_14_0.md +79 -0
- data/doc/release_notes/0_14_1.md +7 -0
- data/doc/release_notes/0_14_2.md +6 -0
- data/lib/httpx.rb +1 -2
- data/lib/httpx/callbacks.rb +12 -3
- data/lib/httpx/connection.rb +12 -9
- data/lib/httpx/connection/http1.rb +32 -14
- data/lib/httpx/connection/http2.rb +61 -15
- data/lib/httpx/headers.rb +7 -3
- data/lib/httpx/io/tcp.rb +3 -1
- data/lib/httpx/io/udp.rb +31 -7
- data/lib/httpx/options.rb +91 -56
- data/lib/httpx/plugins/aws_sdk_authentication.rb +5 -2
- data/lib/httpx/plugins/aws_sigv4.rb +5 -4
- data/lib/httpx/plugins/basic_authentication.rb +8 -3
- data/lib/httpx/plugins/compression.rb +8 -8
- data/lib/httpx/plugins/compression/brotli.rb +4 -3
- data/lib/httpx/plugins/compression/deflate.rb +4 -3
- data/lib/httpx/plugins/compression/gzip.rb +2 -1
- data/lib/httpx/plugins/cookies.rb +3 -7
- data/lib/httpx/plugins/digest_authentication.rb +4 -4
- data/lib/httpx/plugins/expect.rb +6 -6
- data/lib/httpx/plugins/follow_redirects.rb +3 -3
- data/lib/httpx/plugins/grpc.rb +247 -0
- data/lib/httpx/plugins/grpc/call.rb +62 -0
- data/lib/httpx/plugins/grpc/message.rb +85 -0
- data/lib/httpx/plugins/multipart/part.rb +2 -2
- data/lib/httpx/plugins/proxy.rb +3 -7
- data/lib/httpx/plugins/proxy/http.rb +5 -4
- data/lib/httpx/plugins/proxy/ssh.rb +3 -3
- data/lib/httpx/plugins/rate_limiter.rb +1 -1
- data/lib/httpx/plugins/retries.rb +13 -14
- data/lib/httpx/plugins/stream.rb +96 -74
- data/lib/httpx/plugins/upgrade.rb +6 -5
- data/lib/httpx/request.rb +25 -2
- data/lib/httpx/resolver/native.rb +7 -3
- data/lib/httpx/response.rb +4 -0
- data/lib/httpx/session.rb +17 -7
- data/lib/httpx/transcoder/chunker.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/callbacks.rbs +2 -0
- data/sig/connection/http1.rbs +5 -1
- data/sig/connection/http2.rbs +6 -2
- data/sig/headers.rbs +2 -2
- data/sig/options.rbs +9 -2
- data/sig/plugins/aws_sdk_authentication.rbs +2 -0
- data/sig/plugins/basic_authentication.rbs +2 -0
- data/sig/plugins/compression.rbs +2 -2
- data/sig/plugins/multipart.rbs +1 -1
- data/sig/plugins/stream.rbs +17 -16
- data/sig/request.rbs +7 -2
- data/sig/response.rbs +1 -0
- data/sig/session.rbs +4 -0
- metadata +18 -7
- data/lib/httpx/timeout.rb +0 -67
- data/sig/timeout.rbs +0 -29
@@ -19,14 +19,14 @@ module HTTPX
|
|
19
19
|
value = value[:body]
|
20
20
|
end
|
21
21
|
|
22
|
-
value = value.open(:binmode => true) if value.is_a?(Pathname)
|
22
|
+
value = value.open(:binmode => true) if Object.const_defined?(:Pathname) && value.is_a?(Pathname)
|
23
23
|
|
24
24
|
if value.is_a?(File)
|
25
25
|
filename ||= File.basename(value.path)
|
26
26
|
content_type ||= MimeTypeDetector.call(value, filename) || "application/octet-stream"
|
27
27
|
[value, content_type, filename]
|
28
28
|
else
|
29
|
-
[StringIO.new(value.to_s), "text/plain"]
|
29
|
+
[StringIO.new(value.to_s), content_type || "text/plain", filename]
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
data/lib/httpx/plugins/proxy.rb
CHANGED
@@ -67,13 +67,9 @@ module HTTPX
|
|
67
67
|
|
68
68
|
def extra_options(options)
|
69
69
|
Class.new(options.class) do
|
70
|
-
def_option(:proxy)
|
71
|
-
|
72
|
-
|
73
|
-
else
|
74
|
-
Hash[pr]
|
75
|
-
end
|
76
|
-
end
|
70
|
+
def_option(:proxy, <<-OUT)
|
71
|
+
value.is_a?(#{Parameters}) ? value : Hash[value]
|
72
|
+
OUT
|
77
73
|
end.new(options)
|
78
74
|
end
|
79
75
|
end
|
@@ -82,11 +82,12 @@ module HTTPX
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def set_protocol_headers(request)
|
85
|
-
super
|
85
|
+
extra_headers = super
|
86
|
+
|
86
87
|
proxy_params = @options.proxy
|
87
|
-
|
88
|
-
|
89
|
-
|
88
|
+
extra_headers["proxy-authorization"] = "Basic #{proxy_params.token_authentication}" if proxy_params.authenticated?
|
89
|
+
extra_headers["proxy-connection"] = extra_headers.delete("connection") if extra_headers.key?("connection")
|
90
|
+
extra_headers
|
90
91
|
end
|
91
92
|
end
|
92
93
|
|
@@ -25,31 +25,30 @@ module HTTPX
|
|
25
25
|
|
26
26
|
def self.extra_options(options)
|
27
27
|
Class.new(options.class) do
|
28
|
-
|
29
|
-
def_option(:retry_after) do |num|
|
28
|
+
def_option(:retry_after, <<-OUT)
|
30
29
|
# return early if callable
|
31
|
-
unless
|
32
|
-
|
33
|
-
raise Error, ":retry_after must be positive" unless
|
30
|
+
unless value.respond_to?(:call)
|
31
|
+
value = Integer(value)
|
32
|
+
raise Error, ":retry_after must be positive" unless value.positive?
|
34
33
|
end
|
35
34
|
|
36
|
-
|
37
|
-
|
35
|
+
value
|
36
|
+
OUT
|
38
37
|
|
39
|
-
def_option(:max_retries)
|
40
|
-
num = Integer(
|
38
|
+
def_option(:max_retries, <<-OUT)
|
39
|
+
num = Integer(value)
|
41
40
|
raise Error, ":max_retries must be positive" unless num.positive?
|
42
41
|
|
43
42
|
num
|
44
|
-
|
43
|
+
OUT
|
45
44
|
|
46
45
|
def_option(:retry_change_requests)
|
47
46
|
|
48
|
-
def_option(:retry_on)
|
49
|
-
raise ":retry_on must be called with the response" unless
|
47
|
+
def_option(:retry_on, <<-OUT)
|
48
|
+
raise ":retry_on must be called with the response" unless value.respond_to?(:call)
|
50
49
|
|
51
|
-
|
52
|
-
|
50
|
+
value
|
51
|
+
OUT
|
53
52
|
end.new(options).merge(max_retries: MAX_RETRIES)
|
54
53
|
end
|
55
54
|
|
data/lib/httpx/plugins/stream.rb
CHANGED
@@ -1,6 +1,93 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module HTTPX
|
4
|
+
class StreamResponse
|
5
|
+
def initialize(request, session, connections)
|
6
|
+
@request = request
|
7
|
+
@session = session
|
8
|
+
@connections = connections
|
9
|
+
@options = @request.options
|
10
|
+
end
|
11
|
+
|
12
|
+
def each(&block)
|
13
|
+
return enum_for(__method__) unless block_given?
|
14
|
+
|
15
|
+
raise Error, "response already streamed" if @response
|
16
|
+
|
17
|
+
@request.stream = self
|
18
|
+
|
19
|
+
begin
|
20
|
+
@on_chunk = block
|
21
|
+
|
22
|
+
if @request.response
|
23
|
+
# if we've already started collecting the payload, yield it first
|
24
|
+
# before proceeding
|
25
|
+
body = @request.response.body
|
26
|
+
|
27
|
+
body.each do |chunk|
|
28
|
+
on_chunk(chunk)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
response.raise_for_status
|
33
|
+
response.close
|
34
|
+
ensure
|
35
|
+
@on_chunk = nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def each_line
|
40
|
+
return enum_for(__method__) unless block_given?
|
41
|
+
|
42
|
+
line = +""
|
43
|
+
|
44
|
+
each do |chunk|
|
45
|
+
line << chunk
|
46
|
+
|
47
|
+
while (idx = line.index("\n"))
|
48
|
+
yield line.byteslice(0..idx - 1)
|
49
|
+
|
50
|
+
line = line.byteslice(idx + 1..-1)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# This is a ghost method. It's to be used ONLY internally, when processing streams
|
56
|
+
def on_chunk(chunk)
|
57
|
+
raise NoMethodError unless @on_chunk
|
58
|
+
|
59
|
+
@on_chunk.call(chunk)
|
60
|
+
end
|
61
|
+
|
62
|
+
# :nocov:
|
63
|
+
def inspect
|
64
|
+
"#<StreamResponse:#{object_id}>"
|
65
|
+
end
|
66
|
+
# :nocov:
|
67
|
+
|
68
|
+
def to_s
|
69
|
+
response.to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def response
|
75
|
+
@session.__send__(:receive_requests, [@request], @connections, @options) until @request.response
|
76
|
+
|
77
|
+
@request.response
|
78
|
+
end
|
79
|
+
|
80
|
+
def respond_to_missing?(meth, *args)
|
81
|
+
response.respond_to?(meth, *args) || super
|
82
|
+
end
|
83
|
+
|
84
|
+
def method_missing(meth, *args, &block)
|
85
|
+
return super unless response.respond_to?(meth)
|
86
|
+
|
87
|
+
response.__send__(meth, *args, &block)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
4
91
|
module Plugins
|
5
92
|
#
|
6
93
|
# This plugin adds support for stream response (text/event-stream).
|
@@ -15,10 +102,13 @@ module HTTPX
|
|
15
102
|
return super(*args, **options) unless stream
|
16
103
|
|
17
104
|
requests = args.first.is_a?(Request) ? args : build_requests(*args, options)
|
18
|
-
|
19
105
|
raise Error, "only 1 response at a time is supported for streaming requests" unless requests.size == 1
|
20
106
|
|
21
|
-
|
107
|
+
request = requests.first
|
108
|
+
|
109
|
+
connections = _send_requests(requests, request.options)
|
110
|
+
|
111
|
+
StreamResponse.new(request, self, connections)
|
22
112
|
end
|
23
113
|
end
|
24
114
|
|
@@ -53,78 +143,10 @@ module HTTPX
|
|
53
143
|
end
|
54
144
|
end
|
55
145
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
@options = @request.options
|
61
|
-
end
|
62
|
-
|
63
|
-
def each(&block)
|
64
|
-
return enum_for(__method__) unless block_given?
|
65
|
-
|
66
|
-
raise Error, "response already streamed" if @response
|
67
|
-
|
68
|
-
@request.stream = self
|
69
|
-
|
70
|
-
begin
|
71
|
-
@on_chunk = block
|
72
|
-
|
73
|
-
response.raise_for_status
|
74
|
-
response.close
|
75
|
-
ensure
|
76
|
-
@on_chunk = nil
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def each_line
|
81
|
-
return enum_for(__method__) unless block_given?
|
82
|
-
|
83
|
-
line = +""
|
84
|
-
|
85
|
-
each do |chunk|
|
86
|
-
line << chunk
|
87
|
-
|
88
|
-
while (idx = line.index("\n"))
|
89
|
-
yield line.byteslice(0..idx - 1)
|
90
|
-
|
91
|
-
line = line.byteslice(idx + 1..-1)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
# This is a ghost method. It's to be used ONLY internally, when processing streams
|
97
|
-
def on_chunk(chunk)
|
98
|
-
raise NoMethodError unless @on_chunk
|
99
|
-
|
100
|
-
@on_chunk.call(chunk)
|
101
|
-
end
|
102
|
-
|
103
|
-
# :nocov:
|
104
|
-
def inspect
|
105
|
-
"#<StreamResponse:#{object_id}>"
|
106
|
-
end
|
107
|
-
# :nocov:
|
108
|
-
|
109
|
-
def to_s
|
110
|
-
response.to_s
|
111
|
-
end
|
112
|
-
|
113
|
-
private
|
114
|
-
|
115
|
-
def response
|
116
|
-
@response ||= @session.__send__(:send_requests, @request, @options).first
|
117
|
-
end
|
118
|
-
|
119
|
-
def respond_to_missing?(*args)
|
120
|
-
@options.response_class.respond_to?(*args) || super
|
121
|
-
end
|
122
|
-
|
123
|
-
def method_missing(meth, *args, &block)
|
124
|
-
return super unless @options.response_class.public_method_defined?(meth)
|
125
|
-
|
126
|
-
response.__send__(meth, *args, &block)
|
127
|
-
end
|
146
|
+
def self.const_missing(const_name)
|
147
|
+
super unless const_name == :StreamResponse
|
148
|
+
warn "DEPRECATION WARNING: the class #{self}::StreamResponse is deprecated. Use HTTPX::StreamResponse instead."
|
149
|
+
HTTPX::StreamResponse
|
128
150
|
end
|
129
151
|
end
|
130
152
|
register_plugin :stream, Stream
|
@@ -20,11 +20,11 @@ module HTTPX
|
|
20
20
|
end
|
21
21
|
|
22
22
|
Class.new(options.class) do
|
23
|
-
def_option(:upgrade_handlers)
|
24
|
-
raise Error, ":upgrade_handlers must be a registry" unless
|
23
|
+
def_option(:upgrade_handlers, <<-OUT)
|
24
|
+
raise Error, ":upgrade_handlers must be a registry" unless value.respond_to?(:registry)
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
value
|
27
|
+
OUT
|
28
28
|
end.new(options).merge(upgrade_handlers: upgrade_handlers)
|
29
29
|
end
|
30
30
|
end
|
@@ -33,7 +33,8 @@ module HTTPX
|
|
33
33
|
def fetch_response(request, connections, options)
|
34
34
|
response = super
|
35
35
|
|
36
|
-
if response
|
36
|
+
if response
|
37
|
+
return response unless response.respond_to?(:headers) && response.headers.key?("upgrade")
|
37
38
|
|
38
39
|
upgrade_protocol = response.headers["upgrade"].split(/ *, */).first
|
39
40
|
|
data/lib/httpx/request.rb
CHANGED
@@ -35,14 +35,22 @@ module HTTPX
|
|
35
35
|
|
36
36
|
attr_reader :verb, :uri, :headers, :body, :state, :options, :response
|
37
37
|
|
38
|
+
# Exception raised during enumerable body writes
|
39
|
+
attr_reader :drain_error
|
40
|
+
|
38
41
|
def_delegator :@body, :empty?
|
39
42
|
|
40
43
|
def_delegator :@body, :chunk!
|
41
44
|
|
42
45
|
def initialize(verb, uri, options = {})
|
43
46
|
@verb = verb.to_s.downcase.to_sym
|
44
|
-
@uri = Utils.uri(uri)
|
45
47
|
@options = Options.new(options)
|
48
|
+
@uri = Utils.uri(uri)
|
49
|
+
if @uri.relative?
|
50
|
+
raise(Error, "invalid URI: #{@uri}") unless @options.origin
|
51
|
+
|
52
|
+
@uri = @options.origin.merge(@uri)
|
53
|
+
end
|
46
54
|
|
47
55
|
raise(Error, "unknown method: #{verb}") unless METHODS.include?(@verb)
|
48
56
|
|
@@ -54,6 +62,14 @@ module HTTPX
|
|
54
62
|
@state = :idle
|
55
63
|
end
|
56
64
|
|
65
|
+
def trailers?
|
66
|
+
defined?(@trailers)
|
67
|
+
end
|
68
|
+
|
69
|
+
def trailers
|
70
|
+
@trailers ||= @options.headers_class.new
|
71
|
+
end
|
72
|
+
|
57
73
|
def interests
|
58
74
|
return :r if @state == :done || @state == :expect
|
59
75
|
|
@@ -124,6 +140,9 @@ module HTTPX
|
|
124
140
|
chunk.dup
|
125
141
|
rescue StopIteration
|
126
142
|
nil
|
143
|
+
rescue StandardError => e
|
144
|
+
@drain_error = e
|
145
|
+
nil
|
127
146
|
end
|
128
147
|
|
129
148
|
# :nocov:
|
@@ -200,7 +219,9 @@ module HTTPX
|
|
200
219
|
end
|
201
220
|
|
202
221
|
def unbounded_body?
|
203
|
-
|
222
|
+
return @unbounded_body if defined?(@unbounded_body)
|
223
|
+
|
224
|
+
@unbounded_body = (chunked? || @body.bytesize == Float::INFINITY)
|
204
225
|
end
|
205
226
|
|
206
227
|
def chunked?
|
@@ -253,6 +274,8 @@ module HTTPX
|
|
253
274
|
nextstate = :expect
|
254
275
|
end
|
255
276
|
end
|
277
|
+
when :trailers
|
278
|
+
return unless @state == :body
|
256
279
|
when :done
|
257
280
|
return if @state == :expect
|
258
281
|
end
|
@@ -101,7 +101,7 @@ module HTTPX
|
|
101
101
|
transition(:open)
|
102
102
|
end
|
103
103
|
|
104
|
-
|
104
|
+
calculate_interests
|
105
105
|
end
|
106
106
|
|
107
107
|
def <<(connection)
|
@@ -127,10 +127,14 @@ module HTTPX
|
|
127
127
|
|
128
128
|
private
|
129
129
|
|
130
|
+
def calculate_interests
|
131
|
+
!@write_buffer.empty? || @queries.empty? ? :w : :r
|
132
|
+
end
|
133
|
+
|
130
134
|
def consume
|
131
|
-
dread
|
135
|
+
dread if calculate_interests == :r
|
132
136
|
do_retry
|
133
|
-
dwrite
|
137
|
+
dwrite if calculate_interests == :w
|
134
138
|
end
|
135
139
|
|
136
140
|
def do_retry
|