http-2 0.10.2 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +44 -36
  6. data/lib/http/2/connection.rb +76 -89
  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 +10 -63
  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 -122
  24. data/.travis.yml +0 -14
  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 -665
  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