1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
|
# frozen_string_literal: true
module HTTPX
module Plugins
#
# This plugin adds support for HTTP/2 Push responses.
#
# In order to benefit from this, requests are sent one at a time, so that
# no push responses are received after corresponding request has been sent.
#
# https://gitlab.com/os85/httpx/wikis/Server-Push
#
module PushPromise
def self.extra_options(options)
options.merge(http2_settings: { settings_enable_push: 1 },
max_concurrent_requests: 1)
end
module ResponseMethods
def pushed?
@__pushed
end
def mark_as_pushed!
@__pushed = true
end
end
module InstanceMethods
private
def promise_headers
@promise_headers ||= {}
end
def on_promise(parser, stream)
stream.on(:promise_headers) do |h|
__on_promise_request(parser, stream, h)
end
stream.on(:headers) do |h|
__on_promise_response(parser, stream, h)
end
end
def __on_promise_request(parser, stream, h)
log(level: 1, color: :yellow) do
# :nocov:
h.map { |k, v| "#{stream.id}: -> PROMISE HEADER: #{k}: #{v}" }.join("\n")
# :nocov:
end
headers = @options.headers_class.new(h)
path = headers[":path"]
authority = headers[":authority"]
request = parser.pending.find { |r| r.authority == authority && r.path == path }
if request
request.merge_headers(headers)
promise_headers[stream] = request
parser.pending.delete(request)
parser.streams[request] = stream
request.transition(:done)
else
stream.refuse
end
end
def __on_promise_response(parser, stream, h)
request = promise_headers.delete(stream)
return unless request
parser.__send__(:on_stream_headers, stream, request, h)
response = request.response
response.mark_as_pushed!
stream.on(:data, &parser.method(:on_stream_data).curry(3)[stream, request])
stream.on(:close, &parser.method(:on_stream_close).curry(3)[stream, request])
end
end
end
register_plugin(:push_promise, PushPromise)
end
end
|