http-2 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -2
  3. data/lib/http/2/buffer.rb +6 -4
  4. data/lib/http/2/client.rb +5 -1
  5. data/lib/http/2/compressor.rb +42 -34
  6. data/lib/http/2/connection.rb +72 -86
  7. data/lib/http/2/emitter.rb +4 -1
  8. data/lib/http/2/error.rb +2 -0
  9. data/lib/http/2/flow_buffer.rb +8 -3
  10. data/lib/http/2/framer.rb +83 -94
  11. data/lib/http/2/huffman.rb +19 -17
  12. data/lib/http/2/server.rb +9 -7
  13. data/lib/http/2/stream.rb +48 -48
  14. data/lib/http/2/version.rb +3 -1
  15. data/lib/http/2.rb +2 -0
  16. metadata +7 -60
  17. data/.autotest +0 -20
  18. data/.coveralls.yml +0 -1
  19. data/.gitignore +0 -20
  20. data/.gitmodules +0 -3
  21. data/.rspec +0 -5
  22. data/.rubocop.yml +0 -93
  23. data/.rubocop_todo.yml +0 -131
  24. data/.travis.yml +0 -17
  25. data/Gemfile +0 -16
  26. data/Guardfile +0 -18
  27. data/Guardfile.h2spec +0 -12
  28. data/Rakefile +0 -49
  29. data/example/Gemfile +0 -3
  30. data/example/README.md +0 -44
  31. data/example/client.rb +0 -122
  32. data/example/helper.rb +0 -19
  33. data/example/keys/server.crt +0 -20
  34. data/example/keys/server.key +0 -27
  35. data/example/server.rb +0 -139
  36. data/example/upgrade_client.rb +0 -153
  37. data/example/upgrade_server.rb +0 -203
  38. data/http-2.gemspec +0 -22
  39. data/lib/tasks/generate_huffman_table.rb +0 -166
  40. data/spec/buffer_spec.rb +0 -28
  41. data/spec/client_spec.rb +0 -188
  42. data/spec/compressor_spec.rb +0 -666
  43. data/spec/connection_spec.rb +0 -681
  44. data/spec/emitter_spec.rb +0 -54
  45. data/spec/framer_spec.rb +0 -487
  46. data/spec/h2spec/h2spec.darwin +0 -0
  47. data/spec/h2spec/output/non_secure.txt +0 -317
  48. data/spec/helper.rb +0 -147
  49. data/spec/hpack_test_spec.rb +0 -84
  50. data/spec/huffman_spec.rb +0 -68
  51. data/spec/server_spec.rb +0 -52
  52. data/spec/stream_spec.rb +0 -878
  53. data/spec/support/deep_dup.rb +0 -55
  54. data/spec/support/duplicable.rb +0 -98
data/Rakefile DELETED
@@ -1,49 +0,0 @@
1
- require 'bundler/gem_tasks'
2
- require 'rspec/core/rake_task'
3
- require 'rubocop/rake_task'
4
- require 'yard'
5
- require 'open3'
6
- require_relative 'lib/tasks/generate_huffman_table'
7
-
8
- RSpec::Core::RakeTask.new(:spec) do |t|
9
- t.exclude_pattern = './spec/hpack_test_spec.rb'
10
- end
11
-
12
- RSpec::Core::RakeTask.new(:hpack) do |t|
13
- t.pattern = './spec/hpack_test_spec.rb'
14
- end
15
-
16
- task :h2spec do
17
- if /darwin/ !~ RUBY_PLATFORM
18
- abort "h2spec rake task currently only works on OSX.
19
- Download other binaries from https://github.com/summerwind/h2spec/releases"
20
- end
21
-
22
- system 'ruby example/server.rb -p 9000 &', out: File::NULL
23
- sleep 1
24
-
25
- output = ''
26
- Open3.popen2e('spec/h2spec/h2spec.darwin -p 9000 -o 1') do |_i, oe, _t|
27
- oe.each do |l|
28
- l.gsub!(/\e\[(\d+)(;\d+)*m/, '')
29
-
30
- output << l
31
- if l =~ /passed.*failed/
32
- puts "\n#{l}"
33
- break # suppress post-summary failure output
34
- else
35
- print '.'
36
- end
37
- end
38
- end
39
-
40
- File.write 'spec/h2spec/output/non_secure.txt', output
41
-
42
- system 'kill `pgrep -f example/server.rb`'
43
- end
44
-
45
- RuboCop::RakeTask.new
46
- YARD::Rake::YardocTask.new
47
-
48
- task default: [:spec, :rubocop]
49
- task all: [:default, :hpack]
data/example/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'http_parser.rb'
data/example/README.md DELETED
@@ -1,44 +0,0 @@
1
- ## Interop
2
-
3
- First, a quick test to ensure that we can talk to ourselves:
4
-
5
- ```bash
6
- # Direct connection
7
- $> ruby server.rb
8
- $> ruby client.rb http://localhost:8080/ # GET
9
- $> ruby client.rb http://localhost:8080/ -d 'some data' # POST
10
-
11
- # Server push
12
- $> ruby server.rb --push
13
- $> ruby client.rb http://localhost:8080/ # GET
14
-
15
- # TLS + NPN negotiation
16
- $> ruby server.rb --secure
17
- $> ruby client.rb https://localhost:8080/ # GET
18
- $> ...
19
- ```
20
-
21
- ### [nghttp2](https://github.com/tatsuhiro-t/nghttp2) (HTTP/2.0 C Library)
22
-
23
- Public test server: http://106.186.112.116 (Upgrade + Direct)
24
-
25
- ```bash
26
- # Direct request (http-2 > nghttp2)
27
- $> ruby client.rb http://106.186.112.116/
28
-
29
- # TLS + NPN request (http-2 > nghttp2)
30
- $> ruby client.rb https://106.186.112.116/
31
-
32
- # Direct request (nghttp2 > http-2)
33
- $> ruby server.rb
34
- $> nghttp -vnu http://localhost:8080 # Direct request to Ruby server
35
- ```
36
-
37
- ### Twitter (Java server)
38
-
39
- ```bash
40
- # NPN + GET request (http-2 > twitter)
41
- $> ruby client.rb https://twitter.com/
42
- ```
43
-
44
- For a complete list of current implementations, see [http2 wiki](https://github.com/http2/http2-spec/wiki/Implementations).
data/example/client.rb DELETED
@@ -1,122 +0,0 @@
1
- require_relative 'helper'
2
-
3
- options = {}
4
- OptionParser.new do |opts|
5
- opts.banner = 'Usage: client.rb [options]'
6
-
7
- opts.on('-d', '--data [String]', 'HTTP payload') do |v|
8
- options[:payload] = v
9
- end
10
- end.parse!
11
-
12
- uri = URI.parse(ARGV[0] || 'http://localhost:8080/')
13
- tcp = TCPSocket.new(uri.host, uri.port)
14
- sock = nil
15
-
16
- if uri.scheme == 'https'
17
- ctx = OpenSSL::SSL::SSLContext.new
18
- ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
19
-
20
- # For ALPN support, Ruby >= 2.3 and OpenSSL >= 1.0.2 are required
21
-
22
- ctx.alpn_protocols = [DRAFT]
23
- ctx.alpn_select_cb = lambda do |protocols|
24
- puts "ALPN protocols supported by server: #{protocols}"
25
- DRAFT if protocols.include? DRAFT
26
- end
27
-
28
- sock = OpenSSL::SSL::SSLSocket.new(tcp, ctx)
29
- sock.sync_close = true
30
- sock.hostname = uri.hostname
31
- sock.connect
32
-
33
- if sock.alpn_protocol != DRAFT
34
- puts "Failed to negotiate #{DRAFT} via ALPN"
35
- exit
36
- end
37
- else
38
- sock = tcp
39
- end
40
-
41
- conn = HTTP2::Client.new
42
- stream = conn.new_stream
43
- log = Logger.new(stream.id)
44
-
45
- conn.on(:frame) do |bytes|
46
- # puts "Sending bytes: #{bytes.unpack("H*").first}"
47
- sock.print bytes
48
- sock.flush
49
- end
50
- conn.on(:frame_sent) do |frame|
51
- puts "Sent frame: #{frame.inspect}"
52
- end
53
- conn.on(:frame_received) do |frame|
54
- puts "Received frame: #{frame.inspect}"
55
- end
56
-
57
- conn.on(:promise) do |promise|
58
- promise.on(:promise_headers) do |h|
59
- log.info "promise request headers: #{h}"
60
- end
61
-
62
- promise.on(:headers) do |h|
63
- log.info "promise headers: #{h}"
64
- end
65
-
66
- promise.on(:data) do |d|
67
- log.info "promise data chunk: <<#{d.size}>>"
68
- end
69
- end
70
-
71
- conn.on(:altsvc) do |f|
72
- log.info "received ALTSVC #{f}"
73
- end
74
-
75
- stream.on(:close) do
76
- log.info 'stream closed'
77
- end
78
-
79
- stream.on(:half_close) do
80
- log.info 'closing client-end of the stream'
81
- end
82
-
83
- stream.on(:headers) do |h|
84
- log.info "response headers: #{h}"
85
- end
86
-
87
- stream.on(:data) do |d|
88
- log.info "response data chunk: <<#{d}>>"
89
- end
90
-
91
- stream.on(:altsvc) do |f|
92
- log.info "received ALTSVC #{f}"
93
- end
94
-
95
- head = {
96
- ':scheme' => uri.scheme,
97
- ':method' => (options[:payload].nil? ? 'GET' : 'POST'),
98
- ':authority' => [uri.host, uri.port].join(':'),
99
- ':path' => uri.path,
100
- 'accept' => '*/*',
101
- }
102
-
103
- puts 'Sending HTTP 2.0 request'
104
- if head[':method'] == 'GET'
105
- stream.headers(head, end_stream: true)
106
- else
107
- stream.headers(head, end_stream: false)
108
- stream.data(options[:payload])
109
- end
110
-
111
- while !sock.closed? && !sock.eof?
112
- data = sock.read_nonblock(1024)
113
- # puts "Received bytes: #{data.unpack("H*").first}"
114
-
115
- begin
116
- conn << data
117
- rescue StandardError => e
118
- puts "#{e.class} exception: #{e.message} - closing socket."
119
- e.backtrace.each { |l| puts "\t" + l }
120
- sock.close
121
- end
122
- end
data/example/helper.rb DELETED
@@ -1,19 +0,0 @@
1
- $LOAD_PATH << 'lib' << '../lib'
2
-
3
- require 'optparse'
4
- require 'socket'
5
- require 'openssl'
6
- require 'http/2'
7
- require 'uri'
8
-
9
- DRAFT = 'h2'.freeze
10
-
11
- class Logger
12
- def initialize(id)
13
- @id = id
14
- end
15
-
16
- def info(msg)
17
- puts "[Stream #{@id}]: #{msg}"
18
- end
19
- end
@@ -1,20 +0,0 @@
1
- -----BEGIN CERTIFICATE-----
2
- MIIDLjCCAhYCCQDIZ/9hq/2pXjANBgkqhkiG9w0BAQUFADBZMQswCQYDVQQGEwJB
3
- VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
4
- cyBQdHkgTHRkMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMTYwNjA2MTk0MzI1WhcN
5
- MTcwNjA2MTk0MzI1WjBZMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0
6
- ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDEwls
7
- b2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCjFXrWqtRZ
8
- EMOO/o6AxGbgDYMgg/7uxCFQJM5Z6C4II6D8V94FDyCd+J0LOK2hB+QUdFqpA1S9
9
- 6RW2MvIwdvRt03RJMgbfcUF0+w4ZItv2xrW9waCfCmLSRDZgcSATacEF6u9p2Vs+
10
- o4J/cHacirSwjy4+m94CgkxtUFGtGcJaFqAZ6Cdj5WvQdJSiAI3x3gNC/UGA+5dL
11
- sp8+vwWx+/TMc6nDBmoRW3GHeG/NApQSh01w3wDv0FmUaFQlA5WPya/Js+CyuYh1
12
- miXbQJEjDnGGaJjnoyRAQpPrk72Jj+bnfOu9kxpzkuLJOsbaofRFkM+/Ar5U+bQz
13
- uU0ErQ8Ih8MPAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBACL8HkKjW8kWLlW4TE5K
14
- EcfsBad2Ah5ugTocJ/pLnr/YEo8uD91gECDtsuFTXul9n2M7U5jJmzbHZ63cjyC3
15
- lb1BxJxUL7aIyaL61IMMcIJMWhC9VGnFUshMDNVBhuRkKs/QvaMD5KefKN1E9I2M
16
- mZ72Yww0VihYwNOu3MTn8gUuy9eU6k/gTYPY7PJVh18QmR+Fs2MaaPp+bDwxiqML
17
- 0o2I6+0ZsqM3vFtcUjxjRASV5s+JkM34pTWFwUOl7TZv1YsxCKSz4f0BXDImZEvU
18
- rwqFdlELp5WOG9LJsrszDRbf1wbFUsG1XXZpIBiWo3d6pOiIyRKrak1vKViNfYvI
19
- W30=
20
- -----END CERTIFICATE-----
@@ -1,27 +0,0 @@
1
- -----BEGIN RSA PRIVATE KEY-----
2
- MIIEpAIBAAKCAQEAoxV61qrUWRDDjv6OgMRm4A2DIIP+7sQhUCTOWeguCCOg/Ffe
3
- BQ8gnfidCzitoQfkFHRaqQNUvekVtjLyMHb0bdN0STIG33FBdPsOGSLb9sa1vcGg
4
- nwpi0kQ2YHEgE2nBBervadlbPqOCf3B2nIq0sI8uPpveAoJMbVBRrRnCWhagGegn
5
- Y+Vr0HSUogCN8d4DQv1BgPuXS7KfPr8Fsfv0zHOpwwZqEVtxh3hvzQKUEodNcN8A
6
- 79BZlGhUJQOVj8mvybPgsrmIdZol20CRIw5xhmiY56MkQEKT65O9iY/m53zrvZMa
7
- c5LiyTrG2qH0RZDPvwK+VPm0M7lNBK0PCIfDDwIDAQABAoIBAQCZShphZsccRJ6c
8
- bPdDX9iW5vyG9qsMgPwTGdWAOrXx3pN2PZ0pwjNVaRcsMgU6JHGlLEz/Kmtf6pQG
9
- 41I0bcuI48Yc+tHs+sadD1IMHHEHP3Yau8KfWyLSI129Pvf4Z2IQjuik5LJYaVbD
10
- NNG4iMQYZS0Bmn6Oey0dXu62t0ywYa0qvbIDse/RmjTQSTipuvGg8/QEAeRGABv8
11
- Nd4Esya0zuxk6hGaNp3hkjyRkeoC7RsBVJbFSnp6gSubPdXwrJyHfySKe9jvrDG3
12
- Q/AzyHUh/6EODd5n66x0p6rq7oo9/PnLvZJY8jIGWG+aEp68RJyEgimrwll0rAWw
13
- /buqijGRAoGBANimL8407fFirmct7BceavaeJfXPK5yWiOhVX0XlJ0phAFuaAxK3
14
- 5HVT7DD+KKV66g1jtS9FUVZGDiYFHlsdsYuHVYcRmr0h5rZr941obrDwNrM9Nf9C
15
- 0uehN5+n/FaeGoQLR3V4THoP3rlkYTlLpQnI5mKA19JukXnIiJM9ARUZAoGBAMC0
16
- mcVsVuSKSFwURtQHHIufxL6SqC2kLTwIQ7exqejNYPCqCiif+ZWOmsTqbVGAGbMK
17
- Ohak4oLwN5IGCl4jNQG+vWagREkx6OXSk5NYcfoNBrOm+0UoFRzoEA85s7Dy6PuD
18
- tBucNZpt1sGauzkCSx7C8jj4ZlSwkv0XhBFfbTZnAoGBAK2wBjF+U6iq4YFM2rLq
19
- KvzOa0Z3MdKXCOmiz//cKDTEMaI+heoyzZCWmIvqpzGLqirT3gUowH23Kk6m2eBY
20
- nOdst0/S+Eha7nkfc9bFe8CUxHXMRAcCTs1ufYadCXtzw3RLCp4NtNpC8N+Wry9d
21
- CtIeYz1jaCOHi0+kSoIobT65AoGAc6hxWkJp7ITqZQlucTdLdKmRheeztKEC3TMA
22
- obGqDqWldww3SKarP431ahZhQjcmNYT/1DNmF7xhPe0OL+3llISMXJn4Ig4ogDdg
23
- h2DgF3nV+eFQkfM6qLzHVrwFE0DXgI1NffzFV0hxSoW5tL+honbStkqv8EiCEBEb
24
- HOovPCUCgYBpXuPARd2ycInAulVHijJmj2rmK7f41ZhVCWovYjcCWyeJyLIO7j+b
25
- MBJZbmwpStJhEjW64nE2zZGWg2HCBbvZz5/SXIr3fp7qVXwpn1TvB/TJDf43t0oF
26
- 3caLgyQYoQCsVHKT3cU4s3wuog/DyHKh9FtRkcJrEy7h9Rrc+ModbA==
27
- -----END RSA PRIVATE KEY-----
data/example/server.rb DELETED
@@ -1,139 +0,0 @@
1
- require_relative 'helper'
2
-
3
- options = { port: 8080 }
4
- OptionParser.new do |opts|
5
- opts.banner = 'Usage: server.rb [options]'
6
-
7
- opts.on('-s', '--secure', 'HTTPS mode') do |v|
8
- options[:secure] = v
9
- end
10
-
11
- opts.on('-p', '--port [Integer]', 'listen port') do |v|
12
- options[:port] = v
13
- end
14
-
15
- opts.on('-u', '--push', 'Push message') do |_v|
16
- options[:push] = true
17
- end
18
- end.parse!
19
-
20
- puts "Starting server on port #{options[:port]}"
21
- server = TCPServer.new(options[:port])
22
-
23
- if options[:secure]
24
- ctx = OpenSSL::SSL::SSLContext.new
25
- ctx.cert = OpenSSL::X509::Certificate.new(File.open('keys/server.crt'))
26
- ctx.key = OpenSSL::PKey::RSA.new(File.open('keys/server.key'))
27
-
28
- ctx.ssl_version = :TLSv1_2
29
- ctx.options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
30
- ctx.ciphers = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:ciphers]
31
-
32
- ctx.alpn_protocols = ['h2']
33
-
34
- ctx.alpn_select_cb = lambda do |protocols|
35
- raise "Protocol #{DRAFT} is required" if protocols.index(DRAFT).nil?
36
- DRAFT
37
- end
38
-
39
- ctx.ecdh_curves = 'P-256'
40
-
41
- server = OpenSSL::SSL::SSLServer.new(server, ctx)
42
- end
43
-
44
- loop do
45
- sock = server.accept
46
- puts 'New TCP connection!'
47
-
48
- conn = HTTP2::Server.new
49
- conn.on(:frame) do |bytes|
50
- # puts "Writing bytes: #{bytes.unpack("H*").first}"
51
- sock.is_a?(TCPSocket) ? sock.sendmsg(bytes) : sock.write(bytes)
52
- end
53
- conn.on(:frame_sent) do |frame|
54
- puts "Sent frame: #{frame.inspect}"
55
- end
56
- conn.on(:frame_received) do |frame|
57
- puts "Received frame: #{frame.inspect}"
58
- end
59
-
60
- conn.on(:stream) do |stream|
61
- log = Logger.new(stream.id)
62
- req, buffer = {}, ''
63
-
64
- stream.on(:active) { log.info 'client opened new stream' }
65
- stream.on(:close) { log.info 'stream closed' }
66
-
67
- stream.on(:headers) do |h|
68
- req = Hash[*h.flatten]
69
- log.info "request headers: #{h}"
70
- end
71
-
72
- stream.on(:data) do |d|
73
- log.info "payload chunk: <<#{d}>>"
74
- buffer << d
75
- end
76
-
77
- stream.on(:half_close) do
78
- log.info 'client closed its end of the stream'
79
-
80
- response = nil
81
- if req[':method'] == 'POST'
82
- log.info "Received POST request, payload: #{buffer}"
83
- response = "Hello HTTP 2.0! POST payload: #{buffer}"
84
- else
85
- log.info 'Received GET request'
86
- response = 'Hello HTTP 2.0! GET request'
87
- end
88
-
89
- stream.headers({
90
- ':status' => '200',
91
- 'content-length' => response.bytesize.to_s,
92
- 'content-type' => 'text/plain',
93
- }, end_stream: false)
94
-
95
- if options[:push]
96
- push_streams = []
97
-
98
- # send 10 promises
99
- 10.times do |i|
100
- puts 'sending push'
101
-
102
- head = { ':method' => 'GET',
103
- ':authority' => 'localhost',
104
- ':scheme' => 'https',
105
- ':path' => "/other_resource/#{i}" }
106
-
107
- stream.promise(head) do |push|
108
- push.headers(':status' => '200', 'content-type' => 'text/plain', 'content-length' => '11')
109
- push_streams << push
110
- end
111
- end
112
- end
113
-
114
- # split response into multiple DATA frames
115
- stream.data(response.slice!(0, 5), end_stream: false)
116
- stream.data(response)
117
-
118
- if options[:push]
119
- push_streams.each_with_index do |push, i|
120
- sleep 1
121
- push.data("push_data #{i}")
122
- end
123
- end
124
- end
125
- end
126
-
127
- while !sock.closed? && !(sock.eof? rescue true) # rubocop:disable Style/RescueModifier
128
- data = sock.readpartial(1024)
129
- # puts "Received bytes: #{data.unpack("H*").first}"
130
-
131
- begin
132
- conn << data
133
- rescue StandardError => e
134
- puts "#{e.class} exception: #{e.message} - closing socket."
135
- e.backtrace.each { |l| puts "\t" + l }
136
- sock.close
137
- end
138
- end
139
- end
@@ -1,153 +0,0 @@
1
- # frozen_string_literals: true
2
-
3
- require_relative 'helper'
4
- require 'http_parser'
5
-
6
- OptionParser.new do |opts|
7
- opts.banner = 'Usage: upgrade_client.rb [options]'
8
- end.parse!
9
-
10
- uri = URI.parse(ARGV[0] || 'http://localhost:8080/')
11
- sock = TCPSocket.new(uri.host, uri.port)
12
-
13
- conn = HTTP2::Client.new
14
-
15
- def request_header_hash
16
- Hash.new do |hash, key|
17
- k = key.to_s.downcase
18
- k.tr! '_', '-'
19
- _, value = hash.find { |header_key, _| header_key.downcase == k }
20
- hash[key] = value if value
21
- end
22
- end
23
-
24
- conn.on(:frame) do |bytes|
25
- sock.print bytes
26
- sock.flush
27
- end
28
- conn.on(:frame_sent) do |frame|
29
- puts "Sent frame: #{frame.inspect}"
30
- end
31
- conn.on(:frame_received) do |frame|
32
- puts "Received frame: #{frame.inspect}"
33
- end
34
-
35
- # upgrader module
36
- class UpgradeHandler
37
- UPGRADE_REQUEST = <<RESP.freeze
38
- GET %s HTTP/1.1
39
- Connection: Upgrade, HTTP2-Settings
40
- HTTP2-Settings: #{HTTP2::Client.settings_header(settings_max_concurrent_streams: 100)}
41
- Upgrade: h2c
42
- Host: %s
43
- User-Agent: http-2 upgrade
44
- Accept: */*
45
-
46
- RESP
47
-
48
- attr_reader :complete, :parsing
49
- def initialize(conn, sock)
50
- @conn = conn
51
- @sock = sock
52
- @headers = request_header_hash
53
- @body = ''.b
54
- @complete, @parsing = false, false
55
- @parser = ::HTTP::Parser.new(self)
56
- end
57
-
58
- def request(uri)
59
- host = "#{uri.hostname}#{":#{uri.port}" if uri.port != uri.default_port}"
60
- req = format(UPGRADE_REQUEST, uri.request_uri, host)
61
- puts req
62
- @sock << req
63
- end
64
-
65
- def <<(data)
66
- @parsing ||= true
67
- @parser << data
68
- return unless complete
69
- upgrade
70
- end
71
-
72
- def complete!
73
- @complete = true
74
- end
75
-
76
- def on_headers_complete(headers)
77
- @headers.merge!(headers)
78
- puts "received headers: #{headers}"
79
- end
80
-
81
- def on_body(chunk)
82
- puts "received chunk: #{chunk}"
83
- @body << chunk
84
- end
85
-
86
- def on_message_complete
87
- fail 'could not upgrade to h2c' unless @parser.status_code == 101
88
- @parsing = false
89
- complete!
90
- end
91
-
92
- def upgrade
93
- stream = @conn.upgrade
94
- log = Logger.new(stream.id)
95
-
96
- stream.on(:close) do
97
- log.info 'stream closed'
98
- end
99
-
100
- stream.on(:half_close) do
101
- log.info 'closing client-end of the stream'
102
- end
103
-
104
- stream.on(:headers) do |h|
105
- log.info "response headers: #{h}"
106
- end
107
-
108
- stream.on(:data) do |d|
109
- log.info "response data chunk: <<#{d}>>"
110
- end
111
-
112
- stream.on(:altsvc) do |f|
113
- log.info "received ALTSVC #{f}"
114
- end
115
-
116
- @conn.on(:promise) do |promise|
117
- promise.on(:headers) do |h|
118
- log.info "promise headers: #{h}"
119
- end
120
-
121
- promise.on(:data) do |d|
122
- log.info "promise data chunk: <<#{d.size}>>"
123
- end
124
- end
125
-
126
- @conn.on(:altsvc) do |f|
127
- log.info "received ALTSVC #{f}"
128
- end
129
- end
130
- end
131
-
132
- uh = UpgradeHandler.new(conn, sock)
133
- puts 'Sending HTTP/1.1 upgrade request'
134
- uh.request(uri)
135
-
136
- while !sock.closed? && !sock.eof?
137
- data = sock.read_nonblock(1024)
138
-
139
- begin
140
- if !uh.parsing && !uh.complete
141
- uh << data
142
- elsif uh.parsing && !uh.complete
143
- uh << data
144
- elsif uh.complete
145
- conn << data
146
- end
147
- rescue StandardError => e
148
- puts "#{e.class} exception: #{e.message} - closing socket."
149
- e.backtrace.each { |l| puts "\t" + l }
150
- conn.close
151
- sock.close
152
- end
153
- end