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
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
module Plugins::Cookies
|
|
5
|
+
# The HTTP Cookie.
|
|
6
|
+
#
|
|
7
|
+
# Contains the single cookie info: name, value and attributes.
|
|
8
|
+
class Cookie
|
|
9
|
+
include Comparable
|
|
10
|
+
# Maximum number of bytes per cookie (RFC 6265 6.1 requires 4096 at
|
|
11
|
+
# least)
|
|
12
|
+
MAX_LENGTH = 4096
|
|
13
|
+
|
|
14
|
+
attr_reader :domain, :path, :name, :value, :created_at
|
|
15
|
+
|
|
16
|
+
def path=(path)
|
|
17
|
+
path = String(path)
|
|
18
|
+
@path = path.start_with?("/") ? path : "/"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# See #domain.
|
|
22
|
+
def domain=(domain)
|
|
23
|
+
domain = String(domain)
|
|
24
|
+
|
|
25
|
+
if domain.start_with?(".")
|
|
26
|
+
@for_domain = true
|
|
27
|
+
domain = domain[1..-1]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
return if domain.empty?
|
|
31
|
+
|
|
32
|
+
@domain_name = DomainName.new(domain)
|
|
33
|
+
# RFC 6265 5.3 5.
|
|
34
|
+
@for_domain = false if @domain_name.domain.nil? # a public suffix or IP address
|
|
35
|
+
|
|
36
|
+
@domain = @domain_name.hostname
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Compares the cookie with another. When there are many cookies with
|
|
40
|
+
# the same name for a URL, the value of the smallest must be used.
|
|
41
|
+
def <=>(other)
|
|
42
|
+
# RFC 6265 5.4
|
|
43
|
+
# Precedence: 1. longer path 2. older creation
|
|
44
|
+
(@name <=> other.name).nonzero? ||
|
|
45
|
+
(other.path.length <=> @path.length).nonzero? ||
|
|
46
|
+
(@created_at <=> other.created_at).nonzero? || 0
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class << self
|
|
50
|
+
def new(cookie, *args)
|
|
51
|
+
return cookie if cookie.is_a?(self)
|
|
52
|
+
|
|
53
|
+
super
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Tests if +target_path+ is under +base_path+ as described in RFC
|
|
57
|
+
# 6265 5.1.4. +base_path+ must be an absolute path.
|
|
58
|
+
# +target_path+ may be empty, in which case it is treated as the
|
|
59
|
+
# root path.
|
|
60
|
+
#
|
|
61
|
+
# e.g.
|
|
62
|
+
#
|
|
63
|
+
# path_match?('/admin/', '/admin/index') == true
|
|
64
|
+
# path_match?('/admin/', '/Admin/index') == false
|
|
65
|
+
# path_match?('/admin/', '/admin/') == true
|
|
66
|
+
# path_match?('/admin/', '/admin') == false
|
|
67
|
+
#
|
|
68
|
+
# path_match?('/admin', '/admin') == true
|
|
69
|
+
# path_match?('/admin', '/Admin') == false
|
|
70
|
+
# path_match?('/admin', '/admins') == false
|
|
71
|
+
# path_match?('/admin', '/admin/') == true
|
|
72
|
+
# path_match?('/admin', '/admin/index') == true
|
|
73
|
+
def path_match?(base_path, target_path)
|
|
74
|
+
base_path.start_with?("/") || (return false)
|
|
75
|
+
# RFC 6265 5.1.4
|
|
76
|
+
bsize = base_path.size
|
|
77
|
+
tsize = target_path.size
|
|
78
|
+
return bsize == 1 if tsize.zero? # treat empty target_path as "/"
|
|
79
|
+
return false unless target_path.start_with?(base_path)
|
|
80
|
+
return true if bsize == tsize || base_path.end_with?("/")
|
|
81
|
+
|
|
82
|
+
target_path[bsize] == "/"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def initialize(arg, *attrs)
|
|
87
|
+
@created_at = Time.now
|
|
88
|
+
|
|
89
|
+
if attrs.empty?
|
|
90
|
+
attr_hash = Hash.try_convert(arg)
|
|
91
|
+
else
|
|
92
|
+
@name = arg
|
|
93
|
+
@value, attr_hash = attrs
|
|
94
|
+
attr_hash = Hash.try_convert(attr_hash)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
attr_hash.each do |key, val|
|
|
98
|
+
key = key.downcase.tr("-", "_").to_sym unless key.is_a?(Symbol)
|
|
99
|
+
|
|
100
|
+
case key
|
|
101
|
+
when :domain, :path
|
|
102
|
+
__send__(:"#{key}=", val)
|
|
103
|
+
else
|
|
104
|
+
instance_variable_set(:"@#{key}", val)
|
|
105
|
+
end
|
|
106
|
+
end if attr_hash
|
|
107
|
+
|
|
108
|
+
@path ||= "/"
|
|
109
|
+
raise ArgumentError, "name must be specified" if @name.nil?
|
|
110
|
+
|
|
111
|
+
@name = @name.to_s
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def expires
|
|
115
|
+
@expires || (@created_at && @max_age ? @created_at + @max_age : nil)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def expired?(time = Time.now)
|
|
119
|
+
return false unless expires
|
|
120
|
+
|
|
121
|
+
expires <= time
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Returns a string for use in the Cookie header, i.e. `name=value`
|
|
125
|
+
# or `name="value"`.
|
|
126
|
+
def cookie_value
|
|
127
|
+
"#{@name}=#{Scanner.quote(@value.to_s)}"
|
|
128
|
+
end
|
|
129
|
+
alias_method :to_s, :cookie_value
|
|
130
|
+
|
|
131
|
+
# Tests if it is OK to send this cookie to a given `uri`. A
|
|
132
|
+
# RuntimeError is raised if the cookie's domain is unknown.
|
|
133
|
+
def valid_for_uri?(uri)
|
|
134
|
+
uri = URI(uri)
|
|
135
|
+
# RFC 6265 5.4
|
|
136
|
+
|
|
137
|
+
return false if @secure && uri.scheme != "https"
|
|
138
|
+
|
|
139
|
+
acceptable_from_uri?(uri) && Cookie.path_match?(@path, uri.path)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
# Tests if it is OK to accept this cookie if it is sent from a given
|
|
145
|
+
# URI/URL, `uri`.
|
|
146
|
+
def acceptable_from_uri?(uri)
|
|
147
|
+
uri = URI(uri)
|
|
148
|
+
|
|
149
|
+
host = DomainName.new(uri.host)
|
|
150
|
+
|
|
151
|
+
# RFC 6265 5.3
|
|
152
|
+
if host.hostname == @domain
|
|
153
|
+
true
|
|
154
|
+
elsif @for_domain # !host-only-flag
|
|
155
|
+
host.cookie_domain?(@domain_name)
|
|
156
|
+
else
|
|
157
|
+
@domain.nil?
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
module Scanner
|
|
162
|
+
RE_BAD_CHAR = /([\x00-\x20\x7F",;\\])/.freeze
|
|
163
|
+
|
|
164
|
+
module_function
|
|
165
|
+
|
|
166
|
+
def quote(s)
|
|
167
|
+
return s unless s.match(RE_BAD_CHAR)
|
|
168
|
+
|
|
169
|
+
"\"#{s.gsub(/([\\"])/, "\\\\\\1")}\""
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
module Plugins::Cookies
|
|
5
|
+
# The Cookie Jar
|
|
6
|
+
#
|
|
7
|
+
# It holds a bunch of cookies.
|
|
8
|
+
class Jar
|
|
9
|
+
using URIExtensions
|
|
10
|
+
|
|
11
|
+
include Enumerable
|
|
12
|
+
|
|
13
|
+
def initialize_dup(orig)
|
|
14
|
+
super
|
|
15
|
+
@cookies = orig.instance_variable_get(:@cookies).dup
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(cookies = nil)
|
|
19
|
+
@cookies = []
|
|
20
|
+
|
|
21
|
+
cookies.each do |elem|
|
|
22
|
+
cookie = case elem
|
|
23
|
+
when Cookie
|
|
24
|
+
elem
|
|
25
|
+
when Array
|
|
26
|
+
Cookie.new(*elem)
|
|
27
|
+
else
|
|
28
|
+
Cookie.new(elem)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
@cookies << cookie
|
|
32
|
+
end if cookies
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def parse(set_cookie)
|
|
36
|
+
SetCookieParser.call(set_cookie) do |name, value, attrs|
|
|
37
|
+
add(Cookie.new(name, value, attrs))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def add(cookie, path = nil)
|
|
42
|
+
c = cookie.dup
|
|
43
|
+
|
|
44
|
+
c.path = path if path && c.path == "/"
|
|
45
|
+
|
|
46
|
+
# If the user agent receives a new cookie with the same cookie-name, domain-value, and path-value
|
|
47
|
+
# as a cookie that it has already stored, the existing cookie is evicted and replaced with the new cookie.
|
|
48
|
+
@cookies.delete_if { |ck| ck.name == c.name && ck.domain == c.domain && ck.path == c.path }
|
|
49
|
+
|
|
50
|
+
@cookies << c
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def [](uri)
|
|
54
|
+
each(uri).sort
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def each(uri = nil, &blk)
|
|
58
|
+
return enum_for(__method__, uri) unless blk
|
|
59
|
+
|
|
60
|
+
return @cookies.each(&blk) unless uri
|
|
61
|
+
|
|
62
|
+
now = Time.now
|
|
63
|
+
tpath = uri.path
|
|
64
|
+
|
|
65
|
+
@cookies.delete_if do |cookie|
|
|
66
|
+
if cookie.expired?(now)
|
|
67
|
+
true
|
|
68
|
+
else
|
|
69
|
+
yield cookie if cookie.valid_for_uri?(uri) && Cookie.path_match?(cookie.path, tpath)
|
|
70
|
+
false
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def merge(other)
|
|
76
|
+
cookies_dup = dup
|
|
77
|
+
|
|
78
|
+
other.each do |elem|
|
|
79
|
+
cookie = case elem
|
|
80
|
+
when Cookie
|
|
81
|
+
elem
|
|
82
|
+
when Array
|
|
83
|
+
Cookie.new(*elem)
|
|
84
|
+
else
|
|
85
|
+
Cookie.new(elem)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
cookies_dup.add(cookie)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
cookies_dup
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "strscan"
|
|
4
|
+
require "time"
|
|
5
|
+
|
|
6
|
+
module HTTPX
|
|
7
|
+
module Plugins::Cookies
|
|
8
|
+
module SetCookieParser
|
|
9
|
+
# Whitespace.
|
|
10
|
+
RE_WSP = /[ \t]+/.freeze
|
|
11
|
+
|
|
12
|
+
# A pattern that matches a cookie name or attribute name which may
|
|
13
|
+
# be empty, capturing trailing whitespace.
|
|
14
|
+
RE_NAME = /(?!#{RE_WSP})[^,;\\"=]*/.freeze
|
|
15
|
+
|
|
16
|
+
RE_BAD_CHAR = /([\x00-\x20\x7F",;\\])/.freeze
|
|
17
|
+
|
|
18
|
+
# A pattern that matches the comma in a (typically date) value.
|
|
19
|
+
RE_COOKIE_COMMA = /,(?=#{RE_WSP}?#{RE_NAME}=)/.freeze
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
def scan_dquoted(scanner)
|
|
24
|
+
s = +""
|
|
25
|
+
|
|
26
|
+
until scanner.eos?
|
|
27
|
+
break if scanner.skip(/"/)
|
|
28
|
+
|
|
29
|
+
if scanner.skip(/\\/)
|
|
30
|
+
s << scanner.getch
|
|
31
|
+
elsif scanner.scan(/[^"\\]+/)
|
|
32
|
+
s << scanner.matched
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
s
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def scan_value(scanner, comma_as_separator = false)
|
|
40
|
+
value = +""
|
|
41
|
+
|
|
42
|
+
until scanner.eos?
|
|
43
|
+
if scanner.scan(/[^,;"]+/)
|
|
44
|
+
value << scanner.matched
|
|
45
|
+
elsif scanner.skip(/"/)
|
|
46
|
+
# RFC 6265 2.2
|
|
47
|
+
# A cookie-value may be DQUOTE'd.
|
|
48
|
+
value << scan_dquoted(scanner)
|
|
49
|
+
elsif scanner.check(/;/)
|
|
50
|
+
break
|
|
51
|
+
elsif comma_as_separator && scanner.check(RE_COOKIE_COMMA)
|
|
52
|
+
break
|
|
53
|
+
else
|
|
54
|
+
value << scanner.getch
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
value.rstrip!
|
|
59
|
+
value
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def scan_name_value(scanner, comma_as_separator = false)
|
|
63
|
+
name = scanner.scan(RE_NAME)
|
|
64
|
+
name.rstrip! if name
|
|
65
|
+
|
|
66
|
+
if scanner.skip(/=/)
|
|
67
|
+
value = scan_value(scanner, comma_as_separator)
|
|
68
|
+
else
|
|
69
|
+
scan_value(scanner, comma_as_separator)
|
|
70
|
+
value = nil
|
|
71
|
+
end
|
|
72
|
+
[name, value]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def call(set_cookie)
|
|
76
|
+
scanner = StringScanner.new(set_cookie)
|
|
77
|
+
|
|
78
|
+
# RFC 6265 4.1.1 & 5.2
|
|
79
|
+
until scanner.eos?
|
|
80
|
+
start = scanner.pos
|
|
81
|
+
len = nil
|
|
82
|
+
|
|
83
|
+
scanner.skip(RE_WSP)
|
|
84
|
+
|
|
85
|
+
name, value = scan_name_value(scanner, true)
|
|
86
|
+
value = nil if name && name.empty?
|
|
87
|
+
|
|
88
|
+
attrs = {}
|
|
89
|
+
|
|
90
|
+
until scanner.eos?
|
|
91
|
+
if scanner.skip(/,/)
|
|
92
|
+
# The comma is used as separator for concatenating multiple
|
|
93
|
+
# values of a header.
|
|
94
|
+
len = (scanner.pos - 1) - start
|
|
95
|
+
break
|
|
96
|
+
elsif scanner.skip(/;/)
|
|
97
|
+
scanner.skip(RE_WSP)
|
|
98
|
+
|
|
99
|
+
aname, avalue = scan_name_value(scanner, true)
|
|
100
|
+
|
|
101
|
+
next if (aname.nil? || aname.empty?) || value.nil?
|
|
102
|
+
|
|
103
|
+
aname.downcase!
|
|
104
|
+
|
|
105
|
+
case aname
|
|
106
|
+
when "expires"
|
|
107
|
+
next unless avalue
|
|
108
|
+
|
|
109
|
+
# RFC 6265 5.2.1
|
|
110
|
+
(avalue = Time.parse(avalue)) || next
|
|
111
|
+
when "max-age"
|
|
112
|
+
next unless avalue
|
|
113
|
+
# RFC 6265 5.2.2
|
|
114
|
+
next unless /\A-?\d+\z/.match?(avalue)
|
|
115
|
+
|
|
116
|
+
avalue = Integer(avalue)
|
|
117
|
+
when "domain"
|
|
118
|
+
# RFC 6265 5.2.3
|
|
119
|
+
# An empty value SHOULD be ignored.
|
|
120
|
+
next if avalue.nil? || avalue.empty?
|
|
121
|
+
when "path"
|
|
122
|
+
# RFC 6265 5.2.4
|
|
123
|
+
# A relative path must be ignored rather than normalizing it
|
|
124
|
+
# to "/".
|
|
125
|
+
next unless avalue && avalue.start_with?("/")
|
|
126
|
+
when "secure", "httponly"
|
|
127
|
+
# RFC 6265 5.2.5, 5.2.6
|
|
128
|
+
avalue = true
|
|
129
|
+
end
|
|
130
|
+
attrs[aname] = avalue
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
len ||= scanner.pos - start
|
|
135
|
+
|
|
136
|
+
next if len > Cookie::MAX_LENGTH
|
|
137
|
+
|
|
138
|
+
yield(name, value, attrs) if name && !name.empty? && value
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
|
|
5
|
+
module HTTPX
|
|
6
|
+
module Plugins
|
|
7
|
+
#
|
|
8
|
+
# This plugin implements a persistent cookie jar for the duration of a session.
|
|
9
|
+
#
|
|
10
|
+
# It also adds a *#cookies* helper, so that you can pre-fill the cookies of a session.
|
|
11
|
+
#
|
|
12
|
+
# https://gitlab.com/os85/httpx/wikis/Cookies
|
|
13
|
+
#
|
|
14
|
+
module Cookies
|
|
15
|
+
def self.load_dependencies(*)
|
|
16
|
+
require "httpx/plugins/cookies/jar"
|
|
17
|
+
require "httpx/plugins/cookies/cookie"
|
|
18
|
+
require "httpx/plugins/cookies/set_cookie_parser"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module InstanceMethods
|
|
22
|
+
extend Forwardable
|
|
23
|
+
|
|
24
|
+
def_delegator :@options, :cookies
|
|
25
|
+
|
|
26
|
+
def initialize(options = {}, &blk)
|
|
27
|
+
super({ cookies: Jar.new }.merge(options), &blk)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def wrap
|
|
31
|
+
return super unless block_given?
|
|
32
|
+
|
|
33
|
+
super do |session|
|
|
34
|
+
old_cookies_jar = @options.cookies.dup
|
|
35
|
+
begin
|
|
36
|
+
yield session
|
|
37
|
+
ensure
|
|
38
|
+
@options = @options.merge(cookies: old_cookies_jar)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def build_request(*)
|
|
44
|
+
request = super
|
|
45
|
+
request.headers.set_cookie(request.options.cookies[request.uri])
|
|
46
|
+
request
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def set_request_callbacks(request)
|
|
52
|
+
super
|
|
53
|
+
request.on(:response) do |response|
|
|
54
|
+
next unless response && response.respond_to?(:headers) && (set_cookie = response.headers["set-cookie"])
|
|
55
|
+
|
|
56
|
+
log { "cookies: set-cookie is over #{Cookie::MAX_LENGTH}" } if set_cookie.bytesize > Cookie::MAX_LENGTH
|
|
57
|
+
|
|
58
|
+
@options.cookies.parse(set_cookie)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
module HeadersMethods
|
|
64
|
+
def set_cookie(cookies)
|
|
65
|
+
return if cookies.empty?
|
|
66
|
+
|
|
67
|
+
header_value = cookies.sort.join("; ")
|
|
68
|
+
|
|
69
|
+
add("cookie", header_value)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# adds support for the following options:
|
|
74
|
+
#
|
|
75
|
+
# :cookies :: cookie jar for the session (can be a Hash, an Array, an instance of HTTPX::Plugins::Cookies::CookieJar)
|
|
76
|
+
module OptionsMethods
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def option_headers(*)
|
|
80
|
+
value = super
|
|
81
|
+
|
|
82
|
+
merge_cookie_in_jar(value.delete("cookie"), @cookies) if defined?(@cookies) && value.key?("cookie")
|
|
83
|
+
|
|
84
|
+
value
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def option_cookies(value)
|
|
88
|
+
jar = value.is_a?(Jar) ? value : Jar.new(value)
|
|
89
|
+
|
|
90
|
+
merge_cookie_in_jar(@headers.delete("cookie"), jar) if defined?(@headers) && @headers.key?("cookie")
|
|
91
|
+
|
|
92
|
+
jar
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def merge_cookie_in_jar(cookies, jar)
|
|
96
|
+
cookies.each do |ck|
|
|
97
|
+
ck.split(/ *; */).each do |cookie|
|
|
98
|
+
name, value = cookie.split("=", 2)
|
|
99
|
+
jar.add(Cookie.new(name, value))
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
register_plugin :cookies, Cookies
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
module Plugins
|
|
5
|
+
#
|
|
6
|
+
# This plugin adds helper methods to implement HTTP Digest Auth (https://datatracker.ietf.org/doc/html/rfc7616)
|
|
7
|
+
#
|
|
8
|
+
# https://gitlab.com/os85/httpx/wikis/Auth#digest-auth
|
|
9
|
+
#
|
|
10
|
+
module DigestAuth
|
|
11
|
+
DigestError = Class.new(Error)
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def extra_options(options)
|
|
15
|
+
options.merge(max_concurrent_requests: 1)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def load_dependencies(*)
|
|
19
|
+
require_relative "auth/digest"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# adds support for the following options:
|
|
24
|
+
#
|
|
25
|
+
# :digest :: instance of HTTPX::Plugins::Authentication::Digest, used to authenticate requests in the session.
|
|
26
|
+
module OptionsMethods
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def option_digest(value)
|
|
30
|
+
raise TypeError, ":digest must be a #{Authentication::Digest}" unless value.is_a?(Authentication::Digest)
|
|
31
|
+
|
|
32
|
+
value
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
module InstanceMethods
|
|
37
|
+
def digest_auth(user, password, hashed: false)
|
|
38
|
+
with(digest: Authentication::Digest.new(user, password, hashed: hashed))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def send_requests(*requests)
|
|
44
|
+
requests.flat_map do |request|
|
|
45
|
+
digest = request.options.digest
|
|
46
|
+
|
|
47
|
+
next super(request) unless digest
|
|
48
|
+
|
|
49
|
+
probe_response = wrap { super(request).first }
|
|
50
|
+
|
|
51
|
+
return probe_response unless probe_response.is_a?(Response)
|
|
52
|
+
|
|
53
|
+
if probe_response.status == 401 && digest.can_authenticate?(probe_response.headers["www-authenticate"])
|
|
54
|
+
request.transition(:idle)
|
|
55
|
+
request.headers["authorization"] = digest.authenticate(request, probe_response.headers["www-authenticate"])
|
|
56
|
+
super(request)
|
|
57
|
+
else
|
|
58
|
+
probe_response
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
register_plugin :digest_auth, DigestAuth
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
module Plugins
|
|
5
|
+
#
|
|
6
|
+
# This plugin makes all HTTP/1.1 requests with a body send the "Expect: 100-continue".
|
|
7
|
+
#
|
|
8
|
+
# https://gitlab.com/os85/httpx/wikis/Expect#expect
|
|
9
|
+
#
|
|
10
|
+
module Expect
|
|
11
|
+
EXPECT_TIMEOUT = 2
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def no_expect_store
|
|
15
|
+
@no_expect_store ||= []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def extra_options(options)
|
|
19
|
+
options.merge(expect_timeout: EXPECT_TIMEOUT)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# adds support for the following options:
|
|
24
|
+
#
|
|
25
|
+
# :expect_timeout :: time (in seconds) to wait for a 100-expect response,
|
|
26
|
+
# before retrying without the Expect header (defaults to <tt>2</tt>).
|
|
27
|
+
# :expect_threshold_size :: min threshold (in bytes) of the request payload to enable the 100-continue negotiation on.
|
|
28
|
+
module OptionsMethods
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def option_expect_timeout(value)
|
|
32
|
+
seconds = Float(value)
|
|
33
|
+
raise TypeError, ":expect_timeout must be positive" unless seconds.positive?
|
|
34
|
+
|
|
35
|
+
seconds
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def option_expect_threshold_size(value)
|
|
39
|
+
bytes = Integer(value)
|
|
40
|
+
raise TypeError, ":expect_threshold_size must be positive" unless bytes.positive?
|
|
41
|
+
|
|
42
|
+
bytes
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
module RequestMethods
|
|
47
|
+
def initialize(*)
|
|
48
|
+
super
|
|
49
|
+
return if @body.empty?
|
|
50
|
+
|
|
51
|
+
threshold = @options.expect_threshold_size
|
|
52
|
+
return if threshold && !@body.unbounded_body? && @body.bytesize < threshold
|
|
53
|
+
|
|
54
|
+
return if Expect.no_expect_store.include?(origin)
|
|
55
|
+
|
|
56
|
+
@headers["expect"] = "100-continue"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def response=(response)
|
|
60
|
+
if response.is_a?(Response) &&
|
|
61
|
+
response.status == 100 &&
|
|
62
|
+
!@headers.key?("expect") &&
|
|
63
|
+
(@state == :body || @state == :done)
|
|
64
|
+
|
|
65
|
+
# if we're past this point, this means that we just received a 100-Continue response,
|
|
66
|
+
# but the request doesn't have the expect flag, and is already flushing (or flushed) the body.
|
|
67
|
+
#
|
|
68
|
+
# this means that expect was deactivated for this request too soon, i.e. response took longer.
|
|
69
|
+
#
|
|
70
|
+
# so we have to reactivate it again.
|
|
71
|
+
@headers["expect"] = "100-continue"
|
|
72
|
+
@informational_status = 100
|
|
73
|
+
Expect.no_expect_store.delete(origin)
|
|
74
|
+
end
|
|
75
|
+
super
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
module ConnectionMethods
|
|
80
|
+
def send_request_to_parser(request)
|
|
81
|
+
super
|
|
82
|
+
|
|
83
|
+
return unless request.headers["expect"] == "100-continue"
|
|
84
|
+
|
|
85
|
+
expect_timeout = request.options.expect_timeout
|
|
86
|
+
|
|
87
|
+
return if expect_timeout.nil? || expect_timeout.infinite?
|
|
88
|
+
|
|
89
|
+
set_request_timeout(:expect_timeout, request, expect_timeout, :expect, %i[body response]) do
|
|
90
|
+
# expect timeout expired
|
|
91
|
+
if request.state == :expect && !request.expects?
|
|
92
|
+
Expect.no_expect_store << request.origin
|
|
93
|
+
request.headers.delete("expect")
|
|
94
|
+
consume
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
module InstanceMethods
|
|
101
|
+
def fetch_response(request, selector, options)
|
|
102
|
+
response = super
|
|
103
|
+
|
|
104
|
+
return unless response
|
|
105
|
+
|
|
106
|
+
if response.is_a?(Response) && response.status == 417 && request.headers.key?("expect")
|
|
107
|
+
response.close
|
|
108
|
+
request.headers.delete("expect")
|
|
109
|
+
request.transition(:idle)
|
|
110
|
+
send_request(request, selector, options)
|
|
111
|
+
return
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
response
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
register_plugin :expect, Expect
|
|
119
|
+
end
|
|
120
|
+
end
|