-
-
Save raggi/32389 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
#!/usr/bin/env rackup | |
require 'forward' | |
run Rack::Forwarder.new('google.com') |
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
require 'socket' | |
require 'net/http' | |
require 'net/https' | |
require 'rack' | |
Net::HTTP.version_1_2 # Sorry if this causes anyone an issue... | |
module Rack | |
class Forwarder | |
# General exceptions which may be raised by Net::HTTP | |
HttpExceptions = [ | |
Timeout::Error, EOFError, SystemCallError, SocketError,NoMemoryError, | |
IOError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, | |
Net::ProtocolError | |
] | |
def initialize(host, port=80, ssl = false) | |
@host, @port, @ssl = host, port, ssl | |
end | |
# Using the block form has the added advantage of catching the exceptions | |
# that could be raised by Net::HTTP for you, otherwise you will have to | |
# handle those yourself. | |
def conn | |
@conn ||= connect | |
if block_given? | |
yield @conn | |
else | |
@conn | |
end | |
rescue *HttpExceptions | |
@conn = connect | |
return if defined?(once) | |
once = true && retry | |
end | |
# Just a wrapper around Net::HTTP.start effectively, but also sets up | |
# use_ssl when specified. | |
def connect | |
http = Net::HTTP.new @host, @port | |
http.use_ssl = @ssl | |
http.start | |
http | |
end | |
def call(env) | |
rackreq = Rack::Request.new(env) | |
headers = Rack::Utils::HeaderHash.new | |
env.each { |key, value| | |
if key =~ /HTTP_(.*)/ | |
headers[$1] = value | |
end | |
} | |
res = conn { |http| | |
m = rackreq.request_method | |
case m | |
when "GET", "HEAD", "DELETE", "OPTIONS", "TRACE" | |
req = Net::HTTP.const_get(m.capitalize).new(rackreq.fullpath, headers) | |
when "PUT", "POST" | |
req = Net::HTTP.const_get(m.capitalize).new(rackreq.fullpath, headers) | |
req.body_stream = rackreq.body | |
else | |
raise "method not supported: #{method}" | |
end | |
http.request(req) | |
} | |
headers = res.to_hash | |
headers.delete(headers.keys.grep(/transfer\-encoding/i).first) | |
[res.code, Rack::Utils::HeaderHash.new(headers), Array(res.body)] | |
end | |
# Errnos that shouldn't ever be passed up to us, but could be - this is a | |
# paranoia stack, for examples sake, not all necessary on all systems. | |
# Common causes of these kinds of errors are adverse system load or | |
# failing hardware. See errors.h on your platform for more details, or the | |
# POSIX spec, or the BSD sockets api. | |
HttpExceptions.concat [ | |
# Permission denied An attempt was made to access a file in a way | |
# forbidden by its file access permissions. | |
Errno::EACCES, | |
# Address in use The specified address is in use. | |
Errno::EADDRINUSE, | |
# Address not available The specified address is not available from the | |
# local system. | |
Errno::EADDRNOTAVAIL, | |
# Address family not supported The implementation does not support the | |
# specified address family, or the specified address is not a valid | |
# address for the address family of the specified socket. | |
Errno::EAFNOSUPPORT, | |
# Resource temporarily unavailable This is a temporary condition and | |
# later calls to the same routine may complete normally. | |
Errno::EAGAIN, | |
# Connection already in progress A connection request is already in | |
# progress for the specified socket. | |
Errno::EALREADY, | |
# Bad file descriptor A file descriptor argument is out of range, refers | |
# to no open file, or a read (write) request is made to a file that is | |
# only open for writing (reading). | |
Errno::EBADF, | |
# Bad message During a read(), getmsg() or ioctl() I_RECVFD request to a | |
# STREAMS device, a message arrived at the head of the STREAM that is | |
# inappropriate for the function receiving the message. | |
# read() - message waiting to be read on a STREAM is not a data message. | |
# getmsg() - a file descriptor was received instead of a control message. | |
# ioctl() - control or data information was received instead of a file descriptor when I_RECVFD was specified. | |
Errno::EBADMSG, | |
# Resource busy An attempt was made to make use of a system resource | |
# that is not currently available, as it is being used by another | |
# process in a manner that would have conflicted with the request being | |
# made by this process. | |
Errno::EBUSY, | |
# Operation canceled The associated asynchronous operation was canceled | |
# before completion. Errno::ECANCELED, Connection aborted The connection | |
# has been aborted. | |
Errno::ECONNABORTED, | |
# Connection refused An attempt to connect to a socket was refused | |
# because there was no process listening or because the queue of | |
# connection requests was full and the underlying protocol does not | |
# support retransmissions. | |
Errno::ECONNREFUSED, | |
# Connection reset The connection was forcibly closed by the peer. | |
Errno::ECONNRESET, | |
# Posix? | |
Errno::EHOSTDOWN, | |
# Host is unreachable The destination host cannot be reached (probably | |
# because the host is down or a remote router cannot reach it). | |
Errno::EHOSTUNREACH, | |
# Operation in progress This code is used to indicate that an | |
# asynchronous operation has not yet completed. | |
Errno::EINPROGRESS, | |
# Invalid argument Some invalid argument was supplied; (for example, | |
# specifying an undefined signal in a signal() function or a kill() | |
# function). | |
Errno::EINVAL, | |
# Input/output error Some physical input or output error has occurred. | |
# This error may be reported on a subsequent operation on the same file | |
# descriptor. Any other error-causing operation on the same file | |
# descriptor may cause the [EIO] error indication to be lost. | |
Errno::EIO, | |
# Too many open files An attempt was made to open more than the maximum | |
# number of {OPEN_MAX} file descriptors allowed in this process. | |
Errno::EMFILE, | |
# Message too large A message sent on a transport provider was larger | |
# than an internal message buffer or some other network limit. | |
Errno::EMSGSIZE, | |
# Network is down The local interface used to reach the destination is | |
# down. | |
Errno::ENETDOWN, | |
# Posix? | |
Errno::ENETRESET, | |
# Network unreachable No route to the network is present. | |
Errno::ENETUNREACH, | |
# Too many files open in system Too many files are currently open in the | |
# system. The system has reached its predefined limit for simultaneously | |
# open files and temporarily cannot accept requests to open another one. | |
Errno::ENFILE, | |
# No buffer space available Insufficient buffer resources were available | |
# in the system to perform the socket operation. | |
Errno::ENOBUFS, | |
# No message available No message is available on the STREAM head read | |
# queue. | |
Errno::ENODATA, | |
# No locks available A system-imposed limit on the number of | |
# simultaneous file and record locks has been reached and no more are | |
# currently available. | |
Errno::ENOLCK, | |
# Not enough space The new process image requires more memory than is | |
# allowed by the hardware or system-imposed memory management | |
# constraints. | |
Errno::ENOMEM, | |
# Protocol not available The protocol option specified to setsockopt() | |
# is not supported by the implementation. | |
Errno::ENOPROTOOPT, | |
# Socket not connected The socket is not connected. | |
Errno::ENOTCONN, | |
# Operation not supported on socket The type of socket (address family | |
# or protocol) does not support the requested operation. | |
Errno::EOPNOTSUPP, | |
# Protocol error Some protocol error occurred. This error is device | |
# specific, but is generally not related to a hardware failure. | |
Errno::EPROTO, | |
# Protocol not supported The protocol is not supported by the address | |
# family, or the protocol is not supported by the implementation. | |
Errno::EPROTONOSUPPORT, | |
# Socket type not supported The socket type is not supported by the | |
# protocol. | |
Errno::EPROTOTYPE, | |
# Result too large or too small The result of the function is too large | |
# (overflow) or too small (underflow) to be represented in the available | |
# space. (Defined in the ISO C standard.) | |
Errno::ERANGE, | |
# STREAM ioctl() timeout The timer set for a STREAMS ioctl() call has | |
# expired. The cause of this error is device specific and could indicate | |
# either a hardware or software failure, or a timeout value that is too | |
# short for the specific operation. The status of the ioctl() operation | |
# is indeterminate. | |
Errno::ETIME, | |
# Connection timed out The connection to a remote machine has timed out. | |
# If the connection timed out during execution of the function that | |
# reported this error (as opposed to timing out prior to the function | |
# being called), it is unspecified whether the function has completed | |
# some or all of the documented behaviour associated with a successful | |
# completion of the function. | |
Errno::ETIMEDOUT | |
] | |
end | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment