biryani 0.0.5 → 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 +21 -16
- data/lib/biryani/error.rb +8 -0
- data/lib/biryani/frame/unknown.rb +1 -3
- data/lib/biryani/frame.rb +5 -0
- data/lib/biryani/http_request.rb +2 -1
- data/lib/biryani/http_response.rb +23 -2
- data/lib/biryani/server.rb +1 -1
- data/lib/biryani/state.rb +12 -7
- data/lib/biryani/stream.rb +10 -3
- data/lib/biryani/streams_context.rb +10 -0
- data/lib/biryani/version.rb +1 -1
- data/lib/biryani.rb +1 -0
- data/spec/connection/{transition_state_send_spec.rb → transition_stream_state_send_spec.rb} +3 -3
- data/spec/http_response_spec.rb +12 -0
- metadata +9 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2a6614c6a74a069a08281c0f3846da8512f379e347ce77e3812c7cd22f2506bc
|
|
4
|
+
data.tar.gz: 5a0f61a32f576c0a693abeb2773490ddcbbdcc573b82dac16a6d3a3e82bf2e0e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0c0add40f15c4e1fa9110a295f9fda392aae21a6ad4dd062a0d3a31fd5dd208f8a384fbcf9009a6fc29e6a940f4fc755f9da0f3e244f68292ee41323cd10e82f
|
|
7
|
+
data.tar.gz: 1af73e5c917c095dfcb635fd7c09548b4330114125e00df539356fb270d8f083e76c0d25262652f6781e4b8415fc3c2da91083b0af19b20ac7175a70b5c28bc1
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.0.
|
|
1
|
+
4.0.1
|
data/Rakefile
CHANGED
|
@@ -5,4 +5,11 @@ require 'rubocop/rake_task'
|
|
|
5
5
|
RuboCop::RakeTask.new
|
|
6
6
|
RSpec::Core::RakeTask.new(:spec)
|
|
7
7
|
|
|
8
|
+
desc 'conformance test using h2spec'
|
|
9
|
+
RSpec::Core::RakeTask.new(:conformance) do |t|
|
|
10
|
+
t.pattern = 'conformance/server_spec.rb'
|
|
11
|
+
t.rspec_opts = %w[--out /dev/null]
|
|
12
|
+
t.verbose = false
|
|
13
|
+
end
|
|
14
|
+
|
|
8
15
|
task default: %i[rubocop spec]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Server do
|
|
4
|
+
before do
|
|
5
|
+
@tcpserver = TCPServer.open(PORT)
|
|
6
|
+
|
|
7
|
+
Ractor.new(@tcpserver) do |socket|
|
|
8
|
+
server = Server.new(
|
|
9
|
+
Ractor.shareable_proc do |_req, res|
|
|
10
|
+
res.status = 200
|
|
11
|
+
res.content = 'OK'
|
|
12
|
+
end
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
server.run(socket)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let(:client) do
|
|
20
|
+
which('h2spec')
|
|
21
|
+
"h2spec --port #{PORT} --verbose"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
after do
|
|
25
|
+
@tcpserver.close
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'should run' do
|
|
29
|
+
system(client)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'open3'
|
|
2
|
+
require 'socket'
|
|
3
|
+
require 'biryani'
|
|
4
|
+
|
|
5
|
+
# rubocop: disable Style/MixinUsage
|
|
6
|
+
include Biryani
|
|
7
|
+
# rubocop: enable Style/MixinUsage
|
|
8
|
+
|
|
9
|
+
PORT = 8888
|
|
10
|
+
|
|
11
|
+
def which(cmd)
|
|
12
|
+
o, = Open3.capture3("which #{cmd}")
|
|
13
|
+
warn "conformance task require `#{cmd}`. Install `#{cmd}`." if o.empty?
|
|
14
|
+
end
|
data/example/echo.rb
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH << "#{__dir__}/../lib"
|
|
4
|
+
|
|
5
|
+
require 'socket'
|
|
6
|
+
require 'biryani'
|
|
7
|
+
|
|
8
|
+
port = ARGV[0] || 8888
|
|
9
|
+
socket = TCPServer.new(port)
|
|
10
|
+
|
|
11
|
+
server = Biryani::Server.new(
|
|
12
|
+
Ractor.shareable_proc do
|
|
13
|
+
raise 'error'
|
|
14
|
+
end
|
|
15
|
+
)
|
|
16
|
+
server.run(socket)
|
|
17
|
+
|
|
18
|
+
# $ bundle exec ruby example/raise_error.rb
|
|
19
|
+
# $ curl -v --http2-prior-knowledge http://localhost:8888
|
data/lib/biryani/connection.rb
CHANGED
|
@@ -44,10 +44,11 @@ module Biryani
|
|
|
44
44
|
|
|
45
45
|
recv_loop(io.clone)
|
|
46
46
|
send_loop(io)
|
|
47
|
-
rescue StandardError
|
|
47
|
+
rescue StandardError => e
|
|
48
|
+
puts e.backtrace
|
|
48
49
|
self.class.do_send(io, Frame::Goaway.new(0, @streams_ctx.last_stream_id, ErrorCode::INTERNAL_ERROR, 'internal error'), true)
|
|
49
50
|
ensure
|
|
50
|
-
io
|
|
51
|
+
io.close_write
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
# @param io [IO]
|
|
@@ -57,7 +58,7 @@ module Biryani
|
|
|
57
58
|
obj = Frame.read(io_)
|
|
58
59
|
break if obj.nil?
|
|
59
60
|
|
|
60
|
-
sock_
|
|
61
|
+
sock_.send(obj, move: true)
|
|
61
62
|
end
|
|
62
63
|
end
|
|
63
64
|
end
|
|
@@ -71,15 +72,14 @@ module Biryani
|
|
|
71
72
|
def send_loop(io)
|
|
72
73
|
loop do
|
|
73
74
|
ports = @streams_ctx.txs
|
|
74
|
-
ports
|
|
75
|
-
next if ports.empty?
|
|
75
|
+
break if ports.empty? && @sock.closed?
|
|
76
76
|
|
|
77
|
-
port, obj = Ractor.select(*ports)
|
|
77
|
+
port, obj = Ractor.select(@sock, *ports)
|
|
78
78
|
if port == @sock
|
|
79
79
|
if Biryani.err?(obj)
|
|
80
80
|
reply_frame = Biryani.unwrap(obj, @streams_ctx.last_stream_id)
|
|
81
81
|
self.class.do_send(io, reply_frame, true)
|
|
82
|
-
close if self.class.
|
|
82
|
+
close if self.class.transition_stream_state_send(reply_frame, @streams_ctx)
|
|
83
83
|
elsif obj.length > @settings[SettingsID::SETTINGS_MAX_FRAME_SIZE]
|
|
84
84
|
self.class.do_send(io, Frame::Goaway.new(0, @streams_ctx.last_stream_id, ErrorCode::FRAME_SIZE_ERROR, 'payload length greater than SETTINGS_MAX_FRAME_SIZE'), true)
|
|
85
85
|
close
|
|
@@ -93,7 +93,7 @@ module Biryani
|
|
|
93
93
|
@streams_ctx[reply_frame.stream_id].recv_window.increase!(reply_frame.window_size_increment)
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
-
close if self.class.
|
|
96
|
+
close if self.class.transition_stream_state_send(reply_frame, @streams_ctx)
|
|
97
97
|
end
|
|
98
98
|
end
|
|
99
99
|
else
|
|
@@ -129,8 +129,12 @@ module Biryani
|
|
|
129
129
|
#
|
|
130
130
|
# @return [Array<Object>, Array<ConnectionError>, Array<StreamError>] frames or errors
|
|
131
131
|
# rubocop: disable Metrics/CyclomaticComplexity
|
|
132
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
|
132
133
|
def handle_connection_frame(frame)
|
|
133
134
|
typ = frame.f_type
|
|
135
|
+
return [ConnectionError.new(ErrorCode::PROTOCOL_ERROR, "invalid frame type #{format('0x%02x', typ)} for stream identifier #{format('0x%02x', stream_id)}")] \
|
|
136
|
+
if @streams_ctx.receiving_continuation? && ([FrameType::SETTINGS, FrameType::PING, FrameType::WINDOW_UPDATE].include?(typ) || FrameType.unknown?(typ))
|
|
137
|
+
|
|
134
138
|
case typ
|
|
135
139
|
when FrameType::DATA, FrameType::HEADERS, FrameType::PRIORITY, FrameType::RST_STREAM, FrameType::PUSH_PROMISE, FrameType::CONTINUATION
|
|
136
140
|
[ConnectionError.new(ErrorCode::PROTOCOL_ERROR, "invalid frame type #{format('0x%02x', typ)} for stream identifier 0x00")]
|
|
@@ -163,6 +167,7 @@ module Biryani
|
|
|
163
167
|
end
|
|
164
168
|
end
|
|
165
169
|
# rubocop: enable Metrics/CyclomaticComplexity
|
|
170
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
|
166
171
|
|
|
167
172
|
# @param frame [Object]
|
|
168
173
|
#
|
|
@@ -179,7 +184,7 @@ module Biryani
|
|
|
179
184
|
max_streams = @peer_settings[SettingsID::SETTINGS_MAX_CONCURRENT_STREAMS]
|
|
180
185
|
send_initial_window_size = @peer_settings[SettingsID::SETTINGS_INITIAL_WINDOW_SIZE]
|
|
181
186
|
recv_initial_window_size = @settings[SettingsID::SETTINGS_INITIAL_WINDOW_SIZE]
|
|
182
|
-
obj = self.class.
|
|
187
|
+
obj = self.class.transition_stream_state_recv(frame, @streams_ctx, stream_id, max_streams, send_initial_window_size, recv_initial_window_size)
|
|
183
188
|
return [obj] if Biryani.err?(obj)
|
|
184
189
|
|
|
185
190
|
ctx = obj
|
|
@@ -241,7 +246,7 @@ module Biryani
|
|
|
241
246
|
# @return [StreamContext, StreamError, ConnectionError]
|
|
242
247
|
# rubocop: disable Metrics/CyclomaticComplexity
|
|
243
248
|
# rubocop: disable Metrics/PerceivedComplexity
|
|
244
|
-
def self.
|
|
249
|
+
def self.transition_stream_state_recv(recv_frame, streams_ctx, stream_id, max_streams, send_initial_window_size, recv_initial_window_size)
|
|
245
250
|
ctx = streams_ctx[stream_id]
|
|
246
251
|
return StreamError.new(ErrorCode::PROTOCOL_ERROR, stream_id, 'exceed max concurrent streams') if ctx.nil? && streams_ctx.count_active + 1 > max_streams
|
|
247
252
|
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'even-numbered stream identifier') if ctx.nil? && stream_id.even?
|
|
@@ -260,7 +265,7 @@ module Biryani
|
|
|
260
265
|
# @param streams_ctx [StreamsContext]
|
|
261
266
|
#
|
|
262
267
|
# @return [Boolean] should close connection?
|
|
263
|
-
def self.
|
|
268
|
+
def self.transition_stream_state_send(send_frame, streams_ctx)
|
|
264
269
|
stream_id = send_frame.stream_id
|
|
265
270
|
typ = send_frame.f_type
|
|
266
271
|
case typ
|
|
@@ -297,7 +302,7 @@ module Biryani
|
|
|
297
302
|
do_send(io, frame, false)
|
|
298
303
|
send_window.consume!(frame.length)
|
|
299
304
|
streams_ctx[stream_id].send_window.consume!(frame.length)
|
|
300
|
-
|
|
305
|
+
transition_stream_state_send(frame, streams_ctx)
|
|
301
306
|
end
|
|
302
307
|
|
|
303
308
|
data_buffer.store(stream_id, remains) unless remains.empty?
|
|
@@ -322,7 +327,7 @@ module Biryani
|
|
|
322
327
|
|
|
323
328
|
frames.each do |frame|
|
|
324
329
|
do_send(io, frame, false)
|
|
325
|
-
|
|
330
|
+
transition_stream_state_send(frame, streams_ctx)
|
|
326
331
|
end
|
|
327
332
|
end
|
|
328
333
|
|
|
@@ -352,7 +357,7 @@ module Biryani
|
|
|
352
357
|
obj = http_request(ctx.fragment, ctx.content, decoder)
|
|
353
358
|
return obj if Biryani.err?(obj)
|
|
354
359
|
|
|
355
|
-
ctx.stream.rx
|
|
360
|
+
ctx.stream.rx.send(obj, move: true)
|
|
356
361
|
end
|
|
357
362
|
|
|
358
363
|
window_updates = []
|
|
@@ -371,9 +376,9 @@ module Biryani
|
|
|
371
376
|
ctx.fragment << headers.fragment
|
|
372
377
|
if ctx.half_closed_remote?
|
|
373
378
|
obj = http_request(ctx.fragment, ctx.content, decoder)
|
|
374
|
-
return
|
|
379
|
+
return obj if Biryani.err?(obj)
|
|
375
380
|
|
|
376
|
-
ctx.stream.rx
|
|
381
|
+
ctx.stream.rx.send(obj, move: true)
|
|
377
382
|
end
|
|
378
383
|
|
|
379
384
|
nil
|
|
@@ -21,9 +21,7 @@ module Biryani
|
|
|
21
21
|
|
|
22
22
|
# @return [String]
|
|
23
23
|
def to_binary_s
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
Frame.to_binary_s_header(payload_length, @f_type, flags, @stream_id) + @payload
|
|
24
|
+
Frame.to_binary_s_header(length, @f_type, @flags, @stream_id) + @payload
|
|
27
25
|
end
|
|
28
26
|
end
|
|
29
27
|
end
|
data/lib/biryani/frame.rb
CHANGED
data/lib/biryani/http_request.rb
CHANGED
|
@@ -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
|
|
@@ -234,5 +234,10 @@ module Biryani
|
|
|
234
234
|
def half_closed_remote?
|
|
235
235
|
@state == :half_closed_remote
|
|
236
236
|
end
|
|
237
|
+
|
|
238
|
+
# @return [Boolean]
|
|
239
|
+
def receiving_continuation?
|
|
240
|
+
%i[receiving_continuation receiving_continuation_and_data].include?(@state)
|
|
241
|
+
end
|
|
237
242
|
end
|
|
238
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
|
|
@@ -131,5 +136,10 @@ module Biryani
|
|
|
131
136
|
def half_closed_remote?
|
|
132
137
|
@state.half_closed_remote?
|
|
133
138
|
end
|
|
139
|
+
|
|
140
|
+
# @return [Boolean]
|
|
141
|
+
def receiving_continuation?
|
|
142
|
+
@state.receiving_continuation?
|
|
143
|
+
end
|
|
134
144
|
end
|
|
135
145
|
end
|
data/lib/biryani/version.rb
CHANGED
data/lib/biryani.rb
CHANGED
|
@@ -3,6 +3,7 @@ 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'
|
|
6
7
|
require_relative 'biryani/frame'
|
|
7
8
|
require_relative 'biryani/hpack'
|
|
8
9
|
require_relative 'biryani/http_request'
|
|
@@ -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
|
|
@@ -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
|