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
@@ -1,164 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module HTTPX
|
4
|
-
module Plugins
|
5
|
-
#
|
6
|
-
# This plugin adds compression support. Namely it:
|
7
|
-
#
|
8
|
-
# * Compresses the request body when passed a supported "Content-Encoding" mime-type;
|
9
|
-
# * Decompresses the response body from a supported "Content-Encoding" mime-type;
|
10
|
-
#
|
11
|
-
# It supports both *gzip* and *deflate*.
|
12
|
-
#
|
13
|
-
# https://gitlab.com/honeyryderchuck/httpx/wikis/Compression
|
14
|
-
#
|
15
|
-
module Compression
|
16
|
-
class << self
|
17
|
-
def configure(klass)
|
18
|
-
klass.plugin(:"compression/gzip")
|
19
|
-
klass.plugin(:"compression/deflate")
|
20
|
-
end
|
21
|
-
|
22
|
-
def extra_options(options)
|
23
|
-
encodings = Module.new do
|
24
|
-
extend Registry
|
25
|
-
end
|
26
|
-
options.merge(encodings: encodings)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
module OptionsMethods
|
31
|
-
def option_compression_threshold_size(value)
|
32
|
-
bytes = Integer(value)
|
33
|
-
raise TypeError, ":expect_threshold_size must be positive" unless bytes.positive?
|
34
|
-
|
35
|
-
bytes
|
36
|
-
end
|
37
|
-
|
38
|
-
def option_encodings(value)
|
39
|
-
raise TypeError, ":encodings must be a registry" unless value.respond_to?(:registry)
|
40
|
-
|
41
|
-
value
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
module RequestMethods
|
46
|
-
def initialize(*)
|
47
|
-
super
|
48
|
-
# forego compression in the Range cases
|
49
|
-
if @headers.key?("range")
|
50
|
-
@headers.delete("accept-encoding")
|
51
|
-
else
|
52
|
-
@headers["accept-encoding"] ||= @options.encodings.registry.keys
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
module RequestBodyMethods
|
58
|
-
def initialize(*, options)
|
59
|
-
super
|
60
|
-
return if @body.nil?
|
61
|
-
|
62
|
-
threshold = options.compression_threshold_size
|
63
|
-
return if threshold && !unbounded_body? && @body.bytesize < threshold
|
64
|
-
|
65
|
-
@headers.get("content-encoding").each do |encoding|
|
66
|
-
next if encoding == "identity"
|
67
|
-
|
68
|
-
@body = Encoder.new(@body, options.encodings.registry(encoding).deflater)
|
69
|
-
end
|
70
|
-
@headers["content-length"] = @body.bytesize unless unbounded_body?
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
module ResponseBodyMethods
|
75
|
-
using ArrayExtensions
|
76
|
-
|
77
|
-
attr_reader :encodings
|
78
|
-
|
79
|
-
def initialize(*)
|
80
|
-
@encodings = []
|
81
|
-
|
82
|
-
super
|
83
|
-
|
84
|
-
return unless @headers.key?("content-encoding")
|
85
|
-
|
86
|
-
# remove encodings that we are able to decode
|
87
|
-
@headers["content-encoding"] = @headers.get("content-encoding") - @encodings
|
88
|
-
|
89
|
-
compressed_length = if @headers.key?("content-length")
|
90
|
-
@headers["content-length"].to_i
|
91
|
-
else
|
92
|
-
Float::INFINITY
|
93
|
-
end
|
94
|
-
|
95
|
-
@_inflaters = @headers.get("content-encoding").filter_map do |encoding|
|
96
|
-
next if encoding == "identity"
|
97
|
-
|
98
|
-
inflater = @options.encodings.registry(encoding).inflater(compressed_length)
|
99
|
-
# do not uncompress if there is no decoder available. In fact, we can't reliably
|
100
|
-
# continue decompressing beyond that, so ignore.
|
101
|
-
break unless inflater
|
102
|
-
|
103
|
-
@encodings << encoding
|
104
|
-
inflater
|
105
|
-
end
|
106
|
-
|
107
|
-
# this can happen if the only declared encoding is "identity"
|
108
|
-
remove_instance_variable(:@_inflaters) if @_inflaters.empty?
|
109
|
-
end
|
110
|
-
|
111
|
-
def write(chunk)
|
112
|
-
return super unless defined?(@_inflaters) && !chunk.empty?
|
113
|
-
|
114
|
-
chunk = decompress(chunk)
|
115
|
-
super(chunk)
|
116
|
-
end
|
117
|
-
|
118
|
-
private
|
119
|
-
|
120
|
-
def decompress(buffer)
|
121
|
-
@_inflaters.reverse_each do |inflater|
|
122
|
-
buffer = inflater.inflate(buffer)
|
123
|
-
end
|
124
|
-
buffer
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
class Encoder
|
129
|
-
attr_reader :content_type
|
130
|
-
|
131
|
-
def initialize(body, deflater)
|
132
|
-
@content_type = body.content_type
|
133
|
-
@body = body.respond_to?(:read) ? body : StringIO.new(body.to_s)
|
134
|
-
@buffer = StringIO.new("".b, File::RDWR)
|
135
|
-
@deflater = deflater
|
136
|
-
end
|
137
|
-
|
138
|
-
def each(&blk)
|
139
|
-
return enum_for(__method__) unless blk
|
140
|
-
|
141
|
-
return deflate(&blk) if @buffer.size.zero?
|
142
|
-
|
143
|
-
@buffer.rewind
|
144
|
-
@buffer.each(&blk)
|
145
|
-
end
|
146
|
-
|
147
|
-
def bytesize
|
148
|
-
deflate
|
149
|
-
@buffer.size
|
150
|
-
end
|
151
|
-
|
152
|
-
private
|
153
|
-
|
154
|
-
def deflate(&blk)
|
155
|
-
return unless @buffer.size.zero?
|
156
|
-
|
157
|
-
@body.rewind
|
158
|
-
@deflater.deflate(@body, @buffer, chunk_size: 16_384, &blk)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
register_plugin :compression, Compression
|
163
|
-
end
|
164
|
-
end
|
@@ -1,187 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "tempfile"
|
4
|
-
require "delegate"
|
5
|
-
|
6
|
-
module HTTPX::Plugins
|
7
|
-
module Multipart
|
8
|
-
using HTTPX::RegexpExtensions unless Regexp.method_defined?(:match?)
|
9
|
-
|
10
|
-
CRLF = "\r\n"
|
11
|
-
|
12
|
-
class FilePart < SimpleDelegator
|
13
|
-
attr_reader :original_filename, :content_type
|
14
|
-
|
15
|
-
def initialize(filename, content_type)
|
16
|
-
@original_filename = filename
|
17
|
-
@content_type = content_type
|
18
|
-
@file = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR)
|
19
|
-
super(@file)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
TOKEN = %r{[^\s()<>,;:\\"/\[\]?=]+}.freeze
|
24
|
-
VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/.freeze
|
25
|
-
CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i.freeze
|
26
|
-
BROKEN_QUOTED = /^#{CONDISP}.*;\s*filename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i.freeze
|
27
|
-
BROKEN_UNQUOTED = /^#{CONDISP}.*;\s*filename=(#{TOKEN})/i.freeze
|
28
|
-
MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{CRLF}/ni.freeze
|
29
|
-
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*;\s*name=(#{VALUE})/ni.freeze
|
30
|
-
MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{CRLF}]*)/ni.freeze
|
31
|
-
# Updated definitions from RFC 2231
|
32
|
-
ATTRIBUTE_CHAR = %r{[^ \t\v\n\r)(><@,;:\\"/\[\]?='*%]}.freeze
|
33
|
-
ATTRIBUTE = /#{ATTRIBUTE_CHAR}+/.freeze
|
34
|
-
SECTION = /\*[0-9]+/.freeze
|
35
|
-
REGULAR_PARAMETER_NAME = /#{ATTRIBUTE}#{SECTION}?/.freeze
|
36
|
-
REGULAR_PARAMETER = /(#{REGULAR_PARAMETER_NAME})=(#{VALUE})/.freeze
|
37
|
-
EXTENDED_OTHER_NAME = /#{ATTRIBUTE}\*[1-9][0-9]*\*/.freeze
|
38
|
-
EXTENDED_OTHER_VALUE = /%[0-9a-fA-F]{2}|#{ATTRIBUTE_CHAR}/.freeze
|
39
|
-
EXTENDED_OTHER_PARAMETER = /(#{EXTENDED_OTHER_NAME})=(#{EXTENDED_OTHER_VALUE}*)/.freeze
|
40
|
-
EXTENDED_INITIAL_NAME = /#{ATTRIBUTE}(?:\*0)?\*/.freeze
|
41
|
-
EXTENDED_INITIAL_VALUE = /[a-zA-Z0-9\-]*'[a-zA-Z0-9\-]*'#{EXTENDED_OTHER_VALUE}*/.freeze
|
42
|
-
EXTENDED_INITIAL_PARAMETER = /(#{EXTENDED_INITIAL_NAME})=(#{EXTENDED_INITIAL_VALUE})/.freeze
|
43
|
-
EXTENDED_PARAMETER = /#{EXTENDED_INITIAL_PARAMETER}|#{EXTENDED_OTHER_PARAMETER}/.freeze
|
44
|
-
DISPPARM = /;\s*(?:#{REGULAR_PARAMETER}|#{EXTENDED_PARAMETER})\s*/.freeze
|
45
|
-
RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i.freeze
|
46
|
-
|
47
|
-
class Decoder
|
48
|
-
BOUNDARY_RE = /;\s*boundary=([^;]+)/i.freeze
|
49
|
-
WINDOW_SIZE = 2 << 14
|
50
|
-
|
51
|
-
def initialize(response)
|
52
|
-
@boundary = begin
|
53
|
-
m = response.headers["content-type"].to_s[BOUNDARY_RE, 1]
|
54
|
-
raise Error, "no boundary declared in content-type header" unless m
|
55
|
-
|
56
|
-
m.strip
|
57
|
-
end
|
58
|
-
@buffer = "".b
|
59
|
-
@parts = {}
|
60
|
-
@intermediate_boundary = "--#{@boundary}"
|
61
|
-
@state = :idle
|
62
|
-
end
|
63
|
-
|
64
|
-
def call(response, _)
|
65
|
-
response.body.each do |chunk|
|
66
|
-
@buffer << chunk
|
67
|
-
|
68
|
-
parse
|
69
|
-
end
|
70
|
-
|
71
|
-
raise Error, "invalid or unsupported multipart format" unless @buffer.empty?
|
72
|
-
|
73
|
-
@parts
|
74
|
-
end
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
def parse
|
79
|
-
case @state
|
80
|
-
when :idle
|
81
|
-
raise Error, "payload does not start with boundary" unless @buffer.start_with?("#{@intermediate_boundary}#{CRLF}")
|
82
|
-
|
83
|
-
@buffer = @buffer.byteslice(@intermediate_boundary.bytesize + 2..-1)
|
84
|
-
|
85
|
-
@state = :part_header
|
86
|
-
when :part_header
|
87
|
-
idx = @buffer.index("#{CRLF}#{CRLF}")
|
88
|
-
|
89
|
-
# raise Error, "couldn't parse part headers" unless idx
|
90
|
-
return unless idx
|
91
|
-
|
92
|
-
head = @buffer.byteslice(0..idx + 4 - 1)
|
93
|
-
|
94
|
-
@buffer = @buffer.byteslice(head.bytesize..-1)
|
95
|
-
|
96
|
-
content_type = head[MULTIPART_CONTENT_TYPE, 1]
|
97
|
-
if (name = head[MULTIPART_CONTENT_DISPOSITION, 1])
|
98
|
-
name = /\A"(.*)"\Z/ =~ name ? Regexp.last_match(1) : name.dup
|
99
|
-
name.gsub!(/\\(.)/, "\\1")
|
100
|
-
name
|
101
|
-
else
|
102
|
-
name = head[MULTIPART_CONTENT_ID, 1]
|
103
|
-
end
|
104
|
-
|
105
|
-
filename = get_filename(head)
|
106
|
-
|
107
|
-
name = filename || +"#{content_type || "text/plain"}[]" if name.nil? || name.empty?
|
108
|
-
|
109
|
-
@current = name
|
110
|
-
|
111
|
-
@parts[name] = if filename
|
112
|
-
FilePart.new(filename, content_type)
|
113
|
-
else
|
114
|
-
"".b
|
115
|
-
end
|
116
|
-
|
117
|
-
@state = :part_body
|
118
|
-
when :part_body
|
119
|
-
part = @parts[@current]
|
120
|
-
|
121
|
-
body_separator = if part.is_a?(FilePart)
|
122
|
-
"#{CRLF}#{CRLF}"
|
123
|
-
else
|
124
|
-
CRLF
|
125
|
-
end
|
126
|
-
idx = @buffer.index(body_separator)
|
127
|
-
|
128
|
-
if idx
|
129
|
-
payload = @buffer.byteslice(0..idx - 1)
|
130
|
-
@buffer = @buffer.byteslice(idx + body_separator.bytesize..-1)
|
131
|
-
part << payload
|
132
|
-
part.rewind if part.respond_to?(:rewind)
|
133
|
-
@state = :parse_boundary
|
134
|
-
else
|
135
|
-
part << @buffer
|
136
|
-
@buffer.clear
|
137
|
-
end
|
138
|
-
when :parse_boundary
|
139
|
-
raise Error, "payload does not start with boundary" unless @buffer.start_with?(@intermediate_boundary)
|
140
|
-
|
141
|
-
@buffer = @buffer.byteslice(@intermediate_boundary.bytesize..-1)
|
142
|
-
|
143
|
-
if @buffer == "--"
|
144
|
-
@buffer.clear
|
145
|
-
@state = :done
|
146
|
-
return
|
147
|
-
elsif @buffer.start_with?(CRLF)
|
148
|
-
@buffer = @buffer.byteslice(2..-1)
|
149
|
-
@state = :part_header
|
150
|
-
else
|
151
|
-
return
|
152
|
-
end
|
153
|
-
when :done
|
154
|
-
raise Error, "parsing should have been over by now"
|
155
|
-
end until @buffer.empty?
|
156
|
-
end
|
157
|
-
|
158
|
-
def get_filename(head)
|
159
|
-
filename = nil
|
160
|
-
case head
|
161
|
-
when RFC2183
|
162
|
-
params = Hash[*head.scan(DISPPARM).flat_map(&:compact)]
|
163
|
-
|
164
|
-
if (filename = params["filename"])
|
165
|
-
filename = Regexp.last_match(1) if filename =~ /^"(.*)"$/
|
166
|
-
elsif (filename = params["filename*"])
|
167
|
-
encoding, _, filename = filename.split("'", 3)
|
168
|
-
end
|
169
|
-
when BROKEN_QUOTED, BROKEN_UNQUOTED
|
170
|
-
filename = Regexp.last_match(1)
|
171
|
-
end
|
172
|
-
|
173
|
-
return unless filename
|
174
|
-
|
175
|
-
filename = URI::DEFAULT_PARSER.unescape(filename) if filename.scan(/%.?.?/).all? { |s| /%[0-9a-fA-F]{2}/.match?(s) }
|
176
|
-
|
177
|
-
filename.scrub!
|
178
|
-
|
179
|
-
filename = filename.gsub(/\\(.)/, '\1') unless /\\[^\\"]/.match?(filename)
|
180
|
-
|
181
|
-
filename.force_encoding ::Encoding.find(encoding) if encoding
|
182
|
-
|
183
|
-
filename
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module HTTPX
|
4
|
-
module Plugins
|
5
|
-
#
|
6
|
-
# This plugin adds support for passing `http-form_data` objects (like file objects) as "multipart/form-data";
|
7
|
-
#
|
8
|
-
# HTTPX.post(URL, form: form: { image: HTTP::FormData::File.new("path/to/file")})
|
9
|
-
#
|
10
|
-
# https://gitlab.com/honeyryderchuck/httpx/wikis/Multipart-Uploads
|
11
|
-
#
|
12
|
-
module Multipart
|
13
|
-
MULTIPART_VALUE_COND = lambda do |value|
|
14
|
-
value.respond_to?(:read) ||
|
15
|
-
(value.respond_to?(:to_hash) &&
|
16
|
-
value.key?(:body) &&
|
17
|
-
(value.key?(:filename) || value.key?(:content_type)))
|
18
|
-
end
|
19
|
-
|
20
|
-
class << self
|
21
|
-
def normalize_keys(key, value, &block)
|
22
|
-
Transcoder.normalize_keys(key, value, MULTIPART_VALUE_COND, &block)
|
23
|
-
end
|
24
|
-
|
25
|
-
def load_dependencies(*)
|
26
|
-
# :nocov:
|
27
|
-
begin
|
28
|
-
unless defined?(HTTP::FormData)
|
29
|
-
# in order not to break legacy code, we'll keep loading http/form_data for them.
|
30
|
-
require "http/form_data"
|
31
|
-
warn "httpx: http/form_data is no longer a requirement to use HTTPX :multipart plugin. See migration instructions under" \
|
32
|
-
"https://honeyryderchuck.gitlab.io/httpx/wiki/Multipart-Uploads.html#notes. \n\n" \
|
33
|
-
"If you'd like to stop seeing this message, require 'http/form_data' yourself."
|
34
|
-
end
|
35
|
-
rescue LoadError
|
36
|
-
end
|
37
|
-
# :nocov:
|
38
|
-
require "httpx/plugins/multipart/encoder"
|
39
|
-
require "httpx/plugins/multipart/decoder"
|
40
|
-
require "httpx/plugins/multipart/part"
|
41
|
-
require "httpx/plugins/multipart/mime_type_detector"
|
42
|
-
end
|
43
|
-
|
44
|
-
def configure(*)
|
45
|
-
Transcoder.register("form", FormTranscoder)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
module FormTranscoder
|
50
|
-
module_function
|
51
|
-
|
52
|
-
def encode(form)
|
53
|
-
if multipart?(form)
|
54
|
-
Encoder.new(form)
|
55
|
-
else
|
56
|
-
Transcoder::Form::Encoder.new(form)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def decode(response)
|
61
|
-
content_type = response.content_type.mime_type
|
62
|
-
|
63
|
-
case content_type
|
64
|
-
when "application/x-www-form-urlencoded"
|
65
|
-
Transcoder::Form.decode(response)
|
66
|
-
when "multipart/form-data"
|
67
|
-
Decoder.new(response)
|
68
|
-
else
|
69
|
-
raise Error, "invalid form mime type (#{content_type})"
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def multipart?(data)
|
74
|
-
data.any? do |_, v|
|
75
|
-
MULTIPART_VALUE_COND.call(v) ||
|
76
|
-
(v.respond_to?(:to_ary) && v.to_ary.any?(&MULTIPART_VALUE_COND)) ||
|
77
|
-
(v.respond_to?(:to_hash) && v.to_hash.any? { |_, e| MULTIPART_VALUE_COND.call(e) })
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
register_plugin :multipart, Multipart
|
83
|
-
end
|
84
|
-
end
|
data/lib/httpx/registry.rb
DELETED
@@ -1,85 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module HTTPX
|
4
|
-
# Adds a general-purpose registry API to a class. It is designed to be a
|
5
|
-
# configuration-level API, i.e. the registry is global to the class and
|
6
|
-
# should be set on **boot time**.
|
7
|
-
#
|
8
|
-
# It is used internally to associate tags with handlers.
|
9
|
-
#
|
10
|
-
# ## Register/Fetch
|
11
|
-
#
|
12
|
-
# One is strongly advised to register handlers when creating the class.
|
13
|
-
#
|
14
|
-
# There is an instance-level method to retrieve from the registry based
|
15
|
-
# on the tag:
|
16
|
-
#
|
17
|
-
# class Server
|
18
|
-
# include HTTPX::Registry
|
19
|
-
#
|
20
|
-
# register "tcp", TCPHandler
|
21
|
-
# register "ssl", SSLHandlers
|
22
|
-
# ...
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# def handle(uri)
|
26
|
-
# scheme = uri.scheme
|
27
|
-
# handler = registry(scheme) #=> TCPHandler
|
28
|
-
# handler.handle
|
29
|
-
# end
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
module Registry
|
33
|
-
# Base Registry Error
|
34
|
-
class Error < Error; end
|
35
|
-
|
36
|
-
def self.extended(klass)
|
37
|
-
super
|
38
|
-
klass.extend(ClassMethods)
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.included(klass)
|
42
|
-
super
|
43
|
-
klass.extend(ClassMethods)
|
44
|
-
klass.__send__(:include, InstanceMethods)
|
45
|
-
end
|
46
|
-
|
47
|
-
# Class Methods
|
48
|
-
module ClassMethods
|
49
|
-
def inherited(klass)
|
50
|
-
super
|
51
|
-
klass.instance_variable_set(:@registry, @registry.dup)
|
52
|
-
end
|
53
|
-
|
54
|
-
# @param [Object] tag the handler identifier in the registry
|
55
|
-
# @return [Symbol, String, Object] the corresponding handler (if Symbol or String,
|
56
|
-
# will assume it referes to an autoloaded module, and will load-and-return it).
|
57
|
-
#
|
58
|
-
def registry(tag = nil)
|
59
|
-
@registry ||= {}
|
60
|
-
return @registry if tag.nil?
|
61
|
-
|
62
|
-
handler = @registry[tag]
|
63
|
-
raise(Error, "#{tag} is not registered in #{self}") unless handler
|
64
|
-
|
65
|
-
handler
|
66
|
-
end
|
67
|
-
|
68
|
-
# @param [Object] tag the identifier for the handler in the registry
|
69
|
-
# @return [Symbol, String, Object] the handler (if Symbol or String, it is
|
70
|
-
# assumed to be an autoloaded module, to be loaded later)
|
71
|
-
#
|
72
|
-
def register(tag, handler)
|
73
|
-
registry[tag] = handler
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# Instance Methods
|
78
|
-
module InstanceMethods
|
79
|
-
# delegates to HTTPX::Registry#registry
|
80
|
-
def registry(tag)
|
81
|
-
self.class.registry(tag)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module HTTPX
|
2
|
-
module Plugins
|
3
|
-
module Compression
|
4
|
-
module Brotli
|
5
|
-
def self.load_dependencies: (singleton(Session)) -> void
|
6
|
-
def self.configure: (singleton(Session)) -> void
|
7
|
-
|
8
|
-
def self?.deflater: () -> _Deflater
|
9
|
-
def self?.decoder: (Numeric bytesize) -> Inflater
|
10
|
-
|
11
|
-
module Deflater
|
12
|
-
extend _Deflater
|
13
|
-
end
|
14
|
-
|
15
|
-
class Inflater
|
16
|
-
include _Inflater
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module HTTPX
|
2
|
-
module Plugins
|
3
|
-
module Compression
|
4
|
-
module Deflate
|
5
|
-
def self.load_dependencies: (singleton(Session)) -> void
|
6
|
-
def self.configure: (singleton(Session)) -> void
|
7
|
-
|
8
|
-
def self?.deflater: () -> _Deflater
|
9
|
-
def self?.inflater: (Numeric bytesize) -> GZIP::Inflater
|
10
|
-
|
11
|
-
module Deflater
|
12
|
-
extend _Deflater
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module HTTPX
|
2
|
-
module Plugins
|
3
|
-
module Compression
|
4
|
-
module GZIP
|
5
|
-
def self.load_dependencies: (singleton(Session)) -> void
|
6
|
-
def self.configure: (singleton(Session)) -> void
|
7
|
-
|
8
|
-
def self?.deflater: () -> _Deflater
|
9
|
-
def self?.inflater: (Numeric bytesize) -> Inflater
|
10
|
-
|
11
|
-
class Deflater
|
12
|
-
include _Deflater
|
13
|
-
|
14
|
-
@compressed_chunk: String
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def initialize: () -> untyped
|
19
|
-
def write: (string) -> void
|
20
|
-
def compressed_chunk: () -> String
|
21
|
-
end
|
22
|
-
|
23
|
-
class Inflater
|
24
|
-
include _Inflater
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
data/sig/registry.rbs
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
module HTTPX::Registry[T, V]
|
2
|
-
class Error < HTTPX::Error
|
3
|
-
end
|
4
|
-
|
5
|
-
# type registrable = Symbol | String | Class
|
6
|
-
|
7
|
-
def self.registry: (T tag) -> Class
|
8
|
-
| () -> Hash[T, V]
|
9
|
-
def self.register: (T tag, V handler) -> void
|
10
|
-
|
11
|
-
def registry: (?T tag) -> V
|
12
|
-
end
|
File without changes
|
File without changes
|