Module: Async::HTTP::Protocol::HTTP2::Connection

Included in:
Client, Server
Defined in:
lib/async/http/protocol/http2/connection.rb

Overview

Provides shared connection behaviour for HTTP/2 client and server connections.

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#countObject (readonly)

Returns the value of attribute count.



148
149
150
# File 'lib/async/http/protocol/http2/connection.rb', line 148

def count
  @count
end

#promisesObject (readonly)

Returns the value of attribute promises.



141
142
143
# File 'lib/async/http/protocol/http2/connection.rb', line 141

def promises
  @promises
end

#streamObject (readonly)

Returns the value of attribute stream.



73
74
75
# File 'lib/async/http/protocol/http2/connection.rb', line 73

def stream
  @stream
end

Instance Method Details

#as_jsonObject



64
65
66
# File 'lib/async/http/protocol/http2/connection.rb', line 64

def as_json(...)
  to_s
end

#close(error = nil) ⇒ Object

Close the connection and stop the background reader.



91
92
93
94
95
96
97
98
99
100
# File 'lib/async/http/protocol/http2/connection.rb', line 91

def close(error = nil)
  if reader = @reader
    @reader = nil
    
    # The reader task can close the connection itself, e.g. when the last stream completes and the connection is released back to the pool. Stopping it here would cancel the current task in the middle of this method, leaving the underlying stream open, so we let it unwind by itself: `closed?` is now true, so the read loop exits.
    reader.stop unless reader.current?
  end
  
  super
end

#close_if_drained!Object

The connection has finished draining the streams which the remote peer accepted before its graceful GOAWAY.

The last of those streams can complete on the sending side, in a task other than the background reader - the response arrived first and the request body was still being written. The reader is then parked in a blocking read and will never notice that the connection is closed, so we stop it and let its ensure close the connection.



105
106
107
108
109
110
111
# File 'lib/async/http/protocol/http2/connection.rb', line 105

def close_if_drained!
  super
  
  if self.closed? and (reader = @reader) and !reader.current?
    reader.stop
  end
end

#concurrencyObject



151
152
153
# File 'lib/async/http/protocol/http2/connection.rb', line 151

def concurrency
  self.maximum_concurrent_streams
end

#http1?Boolean

Returns:

  • (Boolean)


76
77
78
# File 'lib/async/http/protocol/http2/connection.rb', line 76

def http1?
  false
end

#http2?Boolean

Returns:

  • (Boolean)


81
82
83
# File 'lib/async/http/protocol/http2/connection.rb', line 81

def http2?
  true
end

#initializeObject

Initialize the connection state.



33
34
35
36
37
38
39
40
# File 'lib/async/http/protocol/http2/connection.rb', line 33

def initialize(...)
  super
  
  @reader = nil
  
  # Writing multiple frames at the same time can cause odd problems if frames are only partially written. So we use a semaphore to ensure frames are written in their entirety.
  @write_frame_guard = Async::Semaphore.new(1)
end

#peerObject



144
145
146
# File 'lib/async/http/protocol/http2/connection.rb', line 144

def peer
  @peer ||= ::Protocol::HTTP::Peer.for(@stream.io)
end

#read_in_background(parent: Task.current) ⇒ Object

Start a transient background task that reads frames from the connection.

Raises:

  • (RuntimeError)


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/async/http/protocol/http2/connection.rb', line 114

def read_in_background(parent: Task.current)
  raise RuntimeError, "Connection is closed!" if closed?
  
  parent.async(transient: true) do |task|
    @reader = task
    
    task.annotate("#{version} reading data for #{self.class}.")
    
    # We don't need to defer stop here as this is already a transient task (ignores stop):
    begin
      while !self.closed?
        self.consume_window
        self.read_frame
      end
    rescue => error
      # Close with error.
    ensure
      # Don't call #close twice.
      if @reader
        @reader = nil
        
        self.close(error)
      end
    end
  end
end

#reusable?Boolean

Once the remote peer has sent a GOAWAY frame, it will not process any new streams on this connection, so it must not be handed out for another request, even while the streams it accepted are still being drained.

Returns:

  • (Boolean)


163
164
165
# File 'lib/async/http/protocol/http2/connection.rb', line 163

def reusable?
  !self.closed? && !self.goaway_received?
end

#start_connectionObject

Start the background reader task if it is not already running.



86
87
88
# File 'lib/async/http/protocol/http2/connection.rb', line 86

def start_connection
  @reader || read_in_background
end

#synchronize(&block) ⇒ Object

Synchronize write access to the connection.



44
45
46
# File 'lib/async/http/protocol/http2/connection.rb', line 44

def synchronize(&block)
  @write_frame_guard.acquire(&block)
end

#to_jsonObject



69
70
71
# File 'lib/async/http/protocol/http2/connection.rb', line 69

def to_json(...)
  as_json.to_json(...)
end

#to_sObject



59
60
61
# File 'lib/async/http/protocol/http2/connection.rb', line 59

def to_s
  "\#<#{self.class} #{@streams.count} active streams>"
end

#versionObject



168
169
170
# File 'lib/async/http/protocol/http2/connection.rb', line 168

def version
  VERSION
end

#viable?Boolean

Can we use this connection to make requests?

Returns:

  • (Boolean)


156
157
158
# File 'lib/async/http/protocol/http2/connection.rb', line 156

def viable?
  !self.goaway_received? && @stream&.readable?
end

#write_frame(frame) ⇒ Object

Write a single frame, deferring stop until the frame is written.



49
50
51
# File 'lib/async/http/protocol/http2/connection.rb', line 49

def write_frame(frame)
  Task.current.defer_stop{super}
end

#write_framesObject

Write multiple frames, deferring stop until the frames are written.



54
55
56
# File 'lib/async/http/protocol/http2/connection.rb', line 54

def write_frames
  Task.current.defer_stop{super}
end