biryani 0.0.4 → 0.0.6
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/Rakefile +7 -0
- data/conformance/server_spec.rb +31 -0
- data/conformance/spec_helper.rb +14 -0
- data/example/echo.rb +0 -1
- data/example/raise_error.rb +19 -0
- data/lib/biryani/connection.rb +23 -18
- data/lib/biryani/data_buffer.rb +1 -1
- data/lib/biryani/error.rb +8 -0
- data/lib/biryani/frame/continuation.rb +4 -7
- data/lib/biryani/frame/data.rb +9 -12
- data/lib/biryani/frame/goaway.rb +6 -6
- data/lib/biryani/frame/headers.rb +25 -18
- data/lib/biryani/frame/ping.rb +5 -7
- data/lib/biryani/frame/priority.rb +6 -5
- data/lib/biryani/frame/push_promise.rb +14 -15
- data/lib/biryani/frame/rst_stream.rb +5 -5
- data/lib/biryani/frame/settings.rb +5 -5
- data/lib/biryani/frame/unknown.rb +1 -15
- data/lib/biryani/frame/window_update.rb +5 -5
- data/lib/biryani/frame.rb +10 -4
- data/lib/biryani/hpack/field.rb +42 -42
- data/lib/biryani/hpack/fields.rb +2 -1
- data/lib/biryani/hpack/huffman.rb +290 -21
- data/lib/biryani/hpack/integer.rb +10 -6
- data/lib/biryani/hpack/string.rb +6 -6
- data/lib/biryani/http_request.rb +3 -2
- data/lib/biryani/http_response.rb +23 -2
- data/lib/biryani/server.rb +1 -1
- data/lib/biryani/state.rb +14 -7
- data/lib/biryani/stream.rb +10 -3
- data/lib/biryani/streams_context.rb +12 -6
- data/lib/biryani/version.rb +1 -1
- data/lib/biryani.rb +1 -1
- data/spec/connection/handle_data_spec.rb +2 -2
- data/spec/connection/handle_headers_spec.rb +1 -1
- data/spec/connection/{transition_state_send_spec.rb → transition_stream_state_send_spec.rb} +3 -3
- data/spec/frame/continuation_spec.rb +2 -2
- data/spec/frame/data_spec.rb +1 -1
- data/spec/frame/goaway_spec.rb +1 -1
- data/spec/frame/headers_spec.rb +2 -2
- data/spec/frame/ping_spec.rb +1 -1
- data/spec/frame/priority_spec.rb +1 -1
- data/spec/frame/push_promise_spec.rb +1 -1
- data/spec/frame/rst_stream_spec.rb +1 -1
- data/spec/frame/settings_spec.rb +1 -1
- data/spec/frame/window_update_spec.rb +1 -1
- data/spec/hpack/field_spec.rb +10 -9
- data/spec/hpack/huffman_spec.rb +4 -4
- data/spec/hpack/integer_spec.rb +5 -5
- data/spec/hpack/string_spec.rb +2 -2
- data/spec/http_response_spec.rb +12 -0
- metadata +9 -3
|
@@ -1,15 +1,37 @@
|
|
|
1
1
|
module Biryani
|
|
2
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
|
+
|
|
3
6
|
attr_accessor :status, :fields, :content
|
|
4
7
|
|
|
5
8
|
# @param status [Integer]
|
|
6
9
|
# @param fields [Hash]
|
|
7
|
-
# @param content [String]
|
|
10
|
+
# @param content [String, nil]
|
|
8
11
|
def initialize(status, fields, content)
|
|
9
12
|
@status = status
|
|
10
13
|
@fields = fields
|
|
11
14
|
@content = content
|
|
12
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
|
|
13
35
|
end
|
|
14
36
|
|
|
15
37
|
class HTTPResponseParser
|
|
@@ -22,7 +44,6 @@ module Biryani
|
|
|
22
44
|
def fields
|
|
23
45
|
fields = [[':status', @res.status.to_s]]
|
|
24
46
|
@res.fields.each do |name, value|
|
|
25
|
-
# TODO: validate fields
|
|
26
47
|
fields << [name.to_s.downcase, value.to_s]
|
|
27
48
|
end
|
|
28
49
|
|
data/lib/biryani/server.rb
CHANGED
data/lib/biryani/state.rb
CHANGED
|
@@ -65,7 +65,7 @@ module Biryani
|
|
|
65
65
|
in [:idle, FrameType::HEADERS, :recv] if frame.end_stream?
|
|
66
66
|
:receiving_continuation
|
|
67
67
|
in [:idle, FrameType::HEADERS, :recv]
|
|
68
|
-
:
|
|
68
|
+
:receiving_continuation_and_data
|
|
69
69
|
in [:idle, FrameType::PRIORITY, :recv]
|
|
70
70
|
state
|
|
71
71
|
in [:idle, FrameType::PUSH_PROMISE, :send]
|
|
@@ -73,16 +73,16 @@ module Biryani
|
|
|
73
73
|
in [:idle, _, _]
|
|
74
74
|
unexpected(ErrorCode::PROTOCOL_ERROR, state, typ, direction)
|
|
75
75
|
|
|
76
|
-
#
|
|
77
|
-
in [:
|
|
76
|
+
# receiving_continuation_and_data
|
|
77
|
+
in [:receiving_continuation_and_data, FrameType::RST_STREAM, _]
|
|
78
78
|
:closed
|
|
79
|
-
in [:
|
|
79
|
+
in [:receiving_continuation_and_data, FrameType::WINDOW_UPDATE, :recv]
|
|
80
80
|
state
|
|
81
|
-
in [:
|
|
81
|
+
in [:receiving_continuation_and_data, FrameType::CONTINUATION, :recv] if frame.end_headers?
|
|
82
82
|
:receiving_data
|
|
83
|
-
in [:
|
|
83
|
+
in [:receiving_continuation_and_data, FrameType::CONTINUATION, :recv]
|
|
84
84
|
state
|
|
85
|
-
in [:
|
|
85
|
+
in [:receiving_continuation_and_data, _, _]
|
|
86
86
|
unexpected(ErrorCode::PROTOCOL_ERROR, state, typ, direction)
|
|
87
87
|
|
|
88
88
|
# receiving_continuation
|
|
@@ -182,6 +182,8 @@ module Biryani
|
|
|
182
182
|
unexpected(ErrorCode::PROTOCOL_ERROR, state, typ, direction)
|
|
183
183
|
|
|
184
184
|
# closed
|
|
185
|
+
in [:closed, FrameType::PRIORITY, :recv]
|
|
186
|
+
state
|
|
185
187
|
in [:closed, FrameType::RST_STREAM, :recv]
|
|
186
188
|
state
|
|
187
189
|
in [:closed, _, :send]
|
|
@@ -232,5 +234,10 @@ module Biryani
|
|
|
232
234
|
def half_closed_remote?
|
|
233
235
|
@state == :half_closed_remote
|
|
234
236
|
end
|
|
237
|
+
|
|
238
|
+
# @return [Boolean]
|
|
239
|
+
def receiving_continuation?
|
|
240
|
+
%i[receiving_continuation receiving_continuation_and_data].include?(@state)
|
|
241
|
+
end
|
|
235
242
|
end
|
|
236
243
|
end
|
data/lib/biryani/stream.rb
CHANGED
|
@@ -8,10 +8,17 @@ module Biryani
|
|
|
8
8
|
def initialize(tx, stream_id, proc)
|
|
9
9
|
@rx = Ractor.new(tx, stream_id, proc) do |tx, stream_id, proc|
|
|
10
10
|
unless (req = Ractor.recv).nil?
|
|
11
|
-
res = HTTPResponse.
|
|
11
|
+
res = HTTPResponse.default
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
begin
|
|
14
|
+
proc.call(req, res)
|
|
15
|
+
res.validate
|
|
16
|
+
rescue StandardError => e
|
|
17
|
+
puts e.backtrace
|
|
18
|
+
res = HTTPResponse.internal_server_error
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
tx.send([res, stream_id], move: true)
|
|
15
22
|
end
|
|
16
23
|
end
|
|
17
24
|
end
|
|
@@ -47,6 +47,11 @@ module Biryani
|
|
|
47
47
|
@h.values.filter(&:active?).length
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
# @return [Boolean]
|
|
51
|
+
def receiving_continuation?
|
|
52
|
+
@h.values.any?(&:receiving_continuation?)
|
|
53
|
+
end
|
|
54
|
+
|
|
50
55
|
# @return [Array<Integer>]
|
|
51
56
|
def closed_stream_ids
|
|
52
57
|
@h.filter { |_, ctx| ctx.closed? }.keys
|
|
@@ -84,16 +89,12 @@ module Biryani
|
|
|
84
89
|
closed_ids = closed_stream_ids.filter { |id| !data_buffer.has?(id) }
|
|
85
90
|
closed_ids.each do |id|
|
|
86
91
|
@h[id].tx.close
|
|
87
|
-
@h[id].fragment.close
|
|
88
|
-
@h[id].content.close
|
|
89
92
|
end
|
|
90
93
|
end
|
|
91
94
|
|
|
92
95
|
def close_all
|
|
93
96
|
each do |ctx|
|
|
94
97
|
ctx.tx.close
|
|
95
|
-
ctx.fragment.close
|
|
96
|
-
ctx.content.close
|
|
97
98
|
ctx.state.close
|
|
98
99
|
end
|
|
99
100
|
end
|
|
@@ -111,8 +112,8 @@ module Biryani
|
|
|
111
112
|
@stream = Stream.new(@tx, stream_id, proc)
|
|
112
113
|
@send_window = Window.new(send_initial_window_size)
|
|
113
114
|
@recv_window = Window.new(recv_initial_window_size)
|
|
114
|
-
@fragment =
|
|
115
|
-
@content =
|
|
115
|
+
@fragment = ''.b
|
|
116
|
+
@content = ''.b
|
|
116
117
|
@state = State.new
|
|
117
118
|
end
|
|
118
119
|
|
|
@@ -135,5 +136,10 @@ module Biryani
|
|
|
135
136
|
def half_closed_remote?
|
|
136
137
|
@state.half_closed_remote?
|
|
137
138
|
end
|
|
139
|
+
|
|
140
|
+
# @return [Boolean]
|
|
141
|
+
def receiving_continuation?
|
|
142
|
+
@state.receiving_continuation?
|
|
143
|
+
end
|
|
138
144
|
end
|
|
139
145
|
end
|
data/lib/biryani/version.rb
CHANGED
data/lib/biryani.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
require 'stringio'
|
|
2
1
|
require 'uri'
|
|
3
2
|
|
|
4
3
|
require_relative 'biryani/connection_error'
|
|
5
4
|
require_relative 'biryani/connection'
|
|
6
5
|
require_relative 'biryani/data_buffer'
|
|
6
|
+
require_relative 'biryani/error'
|
|
7
7
|
require_relative 'biryani/frame'
|
|
8
8
|
require_relative 'biryani/hpack'
|
|
9
9
|
require_relative 'biryani/http_request'
|
|
@@ -17,7 +17,7 @@ RSpec.describe Connection do
|
|
|
17
17
|
end
|
|
18
18
|
it 'should handle' do
|
|
19
19
|
expect(Connection.handle_data(2, 'Hello, world!', recv_window1, streams_ctx1, decoder)).to eq []
|
|
20
|
-
expect(streams_ctx1[2].content
|
|
20
|
+
expect(streams_ctx1[2].content).to eq 'Hello, world!'
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
let(:recv_window2) do
|
|
@@ -37,7 +37,7 @@ RSpec.describe Connection do
|
|
|
37
37
|
expect(frames.map(&:f_type)).to eq [FrameType::WINDOW_UPDATE, FrameType::WINDOW_UPDATE]
|
|
38
38
|
expect(frames.map(&:stream_id)).to eq [0, 2]
|
|
39
39
|
expect(frames.map(&:window_size_increment)).to eq [65_535 / 2 + 13, 65_535 / 2 + 13]
|
|
40
|
-
expect(streams_ctx2[2].content
|
|
40
|
+
expect(streams_ctx2[2].content).to eq 'Hello, world!'
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
let(:recv_window3) do
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require_relative '../spec_helper'
|
|
2
2
|
|
|
3
3
|
RSpec.describe Connection do
|
|
4
|
-
context '
|
|
4
|
+
context 'transition_stream_state_send' do
|
|
5
5
|
let(:streams_ctx) do
|
|
6
6
|
streams_ctx = StreamsContext.new(do_nothing_proc)
|
|
7
7
|
streams_ctx.new_context(1, 65_535, 65_535)
|
|
@@ -14,7 +14,7 @@ RSpec.describe Connection do
|
|
|
14
14
|
end
|
|
15
15
|
it 'should transition' do
|
|
16
16
|
streams_ctx[2].state.transition!(headers, :recv)
|
|
17
|
-
Connection.
|
|
17
|
+
Connection.transition_stream_state_send(headers, streams_ctx)
|
|
18
18
|
expect(streams_ctx.length).to eq 2
|
|
19
19
|
end
|
|
20
20
|
|
|
@@ -31,7 +31,7 @@ RSpec.describe Connection do
|
|
|
31
31
|
end
|
|
32
32
|
it 'should transition' do
|
|
33
33
|
streams_ctx[2].state.transition!(headers, :recv)
|
|
34
|
-
Connection.
|
|
34
|
+
Connection.transition_stream_state_send(rst_stream, streams_ctx)
|
|
35
35
|
expect { streams_ctx[1].tx << nil }.to_not raise_error
|
|
36
36
|
expect { streams_ctx[2].tx << nil }.to_not raise_error
|
|
37
37
|
end
|
|
@@ -17,7 +17,7 @@ RSpec.describe Frame::Continuation do
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
let(:continuation3) do
|
|
20
|
-
Frame::Continuation.read(
|
|
20
|
+
Frame::Continuation.read(''.b, 0, 50)
|
|
21
21
|
end
|
|
22
22
|
it 'should decode' do
|
|
23
23
|
expect(continuation3.f_type).to eq FrameType::CONTINUATION
|
|
@@ -27,7 +27,7 @@ RSpec.describe Frame::Continuation do
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
let(:continuation4) do
|
|
30
|
-
Frame::Continuation.read("\
|
|
30
|
+
Frame::Continuation.read("\x74\x68\x69\x73\x20\x69\x73\x20\x64\x75\x6d\x6d\x79".b, 9, 50)
|
|
31
31
|
end
|
|
32
32
|
it 'should decode' do
|
|
33
33
|
expect(continuation4.f_type).to eq FrameType::CONTINUATION
|
data/spec/frame/data_spec.rb
CHANGED
|
@@ -10,7 +10,7 @@ RSpec.describe Frame::Data do
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
let(:data2) do
|
|
13
|
-
Frame::Data.read("\
|
|
13
|
+
Frame::Data.read("\x06\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21\x48\x6f\x77\x64\x79\x21".b, 8, 2)
|
|
14
14
|
end
|
|
15
15
|
it 'should decode' do
|
|
16
16
|
expect(data2.f_type).to eq FrameType::DATA
|
data/spec/frame/goaway_spec.rb
CHANGED
|
@@ -10,7 +10,7 @@ RSpec.describe Frame::Goaway do
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
let(:goaway2) do
|
|
13
|
-
Frame::Goaway.read("\x00\x00\
|
|
13
|
+
Frame::Goaway.read("\x00\x00\x00\x1e\x00\x00\x00\x09\x68\x70\x61\x63\x6b\x20\x69\x73\x20\x62\x72\x6f\x6b\x65\x6e".b, 7, 0)
|
|
14
14
|
end
|
|
15
15
|
it 'should decode' do
|
|
16
16
|
expect(goaway2.f_type).to eq FrameType::GOAWAY
|
data/spec/frame/headers_spec.rb
CHANGED
|
@@ -18,7 +18,7 @@ RSpec.describe Frame::Headers do
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
let(:headers3) do
|
|
21
|
-
Frame::Headers.read("\
|
|
21
|
+
Frame::Headers.read("\x74\x68\x69\x73\x20\x69\x73\x20\x64\x75\x6d\x6d\x79".b, 4, 1)
|
|
22
22
|
end
|
|
23
23
|
it 'should decode' do
|
|
24
24
|
expect(headers3.f_type).to eq FrameType::HEADERS
|
|
@@ -34,7 +34,7 @@ RSpec.describe Frame::Headers do
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
let(:headers4) do
|
|
37
|
-
Frame::Headers.read("\
|
|
37
|
+
Frame::Headers.read("\x10\x00\x00\x00\x14\x0a\x74\x68\x69\x73\x20\x69\x73\x20\x64\x75\x6d\x6d\x79\x54\x68\x69\x73\x20\x69\x73\x20\x70\x61\x64\x64\x69\x6e\x67\x2e".b, 44, 3)
|
|
38
38
|
end
|
|
39
39
|
it 'should decode' do
|
|
40
40
|
expect(headers4.f_type).to eq FrameType::HEADERS
|
data/spec/frame/ping_spec.rb
CHANGED
|
@@ -10,7 +10,7 @@ RSpec.describe Frame::Ping do
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
let(:ping2) do
|
|
13
|
-
Frame::Ping.read("\
|
|
13
|
+
Frame::Ping.read("\x64\x65\x61\x64\x62\x65\x65\x66".b, 0, 0)
|
|
14
14
|
end
|
|
15
15
|
it 'should decode' do
|
|
16
16
|
expect(ping2.f_type).to eq FrameType::PING
|
data/spec/frame/priority_spec.rb
CHANGED
|
@@ -10,7 +10,7 @@ RSpec.describe Frame::Priority do
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
let(:priority2) do
|
|
13
|
-
Frame::Priority.read("\x00\x00\
|
|
13
|
+
Frame::Priority.read("\x00\x00\x00\x0b\x08".b, 0, 9)
|
|
14
14
|
end
|
|
15
15
|
it 'should decode' do
|
|
16
16
|
expect(priority2.f_type).to eq FrameType::PRIORITY
|
|
@@ -10,7 +10,7 @@ RSpec.describe Frame::PushPromise do
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
let(:push_promise2) do
|
|
13
|
-
Frame::PushPromise.read("\
|
|
13
|
+
Frame::PushPromise.read("\x06\x00\x00\x00\x0c\x74\x68\x69\x73\x20\x69\x73\x20\x64\x75\x6d\x6d\x79\x48\x6f\x77\x64\x79\x21".b, 12, 10)
|
|
14
14
|
end
|
|
15
15
|
it 'should decode' do
|
|
16
16
|
expect(push_promise2.f_type).to eq FrameType::PUSH_PROMISE
|
|
@@ -10,7 +10,7 @@ RSpec.describe Frame::RstStream do
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
let(:rst_stream2) do
|
|
13
|
-
Frame::RstStream.read("\x00\x00\
|
|
13
|
+
Frame::RstStream.read("\x00\x00\x00\x08".b, 0, 5)
|
|
14
14
|
end
|
|
15
15
|
it 'should decode' do
|
|
16
16
|
expect(rst_stream2.f_type).to eq FrameType::RST_STREAM
|
data/spec/frame/settings_spec.rb
CHANGED
|
@@ -10,7 +10,7 @@ RSpec.describe Frame::Settings do
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
let(:settings2) do
|
|
13
|
-
Frame::Settings.read("\x00\
|
|
13
|
+
Frame::Settings.read("\x00\x01\x00\x00\x20\x00\x00\x03\x00\x00\x13\x88".b, 0, 0)
|
|
14
14
|
end
|
|
15
15
|
it 'should decode' do
|
|
16
16
|
expect(settings2.f_type).to eq FrameType::SETTINGS
|
|
@@ -10,7 +10,7 @@ RSpec.describe Frame::WindowUpdate do
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
let(:window_update2) do
|
|
13
|
-
Frame::WindowUpdate.read("\x00\x00\
|
|
13
|
+
Frame::WindowUpdate.read("\x00\x00\x03\xe8".b, 0, 50)
|
|
14
14
|
end
|
|
15
15
|
it 'should decode' do
|
|
16
16
|
expect(window_update2.f_type).to eq FrameType::WINDOW_UPDATE
|
data/spec/hpack/field_spec.rb
CHANGED
|
@@ -18,25 +18,26 @@ RSpec.describe HPACK::Field do
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
it 'should decode' do
|
|
21
|
-
expect(HPACK::Field.decode("\x82".b, cursor, dynamic_table)).to eq [%w[:method GET], 1]
|
|
22
|
-
expect(HPACK::Field.decode("\x48\x82\x64\x02".b, cursor, dynamic_table)).to eq [%w[:status 302], 4]
|
|
23
|
-
expect(HPACK::Field.decode("\x6e\x91\x9d\x29\xad\x17\x18\x63\xc7\x8f\x0b\x97\xc8\xe9\xae\x82\xae\x43\xd3".b, cursor, dynamic_table))
|
|
24
|
-
|
|
25
|
-
expect(HPACK::Field.decode("\
|
|
21
|
+
expect(HPACK::Field.decode(IO::Buffer.for("\x82".b), cursor, dynamic_table)).to eq [%w[:method GET], 1]
|
|
22
|
+
expect(HPACK::Field.decode(IO::Buffer.for("\x48\x82\x64\x02".b), cursor, dynamic_table)).to eq [%w[:status 302], 4]
|
|
23
|
+
expect(HPACK::Field.decode(IO::Buffer.for("\x6e\x91\x9d\x29\xad\x17\x18\x63\xc7\x8f\x0b\x97\xc8\xe9\xae\x82\xae\x43\xd3".b), cursor, dynamic_table))
|
|
24
|
+
.to eq [%w[location https://www.example.com], 19]
|
|
25
|
+
expect(HPACK::Field.decode(IO::Buffer.for("\x40\x88\x25\xa8\x49\xe9\x5b\xa9\x7d\x7f\x89\x25\xa8\x49\xe9\x5a\x72\x8e\x42\xd9".b), cursor, dynamic_table)).to eq [%w[custom-key custom-header], 20]
|
|
26
|
+
expect(HPACK::Field.decode(IO::Buffer.for("\x10\x08\x70\x61\x73\x73\x77\x6f\x72\x64\x06\x73\x65\x63\x72\x65\x74".b), cursor, dynamic_table)).to eq [%w[password secret], 17]
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
it 'should decode' do
|
|
29
|
-
expect(HPACK::Field.decode("\x40\x88\x25\xa8\x49\xe9\x5b\xa9\x7d\x7f\x89\x25\xa8\x49\xe9\x5a\x72\x8e\x42\xd9".b, cursor, dynamic_table)).to eq [%w[custom-key custom-header], 20]
|
|
30
|
+
expect(HPACK::Field.decode(IO::Buffer.for("\x40\x88\x25\xa8\x49\xe9\x5b\xa9\x7d\x7f\x89\x25\xa8\x49\xe9\x5a\x72\x8e\x42\xd9".b), cursor, dynamic_table)).to eq [%w[custom-key custom-header], 20]
|
|
30
31
|
expect(dynamic_table.limit).to eq 4096
|
|
31
32
|
expect(dynamic_table.size).to eq 'custom-keycustom-header'.bytesize + 32
|
|
32
|
-
expect(HPACK::Field.decode("\x20".b, cursor, dynamic_table)).to eq [nil, 1]
|
|
33
|
+
expect(HPACK::Field.decode(IO::Buffer.for("\x20".b), cursor, dynamic_table)).to eq [nil, 1]
|
|
33
34
|
expect(dynamic_table.limit).to eq 4096
|
|
34
35
|
expect(dynamic_table.size).to eq 0
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
it 'should not decode' do
|
|
38
|
-
expect { HPACK::Field.decode("\xbe".b, cursor, dynamic_table) }.to raise_error HPACK::Error::HPACKDecodeError
|
|
39
|
-
expect { HPACK::Field.decode("\x3f\xe2\x1f".b, cursor, dynamic_table) }.to raise_error HPACK::Error::HPACKDecodeError
|
|
39
|
+
expect { HPACK::Field.decode(IO::Buffer.for("\xbe".b), cursor, dynamic_table) }.to raise_error HPACK::Error::HPACKDecodeError
|
|
40
|
+
expect { HPACK::Field.decode(IO::Buffer.for("\x3f\xe2\x1f".b), cursor, dynamic_table) }.to raise_error HPACK::Error::HPACKDecodeError
|
|
40
41
|
end
|
|
41
42
|
end
|
|
42
43
|
end
|
data/spec/hpack/huffman_spec.rb
CHANGED
|
@@ -8,13 +8,13 @@ RSpec.describe HPACK::Huffman do
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
it 'should decode' do
|
|
11
|
-
expect(HPACK::Huffman.decode("\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff".b)).to eq 'www.example.com'
|
|
12
|
-
expect(HPACK::Huffman.decode("\xa8\xeb\x10\x64\x9c\xbf".b)).to eq 'no-cache'
|
|
11
|
+
expect(HPACK::Huffman.decode(IO::Buffer.for("\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff".b), 0, 12)).to eq 'www.example.com'
|
|
12
|
+
expect(HPACK::Huffman.decode(IO::Buffer.for("\xa8\xeb\x10\x64\x9c\xbf".b), 0, 6)).to eq 'no-cache'
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
it 'should not decode' do
|
|
16
|
-
expect { HPACK::Huffman.decode("\xff\xff\xff\xff".b) }.to raise_error HPACK::Error::HuffmanDecodeError
|
|
17
|
-
expect { HPACK::Huffman.decode("\xf8\xff".b) }.to raise_error HPACK::Error::HuffmanDecodeError
|
|
16
|
+
expect { HPACK::Huffman.decode(IO::Buffer.for("\xff\xff\xff\xff".b), 0, 4) }.to raise_error HPACK::Error::HuffmanDecodeError
|
|
17
|
+
expect { HPACK::Huffman.decode(IO::Buffer.for("\xf8\xff".b), 0, 2) }.to raise_error HPACK::Error::HuffmanDecodeError
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
end
|
data/spec/hpack/integer_spec.rb
CHANGED
|
@@ -17,11 +17,11 @@ RSpec.describe HPACK::Integer do
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
it 'should decode' do
|
|
20
|
-
expect(HPACK::Integer.decode("\x0a".b, 5, cursor)).to eq [10, 1]
|
|
21
|
-
expect(HPACK::Integer.decode("\x1f\x9a\x0a".b, 5, cursor)).to eq [1337, 3]
|
|
22
|
-
expect(HPACK::Integer.decode("\x2a".b, 8, cursor)).to eq [42, 1]
|
|
23
|
-
expect(HPACK::Integer.decode("\x8a".b, 5, cursor)).to eq [10, 1]
|
|
24
|
-
expect(HPACK::Integer.decode("\x0a\x0b".b, 5, 1)).to eq [11, 2]
|
|
20
|
+
expect(HPACK::Integer.decode(IO::Buffer.for("\x0a".b), 5, cursor)).to eq [10, 1]
|
|
21
|
+
expect(HPACK::Integer.decode(IO::Buffer.for("\x1f\x9a\x0a".b), 5, cursor)).to eq [1337, 3]
|
|
22
|
+
expect(HPACK::Integer.decode(IO::Buffer.for("\x2a".b), 8, cursor)).to eq [42, 1]
|
|
23
|
+
expect(HPACK::Integer.decode(IO::Buffer.for("\x8a".b), 5, cursor)).to eq [10, 1]
|
|
24
|
+
expect(HPACK::Integer.decode(IO::Buffer.for("\x0a\x0b".b), 5, 1)).to eq [11, 2]
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
end
|
data/spec/hpack/string_spec.rb
CHANGED
|
@@ -12,8 +12,8 @@ RSpec.describe HPACK::String do
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
it 'should decode' do
|
|
15
|
-
expect(HPACK::String.decode("\x88\x25\xa8\x49\xe9\x5b\xa9\x7d\x7f".b, cursor)).to eq ['custom-key', 9]
|
|
16
|
-
expect(HPACK::String.decode("\x01\x5c".b, cursor)).to eq ['\\', 2]
|
|
15
|
+
expect(HPACK::String.decode(IO::Buffer.for("\x88\x25\xa8\x49\xe9\x5b\xa9\x7d\x7f".b), cursor)).to eq ['custom-key', 9]
|
|
16
|
+
expect(HPACK::String.decode(IO::Buffer.for("\x01\x5c".b), cursor)).to eq ['\\', 2]
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe HTTPResponse do
|
|
4
|
+
it 'should validate' do
|
|
5
|
+
expect { HTTPResponse.new(600, {}, nil).validate }.to raise_error Error::InvalidHTTPResponseError
|
|
6
|
+
expect { HTTPResponse.new(200, { "\x00key" => 'value' }, nil).validate }.to raise_error Error::InvalidHTTPResponseError
|
|
7
|
+
expect { HTTPResponse.new(200, { 'key:' => 'value' }, nil).validate }.to raise_error Error::InvalidHTTPResponseError
|
|
8
|
+
expect { HTTPResponse.new(200, { 'key' => "one\ntwo" }, nil).validate }.to raise_error Error::InvalidHTTPResponseError
|
|
9
|
+
expect { HTTPResponse.new(200, { 'key' => ' value' }, nil).validate }.to raise_error Error::InvalidHTTPResponseError
|
|
10
|
+
expect { HTTPResponse.new(200, { 'key' => "value\t" }, nil).validate }.to raise_error Error::InvalidHTTPResponseError
|
|
11
|
+
end
|
|
12
|
+
end
|
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.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- thekuwayama
|
|
@@ -39,12 +39,16 @@ files:
|
|
|
39
39
|
- README.md
|
|
40
40
|
- Rakefile
|
|
41
41
|
- biryani.gemspec
|
|
42
|
+
- conformance/server_spec.rb
|
|
43
|
+
- conformance/spec_helper.rb
|
|
42
44
|
- example/echo.rb
|
|
43
45
|
- example/hello_world.rb
|
|
46
|
+
- example/raise_error.rb
|
|
44
47
|
- lib/biryani.rb
|
|
45
48
|
- lib/biryani/connection.rb
|
|
46
49
|
- lib/biryani/connection_error.rb
|
|
47
50
|
- lib/biryani/data_buffer.rb
|
|
51
|
+
- lib/biryani/error.rb
|
|
48
52
|
- lib/biryani/frame.rb
|
|
49
53
|
- lib/biryani/frame/continuation.rb
|
|
50
54
|
- lib/biryani/frame/data.rb
|
|
@@ -87,7 +91,7 @@ files:
|
|
|
87
91
|
- spec/connection/handle_stream_window_update_spec.rb
|
|
88
92
|
- spec/connection/read_http2_magic_spec.rb
|
|
89
93
|
- spec/connection/send_spec.rb
|
|
90
|
-
- spec/connection/
|
|
94
|
+
- spec/connection/transition_stream_state_send_spec.rb
|
|
91
95
|
- spec/data_buffer_spec.rb
|
|
92
96
|
- spec/frame/continuation_spec.rb
|
|
93
97
|
- spec/frame/data_spec.rb
|
|
@@ -108,6 +112,7 @@ files:
|
|
|
108
112
|
- spec/hpack/integer_spec.rb
|
|
109
113
|
- spec/hpack/string_spec.rb
|
|
110
114
|
- spec/http_request_builder_spec.rb
|
|
115
|
+
- spec/http_response_spec.rb
|
|
111
116
|
- spec/spec_helper.rb
|
|
112
117
|
- spec/streams_context_spec.rb
|
|
113
118
|
- spec/utils_spec.rb
|
|
@@ -142,7 +147,7 @@ test_files:
|
|
|
142
147
|
- spec/connection/handle_stream_window_update_spec.rb
|
|
143
148
|
- spec/connection/read_http2_magic_spec.rb
|
|
144
149
|
- spec/connection/send_spec.rb
|
|
145
|
-
- spec/connection/
|
|
150
|
+
- spec/connection/transition_stream_state_send_spec.rb
|
|
146
151
|
- spec/data_buffer_spec.rb
|
|
147
152
|
- spec/frame/continuation_spec.rb
|
|
148
153
|
- spec/frame/data_spec.rb
|
|
@@ -163,6 +168,7 @@ test_files:
|
|
|
163
168
|
- spec/hpack/integer_spec.rb
|
|
164
169
|
- spec/hpack/string_spec.rb
|
|
165
170
|
- spec/http_request_builder_spec.rb
|
|
171
|
+
- spec/http_response_spec.rb
|
|
166
172
|
- spec/spec_helper.rb
|
|
167
173
|
- spec/streams_context_spec.rb
|
|
168
174
|
- spec/utils_spec.rb
|