added ssl channel, with alpn negotiation support

This commit is contained in:
HoneyryderChuck 2017-11-28 15:09:11 +00:00
parent 85f08f0c7a
commit f3255ff182
5 changed files with 73 additions and 10 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/**/*.swp
*.gem
.bundle
.config

View File

@ -3,12 +3,12 @@
module HTTPX::Channel
module_function
def by(uri)
def by(uri, *options)
case uri.scheme
when "http"
TCP.new(uri)
TCP.new(uri, *options)
when "https"
TLS.new(uri)
SSL.new(uri, *options)
else
raise "#{uri.scheme}: unrecognized channel"
end
@ -17,3 +17,4 @@ end
require "httpx/channel/http2"
require "httpx/channel/tcp"
require "httpx/channel/ssl"

64
lib/httpx/channel/ssl.rb Normal file
View File

@ -0,0 +1,64 @@
# frozen_string_literal: true
require "forwardable"
require "openssl"
module HTTPX::Channel
class SSL < TCP
def initialize(uri, ssl = {}, **)
ctx = OpenSSL::SSL::SSLContext.new
ctx.set_params(ssl)
ctx.alpn_protocols = %w[h2 http/1.1] if ctx.respond_to?(:alpn_protocols=)
ctx.alpn_select_cb = lambda do |pr|
pr.first unless pr.nil? || pr.empty?
end if ctx.respond_to?(:alpn_select_cb=)
super
@io = OpenSSL::SSL::SSLSocket.new(@io, ctx)
@io.hostname = uri.host
@io.sync_close = true
@io.connect # TODO: non-block variant missing
end
def protocol
@io.alpn_protocol
end
def send(request, &block)
if @processor.nil?
@processor = PROTOCOLS[protocol].new(@write_buffer)
@processor.on(:response, &block)
end
@processor.send(request)
end
if OpenSSL::VERSION < "2.0.6"
# OpenSSL < 2.0.6 has a leak in the buffer destination data.
# It has been fixed as of 2.0.6: https://github.com/ruby/openssl/pull/153
def dread(size = BUFFER_SIZE)
begin
loop do
@io.read_nonblock(size, @read_buffer)
@processor << @read_buffer
end
rescue IO::WaitReadable
# wait read/write
rescue EOFError
# EOF
throw(:close, self)
end
end
end
private
def perform_io
yield
rescue IO::WaitReadable, IO::WaitWritable
# wait read/write
rescue EOFError
# EOF
@io.close
end
end
end

View File

@ -13,25 +13,22 @@ module HTTPX::Channel
BUFFER_SIZE = 1 << 16
attr_reader :remote_ip, :remote_port
attr_reader :remote_ip, :remote_port, :protocol
def_delegator :@io, :to_io
def initialize(uri)
def initialize(uri, *)
@io = TCPSocket.new(uri.host, uri.port)
_, @remote_port, _,@remote_ip = @io.peeraddr
@read_buffer = +""
@write_buffer = +""
@protocol = "h2"
end
def close
@io.close
end
def protocol
"h2"
end
def empty?
@write_buffer.empty?
end

View File

@ -62,7 +62,7 @@ module HTTPX
def close(channel)
@channels.delete(channel)
@channel.close
channel.close
end
end
end