Class: HTTPX::Channel

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Callbacks, Loggable, Registry
Defined in:
lib/httpx/channel.rb

Overview

The Channel entity can be watched for IO events.

It contains the io object to read/write from, and knows what to do when it can.

It defers connecting until absolutely necessary. Connection should be triggered from the IO selector (until then, any request will be queued).

A channel boots up its parser after connection is established. All pending requests will be redirected there after connection.

A channel can be prevented from closing by the parser, that is, if there are pending requests. This will signal that the channel was prematurely closed, due to a possible number of conditions:

  • Remote peer closed the connection (“Connection: close”);

  • Remote peer doesn’t support pipelining;

A channel may also route requests for a different host for which the io was connected to, provided that the IP is the same and the port and scheme as well. This will allow to share the same socket to send HTTP/2 requests to different hosts. TODO: For this to succeed, the certificates sent by the servers to the client must be

identical (or match both hosts).

Direct Known Subclasses

ProxyChannel

Defined Under Namespace

Classes: HTTP1, HTTP2

Constant Summary collapse

BUFFER_SIZE =
1 << 14

Constants included from Registry

Registry::Error

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Callbacks

#emit, #on, #once

Methods included from Loggable

#log

Methods included from Registry

extended, included

Constructor Details

#initialize(io, options) ⇒ Channel

Returns a new instance of Channel.



60
61
62
63
64
65
66
67
68
# File 'lib/httpx/channel.rb', line 60

def initialize(io, options)
  @io = io
  @options = Options.new(options)
  @window_size = @options.window_size
  @read_buffer = Buffer.new(BUFFER_SIZE)
  @write_buffer = Buffer.new(BUFFER_SIZE)
  @pending = []
  @state = :idle
end

Class Method Details

.by(uri, options) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
# File 'lib/httpx/channel.rb', line 43

def by(uri, options)
  io = case uri.scheme
       when "http"
         IO.registry("tcp").new(uri.host, uri.port, options)
       when "https"
         IO.registry("ssl").new(uri.host, uri.port, options)
       else
         raise Error, "#{uri}: #{uri.scheme}: unrecognized channel"
  end
  new(io, options)
end

Instance Method Details

#callObject



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/httpx/channel.rb', line 129

def call
  case @state
  when :closed
    return
  when :closing
    dwrite
    transition(:closed)
    emit(:close)
  else
    catch(:called) do
      dread
      dwrite
      parser.consume
    end
  end
  nil
end

#close(hard = false) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/httpx/channel.rb', line 101

def close(hard = false)
  pr = @parser
  transition(:closing)
  if hard || (pr && pr.empty?)
    pr.close
    @parser = nil
  else
    transition(:idle)
    @parser = pr
    parser.reenqueue!
    return
  end
end

#interestsObject



82
83
84
85
86
87
88
89
90
91
# File 'lib/httpx/channel.rb', line 82

def interests
  return :w if @state == :idle
  readable = !@read_buffer.full?
  writable = !@write_buffer.empty?
  if readable
    writable ? :rw : :r
  else
    writable ? :w : :r
  end
end

#match?(uri) ⇒ Boolean

Returns:

  • (Boolean)


70
71
72
73
74
75
76
77
78
79
80
# File 'lib/httpx/channel.rb', line 70

def match?(uri)
  ip = begin
         TCPSocket.getaddress(uri.host)
       rescue StandardError
         uri.host
       end

  ip == @io.ip &&
    uri.port == @io.port &&
    uri.scheme == @io.scheme
end

#resetObject



115
116
117
118
119
# File 'lib/httpx/channel.rb', line 115

def reset
  transition(:closing)
  transition(:closed)
  emit(:close)
end

#send(request, **args) ⇒ Object



121
122
123
124
125
126
127
# File 'lib/httpx/channel.rb', line 121

def send(request, **args)
  if @parser && !@write_buffer.full?
    parser.send(request, **args)
  else
    @pending << [request, args]
  end
end

#to_ioObject



93
94
95
96
97
98
99
# File 'lib/httpx/channel.rb', line 93

def to_io
  case @state
  when :idle
    transition(:open)
  end
  @io.to_io
end

#upgrade_parser(protocol) ⇒ Object



147
148
149
150
# File 'lib/httpx/channel.rb', line 147

def upgrade_parser(protocol)
  @parser.reset if @parser
  @parser = build_parser(protocol)
end