Class: Protocol::HTTP2::Connection
- Inherits:
-
Object
- Object
- Protocol::HTTP2::Connection
- Includes:
- FlowControlled
- Defined in:
- lib/protocol/http2/connection.rb
Overview
This is the core connection class that handles HTTP/2 protocol semantics including stream management, settings negotiation, and frame processing.
Instance Attribute Summary collapse
-
#dependencies ⇒ Object
readonly
Returns the value of attribute dependencies.
-
#dependency ⇒ Object
readonly
Returns the value of attribute dependency.
-
#framer ⇒ Object
readonly
Returns the value of attribute framer.
-
#local_settings ⇒ Object
Current settings value for local and peer.
-
#local_window ⇒ Object
readonly
Our window for receiving data.
-
#remote_settings ⇒ Object
Returns the value of attribute remote_settings.
-
#remote_stream_id ⇒ Object
readonly
The highest stream_id that has been successfully accepted by this connection.
-
#remote_window ⇒ Object
readonly
Our window for sending data.
-
#state ⇒ Object
Connection state (:new, :open, :closed).
-
#streams ⇒ Object
readonly
Returns the value of attribute streams.
Instance Method Summary collapse
-
#[](id) ⇒ Object
Access streams by ID, with 0 returning the connection itself.
-
#accept_push_promise_stream(stream_id, &block) ⇒ Object
Accept an incoming push promise from the other side of the connection.
-
#accept_stream(stream_id, &block) ⇒ Object
Accept an incoming stream from the other side of the connnection.
-
#client_stream_id?(id) ⇒ Boolean
Check if the given stream ID represents a client-initiated stream.
-
#close(error = nil) ⇒ Object
Close the underlying framer and all streams.
-
#close! ⇒ Object
Transition the connection into the closed state.
-
#closed? ⇒ Boolean
Whether the connection is effectively or actually closed.
-
#closed_stream_id?(id) ⇒ Boolean
This is only valid if the stream doesn't exist in
@streams. -
#consume_window(size = self.available_size) ⇒ Object
Traverse active streams and allow them to consume the available flow-control window.
-
#create_push_promise_stream(&block) ⇒ Object
Create a push promise stream.
-
#create_stream(id = next_stream_id, &block) ⇒ Stream
Create a stream, defaults to an outgoing stream.
-
#decode_headers(data) ⇒ Object
Decode headers using HPACK decompression.
-
#delete(id) ⇒ Object
Remove a stream from the active streams collection.
-
#encode_headers(headers, buffer = String.new.b) ⇒ Object
Encode headers using HPACK compression.
-
#id ⇒ Object
The connection stream ID (always 0 for connection-level operations).
-
#idle_stream_id?(id) ⇒ Boolean
Check if the given stream ID represents an idle stream.
-
#ignore_frame?(frame) ⇒ Boolean
6.8.
-
#initialize(framer, local_stream_id) ⇒ Connection
constructor
Initialize a new HTTP/2 connection.
-
#maximum_concurrent_streams ⇒ Object
The maximum number of concurrent streams that this connection can initiate.
-
#maximum_frame_size ⇒ Object
The size of a frame payload is limited by the maximum size that a receiver advertises in the SETTINGS_MAX_FRAME_SIZE setting.
-
#next_stream_id ⇒ Object
Streams are identified with an unsigned 31-bit integer.
-
#open! ⇒ Object
Transition the connection to the open state.
-
#process_settings(frame) ⇒ Boolean
In addition to changing the flow-control window for streams that are not yet active, a SETTINGS frame can alter the initial flow-control window size for streams with active flow-control windows (that is, streams in the "open" or "half-closed (remote)" state).
-
#read_frame ⇒ Object
Reads one frame from the network and processes.
-
#receive_continuation(frame) ⇒ Object
Receive and process a CONTINUATION frame.
-
#receive_data(frame) ⇒ Object
Process a DATA frame from the remote peer.
-
#receive_frame(frame) ⇒ Object
Receive and process a generic frame (default handler).
-
#receive_goaway(frame) ⇒ Object
Process a GOAWAY frame from the remote peer.
-
#receive_headers(frame) ⇒ Object
On the server side, starts a new request.
-
#receive_ping(frame) ⇒ Object
Process a PING frame from the remote peer.
-
#receive_priority_update(frame) ⇒ Object
Receive and process a PRIORITY_UPDATE frame.
-
#receive_push_promise(frame) ⇒ Object
Receive and process a PUSH_PROMISE frame.
-
#receive_reset_stream(frame) ⇒ Object
Receive and process a RST_STREAM frame.
-
#receive_settings(frame) ⇒ Object
Receive and process a SETTINGS frame from the remote peer.
-
#receive_window_update(frame) ⇒ Object
Receive and process a WINDOW_UPDATE frame.
-
#send_goaway(error_code = 0, message = "") ⇒ Object
Tell the remote end that the connection is being shut down.
-
#send_ping(data) ⇒ Object
Send a PING frame to the remote peer.
-
#send_settings(changes) ⇒ Object
Send updated settings to the remote peer.
-
#server_stream_id?(id) ⇒ Boolean
Check if the given stream ID represents a server-initiated stream.
-
#synchronize ⇒ Object
Execute a block within a synchronized context.
-
#update_local_settings(changes) ⇒ Object
Update local settings and adjust stream window capacities.
-
#update_remote_settings(changes) ⇒ Object
Update remote settings and adjust stream window capacities.
-
#valid_remote_stream_id?(stream_id) ⇒ Boolean
Check if the given stream ID is valid for remote initiation.
-
#write_frame(frame) ⇒ Object
Write a single frame to the connection.
-
#write_frames ⇒ Object
Write multiple frames within a synchronized block.
Methods included from FlowControlled
#available_frame_size, #available_size, #consume_local_window, #consume_remote_window, #request_window_update, #send_window_update, #update_local_window, #window_updated
Constructor Details
#initialize(framer, local_stream_id) ⇒ Connection
Initialize a new HTTP/2 connection.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/protocol/http2/connection.rb', line 23 def initialize(framer, local_stream_id) super() @state = :new # Hash(Integer, Stream) @streams = {} @framer = framer # The next stream id to use: @local_stream_id = local_stream_id # The biggest remote stream id seen thus far: @remote_stream_id = 0 @local_settings = PendingSettings.new @remote_settings = Settings.new @decoder = HPACK::Context.new @encoder = HPACK::Context.new @local_window = LocalWindow.new @remote_window = Window.new end |
Instance Attribute Details
#dependencies ⇒ Object (readonly)
Returns the value of attribute dependencies.
154 155 156 |
# File 'lib/protocol/http2/connection.rb', line 154 def dependencies @dependencies end |
#dependency ⇒ Object (readonly)
Returns the value of attribute dependency.
156 157 158 |
# File 'lib/protocol/http2/connection.rb', line 156 def dependency @dependency end |
#framer ⇒ Object (readonly)
Returns the value of attribute framer.
78 79 80 |
# File 'lib/protocol/http2/connection.rb', line 78 def framer @framer end |
#local_settings ⇒ Object
Current settings value for local and peer
84 85 86 |
# File 'lib/protocol/http2/connection.rb', line 84 def local_settings @local_settings end |
#local_window ⇒ Object (readonly)
Our window for receiving data. When we receive data, it reduces this window. If the window gets too small, we must send a window update.
89 90 91 |
# File 'lib/protocol/http2/connection.rb', line 89 def local_window @local_window end |
#remote_settings ⇒ Object
Returns the value of attribute remote_settings.
85 86 87 |
# File 'lib/protocol/http2/connection.rb', line 85 def remote_settings @remote_settings end |
#remote_stream_id ⇒ Object (readonly)
The highest stream_id that has been successfully accepted by this connection.
95 96 97 |
# File 'lib/protocol/http2/connection.rb', line 95 def remote_stream_id @remote_stream_id end |
#remote_window ⇒ Object (readonly)
Our window for sending data. When we send data, it reduces this window.
92 93 94 |
# File 'lib/protocol/http2/connection.rb', line 92 def remote_window @remote_window end |
#state ⇒ Object
Connection state (:new, :open, :closed).
81 82 83 |
# File 'lib/protocol/http2/connection.rb', line 81 def state @state end |
#streams ⇒ Object (readonly)
Returns the value of attribute streams.
152 153 154 |
# File 'lib/protocol/http2/connection.rb', line 152 def streams @streams end |
Instance Method Details
#[](id) ⇒ Object
Access streams by ID, with 0 returning the connection itself.
58 59 60 61 62 63 64 |
# File 'lib/protocol/http2/connection.rb', line 58 def [] id if id.zero? self else @streams[id] end end |
#accept_push_promise_stream(stream_id, &block) ⇒ Object
Accept an incoming push promise from the other side of the connection. On the client side, we accept push promise streams. On the server side, existing streams create push promise streams.
420 421 422 |
# File 'lib/protocol/http2/connection.rb', line 420 def accept_push_promise_stream(stream_id, &block) accept_stream(stream_id, &block) end |
#accept_stream(stream_id, &block) ⇒ Object
Accept an incoming stream from the other side of the connnection. On the server side, we accept requests.
409 410 411 412 413 414 415 |
# File 'lib/protocol/http2/connection.rb', line 409 def accept_stream(stream_id, &block) unless valid_remote_stream_id?(stream_id) raise ProtocolError, "Invalid stream id: #{stream_id}" end create_stream(stream_id, &block) end |
#client_stream_id?(id) ⇒ Boolean
Check if the given stream ID represents a client-initiated stream. Client streams always have odd numbered IDs.
501 502 503 |
# File 'lib/protocol/http2/connection.rb', line 501 def client_stream_id?(id) id.odd? end |
#close(error = nil) ⇒ Object
Close the underlying framer and all streams.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/protocol/http2/connection.rb', line 110 def close(error = nil) # The underlying socket may already be closed by this point. # If there are active streams when the connection closes, it's an error for those streams, even if the connection itself closed cleanly: if @streams.any? and error.nil? error = EOFError.new("Connection closed with #{@streams.size} active stream(s)!") end @streams.each_value{|stream| stream.close(error)} @streams.clear ensure if @framer @framer.close @framer = nil end end |
#close! ⇒ Object
Transition the connection into the closed state.
215 216 217 218 219 |
# File 'lib/protocol/http2/connection.rb', line 215 def close! @state = :closed return self end |
#closed? ⇒ Boolean
Whether the connection is effectively or actually closed.
98 99 100 |
# File 'lib/protocol/http2/connection.rb', line 98 def closed? @state == :closed || @framer.nil? end |
#closed_stream_id?(id) ⇒ Boolean
This is only valid if the stream doesn't exist in @streams.
535 536 537 538 539 540 541 542 |
# File 'lib/protocol/http2/connection.rb', line 535 def closed_stream_id?(id) if id.zero? # The connection "stream id" can never be closed: false else !idle_stream_id?(id) end end |
#consume_window(size = self.available_size) ⇒ Object
Traverse active streams and allow them to consume the available flow-control window.
561 562 563 564 565 566 567 568 569 570 |
# File 'lib/protocol/http2/connection.rb', line 561 def consume_window(size = self.available_size) # Return if there is no window to consume: return unless size > 0 @streams.each_value do |stream| if stream.active? stream.window_updated(size) end end end |
#create_push_promise_stream(&block) ⇒ Object
Create a push promise stream. This method should be overridden by client/server implementations.
443 444 445 |
# File 'lib/protocol/http2/connection.rb', line 443 def create_push_promise_stream(&block) create_stream(&block) end |
#create_stream(id = next_stream_id, &block) ⇒ Stream
Create a stream, defaults to an outgoing stream. On the client side, we create requests.
427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/protocol/http2/connection.rb', line 427 def create_stream(id = next_stream_id, &block) if @streams.key?(id) raise ProtocolError, "Cannot create stream with id #{id}, already exists!" end if block_given? return yield(self, id) else return Stream.create(self, id) end end |
#decode_headers(data) ⇒ Object
Decode headers using HPACK decompression.
139 140 141 |
# File 'lib/protocol/http2/connection.rb', line 139 def decode_headers(data) HPACK::Decompressor.new(data, @decoder, table_size_limit: @local_settings.header_table_size).decode end |
#delete(id) ⇒ Object
Remove a stream from the active streams collection.
105 106 107 |
# File 'lib/protocol/http2/connection.rb', line 105 def delete(id) @streams.delete(id) end |
#encode_headers(headers, buffer = String.new.b) ⇒ Object
Encode headers using HPACK compression.
132 133 134 |
# File 'lib/protocol/http2/connection.rb', line 132 def encode_headers(headers, buffer = String.new.b) HPACK::Compressor.new(buffer, @encoder, table_size_limit: @remote_settings.header_table_size).encode(headers) end |
#id ⇒ Object
The connection stream ID (always 0 for connection-level operations).
51 52 53 |
# File 'lib/protocol/http2/connection.rb', line 51 def id 0 end |
#idle_stream_id?(id) ⇒ Boolean
Check if the given stream ID represents an idle stream.
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 |
# File 'lib/protocol/http2/connection.rb', line 516 def idle_stream_id?(id) if id.even? # Server-initiated streams are even. if @local_stream_id.even? id >= @local_stream_id else id > @remote_stream_id end elsif id.odd? # Client-initiated streams are odd. if @local_stream_id.odd? id >= @local_stream_id else id > @remote_stream_id end end end |
#ignore_frame?(frame) ⇒ Boolean
6.8. GOAWAY There is an inherent race condition between an endpoint starting new streams and the remote sending a GOAWAY frame. To deal with this case, the GOAWAY contains the stream identifier of the last peer-initiated stream that was or might be processed on the sending endpoint in this connection. For instance, if the server sends a GOAWAY frame, the identified stream is the highest-numbered stream initiated by the client. Once sent, the sender will ignore frames sent on streams initiated by the receiver if the stream has an identifier higher than the included last stream identifier. Receivers of a GOAWAY frame MUST NOT open additional streams on the connection, although a new connection can be established for new streams.
161 162 163 164 165 166 167 168 |
# File 'lib/protocol/http2/connection.rb', line 161 def ignore_frame?(frame) if self.closed? # puts "ignore_frame? #{frame.stream_id} -> #{valid_remote_stream_id?(frame.stream_id)} > #{@remote_stream_id}" if valid_remote_stream_id?(frame.stream_id) return frame.stream_id > @remote_stream_id end end end |
#maximum_concurrent_streams ⇒ Object
The maximum number of concurrent streams that this connection can initiate. This is a setting that can be changed by the remote peer.
It is not the same as the number of streams that can be accepted by the connection. The number of streams that can be accepted is determined by the local settings, and the number of streams that can be initiated is determined by the remote settings.
74 75 76 |
# File 'lib/protocol/http2/connection.rb', line 74 def maximum_concurrent_streams @remote_settings.maximum_concurrent_streams end |
#maximum_frame_size ⇒ Object
The size of a frame payload is limited by the maximum size that a receiver advertises in the SETTINGS_MAX_FRAME_SIZE setting.
67 68 69 |
# File 'lib/protocol/http2/connection.rb', line 67 def maximum_frame_size @remote_settings.maximum_frame_size end |
#next_stream_id ⇒ Object
Streams are identified with an unsigned 31-bit integer. Streams initiated by a client MUST use odd-numbered stream identifiers; those initiated by the server MUST use even-numbered stream identifiers. A stream identifier of zero (0x0) is used for connection control messages; the stream identifier of zero cannot be used to establish a new stream.
144 145 146 147 148 149 150 |
# File 'lib/protocol/http2/connection.rb', line 144 def next_stream_id id = @local_stream_id @local_stream_id += 2 return id end |
#open! ⇒ Object
Transition the connection to the open state.
331 332 333 334 335 |
# File 'lib/protocol/http2/connection.rb', line 331 def open! @state = :open return self end |
#process_settings(frame) ⇒ Boolean
In addition to changing the flow-control window for streams that are not yet active, a SETTINGS frame can alter the initial flow-control window size for streams with active flow-control windows (that is, streams in the "open" or "half-closed (remote)" state). When the value of SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver MUST adjust the size of all stream flow-control windows that it maintains by the difference between the new value and the old value.
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/protocol/http2/connection.rb', line 306 def process_settings(frame) if frame.acknowledgement? # The remote end has confirmed the settings have been received: changes = @local_settings.acknowledge update_local_settings(changes) return true else # The remote end is updating the settings, we reply with acknowledgement: reply = frame.acknowledge write_frame(reply) changes = frame.unpack @remote_settings.update(changes) update_remote_settings(changes) return false end end |
#read_frame ⇒ Object
Reads one frame from the network and processes. Processing the frame updates the state of the connection and related streams. If the frame triggers an error, e.g. a protocol error, the connection will typically emit a goaway frame and re-raise the exception. You should continue processing frames until the underlying connection is closed.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/protocol/http2/connection.rb', line 178 def read_frame frame = @framer.read_frame(@local_settings.maximum_frame_size) # puts "#{self.class} #{@state} read_frame: class=#{frame.class} stream_id=#{frame.stream_id} flags=#{frame.flags} length=#{frame.length} (remote_stream_id=#{@remote_stream_id})" # puts "Windows: local_window=#{@local_window.inspect}; remote_window=#{@remote_window.inspect}" return if ignore_frame?(frame) yield frame if block_given? frame.apply(self) return frame rescue GoawayError => error # Go directly to jail. Do not pass go, do not collect $200. raise rescue ProtocolError => error send_goaway(error.code || PROTOCOL_ERROR, error.) raise rescue HPACK::Error => error send_goaway(COMPRESSION_ERROR, error.) raise end |
#receive_continuation(frame) ⇒ Object
Receive and process a CONTINUATION frame.
596 597 598 |
# File 'lib/protocol/http2/connection.rb', line 596 def receive_continuation(frame) raise ProtocolError, "Received unexpected continuation: #{frame.class}" end |
#receive_data(frame) ⇒ Object
Process a DATA frame from the remote peer.
387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/protocol/http2/connection.rb', line 387 def receive_data(frame) update_local_window(frame) if stream = @streams[frame.stream_id] stream.receive_data(frame) elsif closed_stream_id?(frame.stream_id) # This can occur if one end sent a stream reset, while the other end was sending a data frame. It's mostly harmless. else raise ProtocolError, "Cannot receive data for stream id #{frame.stream_id}" end end |
#receive_frame(frame) ⇒ Object
Receive and process a generic frame (default handler).
602 603 604 |
# File 'lib/protocol/http2/connection.rb', line 602 def receive_frame(frame) # ignore. end |
#receive_goaway(frame) ⇒ Object
Process a GOAWAY frame from the remote peer.
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/protocol/http2/connection.rb', line 234 def receive_goaway(frame) # We capture the last stream that was processed. @remote_stream_id, error_code, = frame.unpack self.close! # Streams above the last stream ID were not processed by the remote peer and are safe to retry (RFC 9113 ยง6.8). error = ::Protocol::HTTP::RefusedError.new("GOAWAY: request not processed.") @streams.each_value do |stream| if stream.id > @remote_stream_id stream.close(error) end end if error_code != 0 # Shut down immediately. raise GoawayError.new(, error_code) end end |
#receive_headers(frame) ⇒ Object
On the server side, starts a new request.
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 |
# File 'lib/protocol/http2/connection.rb', line 448 def receive_headers(frame) stream_id = frame.stream_id if stream_id.zero? raise ProtocolError, "Cannot receive headers for stream 0!" end if stream = @streams[stream_id] stream.receive_headers(frame) else if stream_id <= @remote_stream_id raise ProtocolError, "Invalid stream id: #{stream_id} <= #{@remote_stream_id}!" end # We need to validate that we have less streams than the specified maximum: if @streams.size < @local_settings.maximum_concurrent_streams stream = accept_stream(stream_id) @remote_stream_id = stream_id stream.receive_headers(frame) else raise ProtocolError, "Exceeded maximum concurrent streams" end end end |
#receive_ping(frame) ⇒ Object
Process a PING frame from the remote peer.
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
# File 'lib/protocol/http2/connection.rb', line 367 def receive_ping(frame) if @state != :closed # This is handled in `read_payload`: # if frame.stream_id != 0 # raise ProtocolError, "Ping received for non-zero stream!" # end unless frame.acknowledgement? reply = frame.acknowledge write_frame(reply) end else raise ProtocolError, "Cannot receive ping in state #{@state}" end end |
#receive_priority_update(frame) ⇒ Object
Receive and process a PRIORITY_UPDATE frame.
484 485 486 487 488 489 490 491 492 493 494 495 |
# File 'lib/protocol/http2/connection.rb', line 484 def receive_priority_update(frame) if frame.stream_id != 0 raise ProtocolError, "Invalid stream id: #{frame.stream_id}" end stream_id, value = frame.unpack # Apparently you can set the priority of idle streams, but I'm not sure why that makes sense, so for now let's ignore it. if stream = @streams[stream_id] stream.priority = Protocol::HTTP::Header::Priority.new(value) end end |
#receive_push_promise(frame) ⇒ Object
Receive and process a PUSH_PROMISE frame.
477 478 479 |
# File 'lib/protocol/http2/connection.rb', line 477 def receive_push_promise(frame) raise ProtocolError, "Unable to receive push promise!" end |
#receive_reset_stream(frame) ⇒ Object
Receive and process a RST_STREAM frame.
547 548 549 550 551 552 553 554 555 556 557 |
# File 'lib/protocol/http2/connection.rb', line 547 def receive_reset_stream(frame) if frame.connection? raise ProtocolError, "Cannot reset connection!" elsif stream = @streams[frame.stream_id] stream.receive_reset_stream(frame) elsif closed_stream_id?(frame.stream_id) # Ignore. else raise StreamClosed, "Cannot reset stream #{frame.stream_id}" end end |
#receive_settings(frame) ⇒ Object
Receive and process a SETTINGS frame from the remote peer.
340 341 342 343 344 345 346 347 348 349 |
# File 'lib/protocol/http2/connection.rb', line 340 def receive_settings(frame) if @state == :new # We transition to :open when we receive acknowledgement of first settings frame: open! if process_settings(frame) elsif @state != :closed process_settings(frame) else raise ProtocolError, "Cannot receive settings in state #{@state}" end end |
#receive_window_update(frame) ⇒ Object
Receive and process a WINDOW_UPDATE frame.
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 |
# File 'lib/protocol/http2/connection.rb', line 574 def receive_window_update(frame) if frame.connection? super self.consume_window elsif stream = @streams[frame.stream_id] begin stream.receive_window_update(frame) rescue ProtocolError => error stream.send_reset_stream(error.code) end elsif closed_stream_id?(frame.stream_id) # Ignore. else # Receiving any frame other than HEADERS or PRIORITY on a stream in this state (idle) MUST be treated as a connection error of type PROTOCOL_ERROR. raise ProtocolError, "Cannot update window of idle stream #{frame.stream_id}" end end |
#send_goaway(error_code = 0, message = "") ⇒ Object
Tell the remote end that the connection is being shut down. If the error_code is 0, this is a graceful shutdown. The other end of the connection should not make any new streams, but existing streams may be completed.
222 223 224 225 226 227 228 229 |
# File 'lib/protocol/http2/connection.rb', line 222 def send_goaway(error_code = 0, = "") frame = GoawayFrame.new frame.pack @remote_stream_id, error_code, write_frame(frame) ensure self.close! end |
#send_ping(data) ⇒ Object
Send a PING frame to the remote peer.
353 354 355 356 357 358 359 360 361 362 |
# File 'lib/protocol/http2/connection.rb', line 353 def send_ping(data) if @state != :closed frame = PingFrame.new frame.pack data write_frame(frame) else raise ProtocolError, "Cannot send ping in state #{@state}" end end |
#send_settings(changes) ⇒ Object
Send updated settings to the remote peer.
205 206 207 208 209 210 211 212 |
# File 'lib/protocol/http2/connection.rb', line 205 def send_settings(changes) @local_settings.append(changes) frame = SettingsFrame.new frame.pack(changes) write_frame(frame) end |
#server_stream_id?(id) ⇒ Boolean
Check if the given stream ID represents a server-initiated stream. Server streams always have even numbered IDs.
509 510 511 |
# File 'lib/protocol/http2/connection.rb', line 509 def server_stream_id?(id) id.even? end |
#synchronize ⇒ Object
Execute a block within a synchronized context. This method provides a synchronization primitive for thread safety.
173 174 175 |
# File 'lib/protocol/http2/connection.rb', line 173 def synchronize yield end |
#update_local_settings(changes) ⇒ Object
Update local settings and adjust stream window capacities.
283 284 285 286 287 288 289 290 291 |
# File 'lib/protocol/http2/connection.rb', line 283 def update_local_settings(changes) capacity = @local_settings.initial_window_size @streams.each_value do |stream| stream.local_window.capacity = capacity end @local_window.desired = capacity end |
#update_remote_settings(changes) ⇒ Object
Update remote settings and adjust stream window capacities.
295 296 297 298 299 300 301 |
# File 'lib/protocol/http2/connection.rb', line 295 def update_remote_settings(changes) capacity = @remote_settings.initial_window_size @streams.each_value do |stream| stream.remote_window.capacity = capacity end end |
#valid_remote_stream_id?(stream_id) ⇒ Boolean
Check if the given stream ID is valid for remote initiation. This method should be overridden by client/server implementations.
403 404 405 |
# File 'lib/protocol/http2/connection.rb', line 403 def valid_remote_stream_id?(stream_id) false end |
#write_frame(frame) ⇒ Object
Write a single frame to the connection.
257 258 259 260 261 262 263 |
# File 'lib/protocol/http2/connection.rb', line 257 def write_frame(frame) synchronize do @framer.write_frame(frame) end @framer.flush end |
#write_frames ⇒ Object
Write multiple frames within a synchronized block.
269 270 271 272 273 274 275 276 277 278 279 |
# File 'lib/protocol/http2/connection.rb', line 269 def write_frames if @framer synchronize do yield @framer end @framer.flush else raise EOFError, "Connection closed!" end end |