httpx-patched 1.6.2.1
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 +7 -0
- data/LICENSE.txt +191 -0
- data/README.md +162 -0
- data/doc/release_notes/0_0_1.md +7 -0
- data/doc/release_notes/0_0_2.md +9 -0
- data/doc/release_notes/0_0_3.md +9 -0
- data/doc/release_notes/0_0_4.md +7 -0
- data/doc/release_notes/0_0_5.md +5 -0
- data/doc/release_notes/0_10_0.md +66 -0
- data/doc/release_notes/0_10_1.md +37 -0
- data/doc/release_notes/0_10_2.md +5 -0
- data/doc/release_notes/0_11_0.md +74 -0
- data/doc/release_notes/0_11_1.md +5 -0
- data/doc/release_notes/0_11_2.md +5 -0
- data/doc/release_notes/0_11_3.md +5 -0
- data/doc/release_notes/0_12_0.md +55 -0
- data/doc/release_notes/0_13_0.md +58 -0
- 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/doc/release_notes/0_14_3.md +5 -0
- data/doc/release_notes/0_14_4.md +5 -0
- data/doc/release_notes/0_14_5.md +11 -0
- data/doc/release_notes/0_15_0.md +53 -0
- data/doc/release_notes/0_15_1.md +8 -0
- data/doc/release_notes/0_15_2.md +9 -0
- data/doc/release_notes/0_15_3.md +5 -0
- data/doc/release_notes/0_15_4.md +5 -0
- data/doc/release_notes/0_16_0.md +93 -0
- data/doc/release_notes/0_16_1.md +5 -0
- data/doc/release_notes/0_17_0.md +49 -0
- data/doc/release_notes/0_18_0.md +69 -0
- data/doc/release_notes/0_18_1.md +12 -0
- data/doc/release_notes/0_18_2.md +10 -0
- data/doc/release_notes/0_18_3.md +7 -0
- data/doc/release_notes/0_18_4.md +14 -0
- data/doc/release_notes/0_18_5.md +10 -0
- data/doc/release_notes/0_18_6.md +5 -0
- data/doc/release_notes/0_18_7.md +5 -0
- data/doc/release_notes/0_19_0.md +39 -0
- data/doc/release_notes/0_19_1.md +5 -0
- data/doc/release_notes/0_19_2.md +7 -0
- data/doc/release_notes/0_19_3.md +6 -0
- data/doc/release_notes/0_19_4.md +14 -0
- data/doc/release_notes/0_19_5.md +13 -0
- data/doc/release_notes/0_19_6.md +5 -0
- data/doc/release_notes/0_19_7.md +5 -0
- data/doc/release_notes/0_19_8.md +5 -0
- data/doc/release_notes/0_1_0.md +9 -0
- data/doc/release_notes/0_20_0.md +36 -0
- 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/0_2_0.md +5 -0
- data/doc/release_notes/0_2_1.md +16 -0
- data/doc/release_notes/0_3_0.md +12 -0
- data/doc/release_notes/0_3_1.md +6 -0
- data/doc/release_notes/0_4_0.md +51 -0
- data/doc/release_notes/0_4_1.md +3 -0
- data/doc/release_notes/0_5_0.md +15 -0
- data/doc/release_notes/0_5_1.md +14 -0
- data/doc/release_notes/0_6_0.md +5 -0
- data/doc/release_notes/0_6_1.md +6 -0
- data/doc/release_notes/0_6_2.md +6 -0
- data/doc/release_notes/0_6_3.md +13 -0
- data/doc/release_notes/0_6_4.md +21 -0
- data/doc/release_notes/0_6_5.md +22 -0
- data/doc/release_notes/0_6_6.md +19 -0
- data/doc/release_notes/0_6_7.md +5 -0
- data/doc/release_notes/0_7_0.md +46 -0
- data/doc/release_notes/0_8_0.md +27 -0
- data/doc/release_notes/0_8_1.md +8 -0
- data/doc/release_notes/0_8_2.md +7 -0
- data/doc/release_notes/0_9_0.md +38 -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/doc/release_notes/1_3_2.md +6 -0
- data/doc/release_notes/1_3_3.md +5 -0
- data/doc/release_notes/1_3_4.md +6 -0
- data/doc/release_notes/1_4_0.md +43 -0
- data/doc/release_notes/1_4_1.md +19 -0
- data/doc/release_notes/1_4_2.md +20 -0
- data/doc/release_notes/1_4_3.md +11 -0
- data/doc/release_notes/1_4_4.md +14 -0
- data/doc/release_notes/1_5_0.md +126 -0
- data/doc/release_notes/1_5_1.md +6 -0
- data/doc/release_notes/1_6_0.md +50 -0
- data/doc/release_notes/1_6_1.md +17 -0
- data/doc/release_notes/1_6_2.md +11 -0
- data/lib/httpx/adapters/datadog.rb +359 -0
- data/lib/httpx/adapters/faraday.rb +303 -0
- data/lib/httpx/adapters/sentry.rb +121 -0
- data/lib/httpx/adapters/webmock.rb +175 -0
- data/lib/httpx/altsvc.rb +163 -0
- data/lib/httpx/base64.rb +27 -0
- data/lib/httpx/buffer.rb +61 -0
- data/lib/httpx/callbacks.rb +35 -0
- data/lib/httpx/chainable.rb +106 -0
- data/lib/httpx/connection/http1.rb +399 -0
- data/lib/httpx/connection/http2.rb +468 -0
- data/lib/httpx/connection.rb +954 -0
- data/lib/httpx/domain_name.rb +145 -0
- data/lib/httpx/errors.rb +111 -0
- data/lib/httpx/extensions.rb +59 -0
- data/lib/httpx/headers.rb +176 -0
- data/lib/httpx/io/ssl.rb +163 -0
- data/lib/httpx/io/tcp.rb +239 -0
- data/lib/httpx/io/udp.rb +62 -0
- data/lib/httpx/io/unix.rb +71 -0
- data/lib/httpx/io.rb +11 -0
- data/lib/httpx/loggable.rb +56 -0
- data/lib/httpx/options.rb +463 -0
- data/lib/httpx/parser/http1.rb +186 -0
- data/lib/httpx/plugins/auth/basic.rb +20 -0
- data/lib/httpx/plugins/auth/digest.rb +102 -0
- data/lib/httpx/plugins/auth/ntlm.rb +35 -0
- data/lib/httpx/plugins/auth/socks5.rb +22 -0
- data/lib/httpx/plugins/auth.rb +25 -0
- data/lib/httpx/plugins/aws_sdk_authentication.rb +111 -0
- data/lib/httpx/plugins/aws_sigv4.rb +239 -0
- data/lib/httpx/plugins/basic_auth.rb +29 -0
- data/lib/httpx/plugins/brotli.rb +50 -0
- data/lib/httpx/plugins/callbacks.rb +127 -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 +147 -0
- data/lib/httpx/plugins/content_digest.rb +204 -0
- data/lib/httpx/plugins/cookies/cookie.rb +174 -0
- data/lib/httpx/plugins/cookies/jar.rb +95 -0
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +143 -0
- data/lib/httpx/plugins/cookies.rb +107 -0
- data/lib/httpx/plugins/digest_auth.rb +67 -0
- data/lib/httpx/plugins/expect.rb +120 -0
- data/lib/httpx/plugins/fiber_concurrency.rb +195 -0
- data/lib/httpx/plugins/follow_redirects.rb +233 -0
- data/lib/httpx/plugins/grpc/call.rb +63 -0
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +90 -0
- data/lib/httpx/plugins/grpc/message.rb +55 -0
- data/lib/httpx/plugins/grpc.rb +282 -0
- data/lib/httpx/plugins/h2c.rb +127 -0
- data/lib/httpx/plugins/internal_telemetry.rb +107 -0
- data/lib/httpx/plugins/ntlm_auth.rb +62 -0
- data/lib/httpx/plugins/oauth.rb +183 -0
- data/lib/httpx/plugins/persistent.rb +82 -0
- data/lib/httpx/plugins/proxy/http.rb +184 -0
- data/lib/httpx/plugins/proxy/socks4.rb +135 -0
- data/lib/httpx/plugins/proxy/socks5.rb +194 -0
- data/lib/httpx/plugins/proxy/ssh.rb +94 -0
- data/lib/httpx/plugins/proxy.rb +349 -0
- data/lib/httpx/plugins/push_promise.rb +81 -0
- data/lib/httpx/plugins/query.rb +35 -0
- data/lib/httpx/plugins/rate_limiter.rb +55 -0
- data/lib/httpx/plugins/response_cache/file_store.rb +140 -0
- data/lib/httpx/plugins/response_cache/store.rb +33 -0
- data/lib/httpx/plugins/response_cache.rb +333 -0
- data/lib/httpx/plugins/retries.rb +230 -0
- data/lib/httpx/plugins/ssrf_filter.rb +145 -0
- data/lib/httpx/plugins/stream.rb +183 -0
- data/lib/httpx/plugins/stream_bidi.rb +315 -0
- data/lib/httpx/plugins/upgrade/h2.rb +64 -0
- data/lib/httpx/plugins/upgrade.rb +86 -0
- data/lib/httpx/plugins/webdav.rb +86 -0
- data/lib/httpx/plugins/xml.rb +76 -0
- data/lib/httpx/pmatch_extensions.rb +33 -0
- data/lib/httpx/pool.rb +190 -0
- data/lib/httpx/punycode.rb +22 -0
- data/lib/httpx/request/body.rb +158 -0
- data/lib/httpx/request.rb +328 -0
- data/lib/httpx/resolver/entry.rb +30 -0
- data/lib/httpx/resolver/https.rb +256 -0
- data/lib/httpx/resolver/multi.rb +102 -0
- data/lib/httpx/resolver/native.rb +547 -0
- data/lib/httpx/resolver/resolver.rb +173 -0
- data/lib/httpx/resolver/system.rb +255 -0
- data/lib/httpx/resolver.rb +189 -0
- data/lib/httpx/response/body.rb +242 -0
- data/lib/httpx/response/buffer.rb +115 -0
- data/lib/httpx/response.rb +304 -0
- data/lib/httpx/selector.rb +282 -0
- data/lib/httpx/session.rb +612 -0
- data/lib/httpx/session_extensions.rb +30 -0
- data/lib/httpx/timers.rb +133 -0
- data/lib/httpx/transcoder/body.rb +43 -0
- data/lib/httpx/transcoder/chunker.rb +115 -0
- data/lib/httpx/transcoder/deflate.rb +37 -0
- data/lib/httpx/transcoder/form.rb +68 -0
- data/lib/httpx/transcoder/gzip.rb +71 -0
- data/lib/httpx/transcoder/json.rb +71 -0
- data/lib/httpx/transcoder/multipart/decoder.rb +141 -0
- data/lib/httpx/transcoder/multipart/encoder.rb +120 -0
- data/lib/httpx/transcoder/multipart/mime_type_detector.rb +78 -0
- data/lib/httpx/transcoder/multipart/part.rb +35 -0
- data/lib/httpx/transcoder/multipart.rb +31 -0
- data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
- data/lib/httpx/transcoder/utils/deflater.rb +75 -0
- data/lib/httpx/transcoder.rb +91 -0
- data/lib/httpx/utils.rb +75 -0
- data/lib/httpx/version.rb +5 -0
- data/lib/httpx.rb +66 -0
- data/sig/altsvc.rbs +33 -0
- data/sig/buffer.rbs +27 -0
- data/sig/callbacks.rbs +15 -0
- data/sig/chainable.rbs +55 -0
- data/sig/connection/http1.rbs +85 -0
- data/sig/connection/http2.rbs +116 -0
- data/sig/connection.rbs +169 -0
- data/sig/domain_name.rbs +17 -0
- data/sig/errors.rbs +69 -0
- data/sig/headers.rbs +49 -0
- data/sig/httpx.rbs +27 -0
- data/sig/io/ssl.rbs +27 -0
- data/sig/io/tcp.rbs +72 -0
- data/sig/io/udp.rbs +25 -0
- data/sig/io/unix.rbs +26 -0
- data/sig/io.rbs +3 -0
- data/sig/loggable.rbs +17 -0
- data/sig/options.rbs +202 -0
- data/sig/parser/http1.rbs +59 -0
- data/sig/plugins/auth/basic.rbs +17 -0
- data/sig/plugins/auth/digest.rbs +25 -0
- data/sig/plugins/auth/ntlm.rbs +20 -0
- data/sig/plugins/auth/socks5.rbs +18 -0
- data/sig/plugins/auth.rbs +13 -0
- data/sig/plugins/aws_sdk_authentication.rbs +43 -0
- data/sig/plugins/aws_sigv4.rbs +78 -0
- data/sig/plugins/basic_auth.rbs +15 -0
- 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 +57 -0
- data/sig/plugins/content_digest.rbs +51 -0
- data/sig/plugins/cookies/cookie.rbs +55 -0
- data/sig/plugins/cookies/jar.rbs +26 -0
- data/sig/plugins/cookies/set_cookie_parser.rbs +22 -0
- data/sig/plugins/cookies.rbs +28 -0
- data/sig/plugins/digest_auth.rbs +21 -0
- data/sig/plugins/expect.rbs +15 -0
- data/sig/plugins/fiber_concurrency.rbs +51 -0
- data/sig/plugins/follow_redirects.rbs +47 -0
- data/sig/plugins/grpc/call.rbs +23 -0
- data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
- data/sig/plugins/grpc/message.rbs +17 -0
- data/sig/plugins/grpc.rbs +65 -0
- data/sig/plugins/h2c.rbs +27 -0
- data/sig/plugins/ntlm_auth.rbs +21 -0
- data/sig/plugins/oauth.rbs +68 -0
- data/sig/plugins/persistent.rbs +14 -0
- data/sig/plugins/proxy/http.rbs +30 -0
- data/sig/plugins/proxy/socks4.rbs +37 -0
- data/sig/plugins/proxy/socks5.rbs +49 -0
- data/sig/plugins/proxy/ssh.rbs +18 -0
- data/sig/plugins/proxy.rbs +70 -0
- data/sig/plugins/push_promise.rbs +23 -0
- data/sig/plugins/query.rbs +18 -0
- data/sig/plugins/rate_limiter.rbs +13 -0
- data/sig/plugins/response_cache/file_store.rbs +19 -0
- data/sig/plugins/response_cache/store.rbs +13 -0
- data/sig/plugins/response_cache.rbs +86 -0
- data/sig/plugins/retries.rbs +66 -0
- data/sig/plugins/ssrf_filter.rbs +26 -0
- data/sig/plugins/stream.rbs +54 -0
- data/sig/plugins/stream_bidi.rbs +68 -0
- data/sig/plugins/upgrade/h2.rbs +9 -0
- data/sig/plugins/upgrade.rbs +29 -0
- data/sig/plugins/webdav.rbs +23 -0
- data/sig/plugins/xml.rbs +37 -0
- data/sig/pool.rbs +51 -0
- data/sig/punycode.rbs +5 -0
- data/sig/request/body.rbs +34 -0
- data/sig/request.rbs +88 -0
- data/sig/resolver/entry.rbs +13 -0
- data/sig/resolver/https.rbs +45 -0
- data/sig/resolver/multi.rbs +32 -0
- data/sig/resolver/native.rbs +74 -0
- data/sig/resolver/resolver.rbs +64 -0
- data/sig/resolver/system.rbs +34 -0
- data/sig/resolver.rbs +48 -0
- data/sig/response/body.rbs +52 -0
- data/sig/response/buffer.rbs +23 -0
- data/sig/response.rbs +103 -0
- data/sig/selector.rbs +68 -0
- data/sig/session.rbs +104 -0
- data/sig/timers.rbs +54 -0
- data/sig/transcoder/body.rbs +24 -0
- data/sig/transcoder/chunker.rbs +49 -0
- data/sig/transcoder/deflate.rbs +12 -0
- data/sig/transcoder/form.rbs +34 -0
- data/sig/transcoder/gzip.rbs +27 -0
- data/sig/transcoder/json.rbs +28 -0
- data/sig/transcoder/multipart.rbs +103 -0
- data/sig/transcoder/utils/body_reader.rbs +15 -0
- data/sig/transcoder/utils/deflater.rbs +28 -0
- data/sig/transcoder.rbs +43 -0
- data/sig/utils.rbs +19 -0
- metadata +518 -0
data/lib/httpx/timers.rb
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
class Timers
|
|
5
|
+
def initialize
|
|
6
|
+
@intervals = []
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def after(interval_in_secs, cb = nil, &blk)
|
|
10
|
+
callback = cb || blk
|
|
11
|
+
|
|
12
|
+
raise Error, "timer must have a callback" unless callback
|
|
13
|
+
|
|
14
|
+
# I'm assuming here that most requests will have the same
|
|
15
|
+
# request timeout, as in most cases they share common set of
|
|
16
|
+
# options. A user setting different request timeouts for 100s of
|
|
17
|
+
# requests will already have a hard time dealing with that.
|
|
18
|
+
unless (interval = @intervals.bsearch { |t| t.interval == interval_in_secs })
|
|
19
|
+
interval = Interval.new(interval_in_secs)
|
|
20
|
+
@intervals << interval
|
|
21
|
+
@intervals.sort!
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
interval << callback
|
|
25
|
+
|
|
26
|
+
@next_interval_at = nil
|
|
27
|
+
|
|
28
|
+
Timer.new(interval, callback)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def wait_interval
|
|
32
|
+
drop_elapsed!
|
|
33
|
+
|
|
34
|
+
return if @intervals.empty?
|
|
35
|
+
|
|
36
|
+
@next_interval_at = Utils.now
|
|
37
|
+
|
|
38
|
+
@intervals.first.interval
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def fire(error = nil)
|
|
42
|
+
raise error if error && error.timeout != @intervals.first
|
|
43
|
+
return if @intervals.empty? || !@next_interval_at
|
|
44
|
+
|
|
45
|
+
elapsed_time = Utils.elapsed_time(@next_interval_at)
|
|
46
|
+
|
|
47
|
+
drop_elapsed!(elapsed_time)
|
|
48
|
+
|
|
49
|
+
@intervals = @intervals.drop_while { |interval| interval.elapse(elapsed_time) <= 0 }
|
|
50
|
+
|
|
51
|
+
@next_interval_at = nil if @intervals.empty?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def drop_elapsed!(elapsed_time = 0)
|
|
57
|
+
# check first, if not elapsed, then return
|
|
58
|
+
first_interval = @intervals.first
|
|
59
|
+
|
|
60
|
+
return unless first_interval && first_interval.elapsed?(elapsed_time)
|
|
61
|
+
|
|
62
|
+
# TODO: would be nice to have a drop_while!
|
|
63
|
+
@intervals = @intervals.drop_while { |interval| interval.elapse(elapsed_time) <= 0 }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class Timer
|
|
67
|
+
def initialize(interval, callback)
|
|
68
|
+
@interval = interval
|
|
69
|
+
@callback = callback
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def cancel
|
|
73
|
+
@interval.delete(@callback)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class Interval
|
|
78
|
+
include Comparable
|
|
79
|
+
|
|
80
|
+
attr_reader :interval
|
|
81
|
+
|
|
82
|
+
def initialize(interval)
|
|
83
|
+
@interval = interval
|
|
84
|
+
@callbacks = []
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def <=>(other)
|
|
88
|
+
@interval <=> other.interval
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def ==(other)
|
|
92
|
+
return @interval == other if other.is_a?(Numeric)
|
|
93
|
+
|
|
94
|
+
@interval == other.to_f # rubocop:disable Lint/FloatComparison
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def to_f
|
|
98
|
+
Float(@interval)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def <<(callback)
|
|
102
|
+
@callbacks << callback
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def delete(callback)
|
|
106
|
+
@callbacks.delete(callback)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def no_callbacks?
|
|
110
|
+
@callbacks.empty?
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def elapsed?(elapsed = 0)
|
|
114
|
+
(@interval - elapsed) <= 0 || @callbacks.empty?
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def elapse(elapsed)
|
|
118
|
+
# same as elapsing
|
|
119
|
+
return 0 if @callbacks.empty?
|
|
120
|
+
|
|
121
|
+
@interval -= elapsed
|
|
122
|
+
|
|
123
|
+
if @interval <= 0
|
|
124
|
+
cb = @callbacks.dup
|
|
125
|
+
cb.each(&:call)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
@interval
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
private_constant :Interval
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "delegate"
|
|
4
|
+
|
|
5
|
+
module HTTPX::Transcoder
|
|
6
|
+
module Body
|
|
7
|
+
class Error < HTTPX::Error; end
|
|
8
|
+
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
class Encoder < SimpleDelegator
|
|
12
|
+
def initialize(body)
|
|
13
|
+
body = body.open(File::RDONLY, encoding: Encoding::BINARY) if Object.const_defined?(:Pathname) && body.is_a?(Pathname)
|
|
14
|
+
@body = body
|
|
15
|
+
super(body)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def bytesize
|
|
19
|
+
if @body.respond_to?(:bytesize)
|
|
20
|
+
@body.bytesize
|
|
21
|
+
elsif @body.respond_to?(:to_ary)
|
|
22
|
+
@body.sum(&:bytesize)
|
|
23
|
+
elsif @body.respond_to?(:size)
|
|
24
|
+
@body.size || Float::INFINITY
|
|
25
|
+
elsif @body.respond_to?(:length)
|
|
26
|
+
@body.length || Float::INFINITY
|
|
27
|
+
elsif @body.respond_to?(:each)
|
|
28
|
+
Float::INFINITY
|
|
29
|
+
else
|
|
30
|
+
raise Error, "cannot determine size of body: #{@body.inspect}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def content_type
|
|
35
|
+
"application/octet-stream"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def encode(body)
|
|
40
|
+
Encoder.new(body)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
|
|
5
|
+
module HTTPX::Transcoder
|
|
6
|
+
module Chunker
|
|
7
|
+
class Error < HTTPX::Error; end
|
|
8
|
+
|
|
9
|
+
CRLF = "\r\n".b
|
|
10
|
+
|
|
11
|
+
class Encoder
|
|
12
|
+
extend Forwardable
|
|
13
|
+
|
|
14
|
+
def initialize(body)
|
|
15
|
+
@raw = body
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def each
|
|
19
|
+
return enum_for(__method__) unless block_given?
|
|
20
|
+
|
|
21
|
+
@raw.each do |chunk|
|
|
22
|
+
yield "#{chunk.bytesize.to_s(16)}#{CRLF}#{chunk}#{CRLF}"
|
|
23
|
+
end
|
|
24
|
+
yield "0#{CRLF}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def respond_to_missing?(meth, *args)
|
|
28
|
+
@raw.respond_to?(meth, *args) || super
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class Decoder
|
|
33
|
+
extend Forwardable
|
|
34
|
+
|
|
35
|
+
def_delegator :@buffer, :empty?
|
|
36
|
+
|
|
37
|
+
def_delegator :@buffer, :<<
|
|
38
|
+
|
|
39
|
+
def_delegator :@buffer, :clear
|
|
40
|
+
|
|
41
|
+
def initialize(buffer, trailers = false)
|
|
42
|
+
@buffer = buffer
|
|
43
|
+
@chunk_buffer = "".b
|
|
44
|
+
@finished = false
|
|
45
|
+
@state = :length
|
|
46
|
+
@trailers = trailers
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def to_s
|
|
50
|
+
@buffer
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def each
|
|
54
|
+
loop do
|
|
55
|
+
case @state
|
|
56
|
+
when :length
|
|
57
|
+
index = @buffer.index(CRLF)
|
|
58
|
+
return unless index && index.positive?
|
|
59
|
+
|
|
60
|
+
# Read hex-length
|
|
61
|
+
hexlen = @buffer.byteslice(0, index)
|
|
62
|
+
@buffer = @buffer.byteslice(index..-1) || "".b
|
|
63
|
+
hexlen[/\h/] || raise(Error, "wrong chunk size line: #{hexlen}")
|
|
64
|
+
@chunk_length = hexlen.hex
|
|
65
|
+
# check if is last chunk
|
|
66
|
+
@finished = @chunk_length.zero?
|
|
67
|
+
nextstate(:crlf)
|
|
68
|
+
when :crlf
|
|
69
|
+
crlf_size = @finished && !@trailers ? 4 : 2
|
|
70
|
+
# consume CRLF
|
|
71
|
+
return if @buffer.bytesize < crlf_size
|
|
72
|
+
raise Error, "wrong chunked encoding format" unless @buffer.start_with?(CRLF * (crlf_size / 2))
|
|
73
|
+
|
|
74
|
+
@buffer = @buffer.byteslice(crlf_size..-1)
|
|
75
|
+
if @chunk_length.nil?
|
|
76
|
+
nextstate(:length)
|
|
77
|
+
else
|
|
78
|
+
return if @finished
|
|
79
|
+
|
|
80
|
+
nextstate(:data)
|
|
81
|
+
end
|
|
82
|
+
when :data
|
|
83
|
+
chunk = @buffer.byteslice(0, @chunk_length)
|
|
84
|
+
@buffer = @buffer.byteslice(@chunk_length..-1) || "".b
|
|
85
|
+
@chunk_buffer << chunk
|
|
86
|
+
@chunk_length -= chunk.bytesize
|
|
87
|
+
if @chunk_length.zero?
|
|
88
|
+
yield @chunk_buffer unless @chunk_buffer.empty?
|
|
89
|
+
@chunk_buffer.clear
|
|
90
|
+
@chunk_length = nil
|
|
91
|
+
nextstate(:crlf)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
break if @buffer.empty?
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def finished?
|
|
99
|
+
@finished
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def nextstate(state)
|
|
105
|
+
@state = state
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
module_function
|
|
110
|
+
|
|
111
|
+
def encode(chunks)
|
|
112
|
+
Encoder.new(chunks)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zlib"
|
|
4
|
+
require_relative "utils/deflater"
|
|
5
|
+
|
|
6
|
+
module HTTPX
|
|
7
|
+
module Transcoder
|
|
8
|
+
module Deflate
|
|
9
|
+
class Deflater < Transcoder::Deflater
|
|
10
|
+
def deflate(chunk)
|
|
11
|
+
@deflater ||= Zlib::Deflate.new
|
|
12
|
+
|
|
13
|
+
if chunk.nil?
|
|
14
|
+
unless @deflater.closed?
|
|
15
|
+
last = @deflater.finish
|
|
16
|
+
@deflater.close
|
|
17
|
+
last.empty? ? nil : last
|
|
18
|
+
end
|
|
19
|
+
else
|
|
20
|
+
@deflater.deflate(chunk)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
def encode(body)
|
|
28
|
+
Deflater.new(body)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def decode(response, bytesize: nil)
|
|
32
|
+
bytesize ||= response.headers.key?("content-length") ? response.headers["content-length"].to_i : Float::INFINITY
|
|
33
|
+
GZIP::Inflater.new(bytesize)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
require "uri"
|
|
5
|
+
require_relative "multipart"
|
|
6
|
+
|
|
7
|
+
module HTTPX
|
|
8
|
+
module Transcoder
|
|
9
|
+
module Form
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
PARAM_DEPTH_LIMIT = 32
|
|
13
|
+
|
|
14
|
+
class Encoder
|
|
15
|
+
extend Forwardable
|
|
16
|
+
|
|
17
|
+
def_delegator :@raw, :to_s
|
|
18
|
+
|
|
19
|
+
def_delegator :@raw, :to_str
|
|
20
|
+
|
|
21
|
+
def_delegator :@raw, :bytesize
|
|
22
|
+
|
|
23
|
+
def_delegator :@raw, :==
|
|
24
|
+
|
|
25
|
+
def initialize(form)
|
|
26
|
+
@raw = form.each_with_object("".b) do |(key, val), buf|
|
|
27
|
+
HTTPX::Transcoder.normalize_keys(key, val) do |k, v|
|
|
28
|
+
buf << "&" unless buf.empty?
|
|
29
|
+
buf << URI.encode_www_form_component(k)
|
|
30
|
+
buf << "=#{URI.encode_www_form_component(v.to_s)}" unless v.nil?
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def content_type
|
|
36
|
+
"application/x-www-form-urlencoded"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module Decoder
|
|
41
|
+
module_function
|
|
42
|
+
|
|
43
|
+
def call(response, *)
|
|
44
|
+
URI.decode_www_form(response.to_s).each_with_object({}) do |(field, value), params|
|
|
45
|
+
HTTPX::Transcoder.normalize_query(params, field, value, PARAM_DEPTH_LIMIT)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def encode(form)
|
|
51
|
+
Encoder.new(form)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def decode(response)
|
|
55
|
+
content_type = response.content_type.mime_type
|
|
56
|
+
|
|
57
|
+
case content_type
|
|
58
|
+
when "application/x-www-form-urlencoded"
|
|
59
|
+
Decoder
|
|
60
|
+
when "multipart/form-data"
|
|
61
|
+
Multipart::Decoder.new(response)
|
|
62
|
+
else
|
|
63
|
+
raise Error, "invalid form mime type (#{content_type})"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zlib"
|
|
4
|
+
|
|
5
|
+
module HTTPX
|
|
6
|
+
module Transcoder
|
|
7
|
+
module GZIP
|
|
8
|
+
class Deflater < Transcoder::Deflater
|
|
9
|
+
def initialize(body)
|
|
10
|
+
@compressed_chunk = "".b
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def deflate(chunk)
|
|
15
|
+
@deflater ||= Zlib::GzipWriter.new(self)
|
|
16
|
+
|
|
17
|
+
if chunk.nil?
|
|
18
|
+
unless @deflater.closed?
|
|
19
|
+
@deflater.flush
|
|
20
|
+
@deflater.close
|
|
21
|
+
compressed_chunk
|
|
22
|
+
end
|
|
23
|
+
else
|
|
24
|
+
@deflater.write(chunk)
|
|
25
|
+
compressed_chunk
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def write(chunk)
|
|
32
|
+
@compressed_chunk << chunk
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def compressed_chunk
|
|
36
|
+
@compressed_chunk.dup
|
|
37
|
+
ensure
|
|
38
|
+
@compressed_chunk.clear
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class Inflater
|
|
43
|
+
def initialize(bytesize)
|
|
44
|
+
@inflater = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
|
|
45
|
+
@bytesize = bytesize
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def call(chunk)
|
|
49
|
+
buffer = @inflater.inflate(chunk)
|
|
50
|
+
@bytesize -= chunk.bytesize
|
|
51
|
+
if @bytesize <= 0
|
|
52
|
+
buffer << @inflater.finish
|
|
53
|
+
@inflater.close
|
|
54
|
+
end
|
|
55
|
+
buffer
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
module_function
|
|
60
|
+
|
|
61
|
+
def encode(body)
|
|
62
|
+
Deflater.new(body)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def decode(response, bytesize: nil)
|
|
66
|
+
bytesize ||= response.headers.key?("content-length") ? response.headers["content-length"].to_i : Float::INFINITY
|
|
67
|
+
Inflater.new(bytesize)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
|
|
5
|
+
module HTTPX::Transcoder
|
|
6
|
+
module JSON
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
JSON_REGEX = %r{
|
|
10
|
+
\b
|
|
11
|
+
application/
|
|
12
|
+
# optional vendor specific type
|
|
13
|
+
(?:
|
|
14
|
+
# token as per https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
|
|
15
|
+
[!#$%&'*+\-.^_`|~0-9a-z]+
|
|
16
|
+
# literal plus sign
|
|
17
|
+
\+
|
|
18
|
+
)?
|
|
19
|
+
json
|
|
20
|
+
\b
|
|
21
|
+
}ix.freeze
|
|
22
|
+
|
|
23
|
+
class Encoder
|
|
24
|
+
extend Forwardable
|
|
25
|
+
|
|
26
|
+
def_delegator :@raw, :to_s
|
|
27
|
+
|
|
28
|
+
def_delegator :@raw, :bytesize
|
|
29
|
+
|
|
30
|
+
def_delegator :@raw, :==
|
|
31
|
+
|
|
32
|
+
def initialize(json)
|
|
33
|
+
@raw = JSON.json_dump(json)
|
|
34
|
+
@charset = @raw.encoding.name.downcase
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def content_type
|
|
38
|
+
"application/json; charset=#{@charset}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def encode(json)
|
|
43
|
+
Encoder.new(json)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def decode(response)
|
|
47
|
+
content_type = response.content_type.mime_type
|
|
48
|
+
|
|
49
|
+
raise HTTPX::Error, "invalid json mime type (#{content_type})" unless JSON_REGEX.match?(content_type)
|
|
50
|
+
|
|
51
|
+
method(:json_load)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# rubocop:disable Style/SingleLineMethods
|
|
55
|
+
if defined?(MultiJson)
|
|
56
|
+
def json_load(*args); MultiJson.load(*args); end
|
|
57
|
+
def json_dump(*args); MultiJson.dump(*args); end
|
|
58
|
+
elsif defined?(Oj)
|
|
59
|
+
def json_load(response, *args); Oj.load(response.to_s, *args); end
|
|
60
|
+
def json_dump(obj, options = {}); Oj.dump(obj, { mode: :compat }.merge(options)); end
|
|
61
|
+
elsif defined?(Yajl)
|
|
62
|
+
def json_load(response, *args); Yajl::Parser.new(*args).parse(response.to_s); end
|
|
63
|
+
def json_dump(*args); Yajl::Encoder.encode(*args); end
|
|
64
|
+
else
|
|
65
|
+
require "json"
|
|
66
|
+
def json_load(*args); ::JSON.parse(*args); end
|
|
67
|
+
def json_dump(*args); ::JSON.dump(*args); end
|
|
68
|
+
end
|
|
69
|
+
# rubocop:enable Style/SingleLineMethods
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tempfile"
|
|
4
|
+
require "delegate"
|
|
5
|
+
|
|
6
|
+
module HTTPX
|
|
7
|
+
module Transcoder
|
|
8
|
+
module Multipart
|
|
9
|
+
class FilePart < SimpleDelegator
|
|
10
|
+
attr_reader :original_filename, :content_type
|
|
11
|
+
|
|
12
|
+
def initialize(filename, content_type)
|
|
13
|
+
@original_filename = filename
|
|
14
|
+
@content_type = content_type
|
|
15
|
+
@current = nil
|
|
16
|
+
@file = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR)
|
|
17
|
+
super(@file)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Decoder
|
|
22
|
+
include HTTPX::Utils
|
|
23
|
+
|
|
24
|
+
CRLF = "\r\n"
|
|
25
|
+
BOUNDARY_RE = /;\s*boundary=([^;]+)/i.freeze
|
|
26
|
+
MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{CRLF}/ni.freeze
|
|
27
|
+
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*;\s*name=(#{VALUE})/ni.freeze
|
|
28
|
+
MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{CRLF}]*)/ni.freeze
|
|
29
|
+
WINDOW_SIZE = 2 << 14
|
|
30
|
+
|
|
31
|
+
def initialize(response)
|
|
32
|
+
@boundary = begin
|
|
33
|
+
m = response.headers["content-type"].to_s[BOUNDARY_RE, 1]
|
|
34
|
+
raise Error, "no boundary declared in content-type header" unless m
|
|
35
|
+
|
|
36
|
+
m.strip
|
|
37
|
+
end
|
|
38
|
+
@buffer = "".b
|
|
39
|
+
@parts = {}
|
|
40
|
+
@intermediate_boundary = "--#{@boundary}"
|
|
41
|
+
@state = :idle
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def call(response, *)
|
|
45
|
+
response.body.each do |chunk|
|
|
46
|
+
@buffer << chunk
|
|
47
|
+
|
|
48
|
+
parse
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
raise Error, "invalid or unsupported multipart format" unless @buffer.empty?
|
|
52
|
+
|
|
53
|
+
@parts
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def parse
|
|
59
|
+
case @state
|
|
60
|
+
when :idle
|
|
61
|
+
raise Error, "payload does not start with boundary" unless @buffer.start_with?("#{@intermediate_boundary}#{CRLF}")
|
|
62
|
+
|
|
63
|
+
@buffer = @buffer.byteslice(@intermediate_boundary.bytesize + 2..-1)
|
|
64
|
+
|
|
65
|
+
@state = :part_header
|
|
66
|
+
when :part_header
|
|
67
|
+
idx = @buffer.index("#{CRLF}#{CRLF}")
|
|
68
|
+
|
|
69
|
+
# raise Error, "couldn't parse part headers" unless idx
|
|
70
|
+
return unless idx
|
|
71
|
+
|
|
72
|
+
# @type var head: String
|
|
73
|
+
head = @buffer.byteslice(0..idx + 4 - 1)
|
|
74
|
+
|
|
75
|
+
@buffer = @buffer.byteslice(head.bytesize..-1)
|
|
76
|
+
|
|
77
|
+
content_type = head[MULTIPART_CONTENT_TYPE, 1] || "text/plain"
|
|
78
|
+
if (name = head[MULTIPART_CONTENT_DISPOSITION, 1])
|
|
79
|
+
name = /\A"(.*)"\Z/ =~ name ? Regexp.last_match(1) : name.dup
|
|
80
|
+
name.gsub!(/\\(.)/, "\\1")
|
|
81
|
+
name
|
|
82
|
+
else
|
|
83
|
+
name = head[MULTIPART_CONTENT_ID, 1]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
filename = HTTPX::Utils.get_filename(head)
|
|
87
|
+
|
|
88
|
+
name = filename || +"#{content_type}[]" if name.nil? || name.empty?
|
|
89
|
+
|
|
90
|
+
@current = name
|
|
91
|
+
|
|
92
|
+
@parts[name] = if filename
|
|
93
|
+
FilePart.new(filename, content_type)
|
|
94
|
+
else
|
|
95
|
+
"".b
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
@state = :part_body
|
|
99
|
+
when :part_body
|
|
100
|
+
part = @parts[@current]
|
|
101
|
+
|
|
102
|
+
body_separator = if part.is_a?(FilePart)
|
|
103
|
+
"#{CRLF}#{CRLF}"
|
|
104
|
+
else
|
|
105
|
+
CRLF
|
|
106
|
+
end
|
|
107
|
+
idx = @buffer.index(body_separator)
|
|
108
|
+
|
|
109
|
+
if idx
|
|
110
|
+
payload = @buffer.byteslice(0..idx - 1)
|
|
111
|
+
@buffer = @buffer.byteslice(idx + body_separator.bytesize..-1)
|
|
112
|
+
part << payload
|
|
113
|
+
part.rewind if part.respond_to?(:rewind)
|
|
114
|
+
@state = :parse_boundary
|
|
115
|
+
else
|
|
116
|
+
part << @buffer
|
|
117
|
+
@buffer.clear
|
|
118
|
+
end
|
|
119
|
+
when :parse_boundary
|
|
120
|
+
raise Error, "payload does not start with boundary" unless @buffer.start_with?(@intermediate_boundary)
|
|
121
|
+
|
|
122
|
+
@buffer = @buffer.byteslice(@intermediate_boundary.bytesize..-1)
|
|
123
|
+
|
|
124
|
+
if @buffer == "--"
|
|
125
|
+
@buffer.clear
|
|
126
|
+
@state = :done
|
|
127
|
+
return
|
|
128
|
+
elsif @buffer.start_with?(CRLF)
|
|
129
|
+
@buffer = @buffer.byteslice(2..-1)
|
|
130
|
+
@state = :part_header
|
|
131
|
+
else
|
|
132
|
+
return
|
|
133
|
+
end
|
|
134
|
+
when :done
|
|
135
|
+
raise Error, "parsing should have been over by now"
|
|
136
|
+
end until @buffer.empty?
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|