-
Notifications
You must be signed in to change notification settings - Fork 63
/
Copy pathclient.rb
130 lines (105 loc) · 2.74 KB
/
client.rb
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