Skip to content

Instantly share code, notes, and snippets.

@jasonswearingen
Created January 31, 2019 19:47
Show Gist options
  • Save jasonswearingen/7a62746a96c8d3ba539220dd9889748f to your computer and use it in GitHub Desktop.
Save jasonswearingen/7a62746a96c8d3ba539220dd9889748f to your computer and use it in GitHub Desktop.
toy proxy for NodeJs (http+https support)
//from: https://stackoverflow.com/a/49864522/1115220 re question: https://stackoverflow.com/questions/8165570/https-proxy-server-in-node-js
import http = require( 'http' )
const port: number = Number.parseInt( process.env.PORT ) || 9191;
import net = require( 'net' )
import url = require( 'url' )
import xlib = require( "xlib" );
import __ = xlib.lolo;
import log = xlib.diagnostics.log;
const requestHandler = ( req, res ) => { // discard all request to proxy server except HTTP/1.1 CONNECT method
res.writeHead( 405, { 'Content-Type': 'text/plain' } )
res.end( 'Method not allowed' )
}
const server = http.createServer( requestHandler )
const listener = server.listen( port, ( err ) => {
if ( err ) {
return console.error( err )
}
const info = listener.address()
if ( typeof ( info ) === "string" ) {
console.log( `Server is listening: info: ${ info }` );
} else {
console.log( `Server is listening on address ${ info.address } port ${ info.port }` )
}
} )
server.on( 'connect', ( req: http.IncomingMessage, clientSocket: net.Socket, head: Buffer ) => { // listen only for HTTP/1.1 CONNECT method
console.log( clientSocket.remoteAddress, clientSocket.remotePort, req.method, req.url )
if ( !req.headers[ 'proxy-authorization' ] ) { // here you can add check for any username/password, I just check that this header must exist!
log.trace( "no auth passed. closing connection. here you can add check for any username/password, I just check that this header must exist!" );
clientSocket.write( [
'HTTP/1.1 407 Proxy Authentication Required',
'Proxy-Authenticate: Basic realm="proxy"',
'Proxy-Connection: close',
].join( '\r\n' ) )
clientSocket.end( '\r\n\r\n' ) // empty body
return;
}
else {
//log.trace( `auth passed: req.headers[ 'proxy-authorization' ]===${ req.headers[ 'proxy-authorization' ] }` );
if ( req.url.includes( "example" ) ) {
log.info( "req passed:", req );
}
}
const destUrl = url.parse( `//${ req.url }`, false, true ) // extract destination host and port from CONNECT request
if ( destUrl.hostname && destUrl.port ) {
const serverErrorHandler = ( err ) => {
console.error( err.message )
if ( clientSocket ) {
clientSocket.end( `HTTP/1.1 500 ${ err.message }\r\n` )
}
}
const serverEndHandler = () => {
if ( clientSocket ) {
clientSocket.end( `HTTP/1.1 500 External Server End\r\n` )
}
}
const serverSocket = net.connect( Number.parseInt( destUrl.port ), destUrl.hostname ) // connect to destination host and port
const clientErrorHandler = ( err ) => {
console.error( err.message )
if ( serverSocket ) {
serverSocket.end()
}
}
const clientEndHandler = () => {
if ( serverSocket ) {
serverSocket.end()
}
}
clientSocket.on( 'error', clientErrorHandler )
clientSocket.on( 'end', clientEndHandler )
serverSocket.on( 'error', serverErrorHandler )
serverSocket.on( 'end', serverEndHandler )
serverSocket.on( 'connect', () => {
clientSocket.write( [
'HTTP/1.1 200 Connection Established',
'Proxy-agent: Node-VPN',
].join( '\r\n' ) )
clientSocket.write( '\r\n\r\n' ) // empty body
// "blindly" (for performance) pipe client socket and destination socket between each other
serverSocket.pipe( clientSocket, { end: false } )
clientSocket.pipe( serverSocket, { end: false } )
} )
} else {
clientSocket.end( 'HTTP/1.1 400 Bad Request\r\n' )
clientSocket.destroy()
}
} )
@jasonswearingen
Copy link
Author

note: https proxy works, but http proxy seems not to. I think http proxy needs to pipe requests (not just handle connect events)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment