File: client.rb

package info (click to toggle)
ruby-http-2 1.1.2-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 13,360 kB
  • sloc: ruby: 6,033; makefile: 4
file content (130 lines) | stat: -rw-r--r-- 2,810 bytes parent folder | download | duplicates (2)
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