Class: Quic::Connection::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/quic/connection.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.new(host:, port:, transport_params: nil, settings: nil) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/quic/connection.rb', line 8

def self.new(host:, port:, transport_params: nil, settings: nil)
  remote_sockaddr = Addrinfo.udp(host, port).to_sockaddr
  local_sockaddr = Addrinfo.udp("0.0.0.0", 0).to_sockaddr

  client = _open(
    local_sockaddr: local_sockaddr,
    remote_sockaddr: remote_sockaddr,
    server_name: host,
    transport_params: transport_params || Quic::TransportParams.default,
    settings: settings || Quic::Settings.default
  )
  client.instance_variable_set(:@host, host)
  client.instance_variable_set(:@port, port)
  client
end

Instance Method Details

#bind(sock) ⇒ Object

Attach an IO.select / #send / #recvfrom-compatible UDP socket so that #run and Stream blocking I/O can drive the wire transparently. Caller is responsible for socket lifetime; we do not close it. The socket must already be #connect’d to the peer (we read #remote_address).



28
29
30
31
# File 'lib/quic/connection.rb', line 28

def bind(sock)
  @sock = sock
  self
end

#bound_socketObject



33
34
35
# File 'lib/quic/connection.rb', line 33

def bound_socket
  @sock
end

#pump_once(timeout: nil) ⇒ Object

One iteration of the I/O loop: drain any outgoing packets via #write_pkt, then wait up to ‘timeout` (or the next ngtcp2 expiry, whichever is sooner) for either an incoming datagram or the timer to fire. Public-ish so Stream blocking ops can share it; documented as “internal” for users.

Raises:

  • (Quic::Error::NotBound)


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/quic/connection.rb', line 51

def pump_once(timeout: nil)
  raise Quic::Error::NotBound, "Quic::Connection::Client#bind(sock) has not been called" if @sock.nil?

  while (pkt = write_pkt)
    @sock.send(pkt, 0)
  end

  wait_for = pump_timeout(timeout)
  ready = IO.select([@sock], nil, nil, wait_for)
  if ready
    data, _addr = @sock.recvfrom(2048)
    # Reuse the same path tuple that Client.new / Client._open passed to
    # ngtcp2_conn_client_new so the read_pkt path matches the stored
    # connection path. local is 0.0.0.0:0 (Client.new convention) and
    # remote comes from the connected socket (assumes #connect was used).
    read_pkt(
      data,
      local_sockaddr: pump_local_sockaddr,
      remote_sockaddr: pump_remote_sockaddr
    )
  else
    handle_expiry
  end
end

#pump_until(&pred) ⇒ Object

Iterate pump_once until ‘pred` returns truthy. Public so Stream blocking methods can share it.



78
79
80
# File 'lib/quic/connection.rb', line 78

def pump_until(&pred)
  pump_once until pred.call
end

#runObject

Drive the handshake to completion using the bound socket. Raises Quic::Error::NotBound if #bind has not been called. Propagates any Quic::Error subclass raised by #read_pkt / #handle_expiry.

Raises:

  • (Quic::Error::NotBound)


40
41
42
43
44
# File 'lib/quic/connection.rb', line 40

def run
  raise Quic::Error::NotBound, "Quic::Connection::Client#bind(sock) has not been called" if @sock.nil?

  pump_until { handshake_completed? }
end