http-2-next 0.1.0 → 0.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b8760a4af91eae4da3ae631c99b68cef2130cc0758ac7c91420bffe53582e55
4
- data.tar.gz: ced41c1e91b125dd4ea957d026b27ccec5cd2aa79ca6376883f829c52199e758
3
+ metadata.gz: e1a2260b9fc7284c07a10b356a5008632319f6511848b8bfe923d908df84b92e
4
+ data.tar.gz: c733242b644fcb16685df75cbac2934ad43fe2d284ea90ad28fb9af89210588e
5
5
  SHA512:
6
- metadata.gz: a3f421c1e495cb9bed15a3635dfdae9996074a77e221f1a3e5ebd75be0df0e154ab395ffc3caf3e333749b2fd4350ec226941f84c55c9c99ef023f18fe1da002
7
- data.tar.gz: 4f0af698fbd7ac5a94b03fba9b93f47c6d8c34d0e44469a63dcec27609932a45e2b59f6ed08a8e55441d14756a238a8f0bea08ae795e3494e061f82d0a4668cf
6
+ metadata.gz: 616823541f91bcd9b13ab0b040c837fafd69f3dc8e0be7fc9a1a04e461f6cd129a5a64be09e6412fdacadcf04d3433039cbf5da56cb6604d9baea28abd7ee5d3
7
+ data.tar.gz: fe807a6f0318f6ed00fb2a7922d5e78202dd9165345df312f668baaa08af926d3e4d641da48899f7d6b830f9b7375096ad61f0bbd70bf2c00cbf8f12ce5bbb50
@@ -68,5 +68,11 @@ module HTTP2Next
68
68
  frame = Framer.new.generate(type: :settings, stream: 0, payload: settings)
69
69
  Base64.urlsafe_encode64(frame[9..-1])
70
70
  end
71
+
72
+ private
73
+
74
+ def verify_pseudo_headers(frame, mandatory_headers = RESPONSE_MANDATORY_HEADERS)
75
+ super(frame, mandatory_headers)
76
+ end
71
77
  end
72
78
  end
@@ -35,6 +35,9 @@ module HTTP2Next
35
35
  # Default connection "fast-fail" preamble string as defined by the spec.
36
36
  CONNECTION_PREFACE_MAGIC = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
37
37
 
38
+ REQUEST_MANDATORY_HEADERS = %w[:scheme :method :authority :path].freeze
39
+ RESPONSE_MANDATORY_HEADERS = %w[:status].freeze
40
+
38
41
  # Connection encapsulates all of the connection, stream, flow-control,
39
42
  # error management, and other processing logic required for a well-behaved
40
43
  # HTTP 2.0 endpoint.
@@ -270,6 +273,8 @@ module HTTP2Next
270
273
 
271
274
  stream = @streams[frame[:stream]]
272
275
  if stream.nil?
276
+ verify_pseudo_headers(frame)
277
+
273
278
  stream = activate_stream(
274
279
  id: frame[:stream],
275
280
  weight: frame[:weight] || DEFAULT_WEIGHT,
@@ -329,6 +334,7 @@ module HTTP2Next
329
334
  end
330
335
  end
331
336
 
337
+ verify_pseudo_headers(frame, REQUEST_MANDATORY_HEADERS)
332
338
  stream = activate_stream(id: pid, parent: parent)
333
339
  verify_stream_order(stream.id)
334
340
  emit(:promise, stream)
@@ -727,6 +733,21 @@ module HTTP2Next
727
733
  @last_stream_id = id
728
734
  end
729
735
 
736
+ def verify_pseudo_headers(frame, mandatory_headers)
737
+ headers = frame[:payload]
738
+ return if headers.is_a?(Buffer)
739
+
740
+ pseudo_headers = headers.take_while do |field, value|
741
+ # use this loop to validate pseudo-headers
742
+ connection_error(:protocol_error, msg: "path is empty") if field == ":path" && value.empty?
743
+ field.start_with?(":")
744
+ end.map(&:first)
745
+ return if mandatory_headers.size == pseudo_headers.size &&
746
+ (mandatory_headers - pseudo_headers).empty?
747
+
748
+ connection_error(:protocol_error, msg: "invalid pseudo-headers")
749
+ end
750
+
730
751
  # Emit GOAWAY error indicating to peer that the connection is being
731
752
  # aborted, and once sent, raise a local exception.
732
753
  #
@@ -114,6 +114,10 @@ module HTTP2Next
114
114
 
115
115
  private
116
116
 
117
+ def verify_pseudo_headers(frame, mandatory_headers = REQUEST_MANDATORY_HEADERS)
118
+ super(frame, mandatory_headers)
119
+ end
120
+
117
121
  # Handle locally initiated server-push event emitted by the stream.
118
122
  #
119
123
  # @param args [Array]
@@ -116,7 +116,8 @@ module HTTP2Next
116
116
  # stream error (Section 5.4.2) of type STREAM_CLOSED.
117
117
  stream_error(:stream_closed) unless @state == :open ||
118
118
  @state == :half_closed_local ||
119
- @state == :half_closing || @state == :closing
119
+ @state == :half_closing || @state == :closing ||
120
+ (@state == :closed && @closed == :local_rst)
120
121
  @received_data = true
121
122
  calculate_content_length(frame[:length])
122
123
  update_local_window(frame)
@@ -124,21 +125,22 @@ module HTTP2Next
124
125
  emit(:data, frame[:payload]) unless frame[:ignore]
125
126
  calculate_window_update(@local_window_max_size)
126
127
  when :headers
127
- stream_error(:stream_closed) if @state == :closed || @state == :remote_closed
128
+ stream_error(:stream_closed) if (@state == :closed && @closed != :local_rst) ||
129
+ @state == :remote_closed
128
130
  @_method ||= frame[:method]
129
131
  @_content_length ||= frame[:content_length]
130
132
  @_trailers ||= frame[:trailer]
131
133
  if @_waiting_on_trailers
132
134
  verify_trailers(frame)
133
- else
134
- verify_pseudo_headers(frame)
135
+ elsif @received_data
136
+ stream_error(:protocol_error, msg: "already received data")
135
137
  end
136
138
  emit(:headers, frame[:payload]) unless frame[:ignore]
137
139
  @_waiting_on_trailers = !@_trailers.nil?
138
140
  when :push_promise
139
141
  emit(:promise_headers, frame[:payload]) unless frame[:ignore]
140
142
  when :continuation
141
- stream_error(:stream_closed) if @state == :closed || @state == :remote_closed
143
+ stream_error(:stream_closed) if (@state == :closed && @closed != :local_rst) || @state == :remote_closed
142
144
  stream_error(:protocol_error) if @received_data
143
145
  when :priority
144
146
  process_priority(frame)
@@ -158,25 +160,6 @@ module HTTP2Next
158
160
  end
159
161
  alias << receive
160
162
 
161
- REQUEST_MANDATORY_HEADERS = %w[:scheme :method :authority :path].freeze
162
- RESPONSE_MANDATORY_HEADERS = %w[:status].freeze
163
-
164
- def verify_pseudo_headers(frame)
165
- headers = frame[:payload]
166
- return if headers.is_a?(Buffer)
167
-
168
- mandatory_headers = @id.odd? ? REQUEST_MANDATORY_HEADERS : RESPONSE_MANDATORY_HEADERS
169
- pseudo_headers = headers.take_while do |field, value|
170
- # use this loop to validate pseudo-headers
171
- stream_error(:protocol_error, msg: "path is empty") if field == ":path" && value.empty?
172
- field.start_with?(":")
173
- end.map(&:first)
174
- return if mandatory_headers.size == pseudo_headers.size &&
175
- (mandatory_headers - pseudo_headers).empty?
176
-
177
- stream_error(:protocol_error, msg: "invalid pseudo-headers")
178
- end
179
-
180
163
  def verify_trailers(frame)
181
164
  stream_error(:protocol_error, msg: "trailer headers frame must close the stream") unless end_stream?(frame)
182
165
  return unless @_trailers
@@ -675,6 +658,8 @@ module HTTP2Next
675
658
  end
676
659
 
677
660
  def stream_error(error = :internal_error, msg: nil)
661
+ # if the stream already broke with an error, ignore subsequent
662
+
678
663
  @error = error
679
664
  close(error) if @state != :closed
680
665
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTP2Next
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: http-2-next
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Cardoso
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2019-11-13 00:00:00.000000000 Z
13
+ date: 2019-11-17 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: Pure-ruby HTTP 2.0 protocol implementation
16
16
  email: