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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Rakefile +7 -0
  4. data/conformance/server_spec.rb +31 -0
  5. data/conformance/spec_helper.rb +14 -0
  6. data/example/echo.rb +0 -1
  7. data/example/raise_error.rb +19 -0
  8. data/lib/biryani/connection.rb +23 -18
  9. data/lib/biryani/data_buffer.rb +1 -1
  10. data/lib/biryani/error.rb +8 -0
  11. data/lib/biryani/frame/continuation.rb +4 -7
  12. data/lib/biryani/frame/data.rb +9 -12
  13. data/lib/biryani/frame/goaway.rb +6 -6
  14. data/lib/biryani/frame/headers.rb +25 -18
  15. data/lib/biryani/frame/ping.rb +5 -7
  16. data/lib/biryani/frame/priority.rb +6 -5
  17. data/lib/biryani/frame/push_promise.rb +14 -15
  18. data/lib/biryani/frame/rst_stream.rb +5 -5
  19. data/lib/biryani/frame/settings.rb +5 -5
  20. data/lib/biryani/frame/unknown.rb +1 -15
  21. data/lib/biryani/frame/window_update.rb +5 -5
  22. data/lib/biryani/frame.rb +10 -4
  23. data/lib/biryani/hpack/field.rb +42 -42
  24. data/lib/biryani/hpack/fields.rb +2 -1
  25. data/lib/biryani/hpack/huffman.rb +290 -21
  26. data/lib/biryani/hpack/integer.rb +10 -6
  27. data/lib/biryani/hpack/string.rb +6 -6
  28. data/lib/biryani/http_request.rb +3 -2
  29. data/lib/biryani/http_response.rb +23 -2
  30. data/lib/biryani/server.rb +1 -1
  31. data/lib/biryani/state.rb +14 -7
  32. data/lib/biryani/stream.rb +10 -3
  33. data/lib/biryani/streams_context.rb +12 -6
  34. data/lib/biryani/version.rb +1 -1
  35. data/lib/biryani.rb +1 -1
  36. data/spec/connection/handle_data_spec.rb +2 -2
  37. data/spec/connection/handle_headers_spec.rb +1 -1
  38. data/spec/connection/{transition_state_send_spec.rb → transition_stream_state_send_spec.rb} +3 -3
  39. data/spec/frame/continuation_spec.rb +2 -2
  40. data/spec/frame/data_spec.rb +1 -1
  41. data/spec/frame/goaway_spec.rb +1 -1
  42. data/spec/frame/headers_spec.rb +2 -2
  43. data/spec/frame/ping_spec.rb +1 -1
  44. data/spec/frame/priority_spec.rb +1 -1
  45. data/spec/frame/push_promise_spec.rb +1 -1
  46. data/spec/frame/rst_stream_spec.rb +1 -1
  47. data/spec/frame/settings_spec.rb +1 -1
  48. data/spec/frame/window_update_spec.rb +1 -1
  49. data/spec/hpack/field_spec.rb +10 -9
  50. data/spec/hpack/huffman_spec.rb +4 -4
  51. data/spec/hpack/integer_spec.rb +5 -5
  52. data/spec/hpack/string_spec.rb +2 -2
  53. data/spec/http_response_spec.rb +12 -0
  54. 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
 
@@ -11,7 +11,7 @@ module Biryani
11
11
  Ractor.new(socket.accept, @proc) do |io, proc|
12
12
  conn = Connection.new(proc)
13
13
  conn.serve(io)
14
- io&.close
14
+ io.close
15
15
  end
16
16
  end
17
17
  end
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
- :receiving_continuation_data
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
- # receiving_continuation_data
77
- in [:receiving_continuation_data, FrameType::RST_STREAM, _]
76
+ # receiving_continuation_and_data
77
+ in [:receiving_continuation_and_data, FrameType::RST_STREAM, _]
78
78
  :closed
79
- in [:receiving_continuation_data, FrameType::WINDOW_UPDATE, :recv]
79
+ in [:receiving_continuation_and_data, FrameType::WINDOW_UPDATE, :recv]
80
80
  state
81
- in [:receiving_continuation_data, FrameType::CONTINUATION, :recv] if frame.end_headers?
81
+ in [:receiving_continuation_and_data, FrameType::CONTINUATION, :recv] if frame.end_headers?
82
82
  :receiving_data
83
- in [:receiving_continuation_data, FrameType::CONTINUATION, :recv]
83
+ in [:receiving_continuation_and_data, FrameType::CONTINUATION, :recv]
84
84
  state
85
- in [:receiving_continuation_data, _, _]
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
@@ -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.new(0, {}, '')
11
+ res = HTTPResponse.default
12
12
 
13
- proc.call(req, res)
14
- tx << [res, stream_id]
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 = StringIO.new
115
- @content = StringIO.new
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
@@ -1,3 +1,3 @@
1
1
  module Biryani
2
- VERSION = '0.0.4'.freeze
2
+ VERSION = '0.0.6'.freeze
3
3
  end
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.string).to eq 'Hello, world!'
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.string).to eq 'Hello, world!'
40
+ expect(streams_ctx2[2].content).to eq 'Hello, world!'
41
41
  end
42
42
 
43
43
  let(:recv_window3) do
@@ -13,7 +13,7 @@ RSpec.describe Connection do
13
13
  end
14
14
  it 'should handle' do
15
15
  expect(Connection.handle_headers(headers, ctx, decoder)).to eq nil
16
- expect(ctx.fragment.string).to eq 'this is dummy'
16
+ expect(ctx.fragment).to eq 'this is dummy'
17
17
  end
18
18
  end
19
19
  end
@@ -1,7 +1,7 @@
1
1
  require_relative '../spec_helper'
2
2
 
3
3
  RSpec.describe Connection do
4
- context 'transition_state_send' do
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.transition_state_send(headers, streams_ctx)
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.transition_state_send(rst_stream, streams_ctx)
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("\x00\x00\x00\x09\x00\x00\x00\x00\x32".b)
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("\x00\x00\x0d\x09\x00\x00\x00\x00\x32\x74\x68\x69\x73\x20\x69\x73\x20\x64\x75\x6d\x6d\x79".b)
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
@@ -10,7 +10,7 @@ RSpec.describe Frame::Data do
10
10
  end
11
11
 
12
12
  let(:data2) do
13
- Frame::Data.read("\x00\x00\x14\x00\x08\x00\x00\x00\x02\x06\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21\x48\x6f\x77\x64\x79\x21".b)
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
@@ -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\x17\x07\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x00\x00\x09\x68\x70\x61\x63\x6b\x20\x69\x73\x20\x62\x72\x6f\x6b\x65\x6e".b)
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
@@ -18,7 +18,7 @@ RSpec.describe Frame::Headers do
18
18
  end
19
19
 
20
20
  let(:headers3) do
21
- Frame::Headers.read("\x00\x00\x0d\x01\x04\x00\x00\x00\x01\x74\x68\x69\x73\x20\x69\x73\x20\x64\x75\x6d\x6d\x79".b)
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("\x00\x00\x23\x01\x2c\x00\x00\x00\x03\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)
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
@@ -10,7 +10,7 @@ RSpec.describe Frame::Ping do
10
10
  end
11
11
 
12
12
  let(:ping2) do
13
- Frame::Ping.read("\x00\x00\x08\x06\x00\x00\x00\x00\x00\x64\x65\x61\x64\x62\x65\x65\x66".b)
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
@@ -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\x05\x02\x00\x00\x00\x00\x09\x00\x00\x00\x0b\x08".b)
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("\x00\x00\x18\x05\x0c\x00\x00\x00\x0a\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)
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\x04\x03\x00\x00\x00\x00\x05\x00\x00\x00\x08".b)
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
@@ -10,7 +10,7 @@ RSpec.describe Frame::Settings do
10
10
  end
11
11
 
12
12
  let(:settings2) do
13
- Frame::Settings.read("\x00\x00\x0c\x04\x00\x00\x00\x00\x00\x00\x01\x00\x00\x20\x00\x00\x03\x00\x00\x13\x88".b)
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\x04\x08\x00\x00\x00\x00\x32\x00\x00\x03\xe8".b)
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
@@ -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)).to eq [%w[location https://www.example.com], 19]
24
- 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]
25
- expect(HPACK::Field.decode("\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]
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
@@ -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
@@ -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
@@ -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
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/transition_state_send_spec.rb
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/transition_state_send_spec.rb
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