class Http2::Connection def initialize(http2) @http2 = http2 @debug = http2.debug @args = http2.args @nl = http2.nl reconnect end def destroy @sock.close if @sock && !@sock.closed? @sock = nil @sock_plain.close if @sock_plain && !@sock_plain.closed? @sock_plain = nil @sock_ssl.close if @sock_ssl && !@sock_ssl.closed? @sock_ssl = nil end def gets @sock.gets end def read(length) @sock.read(length) end def closed? @sock.closed? end def sock_write(str) str = str.to_s return if str.empty? count = @sock.write(str) raise "Couldnt write to socket: '#{count}', '#{str}'." if count <= 0 end def sock_puts(str) sock_write("#{str}#{@nl}") end # Tries to write a string to the socket. If it fails it reconnects and tries again. def write(str) reconnect unless socket_working? puts "Http2: Writing: #{str}" if @debug begin raise Errno::EPIPE, "The socket is closed." if !@sock || @sock.closed? sock_write(str) rescue Errno::EPIPE # this can also be thrown by puts. reconnect sock_write(str) end @request_last = Time.now end # Reconnects to the host. def reconnect puts "Http2: Reconnect." if @debug # Open connection. if @args[:proxy] if @args[:proxy][:connect] connect_proxy_connect else connect_proxy end else puts "Http2: Opening socket connection to '#{@http2.host}:#{@http2.port}'." if @debug @sock_plain = TCPSocket.new(@http2.host, @http2.port) end if @args[:ssl] apply_ssl else @sock = @sock_plain end end # Returns boolean based on the if the object is connected and the socket is working. #===Examples # puts "Socket is working." if http.socket_working? def socket_working? return false if !@sock || @sock.closed? if @keepalive_timeout && @request_last between = Time.now.to_i - @request_last.to_i if between >= @keepalive_timeout puts "Http2: We are over the keepalive-wait - returning false for socket_working?." if @debug return false end end true end # Closes the current connection if any. def close @sock.close if @sock && !@sock.closed? @sock_ssl.close if @sock_ssl && !@sock_ssl.closed? @sock_plain.close if @sock_plain && !@sock_plain.closed? end def connect_proxy_connect puts "Http2: Initializing proxy connect to '#{@args[:host]}:#{@args[:port]}' through proxy '#{@args[:proxy][:host]}:#{@args[:proxy][:port]}'." if @debug @sock_plain = TCPSocket.new(@args[:proxy][:host], @args[:proxy][:port]) connect = "CONNECT #{@args[:host]}:#{@args[:port]} HTTP/1.1#{@nl}" puts "Http2: Sending connect: #{connect}" if @debug @sock_plain.write(connect) headers = { "Host" => "#{@args[:host]}:#{@args[:port]}" } if @args[:proxy][:user] && @args[:proxy][:passwd] headers["Proxy-Authorization"] = "Basic #{["#{@args[:proxy][:user]}:#{@args[:proxy][:passwd]}"].pack("m").chomp}" end headers.each do |key, value| header = "#{key}: #{value}" puts "Http2: Sending header to proxy: #{header}" if @debug @sock_plain.write("#{header}#{@nl}") end @sock_plain.write(@nl) res = @sock_plain.gets.to_s raise "Couldn't connect through proxy: #{res}" unless res.match?(/^http\/1\.(0|1)\s+200/i) @sock_plain.gets @proxy_connect = true end def proxy_connect? @proxy_connect end def connect_proxy puts "Http2: Opening socket connection to '#{@args[:host]}:#{@args[:port]}' through proxy '#{@args[:proxy][:host]}:#{@args[:proxy][:port]}'." if @debug @sock_plain = TCPSocket.new(@args[:proxy][:host], @args[:proxy][:port].to_i) end def apply_ssl puts "Http2: Initializing SSL." if @debug require "openssl" unless ::Kernel.const_defined?(:OpenSSL) ssl_context = OpenSSL::SSL::SSLContext.new if @args[:ssl_skip_verify] ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE else ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER end @sock_ssl = OpenSSL::SSL::SSLSocket.new(@sock_plain, ssl_context) @sock_ssl.hostname = @http2.host @sock_ssl.sync_close = true @sock_ssl.connect @sock = @sock_ssl end end