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
54
55
# 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 = if @http_wire_trace
    options[:logger] || Logger.new($stdout)
  end
  @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.



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

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.



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

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.



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

def close!
  @mutex.synchronize {
    self.debug_output('closing connection ...')
    if @socket
      @socket.close
      @socket = 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)


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

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.



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
102
103
# File 'lib/seahorse/client/h2/connection.rb', line 75

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.



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

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.



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

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.



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
137
138
139
# File 'lib/seahorse/client/h2/connection.rb', line 105

def start(stream)
  @mutex.synchronize {
    return if @socket_thread
    @socket_thread = Thread.new do
      while @socket && !@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
      @socket_thread = nil
    end
    @socket_thread.abort_on_exception = true
  }
end