Skip to content

Instantly share code, notes, and snippets.

@eventualbuddha
Created April 28, 2011 22:42
Show Gist options
  • Save eventualbuddha/947505 to your computer and use it in GitHub Desktop.
Save eventualbuddha/947505 to your computer and use it in GitHub Desktop.
An HTTP proxy server that can act as a system proxy or a local port forwarder with an optional delay
#!/usr/bin/env coffee
sys = require('sys')
http = require('http')
## Argument parsing
args = process.argv.slice(2)
port = 9090
delay = 0
proxyHost = null
proxyPort = 80
usage = (error) ->
sys.error("error: #{error}\n") if error
sys.error "Usage: proxy [-p PORT] [-h HOST] -P PORT"
sys.error ""
sys.error ""
sys.error " -p, --port Run the file server on this port (defaults to #{port})"
sys.error " -d, --delay The delay (in seconds) to add to the response time for each request (defaults to #{delay})"
sys.error ""
sys.error " -h, --proxy-host The destination host to forward requests to (defaults to the request's Host header)"
sys.error " -P, --proxy-port The destination port to forward requests to (defaults to #{proxyPort})"
process.exit(if error then 1 else 0)
while args.length
arg = args.shift()
match = arg.match(/^(--\w+[\w-])(?:=(.*))?$/)
if match
# --port=1234, --port, etc
arg = match[1]
args.unshift(match[2]) if match[2]
switch arg
when '-p', '--port'
portArg = args.shift()
if portArg == undefined
usage "expected port number after #{arg}"
port = Number(portArg)
if port == 0 or isNaN(port)
usage "invalid port number #{port}"
when '-d', '--delay'
delayArg = args.shift()
if delayArg == undefined
usage "expected delay number after #{arg}"
delay = Number(delayArg)
if isNaN(delay)
usage "invalid delay number #{delay}"
when '-h', '--proxy-host'
proxyHostArg = args.shift()
if proxyHostArg == undefined
usage "expected host after #{arg}"
proxyHost = proxyHostArg
when '-P', '--proxy-port'
proxyPortArg = args.shift()
if proxyPortArg == undefined
usage "expected port after #{arg}"
proxyPort = Number(proxyPortArg)
if proxyPort == 0 or isNaN(proxyPort)
usage "invalid port number #{proxyPortArg}"
when '--help'
usage()
else
usage "unrecognized argument #{arg}"
## Support
class DelayedHTTPClientRequest
delayed: true
_delayedCalls: null
constructor: (@request) ->
@_delayedCalls = []
addListener: (args...) ->
@request.addListener(args...)
write: (args...) ->
@send 'write', args...
end: (args...) ->
@send 'end', args...
send: (name, args...) ->
if @delayed
@_delayedCalls.push [name, args]
else
@request[name].call(@request, args...)
setDelayed: (delayed) ->
return if @delayed == delayed
if not (@delayed = delayed)
for [name, args] in @_delayedCalls
@request[name].call(@request, args...)
@_delayedCalls = []
## Guts
server = (request, response) ->
console.log("#{request.method} #{request.url}")
proxy = http.createClient(proxyPort, proxyHost || request.headers['host'])
proxy_request = new DelayedHTTPClientRequest(proxy.request(request.method, request.url, request.headers))
proxy_request.addListener 'response', (proxy_response) =>
proxy_response.addListener 'data', (chunk) =>
response.write(chunk, 'binary')
proxy_response.addListener 'end', =>
response.end()
response.writeHead(proxy_response.statusCode, proxy_response.headers)
request.addListener 'data', (chunk) =>
proxy_request.write(chunk, 'binary')
request.addListener 'end', =>
proxy_request.end()
setTimeout((-> proxy_request.setDelayed(false)), delay*1000)
http.createServer(server).listen(port)
info = if proxyHost
"~ Forwarding HTTP traffic from 127.0.0.1:#{port} to #{proxyHost}:#{proxyPort}"
else
"~ Proxying HTTP traffic at 127.0.0.1:#{port}"
info += " with a #{delay}s delay" if delay > 0
sys.puts info
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment