nattfodd nattfodd - 12 days ago 6
Ruby Question

HTTP2 requests through PROXY, ruby

I have some remote REST API running through HTTP2. It runs through SSL using certificate. The goal is to send and receive data via HTTP2 with SSL certificate via Proxy.

There are http-2 & net-http2 gems that allow to send requests with HTTP2. But what about proxy? In a standard Net::HTTP library, there is a child class, Net::HTTP::Proxy that duplicates behavior of parent's Net::HTTP class except the fact it sends requests via proxy-server. But HTTP2 gems does not support it.

The closes idea I came up is to make something similar to Proxy implementation of http1.1 - to write "Host:" and "Proxy-Authorization: " fields to the socket, that Net-Http2 gem uses:

@socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
@address, @port, HTTPVersion)
@socket.writeline "Host: #{@address}:#{@port}"
if proxy_user
credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
credential.delete!("\r\n")
@socket.writeline "Proxy-Authorization: Basic #{credential}"
end
@socket.writeline ''


But it ends up with:

SSL_connect SYSCALL returned=5 errno=0 state=SSLv2/v3 read server hello A


I might miss some technical knowledge to achieve this, so any help related to direction of research is appreciated.

Answer

After all, I finished my idea with an example present in net/http standard library and created a pull request for Net-http2 gem: https://github.com/ostinelli/net-http2/pull/11

The idea was correct, all we have to do is to send proxy "CONNECT" message using any tcp socket, with an address where we want to connect, so it creates a TCP tunnel which bypass all the data in and out, doesn't matter if it is HTTP1.1 or HTTP2, or anything else.

Here is a part of code:

def self.proxy_tcp_socket(uri, options)
  proxy_addr = options[:proxy_addr]
  proxy_port = options[:proxy_port]
  proxy_user = options[:proxy_user]
  proxy_pass = options[:proxy_pass]

  proxy_uri = URI.parse("#{proxy_addr}:#{proxy_port}")
  # create a regular TCP socket (with or w/o SSL, if needed)
  proxy_socket = tcp_socket(proxy_uri, options)

  # The majority of proxies do not explicitly support HTTP/2 protocol,
  # while they successfully create a TCP tunnel
  # which can pass through binary data of HTTP/2 connection.
  # So we’ll keep HTTP/1.1
  http_version = '1.1'

  buf = "CONNECT #{uri.host}:#{uri.port} HTTP/#{http_version}\r\n"
  buf << "Host: #{uri.host}:#{uri.port}\r\n"
  if proxy_user
    credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
    credential.delete!("\r\n")
    buf << "Proxy-Authorization: Basic #{credential}\r\n"
  end
  buf << "\r\n"
  proxy_socket.write(buf)
  validate_proxy_response!(proxy_socket)

  proxy_socket
end
Comments