Class: H2::Client
Defined Under Namespace
Modules: Celluloid, Concurrent
Classes: TCPSocket
Constant Summary
collapse
- CONNECTION_EVENTS =
[
:close,
:frame,
:goaway,
:promise
]
- ALPN_PROTOCOLS =
['h2']
- DEFAULT_MAXLEN =
4096
Instance Attribute Summary collapse
Instance Method Summary
collapse
-
#_read(maxlen = DEFAULT_MAXLEN) ⇒ Object
-
#add_params(params, path) ⇒ Object
-
#add_stream(method:, path:, stream:, &block) ⇒ Object
-
#bind_events ⇒ Object
-
#build_headers(method:, path:, headers:) ⇒ Object
-
#close ⇒ Object
-
#closed? ⇒ Boolean
-
#create_ssl_context ⇒ Object
builds a new SSLContext suitable for use in ‘h2’ connections.
-
#eof? ⇒ Boolean
-
#goaway(block: false) ⇒ Object
-
#goaway! ⇒ Object
-
#initialize(addr: nil, port: nil, url: nil, tls: {}) {|_self| ... } ⇒ Client
constructor
A new instance of Client.
-
#on_close ⇒ Object
-
#on_frame(bytes) ⇒ Object
-
#on_goaway(*args) ⇒ Object
-
#on_promise(promise) ⇒ Object
-
#read(maxlen = DEFAULT_MAXLEN) ⇒ Object
-
#request(method:, path:, headers: {}, params: {}, body: nil, &block) ⇒ Object
-
#stringify_headers(hash) ⇒ Object
-
#tls_socket(socket) ⇒ Object
thread_pool
thread_pool
Methods included from On
#on
Methods included from Blockable
#block!, #init_blocking, #unblock!
Constructor Details
#initialize(addr: nil, port: nil, url: nil, tls: {}) {|_self| ... } ⇒ Client
Returns a new instance of Client.
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
# File 'lib/h2/client.rb', line 22
def initialize addr: nil, port: nil, url: nil, tls: {}
raise ArgumentError if url.nil? && (addr.nil? || port.nil?)
if url
url = URI.parse url unless URI === url
@addr = url.host
@port = url.port
@scheme = url.scheme
tls = false if 'http' == @scheme
else
@addr = addr
@port = port
@scheme = tls ? 'https' : 'http'
end
@tls = tls
@streams = {}
@socket = TCPSocket.new(@addr, @port)
@socket = tls_socket @socket if @tls
@client = HTTP2::Client.new
init_blocking
yield self if block_given?
bind_events
read
end
|
Instance Attribute Details
#client ⇒ Object
Returns the value of attribute client.
20
21
22
|
# File 'lib/h2/client.rb', line 20
def client
@client
end
|
#last_stream ⇒ Object
Returns the value of attribute last_stream.
19
20
21
|
# File 'lib/h2/client.rb', line 19
def last_stream
@last_stream
end
|
#reader ⇒ Object
Returns the value of attribute reader.
20
21
22
|
# File 'lib/h2/client.rb', line 20
def reader
@reader
end
|
#scheme ⇒ Object
Returns the value of attribute scheme.
20
21
22
|
# File 'lib/h2/client.rb', line 20
def scheme
@scheme
end
|
#socket ⇒ Object
Returns the value of attribute socket.
20
21
22
|
# File 'lib/h2/client.rb', line 20
def socket
@socket
end
|
#streams ⇒ Object
Returns the value of attribute streams.
20
21
22
|
# File 'lib/h2/client.rb', line 20
def streams
@streams
end
|
Instance Method Details
#_read(maxlen = DEFAULT_MAXLEN) ⇒ Object
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
# File 'lib/h2/client.rb', line 142
def _read maxlen = DEFAULT_MAXLEN
begin
data = nil
selector = [@socket]
loop do
data = @socket.read_nonblock maxlen, exception: false
case data
when :wait_readable
IO.select selector
when NilClass
break
else
begin
@client << data
rescue HTTP2::Error::ProtocolError => pe
STDERR.puts 'mystery protocol error!'
STDERR.puts pe.backtrace.map {|l| "\t" + l}
end
end
end
rescue IOError, Errno::EBADF
close
ensure
unblock!
end
end
|
#add_params(params, path) ⇒ Object
123
124
125
126
127
|
# File 'lib/h2/client.rb', line 123
def add_params params, path
appendage = path.index('?') ? '&' : '?'
path << appendage
path << URI.encode_www_form(params)
end
|
#add_stream(method:, path:, stream:, &block) ⇒ Object
114
115
116
117
118
119
120
121
|
# File 'lib/h2/client.rb', line 114
def add_stream method:, path:, stream:, &block
@streams[method] ||= {}
@streams[method][path] ||= []
stream = Stream.new client: self, stream: stream, &block unless Stream === stream
@streams[method][path] << stream
@streams[stream.id] = stream
stream
end
|
#bind_events ⇒ Object
73
74
75
76
77
|
# File 'lib/h2/client.rb', line 73
def bind_events
CONNECTION_EVENTS.each do |e|
@client.on(e){|*a| __send__ "on_#{e}", *a}
end
end
|
104
105
106
107
108
109
110
111
112
|
# File 'lib/h2/client.rb', line 104
def method:, path:, headers:
h = {
AUTHORITY_KEY => [@addr, @port.to_s].join(':'),
METHOD_KEY => method.to_s.upcase,
PATH_KEY => path,
SCHEME_KEY => @scheme
}.merge USER_AGENT
h.merge! ()
end
|
#close ⇒ Object
54
55
56
57
|
# File 'lib/h2/client.rb', line 54
def close
unblock!
@socket.close unless closed?
end
|
#closed? ⇒ Boolean
50
51
52
|
# File 'lib/h2/client.rb', line 50
def closed?
@socket.closed?
end
|
#create_ssl_context ⇒ Object
builds a new SSLContext suitable for use in ‘h2’ connections
231
232
233
234
235
236
237
238
239
240
241
242
|
# File 'lib/h2/client.rb', line 231
def create_ssl_context
ctx = OpenSSL::SSL::SSLContext.new
ctx.alpn_protocols = ALPN_PROTOCOLS
ctx.ca_file = @tls[:ca_file] if @tls[:ca_file]
ctx.ca_path = @tls[:ca_path] if @tls[:ca_path]
ctx.ciphers = @tls[:ciphers] || OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:ciphers]
ctx.options = @tls[:options] || OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
ctx.ssl_version = :TLSv1_2
ctx.verify_mode = @tls[:verify_mode] || ( OpenSSL::SSL::VERIFY_PEER |
OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT )
ctx
end
|
#eof? ⇒ Boolean
59
60
61
|
# File 'lib/h2/client.rb', line 59
def eof?
@socket.eof?
end
|
#goaway(block: false) ⇒ Object
67
68
69
70
71
|
# File 'lib/h2/client.rb', line 67
def goaway block: false
return false if closed?
@client.goaway
block! if block
end
|
#goaway! ⇒ Object
63
64
65
|
# File 'lib/h2/client.rb', line 63
def goaway!
goaway block: true
end
|
#on_close ⇒ Object
173
174
175
176
|
# File 'lib/h2/client.rb', line 173
def on_close
on :close
close
end
|
#on_frame(bytes) ⇒ Object
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
# File 'lib/h2/client.rb', line 178
def on_frame bytes
on :frame, bytes
if ::H2::Client::TCPSocket === @socket
total = bytes.bytesize
loop do
n = @socket.sendmsg_nonblock bytes, exception: false
if n == :wait_writable
IO.select nil, @socket.selector
elsif n < total
bytes = bytes.byteslice n, total
else
break
end
end
else
@socket.write bytes
end
@socket.flush
end
|
#on_goaway(*args) ⇒ Object
199
200
201
202
|
# File 'lib/h2/client.rb', line 199
def on_goaway *args
on :goaway, *args
close
end
|
#on_promise(promise) ⇒ Object
204
205
206
207
208
209
210
211
212
213
214
215
216
217
|
# File 'lib/h2/client.rb', line 204
def on_promise promise
push_promise = Stream.new client: self,
parent: @streams[promise.parent.id],
push: true,
stream: promise do |p|
p.on :close do
method = p.[METHOD_KEY].downcase.to_sym rescue :error
path = p.[PATH_KEY]
add_stream method: method, path: path, stream: p
end
end
on :promise, push_promise
end
|
#read(maxlen = DEFAULT_MAXLEN) ⇒ Object
131
132
133
134
135
136
137
138
139
140
|
# File 'lib/h2/client.rb', line 131
def read maxlen = DEFAULT_MAXLEN
main = Thread.current
@reader = Thread.new do
begin
_read maxlen
rescue => e
main.raise e
end
end
end
|
#request(method:, path:, headers: {}, params: {}, body: nil, &block) ⇒ Object
85
86
87
88
89
90
91
92
93
94
|
# File 'lib/h2/client.rb', line 85
def request method:, path:, headers: {}, params: {}, body: nil, &block
s = @client.new_stream
stream = add_stream method: method, path: path, stream: s, &block
add_params params, path unless params.empty?
h = method: method, path: path, headers:
s. h, end_stream: body.nil?
s.data body if body
stream
end
|
96
97
98
99
100
101
102
|
# File 'lib/h2/client.rb', line 96
def hash
hash.keys.each do |key|
hash[key] = hash[key].to_s unless String === hash[key]
hash[key.to_s] = hash.delete key unless String === key
end
hash
end
|
#tls_socket(socket) ⇒ Object
221
222
223
224
225
226
227
|
# File 'lib/h2/client.rb', line 221
def tls_socket socket
socket = OpenSSL::SSL::SSLSocket.new socket, create_ssl_context
socket.sync_close = true
socket.hostname = @addr
socket.connect
socket
end
|