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,194 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
class Socks5Error < ProxyError; end
|
|
5
|
+
|
|
6
|
+
module Plugins
|
|
7
|
+
module Proxy
|
|
8
|
+
module Socks5
|
|
9
|
+
VERSION = 5
|
|
10
|
+
NOAUTH = 0
|
|
11
|
+
PASSWD = 2
|
|
12
|
+
NONE = 0xff
|
|
13
|
+
CONNECT = 1
|
|
14
|
+
IPV4 = 1
|
|
15
|
+
DOMAIN = 3
|
|
16
|
+
IPV6 = 4
|
|
17
|
+
SUCCESS = 0
|
|
18
|
+
|
|
19
|
+
Error = Socks5Error
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
def load_dependencies(*)
|
|
23
|
+
require_relative "../auth/socks5"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def extra_options(options)
|
|
27
|
+
options.merge(supported_proxy_protocols: options.supported_proxy_protocols + %w[socks5])
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module ConnectionMethods
|
|
32
|
+
def call
|
|
33
|
+
super
|
|
34
|
+
|
|
35
|
+
return unless @options.proxy && @options.proxy.uri.scheme == "socks5"
|
|
36
|
+
|
|
37
|
+
case @state
|
|
38
|
+
when :connecting,
|
|
39
|
+
:negotiating,
|
|
40
|
+
:authenticating
|
|
41
|
+
consume
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def connecting?
|
|
46
|
+
super || @state == :authenticating || @state == :negotiating
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def interests
|
|
50
|
+
if @state == :connecting || @state == :authenticating || @state == :negotiating
|
|
51
|
+
return @write_buffer.empty? ? :r : :w
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
super
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def handle_transition(nextstate)
|
|
60
|
+
return super unless @options.proxy && @options.proxy.uri.scheme == "socks5"
|
|
61
|
+
|
|
62
|
+
case nextstate
|
|
63
|
+
when :connecting
|
|
64
|
+
return unless @state == :idle
|
|
65
|
+
|
|
66
|
+
@io.connect
|
|
67
|
+
return unless @io.connected?
|
|
68
|
+
|
|
69
|
+
@write_buffer << Packet.negotiate(@options.proxy)
|
|
70
|
+
__socks5_proxy_connect
|
|
71
|
+
when :authenticating
|
|
72
|
+
return unless @state == :connecting
|
|
73
|
+
|
|
74
|
+
@write_buffer << Packet.authenticate(@options.proxy)
|
|
75
|
+
when :negotiating
|
|
76
|
+
return unless @state == :connecting || @state == :authenticating
|
|
77
|
+
|
|
78
|
+
req = @pending.first
|
|
79
|
+
request_uri = req.uri
|
|
80
|
+
@write_buffer << Packet.connect(request_uri)
|
|
81
|
+
when :connected
|
|
82
|
+
return unless @state == :negotiating
|
|
83
|
+
|
|
84
|
+
@parser = nil
|
|
85
|
+
end
|
|
86
|
+
log(level: 1) { "SOCKS5: #{nextstate}: #{@write_buffer.to_s.inspect}" } unless nextstate == :open
|
|
87
|
+
super
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def __socks5_proxy_connect
|
|
91
|
+
@parser = SocksParser.new(@write_buffer, @options)
|
|
92
|
+
@parser.on(:packet, &method(:__socks5_on_packet))
|
|
93
|
+
transition(:negotiating)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def __socks5_on_packet(packet)
|
|
97
|
+
case @state
|
|
98
|
+
when :connecting
|
|
99
|
+
version, method = packet.unpack("CC")
|
|
100
|
+
__socks5_check_version(version)
|
|
101
|
+
case method
|
|
102
|
+
when PASSWD
|
|
103
|
+
transition(:authenticating)
|
|
104
|
+
nil
|
|
105
|
+
when NONE
|
|
106
|
+
__on_socks5_error("no supported authorization methods")
|
|
107
|
+
else
|
|
108
|
+
transition(:negotiating)
|
|
109
|
+
end
|
|
110
|
+
when :authenticating
|
|
111
|
+
_, status = packet.unpack("CC")
|
|
112
|
+
return transition(:negotiating) if status == SUCCESS
|
|
113
|
+
|
|
114
|
+
__on_socks5_error("socks authentication error: #{status}")
|
|
115
|
+
when :negotiating
|
|
116
|
+
version, reply, = packet.unpack("CC")
|
|
117
|
+
__socks5_check_version(version)
|
|
118
|
+
__on_socks5_error("socks5 negotiation error: #{reply}") unless reply == SUCCESS
|
|
119
|
+
req = @pending.first
|
|
120
|
+
request_uri = req.uri
|
|
121
|
+
@io = ProxySSL.new(@io, request_uri, @options) if request_uri.scheme == "https"
|
|
122
|
+
transition(:connected)
|
|
123
|
+
throw(:called)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def __socks5_check_version(version)
|
|
128
|
+
__on_socks5_error("invalid SOCKS version (#{version})") if version != 5
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def __on_socks5_error(message)
|
|
132
|
+
ex = Error.new(message)
|
|
133
|
+
ex.set_backtrace(caller)
|
|
134
|
+
on_error(ex)
|
|
135
|
+
throw(:called)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
class SocksParser
|
|
140
|
+
include HTTPX::Callbacks
|
|
141
|
+
|
|
142
|
+
def initialize(buffer, options)
|
|
143
|
+
@buffer = buffer
|
|
144
|
+
@options = options
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def close; end
|
|
148
|
+
|
|
149
|
+
def consume(*); end
|
|
150
|
+
|
|
151
|
+
def empty?
|
|
152
|
+
true
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def <<(packet)
|
|
156
|
+
emit(:packet, packet)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
module Packet
|
|
161
|
+
module_function
|
|
162
|
+
|
|
163
|
+
def negotiate(parameters)
|
|
164
|
+
methods = [NOAUTH]
|
|
165
|
+
methods << PASSWD if parameters.can_authenticate?
|
|
166
|
+
methods.unshift(methods.size)
|
|
167
|
+
methods.unshift(VERSION)
|
|
168
|
+
methods.pack("C*")
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def authenticate(parameters)
|
|
172
|
+
parameters.authenticate
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def connect(uri)
|
|
176
|
+
packet = [VERSION, CONNECT, 0].pack("C*")
|
|
177
|
+
begin
|
|
178
|
+
ip = IPAddr.new(uri.host)
|
|
179
|
+
|
|
180
|
+
ipcode = ip.ipv6? ? IPV6 : IPV4
|
|
181
|
+
|
|
182
|
+
packet << [ipcode].pack("C") << ip.hton
|
|
183
|
+
rescue IPAddr::InvalidAddressError
|
|
184
|
+
packet << [DOMAIN, uri.host.bytesize, uri.host].pack("CCA*")
|
|
185
|
+
end
|
|
186
|
+
packet << [uri.port].pack("n")
|
|
187
|
+
packet
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
register_plugin :"proxy/socks5", Proxy::Socks5
|
|
193
|
+
end
|
|
194
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "httpx/plugins/proxy"
|
|
4
|
+
|
|
5
|
+
module HTTPX
|
|
6
|
+
module Plugins
|
|
7
|
+
module Proxy
|
|
8
|
+
module SSH
|
|
9
|
+
class << self
|
|
10
|
+
def load_dependencies(*)
|
|
11
|
+
require "net/ssh/gateway"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module OptionsMethods
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def option_proxy(value)
|
|
19
|
+
Hash[value]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module InstanceMethods
|
|
24
|
+
def request(*args, **options)
|
|
25
|
+
raise ArgumentError, "must perform at least one request" if args.empty?
|
|
26
|
+
|
|
27
|
+
requests = args.first.is_a?(Request) ? args : build_requests(*args, options)
|
|
28
|
+
|
|
29
|
+
request = requests.first or return super
|
|
30
|
+
|
|
31
|
+
request_options = request.options
|
|
32
|
+
|
|
33
|
+
return super unless request_options.proxy
|
|
34
|
+
|
|
35
|
+
ssh_options = request_options.proxy
|
|
36
|
+
ssh_uris = ssh_options.delete(:uri)
|
|
37
|
+
ssh_uri = URI.parse(ssh_uris.shift)
|
|
38
|
+
|
|
39
|
+
return super unless ssh_uri.scheme == "ssh"
|
|
40
|
+
|
|
41
|
+
ssh_username = ssh_options.delete(:username)
|
|
42
|
+
ssh_options[:port] ||= ssh_uri.port || 22
|
|
43
|
+
if request_options.debug
|
|
44
|
+
ssh_options[:verbose] = request_options.debug_level == 2 ? :debug : :info
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
request_uri = URI(requests.first.uri)
|
|
48
|
+
@_gateway = Net::SSH::Gateway.new(ssh_uri.host, ssh_username, ssh_options)
|
|
49
|
+
begin
|
|
50
|
+
@_gateway.open(request_uri.host, request_uri.port) do |local_port|
|
|
51
|
+
io = build_gateway_socket(local_port, request_uri, request_options)
|
|
52
|
+
super(*args, **options.merge(io: io))
|
|
53
|
+
end
|
|
54
|
+
ensure
|
|
55
|
+
@_gateway.shutdown!
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def build_gateway_socket(port, request_uri, options)
|
|
62
|
+
case request_uri.scheme
|
|
63
|
+
when "https"
|
|
64
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
|
65
|
+
ctx_options = SSL::TLS_OPTIONS.merge(options.ssl)
|
|
66
|
+
ctx.set_params(ctx_options) unless ctx_options.empty?
|
|
67
|
+
sock = TCPSocket.open("localhost", port)
|
|
68
|
+
io = OpenSSL::SSL::SSLSocket.new(sock, ctx)
|
|
69
|
+
io.hostname = request_uri.host
|
|
70
|
+
io.sync_close = true
|
|
71
|
+
io.connect
|
|
72
|
+
io.post_connection_check(request_uri.host) if ctx.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
|
73
|
+
io
|
|
74
|
+
when "http"
|
|
75
|
+
TCPSocket.open("localhost", port)
|
|
76
|
+
else
|
|
77
|
+
raise TypeError, "unexpected scheme: #{request_uri.scheme}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
module ConnectionMethods
|
|
83
|
+
# should not coalesce connections here, as the IP is the IP of the proxy
|
|
84
|
+
def coalescable?(*)
|
|
85
|
+
return super unless @options.proxy
|
|
86
|
+
|
|
87
|
+
false
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
register_plugin :"proxy/ssh", Proxy::SSH
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
class ProxyError < ConnectionError; end
|
|
5
|
+
|
|
6
|
+
module Plugins
|
|
7
|
+
#
|
|
8
|
+
# This plugin adds support for proxies. It ships with support for:
|
|
9
|
+
#
|
|
10
|
+
# * HTTP proxies
|
|
11
|
+
# * HTTPS proxies
|
|
12
|
+
# * Socks4/4a proxies
|
|
13
|
+
# * Socks5 proxies
|
|
14
|
+
#
|
|
15
|
+
# https://gitlab.com/os85/httpx/wikis/Proxy
|
|
16
|
+
#
|
|
17
|
+
module Proxy
|
|
18
|
+
class ProxyConnectionError < ProxyError; end
|
|
19
|
+
|
|
20
|
+
PROXY_ERRORS = [TimeoutError, IOError, SystemCallError, Error].freeze
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
def configure(klass)
|
|
24
|
+
klass.plugin(:"proxy/http")
|
|
25
|
+
klass.plugin(:"proxy/socks4")
|
|
26
|
+
klass.plugin(:"proxy/socks5")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def extra_options(options)
|
|
30
|
+
options.merge(supported_proxy_protocols: [])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def subplugins
|
|
34
|
+
{
|
|
35
|
+
retries: ProxyRetries,
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Parameters
|
|
41
|
+
attr_reader :uri, :username, :password, :scheme, :no_proxy
|
|
42
|
+
|
|
43
|
+
def initialize(uri: nil, scheme: nil, username: nil, password: nil, no_proxy: nil, **extra)
|
|
44
|
+
@no_proxy = Array(no_proxy) if no_proxy
|
|
45
|
+
@uris = Array(uri)
|
|
46
|
+
uri = @uris.first
|
|
47
|
+
|
|
48
|
+
@username = username
|
|
49
|
+
@password = password
|
|
50
|
+
|
|
51
|
+
@ns = 0
|
|
52
|
+
|
|
53
|
+
if uri
|
|
54
|
+
@uri = uri.is_a?(URI::Generic) ? uri : URI(uri)
|
|
55
|
+
@username ||= @uri.user
|
|
56
|
+
@password ||= @uri.password
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
@scheme = scheme
|
|
60
|
+
|
|
61
|
+
return unless @uri && @username && @password
|
|
62
|
+
|
|
63
|
+
@authenticator = nil
|
|
64
|
+
@scheme ||= infer_default_auth_scheme(@uri)
|
|
65
|
+
|
|
66
|
+
return unless @scheme
|
|
67
|
+
|
|
68
|
+
@authenticator = load_authenticator(@scheme, @username, @password, **extra)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def shift
|
|
72
|
+
# TODO: this operation must be synchronized
|
|
73
|
+
@ns += 1
|
|
74
|
+
@uri = @uris[@ns]
|
|
75
|
+
|
|
76
|
+
return unless @uri
|
|
77
|
+
|
|
78
|
+
@uri = URI(@uri) unless @uri.is_a?(URI::Generic)
|
|
79
|
+
|
|
80
|
+
scheme = infer_default_auth_scheme(@uri)
|
|
81
|
+
|
|
82
|
+
return unless scheme != @scheme
|
|
83
|
+
|
|
84
|
+
@scheme = scheme
|
|
85
|
+
@username = username || @uri.user
|
|
86
|
+
@password = password || @uri.password
|
|
87
|
+
@authenticator = load_authenticator(scheme, @username, @password)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def can_authenticate?(*args)
|
|
91
|
+
return false unless @authenticator
|
|
92
|
+
|
|
93
|
+
@authenticator.can_authenticate?(*args)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def authenticate(*args)
|
|
97
|
+
return unless @authenticator
|
|
98
|
+
|
|
99
|
+
@authenticator.authenticate(*args)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def ==(other)
|
|
103
|
+
case other
|
|
104
|
+
when Parameters
|
|
105
|
+
@uri == other.uri &&
|
|
106
|
+
@username == other.username &&
|
|
107
|
+
@password == other.password &&
|
|
108
|
+
@scheme == other.scheme
|
|
109
|
+
when URI::Generic, String
|
|
110
|
+
proxy_uri = @uri.dup
|
|
111
|
+
proxy_uri.user = @username
|
|
112
|
+
proxy_uri.password = @password
|
|
113
|
+
other_uri = other.is_a?(URI::Generic) ? other : URI.parse(other)
|
|
114
|
+
proxy_uri == other_uri
|
|
115
|
+
else
|
|
116
|
+
super
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
def infer_default_auth_scheme(uri)
|
|
123
|
+
case uri.scheme
|
|
124
|
+
when "socks5"
|
|
125
|
+
uri.scheme
|
|
126
|
+
when "http", "https"
|
|
127
|
+
"basic"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def load_authenticator(scheme, username, password, **extra)
|
|
132
|
+
auth_scheme = scheme.to_s.capitalize
|
|
133
|
+
|
|
134
|
+
require_relative "auth/#{scheme}" unless defined?(Authentication) && Authentication.const_defined?(auth_scheme, false)
|
|
135
|
+
|
|
136
|
+
Authentication.const_get(auth_scheme).new(username, password, **extra)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# adds support for the following options:
|
|
141
|
+
#
|
|
142
|
+
# :proxy :: proxy options defining *:uri*, *:username*, *:password* or
|
|
143
|
+
# *:scheme* (i.e. <tt>{ uri: "http://proxy" }</tt>)
|
|
144
|
+
module OptionsMethods
|
|
145
|
+
private
|
|
146
|
+
|
|
147
|
+
def option_proxy(value)
|
|
148
|
+
value.is_a?(Parameters) ? value : Parameters.new(**Hash[value])
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def option_supported_proxy_protocols(value)
|
|
152
|
+
raise TypeError, ":supported_proxy_protocols must be an Array" unless value.is_a?(Array)
|
|
153
|
+
|
|
154
|
+
value.map(&:to_s)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
module InstanceMethods
|
|
159
|
+
def find_connection(request_uri, selector, options)
|
|
160
|
+
return super unless options.respond_to?(:proxy)
|
|
161
|
+
|
|
162
|
+
if (next_proxy = request_uri.find_proxy)
|
|
163
|
+
return super(request_uri, selector, options.merge(proxy: Parameters.new(uri: next_proxy)))
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
proxy = options.proxy
|
|
167
|
+
|
|
168
|
+
return super unless proxy
|
|
169
|
+
|
|
170
|
+
next_proxy = proxy.uri
|
|
171
|
+
|
|
172
|
+
raise ProxyError, "Failed to connect to proxy" unless next_proxy
|
|
173
|
+
|
|
174
|
+
raise ProxyError,
|
|
175
|
+
"#{next_proxy.scheme}: unsupported proxy protocol" unless options.supported_proxy_protocols.include?(next_proxy.scheme)
|
|
176
|
+
|
|
177
|
+
if (no_proxy = proxy.no_proxy)
|
|
178
|
+
no_proxy = no_proxy.join(",") if no_proxy.is_a?(Array)
|
|
179
|
+
|
|
180
|
+
# TODO: setting proxy to nil leaks the connection object in the pool
|
|
181
|
+
return super(request_uri, selector, options.merge(proxy: nil)) unless URI::Generic.use_proxy?(request_uri.host, next_proxy.host,
|
|
182
|
+
next_proxy.port, no_proxy)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
super(request_uri, selector, options.merge(proxy: proxy))
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
private
|
|
189
|
+
|
|
190
|
+
def fetch_response(request, selector, options)
|
|
191
|
+
response = request.response # in case it goes wrong later
|
|
192
|
+
|
|
193
|
+
begin
|
|
194
|
+
response = super
|
|
195
|
+
|
|
196
|
+
if response.is_a?(ErrorResponse) && proxy_error?(request, response, options)
|
|
197
|
+
options.proxy.shift
|
|
198
|
+
|
|
199
|
+
# return last error response if no more proxies to try
|
|
200
|
+
return response if options.proxy.uri.nil?
|
|
201
|
+
|
|
202
|
+
log { "failed connecting to proxy, trying next..." }
|
|
203
|
+
request.transition(:idle)
|
|
204
|
+
send_request(request, selector, options)
|
|
205
|
+
return
|
|
206
|
+
end
|
|
207
|
+
response
|
|
208
|
+
rescue ProxyError
|
|
209
|
+
# may happen if coupled with retries, and there are no more proxies to try, in which case
|
|
210
|
+
# it'll end up here
|
|
211
|
+
response
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def proxy_error?(_request, response, options)
|
|
216
|
+
return false unless options.proxy
|
|
217
|
+
|
|
218
|
+
error = response.error
|
|
219
|
+
case error
|
|
220
|
+
when NativeResolveError
|
|
221
|
+
proxy_uri = URI(options.proxy.uri)
|
|
222
|
+
|
|
223
|
+
unresolved_host = error.host
|
|
224
|
+
|
|
225
|
+
# failed resolving proxy domain
|
|
226
|
+
unresolved_host == proxy_uri.host
|
|
227
|
+
when ResolveError
|
|
228
|
+
proxy_uri = URI(options.proxy.uri)
|
|
229
|
+
|
|
230
|
+
error.message.end_with?(proxy_uri.to_s)
|
|
231
|
+
when ProxyConnectionError
|
|
232
|
+
# timeout errors connecting to proxy
|
|
233
|
+
true
|
|
234
|
+
else
|
|
235
|
+
false
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
module ConnectionMethods
|
|
241
|
+
using URIExtensions
|
|
242
|
+
|
|
243
|
+
def initialize(*)
|
|
244
|
+
super
|
|
245
|
+
return unless @options.proxy
|
|
246
|
+
|
|
247
|
+
# redefining the connection origin as the proxy's URI,
|
|
248
|
+
# as this will be used as the tcp peer ip.
|
|
249
|
+
@proxy_uri = URI(@options.proxy.uri)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def peer
|
|
253
|
+
@proxy_uri || super
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def connecting?
|
|
257
|
+
return super unless @options.proxy
|
|
258
|
+
|
|
259
|
+
super || @state == :connecting || @state == :connected
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def call
|
|
263
|
+
super
|
|
264
|
+
|
|
265
|
+
return unless @options.proxy
|
|
266
|
+
|
|
267
|
+
case @state
|
|
268
|
+
when :connecting
|
|
269
|
+
consume
|
|
270
|
+
end
|
|
271
|
+
rescue *PROXY_ERRORS => e
|
|
272
|
+
if connecting?
|
|
273
|
+
error = ProxyConnectionError.new(e.message)
|
|
274
|
+
error.set_backtrace(e.backtrace)
|
|
275
|
+
raise error
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
raise e
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def reset
|
|
282
|
+
return super unless @options.proxy
|
|
283
|
+
|
|
284
|
+
@state = :open
|
|
285
|
+
|
|
286
|
+
super
|
|
287
|
+
# emit(:close)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
private
|
|
291
|
+
|
|
292
|
+
def initialize_type(uri, options)
|
|
293
|
+
return super unless options.proxy
|
|
294
|
+
|
|
295
|
+
"tcp"
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def connect
|
|
299
|
+
return super unless @options.proxy
|
|
300
|
+
|
|
301
|
+
case @state
|
|
302
|
+
when :idle
|
|
303
|
+
transition(:connecting)
|
|
304
|
+
when :connected
|
|
305
|
+
transition(:open)
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def handle_transition(nextstate)
|
|
310
|
+
return super unless @options.proxy
|
|
311
|
+
|
|
312
|
+
case nextstate
|
|
313
|
+
when :closing
|
|
314
|
+
# this is a hack so that we can use the super method
|
|
315
|
+
# and it'll think that the current state is open
|
|
316
|
+
@state = :open if @state == :connecting
|
|
317
|
+
end
|
|
318
|
+
super
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def purge_after_closed
|
|
322
|
+
super
|
|
323
|
+
@io = @io.proxy_io if @io.respond_to?(:proxy_io)
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
module ProxyRetries
|
|
328
|
+
module InstanceMethods
|
|
329
|
+
def retryable_error?(ex)
|
|
330
|
+
super || ex.is_a?(ProxyConnectionError)
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
register_plugin :proxy, Proxy
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
class ProxySSL < SSL
|
|
339
|
+
attr_reader :proxy_io
|
|
340
|
+
|
|
341
|
+
def initialize(tcp, request_uri, options)
|
|
342
|
+
@proxy_io = tcp
|
|
343
|
+
@io = tcp.to_io
|
|
344
|
+
super(request_uri, tcp.addresses, options)
|
|
345
|
+
@hostname = request_uri.host
|
|
346
|
+
@state = :connected
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
module Plugins
|
|
5
|
+
#
|
|
6
|
+
# This plugin adds support for HTTP/2 Push responses.
|
|
7
|
+
#
|
|
8
|
+
# In order to benefit from this, requests are sent one at a time, so that
|
|
9
|
+
# no push responses are received after corresponding request has been sent.
|
|
10
|
+
#
|
|
11
|
+
# https://gitlab.com/os85/httpx/wikis/Server-Push
|
|
12
|
+
#
|
|
13
|
+
module PushPromise
|
|
14
|
+
def self.extra_options(options)
|
|
15
|
+
options.merge(http2_settings: { settings_enable_push: 1 },
|
|
16
|
+
max_concurrent_requests: 1)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module ResponseMethods
|
|
20
|
+
def pushed?
|
|
21
|
+
@__pushed
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def mark_as_pushed!
|
|
25
|
+
@__pushed = true
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module InstanceMethods
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def promise_headers
|
|
33
|
+
@promise_headers ||= {}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def on_promise(parser, stream)
|
|
37
|
+
stream.on(:promise_headers) do |h|
|
|
38
|
+
__on_promise_request(parser, stream, h)
|
|
39
|
+
end
|
|
40
|
+
stream.on(:headers) do |h|
|
|
41
|
+
__on_promise_response(parser, stream, h)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def __on_promise_request(parser, stream, h)
|
|
46
|
+
log(level: 1, color: :yellow) do
|
|
47
|
+
# :nocov:
|
|
48
|
+
h.map { |k, v| "#{stream.id}: -> PROMISE HEADER: #{k}: #{v}" }.join("\n")
|
|
49
|
+
# :nocov:
|
|
50
|
+
end
|
|
51
|
+
headers = @options.headers_class.new(h)
|
|
52
|
+
path = headers[":path"]
|
|
53
|
+
authority = headers[":authority"]
|
|
54
|
+
|
|
55
|
+
request = parser.pending.find { |r| r.authority == authority && r.path == path }
|
|
56
|
+
if request
|
|
57
|
+
request.merge_headers(headers)
|
|
58
|
+
promise_headers[stream] = request
|
|
59
|
+
parser.pending.delete(request)
|
|
60
|
+
parser.streams[request] = stream
|
|
61
|
+
request.transition(:done)
|
|
62
|
+
else
|
|
63
|
+
stream.refuse
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def __on_promise_response(parser, stream, h)
|
|
68
|
+
request = promise_headers.delete(stream)
|
|
69
|
+
return unless request
|
|
70
|
+
|
|
71
|
+
parser.__send__(:on_stream_headers, stream, request, h)
|
|
72
|
+
response = request.response
|
|
73
|
+
response.mark_as_pushed!
|
|
74
|
+
stream.on(:data, &parser.method(:on_stream_data).curry(3)[stream, request])
|
|
75
|
+
stream.on(:close, &parser.method(:on_stream_close).curry(3)[stream, request])
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
register_plugin(:push_promise, PushPromise)
|
|
80
|
+
end
|
|
81
|
+
end
|