Skip to content

Instantly share code, notes, and snippets.

@bmmcginty
Created February 8, 2017 02:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bmmcginty/0760dfd9c58861cfcac43c44745cf289 to your computer and use it in GitHub Desktop.
Save bmmcginty/0760dfd9c58861cfcac43c44745cf289 to your computer and use it in GitHub Desktop.
# # PROXY
require "openssl"
# ifdef !without_openssl
require "socket"
require "base64"
# Based on https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/proxy/http.rb
class HTTPProxy
# The hostname or IP address of the HTTP proxy.
getter proxy_host : String
# The port number of the proxy.
getter proxy_port : Int32
# The map of additional options that were given to the object at
# initialization.
getter options : Hash(Symbol, String)
getter tls : OpenSSL::SSL::Context::Client?
# Create a new socket factory that tunnels via the given host and
# port. The +options+ parameter is a hash of additional settings that
# can be used to tweak this proxy connection. Specifically, the following
# options are supported:
#
# * :user => the user name to use when authenticating to the proxy
# * :password => the password to use when authenticating
def initialize(@proxy_host, @proxy_port = 80, @options = {} of Symbol => String)
end
# Return a new socket connected to the given host and port via the
# proxy that was requested when the socket factory was instantiated.
def open(host, port, tls = nil, connection_options = {} of Symbol => Float64 | Nil)
dns_timeout = connection_options.fetch(:dns_timeout, nil)
connect_timeout = connection_options.fetch(:connect_timeout, nil)
read_timeout = connection_options.fetch(:read_timeout, nil)
socket = TCPSocket.new @proxy_host, @proxy_port, dns_timeout, connect_timeout
socket.read_timeout = read_timeout if read_timeout
socket.sync = true
socket << "CONNECT #{host}:#{port} HTTP/1.0\r\n"
if options[:user]?
credentials = Base64.strict_encode("#{options[:user]}:#{options[:password]}")
credentials = "#{credentials}\n".gsub(/\s/, "")
socket << "Proxy-Authorization: Basic #{credentials}\r\n"
end
socket << "\r\n"
puts "parsing response"
resp = parse_response(socket)
if resp
if resp[:code]? == 200
if tls
tls_socket = OpenSSL::SSL::Socket::Client.new(socket, context: tls, sync_close: true, hostname: host)
socket = tls_socket
end
end
return socket
else
socket.close
raise IO::Error.new(resp.inspect)
end
end
private def parse_response(socket)
resp = {} of Symbol => Int32 | String | Hash(String, String)
begin
version, code, reason = socket.gets.as(String).chomp.split(/ /, 3)
puts "code #{code}"
headers = {} of String => String
while (line = socket.gets.as(String)) && (line.chomp != "")
puts "line #{line}"
name, value = line.split(/:/, 2)
headers[name.strip] = value.strip
end
puts "got lines"
resp[:version] = version
resp[:code] = code.to_i
resp[:reason] = reason
resp[:headers] = headers
rescue e
puts e
end
puts "returning resp"
return resp
end
end
# # CLIENT
require "http/client"
class HTTPClient < ::HTTP::Client
def set_proxy(proxy : HTTPProxy)
begin
@socket = proxy.open(host: @host, port: @port, tls: @tls, connection_options: proxy_connection_options)
rescue IO::Error
@socket = nil
end
end
def proxy_connection_options
opts = {} of Symbol => Float64 | Nil
opts[:dns_timeout] = @dns_timeout
opts[:connect_timeout] = @connect_timeout
opts[:read_timeout] = @read_timeout
return opts
end
end
# # USE CASE
proxy_host = "127.0.0.1"
proxy_port = 8123
options = {} of Symbol => String
# options[:user] = "usr1"
# options[:password] = "pw1"
proxy = HTTPProxy.new(proxy_host: proxy_host, proxy_port: proxy_port, options: options)
client = HTTPClient.new("bmcginty.us", 443, true)
client.set_proxy(proxy)
response = client.get("/10mb") do |resp|
puts resp.headers
bio = resp.body_io
# puts bio.gets_to_end
# puts bio.gets(20)
# puts resp.body_io
fh = File.open("test.txt", "wb")
fh << bio.gets_to_end
fh.close
end
# puts "Response status: #{response.try &.status_code}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment