Skip to content

Instantly share code, notes, and snippets.

@evanlucas
Last active July 1, 2020 09:02
Show Gist options
  • Save evanlucas/245f6841c6518eb4bb223a80ce744a3e to your computer and use it in GitHub Desktop.
Save evanlucas/245f6841c6518eb4bb223a80ce744a3e to your computer and use it in GitHub Desktop.
Node proxy
'use strict'
const http = require('http')
const url = require('url')
function random(ar) {
return ar[Math.floor(Math.random() * ar.length)]
}
function createDownstream() {
return new Promise((resolve, reject) => {
const server = http.createServer(handle)
const headers = [
'x-biscuits'
, 'x-toast'
, 'x-javascript'
, 'x-twitter'
]
function handle(req, res) {
const header = random(headers)
res.writeHead(200, {
[header]: 'HI'
})
res.end('DOWNSTREAM')
}
const done = (err) => {
server.removeListener('error', done)
server.removeListener('listening', done)
if (err) return reject(err)
resolve(server)
}
server.on('error', done)
server.on('listening', done)
server.listen()
})
}
let port
let downstream_url
async function main() {
const downstream = await createDownstream()
port = downstream.address().port
console.log('listen downstream', port)
downstream_url = `http://localhost:${port}`
const server = http.createServer(proxy)
server.listen(() => {
const p = server.address().port
console.log('service listen', p)
console.log('make a few requests to', `http://localhost:${p}`)
console.log('like this:')
console.log(` curl http://localhost:${p}`)
})
server.on('error', console.error)
}
main().catch((err) => {
process.nextTick(() => {
throw err
})
})
function proxy(req, res) {
const outbound = url.parse(downstream_url)
outbound.method = req.method
outbound.path = outbound.pathname = req.url
outbound.headers = {
connection: 'close'
}
// Add more headers here if you want
const proxy = http.request(outbound)
const onError = (err) => {
console.log('onError', err)
}
proxy.on('error', onError)
req.on('error', onError)
req.on('aborted', () => {
proxy.abort()
})
proxy.once('response', (proxy_res) => {
console.log('got response', proxy_res.statusCode, proxy_res.headers)
res.statusCode = proxy_res.statusCode
// Add your logic here to determine if you should takeover
if (proxy_res.headers['x-biscuits']) {
// take over
// This reads the response into memory.
// You probably want to use a transform stream though if possible to
// reduce memory pressure
let buf = ''
proxy_res.on('data', (chunk) => {
buf += chunk.toString()
})
proxy_res.on('end', () => {
// Lowercase the result and then split it into characters
// `DOWNSTREAM` becomes `d o w n s t r e a m`
buf = buf.toLowerCase().split('').join(' ')
res.end(buf)
})
} else {
proxy_res.pipe(res)
}
})
req.pipe(proxy)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment