-
-
Save bmmcginty/0760dfd9c58861cfcac43c44745cf289 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# # 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