biryani 0.0.8 → 0.0.10
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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/README.md +2 -2
- data/biryani.gemspec +5 -2
- data/example/echo.rb +2 -2
- data/example/hello_world.rb +2 -2
- data/lib/biryani/connection.rb +62 -47
- data/lib/biryani/connection_error.rb +1 -1
- data/lib/biryani/frame/goaway.rb +5 -3
- data/lib/biryani/hpack/field.rb +5 -9
- data/lib/biryani/http/error.rb +10 -0
- data/lib/biryani/http/request.rb +97 -0
- data/lib/biryani/http/response.rb +68 -0
- data/lib/biryani/http.rb +3 -0
- data/lib/biryani/state.rb +1 -1
- data/lib/biryani/stream.rb +3 -3
- data/lib/biryani/streams_context.rb +16 -16
- data/lib/biryani/version.rb +1 -1
- data/lib/biryani.rb +1 -3
- metadata +7 -77
- data/.github/workflows/ci.yml +0 -30
- data/.github/workflows/conformance.yml +0 -46
- data/.gitignore +0 -18
- data/lib/biryani/error.rb +0 -8
- data/lib/biryani/http_request.rb +0 -95
- data/lib/biryani/http_response.rb +0 -66
- data/spec/connection/handle_connection_window_update_spec.rb +0 -16
- data/spec/connection/handle_data_spec.rb +0 -58
- data/spec/connection/handle_headers_spec.rb +0 -19
- data/spec/connection/handle_ping_spec.rb +0 -21
- data/spec/connection/handle_rst_stream_spec.rb +0 -16
- data/spec/connection/handle_settings_spec.rb +0 -37
- data/spec/connection/handle_stream_window_update_spec.rb +0 -20
- data/spec/connection/read_http2_magic_spec.rb +0 -26
- data/spec/connection/send_spec.rb +0 -104
- data/spec/connection/transition_stream_state_send_spec.rb +0 -39
- data/spec/data_buffer_spec.rb +0 -135
- data/spec/frame/continuation_spec.rb +0 -39
- data/spec/frame/data_spec.rb +0 -25
- data/spec/frame/goaway_spec.rb +0 -23
- data/spec/frame/headers_spec.rb +0 -52
- data/spec/frame/ping_spec.rb +0 -22
- data/spec/frame/priority_spec.rb +0 -22
- data/spec/frame/push_promise_spec.rb +0 -24
- data/spec/frame/read_spec.rb +0 -30
- data/spec/frame/rst_stream_spec.rb +0 -21
- data/spec/frame/settings_spec.rb +0 -23
- data/spec/frame/window_update_spec.rb +0 -21
- data/spec/hpack/decoder_spec.rb +0 -170
- data/spec/hpack/encoder_spec.rb +0 -48
- data/spec/hpack/field_spec.rb +0 -43
- data/spec/hpack/fields_spec.rb +0 -17
- data/spec/hpack/huffman_spec.rb +0 -20
- data/spec/hpack/integer_spec.rb +0 -27
- data/spec/hpack/string_spec.rb +0 -19
- data/spec/http_request_spec.rb +0 -62
- data/spec/http_response_spec.rb +0 -12
- data/spec/spec_helper.rb +0 -9
- data/spec/streams_context_spec.rb +0 -79
- data/spec/utils_spec.rb +0 -41
data/lib/biryani.rb
CHANGED
|
@@ -3,11 +3,9 @@ require 'uri'
|
|
|
3
3
|
require_relative 'biryani/connection_error'
|
|
4
4
|
require_relative 'biryani/connection'
|
|
5
5
|
require_relative 'biryani/data_buffer'
|
|
6
|
-
require_relative 'biryani/error'
|
|
7
6
|
require_relative 'biryani/frame'
|
|
8
7
|
require_relative 'biryani/hpack'
|
|
9
|
-
require_relative 'biryani/
|
|
10
|
-
require_relative 'biryani/http_response'
|
|
8
|
+
require_relative 'biryani/http'
|
|
11
9
|
require_relative 'biryani/server'
|
|
12
10
|
require_relative 'biryani/state'
|
|
13
11
|
require_relative 'biryani/stream_error'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: biryani
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- thekuwayama
|
|
@@ -30,9 +30,6 @@ executables: []
|
|
|
30
30
|
extensions: []
|
|
31
31
|
extra_rdoc_files: []
|
|
32
32
|
files:
|
|
33
|
-
- ".github/workflows/ci.yml"
|
|
34
|
-
- ".github/workflows/conformance.yml"
|
|
35
|
-
- ".gitignore"
|
|
36
33
|
- ".rubocop.yml"
|
|
37
34
|
- ".ruby-version"
|
|
38
35
|
- Gemfile
|
|
@@ -49,7 +46,6 @@ files:
|
|
|
49
46
|
- lib/biryani/connection.rb
|
|
50
47
|
- lib/biryani/connection_error.rb
|
|
51
48
|
- lib/biryani/data_buffer.rb
|
|
52
|
-
- lib/biryani/error.rb
|
|
53
49
|
- lib/biryani/frame.rb
|
|
54
50
|
- lib/biryani/frame/continuation.rb
|
|
55
51
|
- lib/biryani/frame/data.rb
|
|
@@ -73,8 +69,10 @@ files:
|
|
|
73
69
|
- lib/biryani/hpack/integer.rb
|
|
74
70
|
- lib/biryani/hpack/option.rb
|
|
75
71
|
- lib/biryani/hpack/string.rb
|
|
76
|
-
- lib/biryani/
|
|
77
|
-
- lib/biryani/
|
|
72
|
+
- lib/biryani/http.rb
|
|
73
|
+
- lib/biryani/http/error.rb
|
|
74
|
+
- lib/biryani/http/request.rb
|
|
75
|
+
- lib/biryani/http/response.rb
|
|
78
76
|
- lib/biryani/server.rb
|
|
79
77
|
- lib/biryani/state.rb
|
|
80
78
|
- lib/biryani/stream.rb
|
|
@@ -83,40 +81,6 @@ files:
|
|
|
83
81
|
- lib/biryani/utils.rb
|
|
84
82
|
- lib/biryani/version.rb
|
|
85
83
|
- lib/biryani/window.rb
|
|
86
|
-
- spec/connection/handle_connection_window_update_spec.rb
|
|
87
|
-
- spec/connection/handle_data_spec.rb
|
|
88
|
-
- spec/connection/handle_headers_spec.rb
|
|
89
|
-
- spec/connection/handle_ping_spec.rb
|
|
90
|
-
- spec/connection/handle_rst_stream_spec.rb
|
|
91
|
-
- spec/connection/handle_settings_spec.rb
|
|
92
|
-
- spec/connection/handle_stream_window_update_spec.rb
|
|
93
|
-
- spec/connection/read_http2_magic_spec.rb
|
|
94
|
-
- spec/connection/send_spec.rb
|
|
95
|
-
- spec/connection/transition_stream_state_send_spec.rb
|
|
96
|
-
- spec/data_buffer_spec.rb
|
|
97
|
-
- spec/frame/continuation_spec.rb
|
|
98
|
-
- spec/frame/data_spec.rb
|
|
99
|
-
- spec/frame/goaway_spec.rb
|
|
100
|
-
- spec/frame/headers_spec.rb
|
|
101
|
-
- spec/frame/ping_spec.rb
|
|
102
|
-
- spec/frame/priority_spec.rb
|
|
103
|
-
- spec/frame/push_promise_spec.rb
|
|
104
|
-
- spec/frame/read_spec.rb
|
|
105
|
-
- spec/frame/rst_stream_spec.rb
|
|
106
|
-
- spec/frame/settings_spec.rb
|
|
107
|
-
- spec/frame/window_update_spec.rb
|
|
108
|
-
- spec/hpack/decoder_spec.rb
|
|
109
|
-
- spec/hpack/encoder_spec.rb
|
|
110
|
-
- spec/hpack/field_spec.rb
|
|
111
|
-
- spec/hpack/fields_spec.rb
|
|
112
|
-
- spec/hpack/huffman_spec.rb
|
|
113
|
-
- spec/hpack/integer_spec.rb
|
|
114
|
-
- spec/hpack/string_spec.rb
|
|
115
|
-
- spec/http_request_spec.rb
|
|
116
|
-
- spec/http_response_spec.rb
|
|
117
|
-
- spec/spec_helper.rb
|
|
118
|
-
- spec/streams_context_spec.rb
|
|
119
|
-
- spec/utils_spec.rb
|
|
120
84
|
homepage: https://github.com/thekuwayama/biryani
|
|
121
85
|
licenses:
|
|
122
86
|
- MIT
|
|
@@ -135,41 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
135
99
|
- !ruby/object:Gem::Version
|
|
136
100
|
version: '0'
|
|
137
101
|
requirements: []
|
|
138
|
-
rubygems_version: 4.0.
|
|
102
|
+
rubygems_version: 4.0.8
|
|
139
103
|
specification_version: 4
|
|
140
104
|
summary: An HTTP/2 server implemented using Ruby Ractor
|
|
141
|
-
test_files:
|
|
142
|
-
- spec/connection/handle_connection_window_update_spec.rb
|
|
143
|
-
- spec/connection/handle_data_spec.rb
|
|
144
|
-
- spec/connection/handle_headers_spec.rb
|
|
145
|
-
- spec/connection/handle_ping_spec.rb
|
|
146
|
-
- spec/connection/handle_rst_stream_spec.rb
|
|
147
|
-
- spec/connection/handle_settings_spec.rb
|
|
148
|
-
- spec/connection/handle_stream_window_update_spec.rb
|
|
149
|
-
- spec/connection/read_http2_magic_spec.rb
|
|
150
|
-
- spec/connection/send_spec.rb
|
|
151
|
-
- spec/connection/transition_stream_state_send_spec.rb
|
|
152
|
-
- spec/data_buffer_spec.rb
|
|
153
|
-
- spec/frame/continuation_spec.rb
|
|
154
|
-
- spec/frame/data_spec.rb
|
|
155
|
-
- spec/frame/goaway_spec.rb
|
|
156
|
-
- spec/frame/headers_spec.rb
|
|
157
|
-
- spec/frame/ping_spec.rb
|
|
158
|
-
- spec/frame/priority_spec.rb
|
|
159
|
-
- spec/frame/push_promise_spec.rb
|
|
160
|
-
- spec/frame/read_spec.rb
|
|
161
|
-
- spec/frame/rst_stream_spec.rb
|
|
162
|
-
- spec/frame/settings_spec.rb
|
|
163
|
-
- spec/frame/window_update_spec.rb
|
|
164
|
-
- spec/hpack/decoder_spec.rb
|
|
165
|
-
- spec/hpack/encoder_spec.rb
|
|
166
|
-
- spec/hpack/field_spec.rb
|
|
167
|
-
- spec/hpack/fields_spec.rb
|
|
168
|
-
- spec/hpack/huffman_spec.rb
|
|
169
|
-
- spec/hpack/integer_spec.rb
|
|
170
|
-
- spec/hpack/string_spec.rb
|
|
171
|
-
- spec/http_request_spec.rb
|
|
172
|
-
- spec/http_response_spec.rb
|
|
173
|
-
- spec/spec_helper.rb
|
|
174
|
-
- spec/streams_context_spec.rb
|
|
175
|
-
- spec/utils_spec.rb
|
|
105
|
+
test_files: []
|
data/.github/workflows/ci.yml
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
name: lint & test
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- main
|
|
7
|
-
pull_request:
|
|
8
|
-
branches:
|
|
9
|
-
- '*'
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
ci:
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
strategy:
|
|
15
|
-
matrix:
|
|
16
|
-
ruby-version: ['4.0']
|
|
17
|
-
steps:
|
|
18
|
-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
19
|
-
- name: Set up Ruby
|
|
20
|
-
uses: ruby/setup-ruby@ae195bbe749a7cef685ac729197124a48305c1cb # v1.276.0
|
|
21
|
-
with:
|
|
22
|
-
ruby-version: ${{ matrix.ruby-version }}
|
|
23
|
-
- name: Install dependencies
|
|
24
|
-
run: |
|
|
25
|
-
gem --version
|
|
26
|
-
gem install bundler
|
|
27
|
-
bundle --version
|
|
28
|
-
bundle install
|
|
29
|
-
- name: Run rubocop & rspec
|
|
30
|
-
run: bundle exec rake
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
name: h2spec
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- main
|
|
7
|
-
pull_request:
|
|
8
|
-
branches:
|
|
9
|
-
- '*'
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
conformance:
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
permissions:
|
|
15
|
-
contents: read
|
|
16
|
-
checks: write
|
|
17
|
-
strategy:
|
|
18
|
-
matrix:
|
|
19
|
-
ruby-version: ['4.0']
|
|
20
|
-
steps:
|
|
21
|
-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
22
|
-
- name: Set up Ruby
|
|
23
|
-
uses: ruby/setup-ruby@ae195bbe749a7cef685ac729197124a48305c1cb # v1.276.0
|
|
24
|
-
with:
|
|
25
|
-
ruby-version: ${{ matrix.ruby-version }}
|
|
26
|
-
- name: Install dependencies
|
|
27
|
-
run: |
|
|
28
|
-
gem --version
|
|
29
|
-
gem install bundler
|
|
30
|
-
bundle --version
|
|
31
|
-
bundle install
|
|
32
|
-
- name: Install h2spec
|
|
33
|
-
run: |
|
|
34
|
-
set -eu
|
|
35
|
-
curl -Ls https://github.com/summerwind/h2spec/releases/download/v2.6.0/h2spec_linux_amd64.tar.gz | tar xz
|
|
36
|
-
mkdir -p $GITHUB_WORKSPACE/bin
|
|
37
|
-
mv h2spec $GITHUB_WORKSPACE/bin/h2spec
|
|
38
|
-
chmod +x $GITHUB_WORKSPACE/bin/h2spec
|
|
39
|
-
echo $GITHUB_WORKSPACE/bin >> $GITHUB_PATH
|
|
40
|
-
- name: Run conformance test
|
|
41
|
-
run: bundle exec rake conformance
|
|
42
|
-
- uses: mikepenz/action-junit-report@a294a61c909bd8a4b563024a2faa28897fd53ebc # v6.1.0
|
|
43
|
-
if: success()
|
|
44
|
-
with:
|
|
45
|
-
report_paths: conformance/reports/h2spec.xml
|
|
46
|
-
detailed_summary: true
|
data/.gitignore
DELETED
data/lib/biryani/error.rb
DELETED
data/lib/biryani/http_request.rb
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
module Biryani
|
|
2
|
-
class HTTPRequest
|
|
3
|
-
attr_accessor :method, :uri, :fields, :content
|
|
4
|
-
|
|
5
|
-
# @param method [String]
|
|
6
|
-
# @param uri [URI]
|
|
7
|
-
# @param fields [Hash<String, Array<String>>]
|
|
8
|
-
# @param content [String]
|
|
9
|
-
def initialize(method, uri, fields, content)
|
|
10
|
-
@method = method
|
|
11
|
-
@uri = uri
|
|
12
|
-
@fields = fields
|
|
13
|
-
@content = content
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# @return [Array<String>, nil]
|
|
17
|
-
def trailers
|
|
18
|
-
# https://datatracker.ietf.org/doc/html/rfc9110#section-6.6.2-4
|
|
19
|
-
keys = (@fields['trailer'] || []).flat_map { |x| x.split(',').map(&:strip) }
|
|
20
|
-
@fields.slice(*keys)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
class HTTPRequestBuilder
|
|
25
|
-
PSEUDO_HEADER_FIELDS = [':authority', ':method', ':path', ':scheme'].freeze
|
|
26
|
-
Ractor.make_shareable(PSEUDO_HEADER_FIELDS)
|
|
27
|
-
|
|
28
|
-
def initialize
|
|
29
|
-
@h = {}
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# @param name [String]
|
|
33
|
-
# @param value [String]
|
|
34
|
-
#
|
|
35
|
-
# @return [nil, ConnectioError]
|
|
36
|
-
# rubocop: disable Metrics/AbcSize
|
|
37
|
-
# rubocop: disable Metrics/CyclomaticComplexity
|
|
38
|
-
# rubocop: disable Metrics/PerceivedComplexity
|
|
39
|
-
def field(name, value)
|
|
40
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'field name has uppercase letter') if name.downcase != name
|
|
41
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'unknown pseudo-header field name') if name[0] == ':' && !PSEUDO_HEADER_FIELDS.include?(name)
|
|
42
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'appear pseudo-header fields after regular fields') if name[0] == ':' && @h.any? { |name_, _| name_[0] != ':' }
|
|
43
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'duplicated pseudo-header fields') if PSEUDO_HEADER_FIELDS.include?(name) && @h.key?(name)
|
|
44
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, "invalid `#{name}` field") if PSEUDO_HEADER_FIELDS.include?(name) && value.empty?
|
|
45
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'connection-specific field is forbidden') if name == 'connection'
|
|
46
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, '`TE` field has a value other than `trailers`') if name == 'te' && value != 'trailers'
|
|
47
|
-
|
|
48
|
-
@h[name] = [] unless @h.key?(name)
|
|
49
|
-
@h[name] << value
|
|
50
|
-
|
|
51
|
-
nil
|
|
52
|
-
end
|
|
53
|
-
# rubocop: enable Metrics/AbcSize
|
|
54
|
-
# rubocop: enable Metrics/CyclomaticComplexity
|
|
55
|
-
# rubocop: enable Metrics/PerceivedComplexity
|
|
56
|
-
|
|
57
|
-
# @param arr [Array]
|
|
58
|
-
#
|
|
59
|
-
# @return [nil, ConnectioError]
|
|
60
|
-
def fields(arr)
|
|
61
|
-
arr.each do |name, value|
|
|
62
|
-
err = field(name, value)
|
|
63
|
-
return err unless err.nil?
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
nil
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# @param s [String]
|
|
70
|
-
#
|
|
71
|
-
# @return [HTTPRequest]
|
|
72
|
-
def build(s)
|
|
73
|
-
# `Ractor.send(req, move: true)` moves entries in HPACK::DynamicTable; therefore, a `dup` call is required.
|
|
74
|
-
h = @h.transform_values { |x| x.map(&:dup) }
|
|
75
|
-
self.class.http_request(h, s)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# @param h [Hash<String, Array<String>>]
|
|
79
|
-
# @param s [String]
|
|
80
|
-
#
|
|
81
|
-
# @return [HTTPRequest, ConnectionError]
|
|
82
|
-
def self.http_request(h, s)
|
|
83
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'missing pseudo-header fields') unless PSEUDO_HEADER_FIELDS.all? { |x| h.key?(x) }
|
|
84
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid content-length') if h.key?('content-length') && !s.empty? && s.length != h['content-length'].to_i
|
|
85
|
-
|
|
86
|
-
scheme = h[':scheme'][0]
|
|
87
|
-
domain = h[':authority'][0]
|
|
88
|
-
path = h[':path'][0]
|
|
89
|
-
uri = URI("#{scheme}://#{domain}#{path}")
|
|
90
|
-
method = h[':method'][0]
|
|
91
|
-
h['cookie'] = [h['cookie'].join('; ')] if h.key?('cookie')
|
|
92
|
-
HTTPRequest.new(method, uri, h, s)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
module Biryani
|
|
2
|
-
class HTTPResponse
|
|
3
|
-
FORBIDDEN_KEY_CHARS = (0x00..0x20).chain([0x3a]).chain(0x41..0x5a).chain(0x7f..0xff).to_a.freeze
|
|
4
|
-
FORBIDDEN_VALUE_CHARS = [0x00, 0x0a, 0x0d].freeze
|
|
5
|
-
|
|
6
|
-
attr_accessor :status, :fields, :content
|
|
7
|
-
|
|
8
|
-
# @param status [Integer]
|
|
9
|
-
# @param fields [Hash]
|
|
10
|
-
# @param content [String, nil]
|
|
11
|
-
def initialize(status, fields, content)
|
|
12
|
-
@status = status
|
|
13
|
-
@fields = fields
|
|
14
|
-
@content = content
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# @raise [InvalidHTTPResponseError]
|
|
18
|
-
# rubocop: disable Metrics/CyclomaticComplexity
|
|
19
|
-
def validate
|
|
20
|
-
# https://datatracker.ietf.org/doc/html/rfc9113#section-8.2.1
|
|
21
|
-
raise Error::InvalidHTTPResponseError, 'invalid HTTP status' if @status < 100 || @status >= 600
|
|
22
|
-
raise Error::InvalidHTTPResponseError, 'HTTP field name contains invalid characters' if (@fields.keys.join.downcase.bytes.uniq & FORBIDDEN_KEY_CHARS).any?
|
|
23
|
-
raise Error::InvalidHTTPResponseError, 'HTTP field value contains NUL, LF or CR' if (@fields.values.join.bytes.uniq & FORBIDDEN_VALUE_CHARS).any?
|
|
24
|
-
raise Error::InvalidHTTPResponseError, 'HTTP field value starts/ends with SP or HTAB' if @fields.values.filter { |s| s.start_with?("\t", ' ') || s.end_with?("\t", ' ') }.any?
|
|
25
|
-
end
|
|
26
|
-
# rubocop: enable Metrics/CyclomaticComplexity
|
|
27
|
-
|
|
28
|
-
def self.default
|
|
29
|
-
HTTPResponse.new(0, {}, nil)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def self.internal_server_error
|
|
33
|
-
HTTPResponse.new(500, {}, 'Internal Server Error')
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
class HTTPResponseParser
|
|
38
|
-
# @param res [HTTPResponse]
|
|
39
|
-
def initialize(res)
|
|
40
|
-
@res = res
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# @return [Array]
|
|
44
|
-
def fields
|
|
45
|
-
fields = [[':status', @res.status.to_s]]
|
|
46
|
-
@res.fields.each do |name, value|
|
|
47
|
-
fields << [name.to_s.downcase, value.to_s]
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
fields
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# @return [String]
|
|
54
|
-
def content
|
|
55
|
-
@res.content
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# @param encoder [Encoder]
|
|
59
|
-
#
|
|
60
|
-
# @return [String] fragment
|
|
61
|
-
# @return [String] data
|
|
62
|
-
def parse(encoder)
|
|
63
|
-
[encoder.encode(fields), content]
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
require_relative '../spec_helper'
|
|
2
|
-
|
|
3
|
-
RSpec.describe Connection do
|
|
4
|
-
context 'handle_connection_window_update' do
|
|
5
|
-
let(:window_update) do
|
|
6
|
-
Frame::WindowUpdate.new(0, 1000)
|
|
7
|
-
end
|
|
8
|
-
let(:send_window) do
|
|
9
|
-
Window.new(65_535)
|
|
10
|
-
end
|
|
11
|
-
it 'should handle' do
|
|
12
|
-
expect { Connection.handle_connection_window_update(window_update, send_window) }.not_to raise_error
|
|
13
|
-
expect(send_window.length).to eq 2**16 - 1 + 1000
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
require_relative '../spec_helper'
|
|
2
|
-
|
|
3
|
-
RSpec.describe Connection do
|
|
4
|
-
context 'handle_data' do
|
|
5
|
-
let(:decoder) do
|
|
6
|
-
HPACK::Decoder.new(4_096)
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
let(:recv_window1) do
|
|
10
|
-
Window.new(65_535)
|
|
11
|
-
end
|
|
12
|
-
let(:streams_ctx1) do
|
|
13
|
-
streams_ctx = StreamsContext.new(do_nothing_proc)
|
|
14
|
-
streams_ctx.new_context(1, 65_535, 65_535)
|
|
15
|
-
streams_ctx.new_context(2, 65_535, 65_535)
|
|
16
|
-
streams_ctx
|
|
17
|
-
end
|
|
18
|
-
it 'should handle' do
|
|
19
|
-
expect(Connection.handle_data(2, 'Hello, world!', recv_window1, streams_ctx1, decoder)).to eq []
|
|
20
|
-
expect(streams_ctx1[2].content).to eq 'Hello, world!'
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
let(:recv_window2) do
|
|
24
|
-
recv_window = Window.new(65_535)
|
|
25
|
-
recv_window.consume!(65_535 / 2)
|
|
26
|
-
recv_window
|
|
27
|
-
end
|
|
28
|
-
let(:streams_ctx2) do
|
|
29
|
-
streams_ctx = StreamsContext.new(do_nothing_proc)
|
|
30
|
-
streams_ctx.new_context(1, 65_535, 65_535)
|
|
31
|
-
streams_ctx.new_context(2, 65_535, 65_535)
|
|
32
|
-
streams_ctx[2].recv_window.consume!(65_535 / 2)
|
|
33
|
-
streams_ctx
|
|
34
|
-
end
|
|
35
|
-
it 'should handle' do
|
|
36
|
-
frames = Connection.handle_data(2, 'Hello, world!', recv_window2, streams_ctx2, decoder)
|
|
37
|
-
expect(frames.map(&:f_type)).to eq [FrameType::WINDOW_UPDATE, FrameType::WINDOW_UPDATE]
|
|
38
|
-
expect(frames.map(&:stream_id)).to eq [0, 2]
|
|
39
|
-
expect(frames.map(&:window_size_increment)).to eq [65_535 / 2 + 13, 65_535 / 2 + 13]
|
|
40
|
-
expect(streams_ctx2[2].content).to eq 'Hello, world!'
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
let(:recv_window3) do
|
|
44
|
-
recv_window = Window.new(65_535)
|
|
45
|
-
recv_window.consume!(65_535)
|
|
46
|
-
recv_window
|
|
47
|
-
end
|
|
48
|
-
let(:streams_ctx3) do
|
|
49
|
-
streams_ctx = StreamsContext.new(do_nothing_proc)
|
|
50
|
-
streams_ctx.new_context(1, 65_535, 65_535)
|
|
51
|
-
streams_ctx.new_context(2, 65_535, 65_535)
|
|
52
|
-
streams_ctx
|
|
53
|
-
end
|
|
54
|
-
it 'should not handle' do
|
|
55
|
-
expect(Connection.handle_data(2, 'Hello, world!', recv_window3, streams_ctx3, decoder)).to be_kind_of ConnectionError
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
require_relative '../spec_helper'
|
|
2
|
-
|
|
3
|
-
RSpec.describe Connection do
|
|
4
|
-
context 'handle_headers' do
|
|
5
|
-
let(:headers) do
|
|
6
|
-
Frame::Headers.new(true, false, 2, nil, nil, 'this is dummy', nil)
|
|
7
|
-
end
|
|
8
|
-
let(:ctx) do
|
|
9
|
-
StreamContext.new(2, 65_535, 65_535, do_nothing_proc)
|
|
10
|
-
end
|
|
11
|
-
let(:decoder) do
|
|
12
|
-
HPACK::Decoder.new(4_096)
|
|
13
|
-
end
|
|
14
|
-
it 'should handle' do
|
|
15
|
-
expect(Connection.handle_headers(headers, ctx, decoder)).to eq nil
|
|
16
|
-
expect(ctx.fragment).to eq 'this is dummy'
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
require_relative '../spec_helper'
|
|
2
|
-
|
|
3
|
-
RSpec.describe Connection do
|
|
4
|
-
context 'handle_ping' do
|
|
5
|
-
let(:ping1) do
|
|
6
|
-
Frame::Ping.new(true, 0, "\x00" * 8)
|
|
7
|
-
end
|
|
8
|
-
it 'should handle' do
|
|
9
|
-
expect(Connection.handle_ping(ping1)).to eq nil
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
let(:ping2) do
|
|
13
|
-
Frame::Ping.new(false, 0, "\x00" * 8)
|
|
14
|
-
end
|
|
15
|
-
it 'should handle' do
|
|
16
|
-
expect(Connection.handle_ping(ping2)).to_not eq nil
|
|
17
|
-
expect(Connection.handle_ping(ping2).ack?).to eq true
|
|
18
|
-
expect(Connection.handle_ping(ping2).opaque).to eq "\x00" * 8
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
require_relative '../spec_helper'
|
|
2
|
-
|
|
3
|
-
RSpec.describe Connection do
|
|
4
|
-
context 'handle_rst_stream' do
|
|
5
|
-
let(:rst_stream) do
|
|
6
|
-
Frame::RstStream.new(2, 0)
|
|
7
|
-
end
|
|
8
|
-
let(:ctx) do
|
|
9
|
-
StreamContext.new(2, 65_535, 65_535, do_nothing_proc)
|
|
10
|
-
end
|
|
11
|
-
it 'should handle' do
|
|
12
|
-
Connection.handle_rst_stream(rst_stream, ctx)
|
|
13
|
-
expect(ctx.closed?).to eq true
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
require_relative '../spec_helper'
|
|
2
|
-
|
|
3
|
-
RSpec.describe Connection do
|
|
4
|
-
context 'handle_settings' do
|
|
5
|
-
let(:send_settings) do
|
|
6
|
-
Connection.default_settings
|
|
7
|
-
end
|
|
8
|
-
let(:decoder) do
|
|
9
|
-
HPACK::Decoder.new(4_096)
|
|
10
|
-
end
|
|
11
|
-
let(:streams_ctx) do
|
|
12
|
-
streams_ctx = StreamsContext.new(do_nothing_proc)
|
|
13
|
-
streams_ctx.new_context(1, 65_535, 65_535)
|
|
14
|
-
streams_ctx.new_context(2, 65_535, 65_535)
|
|
15
|
-
streams_ctx
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
let(:settings1) do
|
|
19
|
-
Frame::Settings.new(false, 0, { SettingsID::SETTINGS_HEADER_TABLE_SIZE => 8_192 })
|
|
20
|
-
end
|
|
21
|
-
it 'should handle' do
|
|
22
|
-
reply_settings = Connection.handle_settings(settings1, send_settings, decoder, streams_ctx)
|
|
23
|
-
expect(reply_settings.ack?).to eq true
|
|
24
|
-
expect(reply_settings.setting.empty?).to eq true
|
|
25
|
-
expect(send_settings[SettingsID::SETTINGS_MAX_CONCURRENT_STREAMS]).to eq 0xffffffff
|
|
26
|
-
expect(send_settings[SettingsID::SETTINGS_MAX_FRAME_SIZE]).to eq 16_384
|
|
27
|
-
expect(send_settings[SettingsID::SETTINGS_HEADER_TABLE_SIZE]).to eq 8_192
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
let(:settings2) do
|
|
31
|
-
Frame::Settings.new(true, 0, {})
|
|
32
|
-
end
|
|
33
|
-
it 'should handle' do
|
|
34
|
-
expect(Connection.handle_settings(settings2, send_settings, decoder, streams_ctx)).to eq nil
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
require_relative '../spec_helper'
|
|
2
|
-
|
|
3
|
-
RSpec.describe Connection do
|
|
4
|
-
context 'handle_stream_window_update' do
|
|
5
|
-
let(:window_update) do
|
|
6
|
-
Frame::WindowUpdate.new(1, 1000)
|
|
7
|
-
end
|
|
8
|
-
let(:streams_ctx) do
|
|
9
|
-
streams_ctx = StreamsContext.new(do_nothing_proc)
|
|
10
|
-
streams_ctx.new_context(1, 65_535, 65_535)
|
|
11
|
-
streams_ctx.new_context(2, 65_535, 65_535)
|
|
12
|
-
streams_ctx
|
|
13
|
-
end
|
|
14
|
-
it 'should handle' do
|
|
15
|
-
expect { Connection.handle_stream_window_update(window_update, streams_ctx) }.not_to raise_error
|
|
16
|
-
expect(streams_ctx[1].send_window.length).to eq 65_535 + 1000
|
|
17
|
-
expect(streams_ctx[2].send_window.length).to eq 65_535
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
require_relative '../spec_helper'
|
|
2
|
-
|
|
3
|
-
RSpec.describe Connection do
|
|
4
|
-
context 'read_http2_magic' do
|
|
5
|
-
let(:io1) do
|
|
6
|
-
StringIO.new("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
|
|
7
|
-
end
|
|
8
|
-
it 'should read' do
|
|
9
|
-
expect(Connection.read_http2_magic(io1)).to eq nil
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
let(:io2) do
|
|
13
|
-
StringIO.new("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n\x00xff")
|
|
14
|
-
end
|
|
15
|
-
it 'should read' do
|
|
16
|
-
expect(Connection.read_http2_magic(io2)).to eq nil
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
let(:io3) do
|
|
20
|
-
StringIO.new("\x00xffPRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
|
|
21
|
-
end
|
|
22
|
-
it 'should not read' do
|
|
23
|
-
expect(Connection.read_http2_magic(io3)).to be_kind_of ConnectionError
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|