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 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
# frozen_string_literal: true
require_relative "helper"
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: client.rb [options]"
opts.on("-d", "--data [String]", "HTTP payload") do |v|
options[:payload] = v
end
end.parse!
uri = URI.parse(ARGV[0] || "http://localhost:8080/")
tcp = TCPSocket.new(uri.host, uri.port)
sock = nil
if uri.scheme == "https"
ctx = OpenSSL::SSL::SSLContext.new
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
# For ALPN support, Ruby >= 2.3 and OpenSSL >= 1.0.2 are required
ctx.alpn_protocols = [DRAFT]
ctx.alpn_select_cb = lambda do |protocols|
puts "ALPN protocols supported by server: #{protocols}"
DRAFT if protocols.include? DRAFT
end
sock = OpenSSL::SSL::SSLSocket.new(tcp, ctx)
sock.sync_close = true
sock.hostname = uri.hostname
sock.connect
if sock.alpn_protocol != DRAFT
puts "Failed to negotiate #{DRAFT} via ALPN"
exit
end
else
sock = tcp
end
conn = HTTP2::Client.new
stream = conn.new_stream
log = Logger.new(stream.id)
conn.on(:frame) do |bytes|
# puts "Sending bytes: #{bytes.unpack("H*").first}"
sock.print bytes
sock.flush
end
conn.on(:frame_sent) do |frame|
puts "Sent frame: #{frame.inspect}"
end
conn.on(:frame_received) do |frame|
puts "Received frame: #{frame.inspect}"
end
conn.on(:promise) do |promise|
promise.on(:promise_headers) do |h|
log.info "promise request headers: #{h}"
end
promise.on(:headers) do |h|
log.info "promise headers: #{h}"
end
promise.on(:data) do |d|
log.info "promise data chunk: <<#{d.size}>>"
end
end
conn.on(:altsvc) do |f|
log.info "received ALTSVC #{f}"
end
stream.on(:close) do
log.info "stream closed"
conn.goaway
end
stream.on(:half_close) do
log.info "closing client-end of the stream"
end
stream.on(:headers) do |h|
log.info "response headers: #{h}"
end
stream.on(:data) do |d|
log.info "response data chunk: <<#{d}>>"
end
stream.on(:altsvc) do |f|
log.info "received ALTSVC #{f}"
end
head = {
":scheme" => uri.scheme,
":method" => (options[:payload].nil? ? "GET" : "POST"),
":authority" => [uri.host, uri.port].join(":"),
":path" => uri.path,
"accept" => "*/*"
}
puts "Sending HTTP 2.0 request"
if head[":method"] == "GET"
stream.headers(head, end_stream: true)
else
stream.headers(head, end_stream: false)
stream.data(options[:payload])
end
require "memory_profiler"
report = MemoryProfiler.report(allow_files: "http-2") do
while !sock.closed? && !sock.eof?
data = sock.read_nonblock(1024)
# puts "Received bytes: #{data.unpack("H*").first}"
begin
conn << data
rescue StandardError => e
puts "#{e.class} exception: #{e.message} - closing socket."
e.backtrace.each { |l| puts "\t#{l}" }
sock.close
end
end
end
report.pretty_print
|