Class: Seahorse::Client::H2::Connection Private

Inherits:
Object
  • Object
show all
Defined in:
lib/seahorse/client/h2/connection.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

H2 Connection build on top of `http/2` gem (requires Ruby >= 2.1) with TLS layer plus ALPN, requires: Ruby >= 2.3 and OpenSSL >= 1.0.2

Constant Summary collapse

OPTIONS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

{
  max_concurrent_streams: 100,
  connection_timeout: 60,
  connection_read_timeout: 60,
  http_wire_trace: false,
  logger: nil,
  ssl_verify_peer: true,
  ssl_ca_bundle: nil,
  ssl_ca_directory: nil,
  ssl_ca_store: nil,
  enable_alpn: false
}
CHUNKSIZE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

chunk read size at socket

1024
SOCKET_FAMILY =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

::Socket::AF_INET

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Connection

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Connection.


38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/seahorse/client/h2/connection.rb', line 38

def initialize(options = {})
  OPTIONS.each_pair do |opt_name, default_value|
    value = options[opt_name].nil? ? default_value : options[opt_name]
    instance_variable_set("@#{opt_name}", value)
  end
  @h2_client = HTTP2::Client.new(
    settings_max_concurrent_streams: max_concurrent_streams
  )
  @logger = options[:logger] || Logger.new($stdout) if @http_wire_trace
  @chunk_size = options[:read_chunk_size] || CHUNKSIZE
  @errors = []
  @status = :ready
  @mutex = Mutex.new # connection can be shared across requests
  @socket = nil
  @socket_thread = nil
end

Instance Attribute Details

#errorsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


61
62
63
# File 'lib/seahorse/client/h2/connection.rb', line 61

def errors
  @errors
end

#input_signal_threadObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


63
64
65
# File 'lib/seahorse/client/h2/connection.rb', line 63

def input_signal_thread
  @input_signal_thread
end

Instance Method Details

#close!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/seahorse/client/h2/connection.rb', line 138

def close!
  @mutex.synchronize {
    self.debug_output('closing connection ...')
    if @socket
      @socket.close
      @socket = nil
    end
    if @socket_thread
      Thread.kill(@socket_thread)
      @socket_thread = nil
    end
    @status = :closed
  }
end

#closed?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)

153
154
155
# File 'lib/seahorse/client/h2/connection.rb', line 153

def closed?
  @status == :closed
end

#connect(endpoint) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/seahorse/client/h2/connection.rb', line 73

def connect(endpoint)
  @mutex.synchronize {
    if @status == :ready
      tcp, addr = _tcp_socket(endpoint)
      debug_output("opening connection to #{endpoint.host}:#{endpoint.port} ...")
      _nonblocking_connect(tcp, addr)
      debug_output('opened')

      if endpoint.scheme == 'https'
        @socket = OpenSSL::SSL::SSLSocket.new(tcp, _tls_context)
        @socket.sync_close = true
        @socket.hostname = endpoint.host

        debug_output("starting TLS for #{endpoint.host}:#{endpoint.port} ...")
        @socket.connect
        debug_output('TLS established')
      else
        @socket = tcp
      end

      _register_h2_callbacks
      @status = :active
    elsif @status == :closed
      msg = 'Async Client HTTP2 Connection is closed, you may'\
            ' use #new_connection to create a new HTTP2 Connection for this client'
      raise Http2ConnectionClosedError.new(msg)
    end
  }
end

#debug_output(msg, type = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


157
158
159
160
161
162
163
164
165
166
# File 'lib/seahorse/client/h2/connection.rb', line 157

def debug_output(msg, type = nil)
  prefix = case type
    when :send then '-> '
    when :receive then '<- '
    else
      ''
    end
  return unless @logger
  _debug_entry(prefix + msg)
end

#new_streamObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


65
66
67
68
69
70
71
# File 'lib/seahorse/client/h2/connection.rb', line 65

def new_stream
  begin
    @h2_client.new_stream
  rescue => error
    raise Http2StreamInitializeError.new(error)
  end
end

#start(stream) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/seahorse/client/h2/connection.rb', line 103

def start(stream)
  @mutex.synchronize {
    return if @socket_thread
    @socket_thread = Thread.new do
      while !@socket.closed?
        begin
          data = @socket.read_nonblock(@chunk_size)
          @h2_client << data
        rescue IO::WaitReadable
          begin
            unless IO.select([@socket], nil, nil, connection_read_timeout)
              self.debug_output('socket connection read time out')
              self.close!
            else
              # available, retry to start reading
              retry
            end
          rescue
            # error can happen when closing the socket
            # while it's waiting for read
            self.close!
          end
        rescue EOFError
          self.close!
        rescue => error
          self.debug_output(error.inspect)
          @errors << error
          self.close!
        end
      end
    end
    @socket_thread.abort_on_exception = true
  }
end