httpx 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/httpx.rb +8 -2
- data/lib/httpx/adapters/faraday.rb +203 -0
- data/lib/httpx/altsvc.rb +4 -0
- data/lib/httpx/callbacks.rb +1 -4
- data/lib/httpx/chainable.rb +4 -3
- data/lib/httpx/connection.rb +326 -104
- data/lib/httpx/{channel → connection}/http1.rb +29 -15
- data/lib/httpx/{channel → connection}/http2.rb +12 -6
- data/lib/httpx/errors.rb +2 -0
- data/lib/httpx/headers.rb +4 -1
- data/lib/httpx/io/ssl.rb +5 -1
- data/lib/httpx/io/tcp.rb +13 -7
- data/lib/httpx/io/udp.rb +1 -0
- data/lib/httpx/io/unix.rb +1 -0
- data/lib/httpx/loggable.rb +34 -9
- data/lib/httpx/options.rb +57 -31
- data/lib/httpx/parser/http1.rb +8 -0
- data/lib/httpx/plugins/authentication.rb +4 -0
- data/lib/httpx/plugins/basic_authentication.rb +4 -0
- data/lib/httpx/plugins/compression.rb +22 -5
- data/lib/httpx/plugins/cookies.rb +89 -36
- data/lib/httpx/plugins/digest_authentication.rb +45 -26
- data/lib/httpx/plugins/follow_redirects.rb +61 -62
- data/lib/httpx/plugins/h2c.rb +78 -39
- data/lib/httpx/plugins/multipart.rb +5 -0
- data/lib/httpx/plugins/persistent.rb +29 -0
- data/lib/httpx/plugins/proxy.rb +125 -78
- data/lib/httpx/plugins/proxy/http.rb +31 -27
- data/lib/httpx/plugins/proxy/socks4.rb +30 -24
- data/lib/httpx/plugins/proxy/socks5.rb +49 -39
- data/lib/httpx/plugins/proxy/ssh.rb +81 -0
- data/lib/httpx/plugins/push_promise.rb +18 -9
- data/lib/httpx/plugins/retries.rb +43 -15
- data/lib/httpx/pool.rb +159 -0
- data/lib/httpx/registry.rb +2 -0
- data/lib/httpx/request.rb +10 -0
- data/lib/httpx/resolver.rb +2 -1
- data/lib/httpx/resolver/https.rb +62 -56
- data/lib/httpx/resolver/native.rb +48 -37
- data/lib/httpx/resolver/resolver_mixin.rb +16 -11
- data/lib/httpx/resolver/system.rb +11 -7
- data/lib/httpx/response.rb +24 -10
- data/lib/httpx/selector.rb +32 -39
- data/lib/httpx/{client.rb → session.rb} +99 -62
- data/lib/httpx/timeout.rb +7 -15
- data/lib/httpx/transcoder/body.rb +4 -0
- data/lib/httpx/transcoder/chunker.rb +4 -0
- data/lib/httpx/version.rb +1 -1
- metadata +10 -8
- data/lib/httpx/channel.rb +0 -367
@@ -6,53 +6,56 @@ module HTTPX
|
|
6
6
|
module Plugins
|
7
7
|
module Proxy
|
8
8
|
module HTTP
|
9
|
-
|
9
|
+
module ConnectionMethods
|
10
10
|
private
|
11
11
|
|
12
|
-
def proxy_connect
|
13
|
-
req, _ = @pending.first
|
14
|
-
# if the first request after CONNECT is to an https address, it is assumed that
|
15
|
-
# all requests in the queue are not only ALL HTTPS, but they also share the certificate,
|
16
|
-
# and therefore, will share the connection.
|
17
|
-
#
|
18
|
-
if req.uri.scheme == "https"
|
19
|
-
connect_request = ConnectRequest.new(req.uri)
|
20
|
-
if @parameters.authenticated?
|
21
|
-
connect_request.headers["proxy-authentication"] = "Basic #{@parameters.token_authentication}"
|
22
|
-
end
|
23
|
-
parser.send(connect_request)
|
24
|
-
else
|
25
|
-
transition(:connected)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
12
|
def transition(nextstate)
|
13
|
+
return super unless @options.proxy && @options.proxy.uri.scheme == "http"
|
14
|
+
|
30
15
|
case nextstate
|
31
16
|
when :connecting
|
32
17
|
return unless @state == :idle
|
18
|
+
|
33
19
|
@io.connect
|
34
20
|
return unless @io.connected?
|
21
|
+
|
35
22
|
@parser = ConnectProxyParser.new(@write_buffer, @options.merge(max_concurrent_requests: 1))
|
36
|
-
@parser.once(:response, &method(:
|
23
|
+
@parser.once(:response, &method(:__http_on_connect))
|
37
24
|
@parser.on(:close) { transition(:closing) }
|
38
|
-
|
25
|
+
__http_proxy_connect
|
39
26
|
return if @state == :connected
|
40
27
|
when :connected
|
41
28
|
return unless @state == :idle || @state == :connecting
|
29
|
+
|
42
30
|
case @state
|
43
31
|
when :connecting
|
44
32
|
@parser.close
|
45
33
|
@parser = nil
|
46
34
|
when :idle
|
47
35
|
@parser = ProxyParser.new(@write_buffer, @options)
|
48
|
-
@parser
|
36
|
+
set_parser_callbacks(@parser)
|
49
37
|
@parser.on(:close) { transition(:closing) }
|
50
38
|
end
|
51
39
|
end
|
52
40
|
super
|
53
41
|
end
|
54
42
|
|
55
|
-
def
|
43
|
+
def __http_proxy_connect
|
44
|
+
req, _ = @pending.first
|
45
|
+
# if the first request after CONNECT is to an https address, it is assumed that
|
46
|
+
# all requests in the queue are not only ALL HTTPS, but they also share the certificate,
|
47
|
+
# and therefore, will share the connection.
|
48
|
+
#
|
49
|
+
if req.uri.scheme == "https"
|
50
|
+
connect_request = ConnectRequest.new(req.uri, @options)
|
51
|
+
|
52
|
+
parser.send(connect_request)
|
53
|
+
else
|
54
|
+
transition(:connected)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def __http_on_connect(_request, response)
|
56
59
|
if response.status == 200
|
57
60
|
req, _ = @pending.first
|
58
61
|
request_uri = req.uri
|
@@ -68,7 +71,7 @@ module HTTPX
|
|
68
71
|
end
|
69
72
|
end
|
70
73
|
|
71
|
-
class ProxyParser <
|
74
|
+
class ProxyParser < Connection::HTTP1
|
72
75
|
def headline_uri(request)
|
73
76
|
request.uri.to_s
|
74
77
|
end
|
@@ -85,6 +88,7 @@ module HTTPX
|
|
85
88
|
|
86
89
|
def headline_uri(request)
|
87
90
|
return super unless request.verb == :connect
|
91
|
+
|
88
92
|
uri = request.uri
|
89
93
|
tunnel = "#{uri.hostname}:#{uri.port}"
|
90
94
|
log { "establishing HTTP proxy tunnel to #{tunnel}" }
|
@@ -98,8 +102,10 @@ module HTTPX
|
|
98
102
|
end
|
99
103
|
|
100
104
|
class ConnectRequest < Request
|
101
|
-
def initialize(uri, options
|
102
|
-
super(:connect, uri,
|
105
|
+
def initialize(uri, options)
|
106
|
+
super(:connect, uri, {})
|
107
|
+
proxy_params = options.proxy
|
108
|
+
@headers["proxy-authentication"] = "Basic #{proxy_params.token_authentication}" if proxy_params.authenticated?
|
103
109
|
@headers.delete("accept")
|
104
110
|
end
|
105
111
|
|
@@ -107,8 +113,6 @@ module HTTPX
|
|
107
113
|
"#{@uri.hostname}:#{@uri.port}"
|
108
114
|
end
|
109
115
|
end
|
110
|
-
|
111
|
-
Parameters.register("http", HTTPProxyChannel)
|
112
116
|
end
|
113
117
|
end
|
114
118
|
register_plugin :"proxy/http", Proxy::HTTP
|
@@ -10,58 +10,63 @@ module HTTPX
|
|
10
10
|
VERSION = 4
|
11
11
|
CONNECT = 1
|
12
12
|
GRANTED = 90
|
13
|
+
PROTOCOLS = %w[socks4 socks4a].freeze
|
13
14
|
|
14
15
|
Error = Class.new(Error)
|
15
16
|
|
16
|
-
|
17
|
+
module ConnectionMethods
|
17
18
|
private
|
18
19
|
|
19
|
-
def proxy_connect
|
20
|
-
@parser = SocksParser.new(@write_buffer, @options)
|
21
|
-
@parser.once(:packet, &method(:on_packet))
|
22
|
-
end
|
23
|
-
|
24
|
-
def on_packet(packet)
|
25
|
-
_version, status, _port, _ip = packet.unpack("CCnN")
|
26
|
-
if status == GRANTED
|
27
|
-
req, _ = @pending.first
|
28
|
-
request_uri = req.uri
|
29
|
-
@io = ProxySSL.new(@io, request_uri, @options) if request_uri.scheme == "https"
|
30
|
-
transition(:connected)
|
31
|
-
throw(:called)
|
32
|
-
else
|
33
|
-
on_socks_error("socks error: #{status}")
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
20
|
def transition(nextstate)
|
21
|
+
return super unless @options.proxy && PROTOCOLS.include?(@options.proxy.uri.scheme)
|
22
|
+
|
38
23
|
case nextstate
|
39
24
|
when :connecting
|
40
25
|
return unless @state == :idle
|
26
|
+
|
41
27
|
@io.connect
|
42
28
|
return unless @io.connected?
|
29
|
+
|
43
30
|
req, _ = @pending.first
|
44
31
|
return unless req
|
32
|
+
|
45
33
|
request_uri = req.uri
|
46
|
-
@write_buffer << Packet.connect(@
|
47
|
-
|
34
|
+
@write_buffer << Packet.connect(@options.proxy, request_uri)
|
35
|
+
__socks4_proxy_connect
|
48
36
|
when :connected
|
49
37
|
return unless @state == :connecting
|
38
|
+
|
50
39
|
@parser = nil
|
51
40
|
end
|
52
41
|
log(level: 1, label: "SOCKS4: ") { "#{nextstate}: #{@write_buffer.to_s.inspect}" } unless nextstate == :open
|
53
42
|
super
|
54
43
|
end
|
55
44
|
|
56
|
-
def
|
45
|
+
def __socks4_proxy_connect
|
46
|
+
@parser = SocksParser.new(@write_buffer, @options)
|
47
|
+
@parser.once(:packet, &method(:__socks4_on_packet))
|
48
|
+
end
|
49
|
+
|
50
|
+
def __socks4_on_packet(packet)
|
51
|
+
_version, status, _port, _ip = packet.unpack("CCnN")
|
52
|
+
if status == GRANTED
|
53
|
+
req, _ = @pending.first
|
54
|
+
request_uri = req.uri
|
55
|
+
@io = ProxySSL.new(@io, request_uri, @options) if request_uri.scheme == "https"
|
56
|
+
transition(:connected)
|
57
|
+
throw(:called)
|
58
|
+
else
|
59
|
+
on_socks4_error("socks error: #{status}")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_socks4_error(message)
|
57
64
|
ex = Error.new(message)
|
58
65
|
ex.set_backtrace(caller)
|
59
66
|
on_error(ex)
|
60
67
|
throw(:called)
|
61
68
|
end
|
62
69
|
end
|
63
|
-
Parameters.register("socks4", Socks4ProxyChannel)
|
64
|
-
Parameters.register("socks4a", Socks4ProxyChannel)
|
65
70
|
|
66
71
|
class SocksParser
|
67
72
|
include Callbacks
|
@@ -92,6 +97,7 @@ module HTTPX
|
|
92
97
|
begin
|
93
98
|
ip = IPAddr.new(uri.host)
|
94
99
|
raise Error, "Socks4 connection to #{ip} not supported" unless ip.ipv4?
|
100
|
+
|
95
101
|
packet << [ip.to_i].pack("N")
|
96
102
|
rescue IPAddr::InvalidAddressError
|
97
103
|
if parameters.uri.scheme == "socks4"
|
@@ -16,9 +16,12 @@ module HTTPX
|
|
16
16
|
|
17
17
|
Error = Class.new(Error)
|
18
18
|
|
19
|
-
|
19
|
+
module ConnectionMethods
|
20
20
|
def call
|
21
21
|
super
|
22
|
+
|
23
|
+
return unless @options.proxy && @options.proxy.uri.scheme == "socks5"
|
24
|
+
|
22
25
|
case @state
|
23
26
|
when :connecting,
|
24
27
|
:negotiating,
|
@@ -29,35 +32,67 @@ module HTTPX
|
|
29
32
|
|
30
33
|
private
|
31
34
|
|
32
|
-
def
|
35
|
+
def transition(nextstate)
|
36
|
+
return super unless @options.proxy && @options.proxy.uri.scheme == "socks5"
|
37
|
+
|
38
|
+
case nextstate
|
39
|
+
when :connecting
|
40
|
+
return unless @state == :idle
|
41
|
+
|
42
|
+
@io.connect
|
43
|
+
return unless @io.connected?
|
44
|
+
|
45
|
+
@write_buffer << Packet.negotiate(@options.proxy)
|
46
|
+
__socks5_proxy_connect
|
47
|
+
when :authenticating
|
48
|
+
return unless @state == :connecting
|
49
|
+
|
50
|
+
@write_buffer << Packet.authenticate(@options.proxy)
|
51
|
+
when :negotiating
|
52
|
+
return unless @state == :connecting || @state == :authenticating
|
53
|
+
|
54
|
+
req, _ = @pending.first
|
55
|
+
request_uri = req.uri
|
56
|
+
@write_buffer << Packet.connect(request_uri)
|
57
|
+
when :connected
|
58
|
+
return unless @state == :negotiating
|
59
|
+
|
60
|
+
@parser = nil
|
61
|
+
end
|
62
|
+
log(level: 1, label: "SOCKS5: ") { "#{nextstate}: #{@write_buffer.to_s.inspect}" } unless nextstate == :open
|
63
|
+
super
|
64
|
+
end
|
65
|
+
|
66
|
+
def __socks5_proxy_connect
|
33
67
|
@parser = SocksParser.new(@write_buffer, @options)
|
34
|
-
@parser.on(:packet, &method(:
|
68
|
+
@parser.on(:packet, &method(:__socks5_on_packet))
|
35
69
|
transition(:negotiating)
|
36
70
|
end
|
37
71
|
|
38
|
-
def
|
72
|
+
def __socks5_on_packet(packet)
|
39
73
|
case @state
|
40
74
|
when :connecting
|
41
75
|
version, method = packet.unpack("CC")
|
42
|
-
|
76
|
+
__socks5_check_version(version)
|
43
77
|
case method
|
44
78
|
when PASSWD
|
45
79
|
transition(:authenticating)
|
46
80
|
return
|
47
81
|
when NONE
|
48
|
-
|
82
|
+
__on_socks5_error("no supported authorization methods")
|
49
83
|
else
|
50
84
|
transition(:negotiating)
|
51
85
|
end
|
52
86
|
when :authenticating
|
53
87
|
version, status = packet.unpack("CC")
|
54
|
-
|
88
|
+
__socks5_check_version(version)
|
55
89
|
return transition(:negotiating) if status == SUCCESS
|
56
|
-
|
90
|
+
|
91
|
+
__on_socks5_error("socks authentication error: #{status}")
|
57
92
|
when :negotiating
|
58
93
|
version, reply, = packet.unpack("CC")
|
59
|
-
|
60
|
-
|
94
|
+
__socks5_check_version(version)
|
95
|
+
__on_socks5_error("socks5 negotiation error: #{reply}") unless reply == SUCCESS
|
61
96
|
req, _ = @pending.first
|
62
97
|
request_uri = req.uri
|
63
98
|
@io = ProxySSL.new(@io, request_uri, @options) if request_uri.scheme == "https"
|
@@ -66,35 +101,11 @@ module HTTPX
|
|
66
101
|
end
|
67
102
|
end
|
68
103
|
|
69
|
-
def
|
70
|
-
|
71
|
-
when :connecting
|
72
|
-
return unless @state == :idle
|
73
|
-
@io.connect
|
74
|
-
return unless @io.connected?
|
75
|
-
@write_buffer << Packet.negotiate(@parameters)
|
76
|
-
proxy_connect
|
77
|
-
when :authenticating
|
78
|
-
return unless @state == :connecting
|
79
|
-
@write_buffer << Packet.authenticate(@parameters)
|
80
|
-
when :negotiating
|
81
|
-
return unless @state == :connecting || @state == :authenticating
|
82
|
-
req, _ = @pending.first
|
83
|
-
request_uri = req.uri
|
84
|
-
@write_buffer << Packet.connect(request_uri)
|
85
|
-
when :connected
|
86
|
-
return unless @state == :negotiating
|
87
|
-
@parser = nil
|
88
|
-
end
|
89
|
-
log(level: 1, label: "SOCKS5: ") { "#{nextstate}: #{@write_buffer.to_s.inspect}" } unless nextstate == :open
|
90
|
-
super
|
91
|
-
end
|
92
|
-
|
93
|
-
def check_version(version)
|
94
|
-
on_socks_error("invalid SOCKS version (#{version})") if version != 5
|
104
|
+
def __socks5_check_version(version)
|
105
|
+
__on_socks5_error("invalid SOCKS version (#{version})") if version != 5
|
95
106
|
end
|
96
107
|
|
97
|
-
def
|
108
|
+
def __on_socks5_error(message)
|
98
109
|
ex = Error.new(message)
|
99
110
|
ex.set_backtrace(caller)
|
100
111
|
on_error(ex)
|
@@ -102,8 +113,6 @@ module HTTPX
|
|
102
113
|
end
|
103
114
|
end
|
104
115
|
|
105
|
-
Parameters.register("socks5", Socks5ProxyChannel)
|
106
|
-
|
107
116
|
class SocksParser
|
108
117
|
include Callbacks
|
109
118
|
|
@@ -147,6 +156,7 @@ module HTTPX
|
|
147
156
|
begin
|
148
157
|
ip = IPAddr.new(uri.host)
|
149
158
|
raise Error, "Socks4 connection to #{ip} not supported" unless ip.ipv4?
|
159
|
+
|
150
160
|
packet << [IPV4, ip.to_i].pack("CN")
|
151
161
|
rescue IPAddr::InvalidAddressError
|
152
162
|
packet << [DOMAIN, uri.host.bytesize, uri.host].pack("CCA*")
|
@@ -0,0 +1,81 @@
|
|
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
|
+
def self.load_dependencies(_klass, *)
|
10
|
+
# klass.plugin(:proxy)
|
11
|
+
require "net/ssh/gateway"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.extra_options(options)
|
15
|
+
Class.new(options.class) do
|
16
|
+
def_option(:proxy) do |pr|
|
17
|
+
Hash[pr]
|
18
|
+
end
|
19
|
+
end.new(options)
|
20
|
+
end
|
21
|
+
|
22
|
+
module InstanceMethods
|
23
|
+
def with_proxy(*args)
|
24
|
+
branch(default_options.with_proxy(*args))
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def send_requests(*requests, options)
|
30
|
+
request_options = @options.merge(options)
|
31
|
+
|
32
|
+
return super unless request_options.proxy
|
33
|
+
|
34
|
+
ssh_options = request_options.proxy
|
35
|
+
ssh_uris = ssh_options.delete(:uri)
|
36
|
+
ssh_uri = URI.parse(ssh_uris.shift)
|
37
|
+
|
38
|
+
return super unless ssh_uri.scheme == "ssh"
|
39
|
+
|
40
|
+
ssh_username = ssh_options.delete(:username)
|
41
|
+
ssh_options[:port] ||= ssh_uri.port || 22
|
42
|
+
if request_options.debug
|
43
|
+
ssh_options[:verbose] = request_options.debug_level == 2 ? :debug : :info
|
44
|
+
end
|
45
|
+
request_uri = URI(requests.first.uri)
|
46
|
+
@_gateway = Net::SSH::Gateway.new(ssh_uri.host, ssh_username, ssh_options)
|
47
|
+
begin
|
48
|
+
@_gateway.open(request_uri.host, request_uri.port) do |local_port|
|
49
|
+
io = build_gateway_socket(local_port, request_uri, request_options)
|
50
|
+
super(*requests, options.merge(io: io))
|
51
|
+
end
|
52
|
+
ensure
|
53
|
+
@_gateway.shutdown!
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_gateway_socket(port, request_uri, options)
|
58
|
+
case request_uri.scheme
|
59
|
+
when "https"
|
60
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
61
|
+
ctx_options = SSL::TLS_OPTIONS.merge(options.ssl)
|
62
|
+
ctx.set_params(ctx_options) unless ctx_options.empty?
|
63
|
+
sock = TCPSocket.open("localhost", port)
|
64
|
+
io = OpenSSL::SSL::SSLSocket.new(sock, ctx)
|
65
|
+
io.hostname = request_uri.host
|
66
|
+
io.sync_close = true
|
67
|
+
io.connect
|
68
|
+
io.post_connection_check(request_uri.host) if ctx.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
69
|
+
io
|
70
|
+
when "http"
|
71
|
+
TCPSocket.open("localhost", port)
|
72
|
+
else
|
73
|
+
raise Error, "unexpected scheme: #{request_uri.scheme}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
register_plugin :"proxy/ssh", Proxy::SSH
|
80
|
+
end
|
81
|
+
end
|
@@ -2,9 +2,17 @@
|
|
2
2
|
|
3
3
|
module HTTPX
|
4
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
|
+
#
|
5
11
|
module PushPromise
|
6
|
-
|
7
|
-
|
12
|
+
def self.extra_options(options)
|
13
|
+
options.merge(http2_settings: { settings_enable_push: 1 },
|
14
|
+
max_concurrent_requests: 1)
|
15
|
+
end
|
8
16
|
|
9
17
|
module ResponseMethods
|
10
18
|
def pushed?
|
@@ -17,13 +25,12 @@ module HTTPX
|
|
17
25
|
end
|
18
26
|
|
19
27
|
module InstanceMethods
|
20
|
-
def initialize(opts = {})
|
21
|
-
super(PUSH_OPTIONS.merge(opts))
|
22
|
-
@promise_headers = {}
|
23
|
-
end
|
24
|
-
|
25
28
|
private
|
26
29
|
|
30
|
+
def promise_headers
|
31
|
+
@promise_headers ||= {}
|
32
|
+
end
|
33
|
+
|
27
34
|
def on_promise(parser, stream)
|
28
35
|
stream.on(:promise_headers) do |h|
|
29
36
|
__on_promise_request(parser, stream, h)
|
@@ -40,10 +47,11 @@ module HTTPX
|
|
40
47
|
headers = @options.headers_class.new(h)
|
41
48
|
path = headers[":path"]
|
42
49
|
authority = headers[":authority"]
|
50
|
+
|
43
51
|
request = parser.pending.find { |r| r.authority == authority && r.path == path }
|
44
52
|
if request
|
45
53
|
request.merge_headers(headers)
|
46
|
-
|
54
|
+
promise_headers[stream] = request
|
47
55
|
parser.pending.delete(request)
|
48
56
|
else
|
49
57
|
stream.refuse
|
@@ -51,8 +59,9 @@ module HTTPX
|
|
51
59
|
end
|
52
60
|
|
53
61
|
def __on_promise_response(parser, stream, h)
|
54
|
-
request =
|
62
|
+
request = promise_headers.delete(stream)
|
55
63
|
return unless request
|
64
|
+
|
56
65
|
parser.__send__(:on_stream_headers, stream, request, h)
|
57
66
|
request.transition(:done)
|
58
67
|
response = request.response
|