httpx 0.20.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +0 -48
- data/README.md +54 -45
- data/doc/release_notes/0_10_0.md +2 -2
- data/doc/release_notes/0_11_0.md +3 -5
- data/doc/release_notes/0_12_0.md +5 -5
- data/doc/release_notes/0_13_0.md +5 -5
- data/doc/release_notes/0_14_0.md +2 -2
- data/doc/release_notes/0_16_0.md +3 -3
- data/doc/release_notes/0_17_0.md +1 -1
- data/doc/release_notes/0_18_0.md +4 -4
- data/doc/release_notes/0_18_2.md +1 -1
- data/doc/release_notes/0_19_0.md +1 -1
- data/doc/release_notes/0_19_8.md +1 -1
- data/doc/release_notes/0_20_0.md +2 -2
- data/doc/release_notes/0_20_1.md +5 -0
- data/doc/release_notes/0_20_2.md +7 -0
- data/doc/release_notes/0_20_3.md +6 -0
- data/doc/release_notes/0_20_4.md +17 -0
- data/doc/release_notes/0_20_5.md +3 -0
- data/doc/release_notes/0_21_0.md +96 -0
- data/doc/release_notes/0_21_1.md +12 -0
- data/doc/release_notes/0_22_0.md +13 -0
- data/doc/release_notes/0_22_1.md +11 -0
- data/doc/release_notes/0_22_2.md +5 -0
- data/doc/release_notes/0_22_3.md +55 -0
- data/doc/release_notes/0_22_4.md +6 -0
- data/doc/release_notes/0_22_5.md +6 -0
- data/doc/release_notes/0_23_0.md +42 -0
- data/doc/release_notes/0_23_1.md +5 -0
- data/doc/release_notes/0_23_2.md +5 -0
- data/doc/release_notes/0_23_3.md +6 -0
- data/doc/release_notes/0_23_4.md +5 -0
- data/doc/release_notes/0_24_0.md +48 -0
- data/doc/release_notes/0_24_1.md +12 -0
- data/doc/release_notes/0_24_2.md +12 -0
- data/doc/release_notes/0_24_3.md +12 -0
- data/doc/release_notes/0_24_4.md +18 -0
- data/doc/release_notes/0_24_5.md +6 -0
- data/doc/release_notes/0_24_6.md +5 -0
- data/doc/release_notes/0_24_7.md +10 -0
- data/doc/release_notes/1_0_0.md +60 -0
- data/doc/release_notes/1_0_1.md +5 -0
- data/doc/release_notes/1_0_2.md +7 -0
- data/doc/release_notes/1_1_0.md +32 -0
- data/doc/release_notes/1_1_1.md +17 -0
- data/doc/release_notes/1_1_2.md +12 -0
- data/doc/release_notes/1_1_3.md +18 -0
- data/doc/release_notes/1_1_4.md +6 -0
- data/doc/release_notes/1_1_5.md +12 -0
- data/doc/release_notes/1_2_0.md +49 -0
- data/doc/release_notes/1_2_1.md +6 -0
- data/doc/release_notes/1_2_2.md +10 -0
- data/doc/release_notes/1_2_3.md +16 -0
- data/doc/release_notes/1_2_4.md +8 -0
- data/doc/release_notes/1_2_5.md +7 -0
- data/doc/release_notes/1_2_6.md +13 -0
- data/doc/release_notes/1_3_0.md +18 -0
- data/doc/release_notes/1_3_1.md +17 -0
- data/lib/httpx/adapters/datadog.rb +215 -122
- data/lib/httpx/adapters/faraday.rb +145 -107
- data/lib/httpx/adapters/sentry.rb +26 -7
- data/lib/httpx/adapters/webmock.rb +34 -18
- data/lib/httpx/altsvc.rb +63 -26
- data/lib/httpx/base64.rb +27 -0
- data/lib/httpx/buffer.rb +12 -0
- data/lib/httpx/callbacks.rb +5 -3
- data/lib/httpx/chainable.rb +54 -39
- data/lib/httpx/connection/http1.rb +75 -44
- data/lib/httpx/connection/http2.rb +31 -38
- data/lib/httpx/connection.rb +287 -117
- data/lib/httpx/domain_name.rb +10 -13
- data/lib/httpx/errors.rb +52 -2
- data/lib/httpx/extensions.rb +24 -131
- data/lib/httpx/io/ssl.rb +83 -77
- data/lib/httpx/io/tcp.rb +48 -71
- data/lib/httpx/io/udp.rb +18 -52
- data/lib/httpx/io/unix.rb +10 -15
- data/lib/httpx/io.rb +3 -9
- data/lib/httpx/loggable.rb +4 -19
- data/lib/httpx/options.rb +176 -118
- data/lib/httpx/parser/http1.rb +4 -0
- data/lib/httpx/plugins/{authentication → auth}/basic.rb +1 -5
- data/lib/httpx/plugins/{authentication → auth}/digest.rb +14 -14
- data/lib/httpx/plugins/{authentication → auth}/ntlm.rb +1 -3
- data/lib/httpx/plugins/{authentication → auth}/socks5.rb +0 -2
- data/lib/httpx/plugins/auth.rb +25 -0
- data/lib/httpx/plugins/aws_sdk_authentication.rb +4 -3
- data/lib/httpx/plugins/aws_sigv4.rb +12 -9
- data/lib/httpx/plugins/basic_auth.rb +29 -0
- data/lib/httpx/plugins/brotli.rb +50 -0
- data/lib/httpx/plugins/callbacks.rb +91 -0
- data/lib/httpx/plugins/circuit_breaker/circuit.rb +100 -0
- data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +53 -0
- data/lib/httpx/plugins/circuit_breaker.rb +148 -0
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +0 -2
- data/lib/httpx/plugins/cookies.rb +30 -17
- data/lib/httpx/plugins/{digest_authentication.rb → digest_auth.rb} +14 -12
- data/lib/httpx/plugins/expect.rb +21 -14
- data/lib/httpx/plugins/follow_redirects.rb +140 -41
- data/lib/httpx/plugins/grpc/call.rb +2 -3
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +88 -0
- data/lib/httpx/plugins/grpc/message.rb +7 -37
- data/lib/httpx/plugins/grpc.rb +36 -29
- data/lib/httpx/plugins/h2c.rb +26 -19
- data/lib/httpx/plugins/internal_telemetry.rb +16 -0
- data/lib/httpx/plugins/{ntlm_authentication.rb → ntlm_auth.rb} +7 -5
- data/lib/httpx/plugins/oauth.rb +175 -0
- data/lib/httpx/plugins/persistent.rb +1 -1
- data/lib/httpx/plugins/proxy/http.rb +23 -13
- data/lib/httpx/plugins/proxy/socks4.rb +9 -7
- data/lib/httpx/plugins/proxy/socks5.rb +11 -9
- data/lib/httpx/plugins/proxy.rb +80 -61
- data/lib/httpx/plugins/push_promise.rb +1 -1
- data/lib/httpx/plugins/rate_limiter.rb +5 -1
- data/lib/httpx/plugins/response_cache/file_store.rb +40 -0
- data/lib/httpx/plugins/response_cache/store.rb +62 -25
- data/lib/httpx/plugins/response_cache.rb +105 -12
- data/lib/httpx/plugins/retries.rb +87 -17
- data/lib/httpx/plugins/ssrf_filter.rb +145 -0
- data/lib/httpx/plugins/stream.rb +27 -23
- data/lib/httpx/plugins/upgrade/h2.rb +4 -4
- data/lib/httpx/plugins/upgrade.rb +8 -10
- data/lib/httpx/plugins/webdav.rb +80 -0
- data/lib/httpx/pool/synch_pool.rb +93 -0
- data/lib/httpx/pool.rb +102 -27
- data/lib/httpx/punycode.rb +9 -291
- data/lib/httpx/request/body.rb +154 -0
- data/lib/httpx/request.rb +130 -146
- data/lib/httpx/resolver/https.rb +62 -27
- data/lib/httpx/resolver/multi.rb +9 -13
- data/lib/httpx/resolver/native.rb +192 -76
- data/lib/httpx/resolver/resolver.rb +34 -9
- data/lib/httpx/resolver/system.rb +16 -11
- data/lib/httpx/resolver.rb +38 -16
- data/lib/httpx/response/body.rb +242 -0
- data/lib/httpx/response/buffer.rb +96 -0
- data/lib/httpx/response.rb +159 -217
- data/lib/httpx/selector.rb +9 -4
- data/lib/httpx/session.rb +137 -89
- data/lib/httpx/session_extensions.rb +4 -1
- data/lib/httpx/timers.rb +34 -8
- data/lib/httpx/transcoder/body.rb +0 -2
- data/lib/httpx/transcoder/chunker.rb +0 -1
- data/lib/httpx/transcoder/deflate.rb +37 -0
- data/lib/httpx/transcoder/form.rb +52 -33
- data/lib/httpx/transcoder/gzip.rb +74 -0
- data/lib/httpx/transcoder/json.rb +21 -8
- data/lib/httpx/transcoder/multipart/decoder.rb +139 -0
- data/lib/httpx/{plugins → transcoder}/multipart/encoder.rb +4 -4
- data/lib/httpx/{plugins → transcoder}/multipart/mime_type_detector.rb +1 -1
- data/lib/httpx/{plugins → transcoder}/multipart/part.rb +3 -2
- data/lib/httpx/transcoder/multipart.rb +17 -0
- data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
- data/lib/httpx/transcoder/utils/deflater.rb +72 -0
- data/lib/httpx/transcoder/utils/inflater.rb +19 -0
- data/lib/httpx/transcoder/xml.rb +52 -0
- data/lib/httpx/transcoder.rb +5 -6
- data/lib/httpx/utils.rb +36 -16
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +12 -14
- data/sig/altsvc.rbs +33 -0
- data/sig/buffer.rbs +2 -1
- data/sig/callbacks.rbs +3 -3
- data/sig/chainable.rbs +11 -9
- data/sig/connection/http1.rbs +8 -7
- data/sig/connection/http2.rbs +19 -19
- data/sig/connection.rbs +64 -24
- data/sig/errors.rbs +22 -3
- data/sig/httpx.rbs +5 -4
- data/sig/io/ssl.rbs +27 -0
- data/sig/io/tcp.rbs +60 -0
- data/sig/io/udp.rbs +20 -0
- data/sig/io/unix.rbs +27 -0
- data/sig/io.rbs +6 -0
- data/sig/options.rbs +32 -22
- data/sig/parser/http1.rbs +1 -1
- data/sig/plugins/{authentication → auth}/basic.rbs +0 -2
- data/sig/plugins/{authentication → auth}/digest.rbs +2 -1
- data/sig/plugins/auth.rbs +13 -0
- data/sig/plugins/{basic_authentication.rbs → basic_auth.rbs} +2 -2
- data/sig/plugins/brotli.rbs +22 -0
- data/sig/plugins/callbacks.rbs +38 -0
- data/sig/plugins/circuit_breaker.rbs +71 -0
- data/sig/plugins/compression.rbs +7 -5
- data/sig/plugins/cookies/jar.rbs +2 -2
- data/sig/plugins/cookies.rbs +2 -0
- data/sig/plugins/{digest_authentication.rbs → digest_auth.rbs} +2 -2
- data/sig/plugins/follow_redirects.rbs +18 -4
- data/sig/plugins/grpc/call.rbs +19 -0
- data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
- data/sig/plugins/grpc/message.rbs +17 -0
- data/sig/plugins/grpc.rbs +7 -32
- data/sig/plugins/h2c.rbs +1 -1
- data/sig/plugins/{ntlm_authentication.rbs → ntlm_auth.rbs} +2 -2
- data/sig/plugins/oauth.rbs +54 -0
- data/sig/plugins/proxy/http.rbs +3 -0
- data/sig/plugins/proxy/socks4.rbs +9 -6
- data/sig/plugins/proxy/socks5.rbs +10 -6
- data/sig/plugins/proxy/ssh.rbs +1 -1
- data/sig/plugins/proxy.rbs +13 -5
- data/sig/plugins/push_promise.rbs +3 -3
- data/sig/plugins/rate_limiter.rbs +1 -1
- data/sig/plugins/response_cache.rbs +36 -7
- data/sig/plugins/retries.rbs +30 -8
- data/sig/plugins/stream.rbs +24 -17
- data/sig/plugins/upgrade.rbs +5 -3
- data/sig/pool.rbs +10 -7
- data/sig/request/body.rbs +38 -0
- data/sig/request.rbs +15 -24
- data/sig/resolver/https.rbs +8 -3
- data/sig/resolver/native.rbs +17 -4
- data/sig/resolver/resolver.rbs +8 -6
- data/sig/resolver/system.rbs +2 -0
- data/sig/resolver.rbs +9 -5
- data/sig/response/body.rbs +53 -0
- data/sig/response/buffer.rbs +24 -0
- data/sig/response.rbs +24 -39
- data/sig/selector.rbs +1 -1
- data/sig/session.rbs +29 -18
- data/sig/timers.rbs +18 -8
- data/sig/transcoder/body.rbs +4 -3
- data/sig/transcoder/deflate.rbs +11 -0
- data/sig/transcoder/form.rbs +5 -3
- data/sig/transcoder/gzip.rbs +24 -0
- data/sig/transcoder/json.rbs +8 -3
- data/sig/{plugins → transcoder}/multipart.rbs +15 -19
- data/sig/transcoder/utils/body_reader.rbs +15 -0
- data/sig/transcoder/utils/deflater.rbs +29 -0
- data/sig/transcoder/utils/inflater.rbs +12 -0
- data/sig/transcoder/xml.rbs +22 -0
- data/sig/transcoder.rbs +24 -9
- data/sig/utils.rbs +8 -2
- metadata +163 -41
- data/lib/httpx/plugins/authentication.rb +0 -20
- data/lib/httpx/plugins/basic_authentication.rb +0 -30
- data/lib/httpx/plugins/compression/brotli.rb +0 -54
- data/lib/httpx/plugins/compression/deflate.rb +0 -49
- data/lib/httpx/plugins/compression/gzip.rb +0 -88
- data/lib/httpx/plugins/compression.rb +0 -164
- data/lib/httpx/plugins/multipart/decoder.rb +0 -187
- data/lib/httpx/plugins/multipart.rb +0 -84
- data/lib/httpx/registry.rb +0 -85
- data/sig/plugins/authentication.rbs +0 -11
- data/sig/plugins/compression/brotli.rbs +0 -21
- data/sig/plugins/compression/deflate.rbs +0 -17
- data/sig/plugins/compression/gzip.rbs +0 -29
- data/sig/registry.rbs +0 -12
- /data/sig/plugins/{authentication → auth}/ntlm.rbs +0 -0
- /data/sig/plugins/{authentication → auth}/socks5.rbs +0 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
# Implementation of the HTTP Request body as a delegator which iterates (responds to +each+) payload chunks.
|
5
|
+
class Request::Body < SimpleDelegator
|
6
|
+
class << self
|
7
|
+
def new(_, options, body: nil, **params)
|
8
|
+
if body.is_a?(self)
|
9
|
+
# request derives its options from body
|
10
|
+
body.options = options.merge(params)
|
11
|
+
return body
|
12
|
+
end
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_accessor :options
|
19
|
+
|
20
|
+
# inits the instance with the request +headers+, +options+ and +params+, which contain the payload definition.
|
21
|
+
# it wraps the given body with the appropriate encoder on initialization.
|
22
|
+
#
|
23
|
+
# ..., json: { foo: "bar" }) #=> json encoder
|
24
|
+
# ..., form: { foo: "bar" }) #=> form urlencoded encoder
|
25
|
+
# ..., form: { foo: Pathname.open("path/to/file") }) #=> multipart urlencoded encoder
|
26
|
+
# ..., form: { foo: File.open("path/to/file") }) #=> multipart urlencoded encoder
|
27
|
+
# ..., form: { body: "bla") }) #=> raw data encoder
|
28
|
+
def initialize(headers, options, body: nil, form: nil, json: nil, xml: nil, **params)
|
29
|
+
@headers = headers
|
30
|
+
@options = options.merge(params)
|
31
|
+
|
32
|
+
@body = if body
|
33
|
+
Transcoder::Body.encode(body)
|
34
|
+
elsif form
|
35
|
+
Transcoder::Form.encode(form)
|
36
|
+
elsif json
|
37
|
+
Transcoder::JSON.encode(json)
|
38
|
+
elsif xml
|
39
|
+
Transcoder::Xml.encode(xml)
|
40
|
+
end
|
41
|
+
|
42
|
+
if @body
|
43
|
+
if @options.compress_request_body && @headers.key?("content-encoding")
|
44
|
+
|
45
|
+
@headers.get("content-encoding").each do |encoding|
|
46
|
+
@body = self.class.initialize_deflater_body(@body, encoding)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
@headers["content-type"] ||= @body.content_type
|
51
|
+
@headers["content-length"] = @body.bytesize unless unbounded_body?
|
52
|
+
end
|
53
|
+
|
54
|
+
super(@body)
|
55
|
+
end
|
56
|
+
|
57
|
+
# consumes and yields the request payload in chunks.
|
58
|
+
def each(&block)
|
59
|
+
return enum_for(__method__) unless block
|
60
|
+
return if @body.nil?
|
61
|
+
|
62
|
+
body = stream(@body)
|
63
|
+
if body.respond_to?(:read)
|
64
|
+
::IO.copy_stream(body, ProcIO.new(block))
|
65
|
+
elsif body.respond_to?(:each)
|
66
|
+
body.each(&block)
|
67
|
+
else
|
68
|
+
block[body.to_s]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# if the +@body+ is rewindable, it rewinnds it.
|
73
|
+
def rewind
|
74
|
+
return if empty?
|
75
|
+
|
76
|
+
@body.rewind if @body.respond_to?(:rewind)
|
77
|
+
end
|
78
|
+
|
79
|
+
# return +true+ if the +body+ has been fully drained (or does nnot exist).
|
80
|
+
def empty?
|
81
|
+
return true if @body.nil?
|
82
|
+
return false if chunked?
|
83
|
+
|
84
|
+
@body.bytesize.zero?
|
85
|
+
end
|
86
|
+
|
87
|
+
# returns the +@body+ payload size in bytes.
|
88
|
+
def bytesize
|
89
|
+
return 0 if @body.nil?
|
90
|
+
|
91
|
+
@body.bytesize
|
92
|
+
end
|
93
|
+
|
94
|
+
# sets the body to yield using chunked trannsfer encoding format.
|
95
|
+
def stream(body)
|
96
|
+
return body unless chunked?
|
97
|
+
|
98
|
+
Transcoder::Chunker.encode(body.enum_for(:each))
|
99
|
+
end
|
100
|
+
|
101
|
+
# returns whether the body yields infinitely.
|
102
|
+
def unbounded_body?
|
103
|
+
return @unbounded_body if defined?(@unbounded_body)
|
104
|
+
|
105
|
+
@unbounded_body = !@body.nil? && (chunked? || @body.bytesize == Float::INFINITY)
|
106
|
+
end
|
107
|
+
|
108
|
+
# returns whether the chunked transfer encoding header is set.
|
109
|
+
def chunked?
|
110
|
+
@headers["transfer-encoding"] == "chunked"
|
111
|
+
end
|
112
|
+
|
113
|
+
# sets the chunked transfer encoding header.
|
114
|
+
def chunk!
|
115
|
+
@headers.add("transfer-encoding", "chunked")
|
116
|
+
end
|
117
|
+
|
118
|
+
# :nocov:
|
119
|
+
def inspect
|
120
|
+
"#<HTTPX::Request::Body:#{object_id} " \
|
121
|
+
"#{unbounded_body? ? "stream" : "@bytesize=#{bytesize}"}>"
|
122
|
+
end
|
123
|
+
# :nocov:
|
124
|
+
|
125
|
+
class << self
|
126
|
+
# returns the +body+ wrapped with the correct deflater accordinng to the given +encodisng+.
|
127
|
+
def initialize_deflater_body(body, encoding)
|
128
|
+
case encoding
|
129
|
+
when "gzip"
|
130
|
+
Transcoder::GZIP.encode(body)
|
131
|
+
when "deflate"
|
132
|
+
Transcoder::Deflate.encode(body)
|
133
|
+
when "identity"
|
134
|
+
body
|
135
|
+
else
|
136
|
+
body
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Wrapper yielder which can be used with functions which expect an IO writer.
|
143
|
+
class ProcIO
|
144
|
+
def initialize(block)
|
145
|
+
@block = block
|
146
|
+
end
|
147
|
+
|
148
|
+
# Implementation the IO write protocol, which yield the given chunk to +@block+.
|
149
|
+
def write(data)
|
150
|
+
@block.call(data.dup)
|
151
|
+
data.bytesize
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/httpx/request.rb
CHANGED
@@ -4,47 +4,85 @@ require "delegate"
|
|
4
4
|
require "forwardable"
|
5
5
|
|
6
6
|
module HTTPX
|
7
|
+
# Defines how an HTTP request is handled internally, both in terms of making attributes accessible,
|
8
|
+
# as well as maintaining the state machine which manages streaming the request onto the wire.
|
7
9
|
class Request
|
8
10
|
extend Forwardable
|
9
11
|
include Callbacks
|
10
12
|
using URIExtensions
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
:options, :get, :head, :post, :put, :delete, :trace, :connect,
|
15
|
-
|
16
|
-
# RFC 2518: HTTP Extensions for Distributed Authoring -- WEBDAV
|
17
|
-
:propfind, :proppatch, :mkcol, :copy, :move, :lock, :unlock,
|
14
|
+
# default value used for "user-agent" header, when not overridden.
|
15
|
+
USER_AGENT = "httpx.rb/#{VERSION}"
|
18
16
|
|
19
|
-
|
20
|
-
|
17
|
+
# the upcased string HTTP verb for this request.
|
18
|
+
attr_reader :verb
|
21
19
|
|
22
|
-
|
23
|
-
|
20
|
+
# the absolute URI object for this request.
|
21
|
+
attr_reader :uri
|
24
22
|
|
25
|
-
|
26
|
-
|
23
|
+
# an HTTPX::Headers object containing the request HTTP headers.
|
24
|
+
attr_reader :headers
|
27
25
|
|
28
|
-
|
29
|
-
|
26
|
+
# an HTTPX::Request::Body object containing the request body payload (or +nil+, whenn there is none).
|
27
|
+
attr_reader :body
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
].freeze
|
29
|
+
# a symbol describing which frame is currently being flushed.
|
30
|
+
attr_reader :state
|
34
31
|
|
35
|
-
|
32
|
+
# an HTTPX::Options object containing request options.
|
33
|
+
attr_reader :options
|
36
34
|
|
37
|
-
|
35
|
+
# the corresponding HTTPX::Response object, when there is one.
|
36
|
+
attr_reader :response
|
38
37
|
|
39
|
-
# Exception raised during enumerable body writes
|
38
|
+
# Exception raised during enumerable body writes.
|
40
39
|
attr_reader :drain_error
|
41
40
|
|
41
|
+
# The IP address from the peer server.
|
42
|
+
attr_accessor :peer_address
|
43
|
+
|
44
|
+
attr_writer :persistent
|
45
|
+
|
46
|
+
# will be +true+ when request body has been completely flushed.
|
42
47
|
def_delegator :@body, :empty?
|
43
48
|
|
44
|
-
|
45
|
-
|
46
|
-
|
49
|
+
# initializes the instance with the given +verb+ (an upppercase String, ex. 'GEt'),
|
50
|
+
# an absolute or relative +uri+ (either as String or URI::HTTP object), the
|
51
|
+
# request +options+ (instance of HTTPX::Options) and an optional Hash of +params+.
|
52
|
+
#
|
53
|
+
# Besides any of the options documented in HTTPX::Options (which would override or merge with what
|
54
|
+
# +options+ sets), it accepts also the following:
|
55
|
+
#
|
56
|
+
# :params :: hash or array of key-values which will be encoded and set in the query string of request uris.
|
57
|
+
# :body :: to be encoded in the request body payload. can be a String, an IO object (i.e. a File), or an Enumerable.
|
58
|
+
# :form :: hash of array of key-values which will be form-urlencoded- or multipart-encoded in requests body payload.
|
59
|
+
# :json :: hash of array of key-values which will be JSON-encoded in requests body payload.
|
60
|
+
# :xml :: Nokogiri XML nodes which will be encoded in requests body payload.
|
61
|
+
#
|
62
|
+
# :body, :form, :json and :xml are all mutually exclusive, i.e. only one of them gets picked up.
|
63
|
+
def initialize(verb, uri, options, params = EMPTY_HASH)
|
64
|
+
@verb = verb.to_s.upcase
|
47
65
|
@uri = Utils.to_uri(uri)
|
66
|
+
|
67
|
+
@headers = options.headers.dup
|
68
|
+
merge_headers(params.delete(:headers)) if params.key?(:headers)
|
69
|
+
|
70
|
+
@headers["user-agent"] ||= USER_AGENT
|
71
|
+
@headers["accept"] ||= "*/*"
|
72
|
+
|
73
|
+
# forego compression in the Range request case
|
74
|
+
if @headers.key?("range")
|
75
|
+
@headers.delete("accept-encoding")
|
76
|
+
else
|
77
|
+
@headers["accept-encoding"] ||= options.supported_compression_formats
|
78
|
+
end
|
79
|
+
|
80
|
+
@query_params = params.delete(:params) if params.key?(:params)
|
81
|
+
|
82
|
+
@body = options.request_body_class.new(@headers, options, **params)
|
83
|
+
|
84
|
+
@options = @body.options
|
85
|
+
|
48
86
|
if @uri.relative?
|
49
87
|
origin = @options.origin
|
50
88
|
raise(Error, "invalid URI: #{@uri}") unless origin
|
@@ -54,92 +92,131 @@ module HTTPX
|
|
54
92
|
@uri = origin.merge("#{base_path}#{@uri}")
|
55
93
|
end
|
56
94
|
|
57
|
-
|
95
|
+
@state = :idle
|
96
|
+
@response = nil
|
97
|
+
@peer_address = nil
|
98
|
+
@persistent = @options.persistent
|
99
|
+
end
|
58
100
|
|
59
|
-
|
60
|
-
|
61
|
-
@
|
101
|
+
# the read timeout defined for this requet.
|
102
|
+
def read_timeout
|
103
|
+
@options.timeout[:read_timeout]
|
104
|
+
end
|
62
105
|
|
63
|
-
|
64
|
-
|
106
|
+
# the write timeout defined for this requet.
|
107
|
+
def write_timeout
|
108
|
+
@options.timeout[:write_timeout]
|
65
109
|
end
|
66
110
|
|
111
|
+
# the request timeout defined for this requet.
|
112
|
+
def request_timeout
|
113
|
+
@options.timeout[:request_timeout]
|
114
|
+
end
|
115
|
+
|
116
|
+
def persistent?
|
117
|
+
@persistent
|
118
|
+
end
|
119
|
+
|
120
|
+
# if the request contains trailer headers
|
67
121
|
def trailers?
|
68
122
|
defined?(@trailers)
|
69
123
|
end
|
70
124
|
|
125
|
+
# returns an instance of HTTPX::Headers containing the trailer headers
|
71
126
|
def trailers
|
72
127
|
@trailers ||= @options.headers_class.new
|
73
128
|
end
|
74
129
|
|
130
|
+
# returns +:r+ or +:w+, depending on whether the request is waiting for a response or flushing.
|
75
131
|
def interests
|
76
132
|
return :r if @state == :done || @state == :expect
|
77
133
|
|
78
134
|
:w
|
79
135
|
end
|
80
136
|
|
81
|
-
|
82
|
-
URIParser = URI::DEFAULT_PARSER
|
83
|
-
|
84
|
-
def initialize_with_escape(verb, uri, options = {})
|
85
|
-
initialize_without_escape(verb, URIParser.escape(uri.to_s), options)
|
86
|
-
end
|
87
|
-
alias_method :initialize_without_escape, :initialize
|
88
|
-
alias_method :initialize, :initialize_with_escape
|
89
|
-
end
|
90
|
-
|
137
|
+
# merges +h+ into the instance of HTTPX::Headers of the request.
|
91
138
|
def merge_headers(h)
|
92
139
|
@headers = @headers.merge(h)
|
93
140
|
end
|
94
141
|
|
142
|
+
# the URI scheme of the request +uri+.
|
95
143
|
def scheme
|
96
144
|
@uri.scheme
|
97
145
|
end
|
98
146
|
|
147
|
+
# sets the +response+ on this request.
|
99
148
|
def response=(response)
|
100
149
|
return unless response
|
101
150
|
|
102
|
-
if response.is_a?(Response) && response.status
|
103
|
-
|
104
|
-
|
151
|
+
if response.is_a?(Response) && response.status < 200
|
152
|
+
# deal with informational responses
|
153
|
+
|
154
|
+
if response.status == 100 && @headers.key?("expect")
|
155
|
+
@informational_status = response.status
|
156
|
+
return
|
157
|
+
end
|
158
|
+
|
159
|
+
# 103 Early Hints advertises resources in document to browsers.
|
160
|
+
# not very relevant for an HTTP client, discard.
|
161
|
+
return if response.status >= 103
|
105
162
|
end
|
163
|
+
|
106
164
|
@response = response
|
165
|
+
|
166
|
+
emit(:response_started, response)
|
107
167
|
end
|
108
168
|
|
169
|
+
# returnns the URI path of the request +uri+.
|
109
170
|
def path
|
110
171
|
path = uri.path.dup
|
172
|
+
path = +"" if path.nil?
|
111
173
|
path << "/" if path.empty?
|
112
174
|
path << "?#{query}" unless query.empty?
|
113
175
|
path
|
114
176
|
end
|
115
177
|
|
116
|
-
#
|
178
|
+
# returs the URI authority of the request.
|
179
|
+
#
|
180
|
+
# session.build_request("GET", "https://google.com/query").authority #=> "google.com"
|
181
|
+
# session.build_request("GET", "http://internal:3182/a").authority #=> "internal:3182"
|
117
182
|
def authority
|
118
183
|
@uri.authority
|
119
184
|
end
|
120
185
|
|
121
|
-
#
|
186
|
+
# returs the URI origin of the request.
|
187
|
+
#
|
188
|
+
# session.build_request("GET", "https://google.com/query").authority #=> "https://google.com"
|
189
|
+
# session.build_request("GET", "http://internal:3182/a").authority #=> "http://internal:3182"
|
122
190
|
def origin
|
123
191
|
@uri.origin
|
124
192
|
end
|
125
193
|
|
194
|
+
# returs the URI query string of the request (when available).
|
195
|
+
#
|
196
|
+
# session.build_request("GET", "https://search.com").query #=> ""
|
197
|
+
# session.build_request("GET", "https://search.com?q=a").query #=> "q=a"
|
198
|
+
# session.build_request("GET", "https://search.com", params: { q: "a"}).query #=> "q=a"
|
199
|
+
# session.build_request("GET", "https://search.com?q=a", params: { foo: "bar"}).query #=> "q=a&foo&bar"
|
126
200
|
def query
|
127
201
|
return @query if defined?(@query)
|
128
202
|
|
129
203
|
query = []
|
130
|
-
if (q = @
|
131
|
-
query << Transcoder.
|
204
|
+
if (q = @query_params)
|
205
|
+
query << Transcoder::Form.encode(q)
|
132
206
|
end
|
133
207
|
query << @uri.query if @uri.query
|
134
208
|
@query = query.join("&")
|
135
209
|
end
|
136
210
|
|
211
|
+
# consumes and returns the next available chunk of request body that can be sent
|
137
212
|
def drain_body
|
138
213
|
return nil if @body.nil?
|
139
214
|
|
140
215
|
@drainer ||= @body.each
|
141
|
-
chunk = @drainer.next
|
142
|
-
|
216
|
+
chunk = @drainer.next.dup
|
217
|
+
|
218
|
+
emit(:body_chunk, chunk)
|
219
|
+
chunk
|
143
220
|
rescue StopIteration
|
144
221
|
nil
|
145
222
|
rescue StandardError => e
|
@@ -150,99 +227,14 @@ module HTTPX
|
|
150
227
|
# :nocov:
|
151
228
|
def inspect
|
152
229
|
"#<HTTPX::Request:#{object_id} " \
|
153
|
-
"#{@verb
|
230
|
+
"#{@verb} " \
|
154
231
|
"#{uri} " \
|
155
232
|
"@headers=#{@headers} " \
|
156
233
|
"@body=#{@body}>"
|
157
234
|
end
|
158
235
|
# :nocov:
|
159
236
|
|
160
|
-
|
161
|
-
class << self
|
162
|
-
def new(_, options)
|
163
|
-
return options.body if options.body.is_a?(self)
|
164
|
-
|
165
|
-
super
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
def initialize(headers, options)
|
170
|
-
@headers = headers
|
171
|
-
@body = if options.body
|
172
|
-
Transcoder.registry("body").encode(options.body)
|
173
|
-
elsif options.form
|
174
|
-
Transcoder.registry("form").encode(options.form)
|
175
|
-
elsif options.json
|
176
|
-
Transcoder.registry("json").encode(options.json)
|
177
|
-
end
|
178
|
-
return if @body.nil?
|
179
|
-
|
180
|
-
@headers["content-type"] ||= @body.content_type
|
181
|
-
@headers["content-length"] = @body.bytesize unless unbounded_body?
|
182
|
-
super(@body)
|
183
|
-
end
|
184
|
-
|
185
|
-
def each(&block)
|
186
|
-
return enum_for(__method__) unless block
|
187
|
-
return if @body.nil?
|
188
|
-
|
189
|
-
body = stream(@body)
|
190
|
-
if body.respond_to?(:read)
|
191
|
-
::IO.copy_stream(body, ProcIO.new(block))
|
192
|
-
elsif body.respond_to?(:each)
|
193
|
-
body.each(&block)
|
194
|
-
else
|
195
|
-
block[body.to_s]
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
def rewind
|
200
|
-
return if empty?
|
201
|
-
|
202
|
-
@body.rewind if @body.respond_to?(:rewind)
|
203
|
-
end
|
204
|
-
|
205
|
-
def empty?
|
206
|
-
return true if @body.nil?
|
207
|
-
return false if chunked?
|
208
|
-
|
209
|
-
@body.bytesize.zero?
|
210
|
-
end
|
211
|
-
|
212
|
-
def bytesize
|
213
|
-
return 0 if @body.nil?
|
214
|
-
|
215
|
-
@body.bytesize
|
216
|
-
end
|
217
|
-
|
218
|
-
def stream(body)
|
219
|
-
encoded = body
|
220
|
-
encoded = Transcoder.registry("chunker").encode(body.enum_for(:each)) if chunked?
|
221
|
-
encoded
|
222
|
-
end
|
223
|
-
|
224
|
-
def unbounded_body?
|
225
|
-
return @unbounded_body if defined?(@unbounded_body)
|
226
|
-
|
227
|
-
@unbounded_body = !@body.nil? && (chunked? || @body.bytesize == Float::INFINITY)
|
228
|
-
end
|
229
|
-
|
230
|
-
def chunked?
|
231
|
-
@headers["transfer-encoding"] == "chunked"
|
232
|
-
end
|
233
|
-
|
234
|
-
def chunk!
|
235
|
-
@headers.add("transfer-encoding", "chunked")
|
236
|
-
end
|
237
|
-
|
238
|
-
# :nocov:
|
239
|
-
def inspect
|
240
|
-
"#<HTTPX::Request::Body:#{object_id} " \
|
241
|
-
"#{unbounded_body? ? "stream" : "@bytesize=#{bytesize}"}>"
|
242
|
-
end
|
243
|
-
# :nocov:
|
244
|
-
end
|
245
|
-
|
237
|
+
# moves on to the +nextstate+ of the request state machine (when all preconditions are met)
|
246
238
|
def transition(nextstate)
|
247
239
|
case nextstate
|
248
240
|
when :idle
|
@@ -277,19 +269,11 @@ module HTTPX
|
|
277
269
|
nil
|
278
270
|
end
|
279
271
|
|
272
|
+
# whether the request supports the 100-continue handshake and already processed the 100 response.
|
280
273
|
def expects?
|
281
274
|
@headers["expect"] == "100-continue" && @informational_status == 100 && !@response
|
282
275
|
end
|
283
|
-
|
284
|
-
class ProcIO
|
285
|
-
def initialize(block)
|
286
|
-
@block = block
|
287
|
-
end
|
288
|
-
|
289
|
-
def write(data)
|
290
|
-
@block.call(data.dup)
|
291
|
-
data.bytesize
|
292
|
-
end
|
293
|
-
end
|
294
276
|
end
|
295
277
|
end
|
278
|
+
|
279
|
+
require_relative "request/body"
|