Sunday, 9 February 2020

I implemented a "search and replace" reverse proxy in node, unfortunately it's too slow

I had a goal of proxying wordpress and search and replacing the hostname / site_url. This way I'd be able to use wordpress with any hostname/ip/port, just like a node.js express app. If you don't know, wordpress hostname is hard coded in the database and will force a redirect to it.Unfortunately, it was too slow, it took sometimes several seconds to load pages, even if everything is running locally. I'm wondering if this problem is solvable.Here's the code:``` const proxiedHost = 'wordpress-test' const proxiedHostBuffer = Buffer.from(proxiedHost)const http = require('http') const zlib = require('zlib') const {Transform} = require('stream') const proxyServer = http.createServer()proxyServer.on('request', (clientRequest, clientResponse) => { console.log(${clientRequest.method} ${clientRequest.url}) console.log(clientRequest.headers)const clientHost = clientRequest.headers.host const clientHostBuffer = Buffer.from(clientHost)const proxiedHeaders = replaceHeaders({headers: clientRequest.headers, pattern: clientHost, replacement: proxiedHost}) const [hostname, port] = proxiedHost.split(':')const proxiedRequestOptions = { hostname, port: port || 80, path: clientRequest.url, method: clientRequest.method, headers: proxiedHeaders }const proxiedRequest = http.request(proxiedRequestOptions, (serverResponse) => { console.log(serverResponse.headers)let serverResponseStream = serverResponse if (serverResponse.headers['content-type'].match(/html|json|xml|application/)) { const proxifyClientResponse = new Transform({ async transform(bufferData, encoding, callback) { console.log('\n\nreplacing host in body\n\n\n') bufferReplace(bufferData, proxiedHostBuffer, clientHostBuffer) console.log(bufferData.toString()) callback(null, bufferData) } }) serverResponseStream = serverResponseStream.pipe(proxifyClientResponse) } const proxiedHeaders = replaceHeaders({headers: serverResponse.headers, pattern: proxiedHost, replacement: clientHost}) // send server response to client if (clientRequest.headers['accept-encoding'] && clientRequest.headers['accept-encoding'].match('gzip')) { clientResponse.setHeader('content-encoding', 'gzip') clientResponse.writeHead(serverResponse.statusCode, proxiedHeaders) serverResponseStream.pipe(zlib.createGzip()).pipe(clientResponse, {end: true}) } else { clientResponse.writeHead(serverResponse.statusCode, proxiedHeaders) serverResponseStream.pipe(clientResponse, {end: true}) } })// send client request to proxy clientRequest.pipe(proxiedRequest, {end: true}) }) proxyServer.listen(5050) console.log('listening on port 5050')const gunzip = (buffer) => new Promise(resolve => { zlib.gunzip( buffer, {finishFlush: zlib.constants.Z_SYNC_FLUSH}, (err, buffer) => resolve(buffer)) })const replaceHeaders = ({headers, pattern, replacement}) => { const newHeaders = {} for (const header in headers) { // do not compress if (header.match(/accept-encoding/i)) { continue } if (typeof headers[header] === 'string') { newHeaders[header] = headers[header].replace(pattern, replacement) } if (Array.isArray(headers[header])) { newHeaders[header] = [] for (const i in headers[header]) { newHeaders[header][i] = headers[header][i].replace(pattern, replacement) } } } return newHeaders }const bufferReplace = (buffer, subBufferPatternToReplace, subBufferReplacement) => { let position = buffer.indexOf(subBufferPatternToReplace) while (position !== -1) { buffer.fill(0, position, position + subBufferPatternToReplace.length) subBufferReplacement.copy( buffer, position, 0, subBufferPatternToReplace.length );// continue search: position = buffer.indexOf(subBufferPatternToReplace, position + subBufferPatternToReplace.length + 1) } }```

Submitted February 09, 2020 at 02:37PM by colorsdontlie

No comments:

Post a Comment