lib/httpx/io.rb in httpx-0.0.5 vs lib/httpx/io.rb in httpx-0.1.0
- old
+ new
@@ -1,255 +1,15 @@
# frozen_string_literal: true
-require "resolv"
require "socket"
-require "openssl"
-require "ipaddr"
+require "httpx/io/tcp"
+require "httpx/io/ssl"
+require "httpx/io/unix"
module HTTPX
- class TCP
- include Loggable
-
- attr_reader :ip, :port
-
- def initialize(hostname, port, options)
- @state = :idle
- @hostname = hostname
- @options = Options.new(options)
- @fallback_protocol = @options.fallback_protocol
- @port = port
- if @options.io
- @io = case @options.io
- when Hash
- @ip = Resolv.getaddress(@hostname)
- @options.io[@ip] || @options.io["#{@ip}:#{@port}"]
- else
- @ip = hostname
- @options.io
- end
- unless @io.nil?
- @keep_open = true
- @state = :connected
- end
- else
- @ip = Resolv.getaddress(@hostname)
- end
- @io ||= build_socket
- end
-
- def scheme
- "http"
- end
-
- def to_io
- @io.to_io
- end
-
- def protocol
- @fallback_protocol
- end
-
- def connect
- return unless closed?
- begin
- if @io.closed?
- transition(:idle)
- @io = build_socket
- end
- @io.connect_nonblock(Socket.sockaddr_in(@port, @ip))
- rescue Errno::EISCONN
- end
- transition(:connected)
- rescue Errno::EINPROGRESS,
- Errno::EALREADY,
- ::IO::WaitReadable
- end
-
- if RUBY_VERSION < "2.3"
- def read(size, buffer)
- @io.read_nonblock(size, buffer)
- buffer.bytesize
- rescue ::IO::WaitReadable
- 0
- rescue EOFError
- nil
- end
-
- def write(buffer)
- siz = @io.write_nonblock(buffer)
- buffer.slice!(0, siz)
- siz
- rescue ::IO::WaitWritable
- 0
- rescue EOFError
- nil
- end
- else
- def read(size, buffer)
- ret = @io.read_nonblock(size, buffer, exception: false)
- return 0 if ret == :wait_readable
- return if ret.nil?
- buffer.bytesize
- end
-
- def write(buffer)
- siz = @io.write_nonblock(buffer, exception: false)
- return 0 if siz == :wait_writable
- return if siz.nil?
- buffer.slice!(0, siz)
- siz
- end
- end
-
- def close
- return if @keep_open || closed?
- begin
- @io.close
- ensure
- transition(:closed)
- end
- end
-
- def connected?
- @state == :connected
- end
-
- def closed?
- @state == :idle || @state == :closed
- end
-
- def inspect
- id = @io.closed? ? "closed" : @io.fileno
- "#<TCP(fd: #{id}): #{@ip}:#{@port} (state: #{@state})>"
- end
-
- private
-
- def build_socket
- addr = IPAddr.new(@ip)
- Socket.new(addr.family, :STREAM, 0)
- end
-
- def transition(nextstate)
- case nextstate
- # when :idle
- when :connected
- return unless @state == :idle
- when :closed
- return unless @state == :connected
- end
- do_transition(nextstate)
- end
-
- def do_transition(nextstate)
- log(level: 1, label: "#{inspect}: ") { nextstate.to_s }
- @state = nextstate
- end
- end
-
- class SSL < TCP
- TLS_OPTIONS = if OpenSSL::SSL::SSLContext.instance_methods.include?(:alpn_protocols)
- { alpn_protocols: %w[h2 http/1.1] }
- else
- {}
- end
-
- def initialize(_, _, options)
- @ctx = OpenSSL::SSL::SSLContext.new
- ctx_options = TLS_OPTIONS.merge(options.ssl)
- @ctx.set_params(ctx_options) unless ctx_options.empty?
- super
- @state = :negotiated if @keep_open
- end
-
- def scheme
- "https"
- end
-
- def protocol
- @io.alpn_protocol || super
- rescue StandardError
- super
- end
-
- def close
- super
- # allow reconnections
- # connect only works if initial @io is a socket
- @io = @io.io if @io.respond_to?(:io)
- @negotiated = false
- end
-
- def connected?
- @state == :negotiated
- end
-
- def connect
- super
- if @keep_open
- @state = :negotiated
- return
- end
- return if @state == :negotiated ||
- @state != :connected
- unless @io.is_a?(OpenSSL::SSL::SSLSocket)
- @io = OpenSSL::SSL::SSLSocket.new(@io, @ctx)
- @io.hostname = @hostname
- @io.sync_close = true
- end
- # TODO: this might block it all
- @io.connect_nonblock
- transition(:negotiated)
- rescue ::IO::WaitReadable,
- ::IO::WaitWritable
- end
-
- if RUBY_VERSION < "2.3"
- def read(*)
- super
- rescue ::IO::WaitWritable
- 0
- end
-
- def write(*)
- super
- rescue ::IO::WaitReadable
- 0
- end
- else
- if OpenSSL::VERSION < "2.0.6"
- def read(size, buffer)
- @io.read_nonblock(size, buffer)
- buffer.bytesize
- rescue ::IO::WaitReadable,
- ::IO::WaitWritable
- 0
- rescue EOFError
- nil
- end
- end
- end
-
- def inspect
- id = @io.closed? ? "closed" : @io.to_io.fileno
- "#<SSL(fd: #{id}): #{@ip}:#{@port} state: #{@state}>"
- end
-
- private
-
- def transition(nextstate)
- case nextstate
- when :negotiated
- return unless @state == :connected
- when :closed
- return unless @state == :negotiated ||
- @state == :connected
- end
- do_transition(nextstate)
- end
- end
module IO
extend Registry
register "tcp", TCP
register "ssl", SSL
+ register "unix", HTTPX::UNIX
end
end