node-http-mitm-proxy icon indicating copy to clipboard operation
node-http-mitm-proxy copied to clipboard

What is the good way to cancel the default behavior?

Open felicienfrancois opened this issue 8 years ago • 6 comments

Examples:

proxy.onRequest(function(ctx, callback) {
  // I want to answer the request myself, without making a request to a server  

  // callback() would create the request to the server with the ctx.proxyToServerRequestOptions
  // callback();

  // callback("Any error") would trigger a "Proxy error"
  // callback("Any error");

  // No callback may not be clean and may have side effect, like not calling next handlers
});
proxy.onResponseData(function(ctx, chunk, callback) {
  // I want to collect all chunks without sending it to the client and then send something to the client at the ResponseEnd

  // callback(chunk) would send the chunk to the client
  // callback(null, chunk);

  // callback(null, null) or callback(null, []) may break or at least send the headers and is not clean at all
  // callback(null, null);
  // callback(null, []);

  // callback("Any error") would trigger a "Proxy error"
  // callback("Any error");

  // No callback may not be clean and may have side effect, like not calling next handlers and may lead to no responseEnd event
});

Maybe the best way is to rewrite the API with a switch to prevent default operation after the handler execution. Maybe the most transparent and intuitive way would be to test the second parameter of the callback and cancel default behavior if it is equals to false or null (but not undefined)

felicienfrancois avatar Nov 18 '15 23:11 felicienfrancois

Since https://github.com/joeferner/node-http-mitm-proxy/commit/0e40d2d550e3d815f6ef516086a34a256a398400 the onResponseData callback could be called with null chunk without breaking.

felicienfrancois avatar Nov 19 '15 15:11 felicienfrancois

@felicienfrancois Hm, not sure I understand the proposed solution to this. I have a simple use case, I want to cache requests in memory after they have been requested one time. I'm trying to do something like this:

var Proxy = require('http-mitm-proxy')
var proxy = Proxy()

var port = 8081

var cache = {}
var chunks = []

proxy.onError(function (ctx, err) {
  console.error('proxy error:', err)
})


proxy.onRequest(function (ctx, callback) {
  chunks = []
  callback()
})

proxy.onResponseData(function (ctx, chunk, dataCallback) {
  chunks.push(chunk)
  return dataCallback(null, chunk)
})

proxy.onResponseEnd(function (ctx, endCallback) {
  const req = ctx.clientToProxyRequest
  const url = req.url
  const host = req.headers.host
  cache[host + url] = chunks
  return endCallback(null, chunks)
})

proxy.listen({ port: port })
console.log('listening on ' + port)

But I'm struggling to understand how I can cancel out the request and just return the cached data. Any pointers on how I can achieve that?

victorb avatar Nov 25 '15 23:11 victorb

As always, finding the answer five seconds after asking the question. Solved it by doing something like this:

proxy.onRequest(function (ctx, callback) {
  const req = ctx.clientToProxyRequest
  const url = req.url
  const host = req.headers.host
  if (cache[host + url] !== undefined) {
    ctx.proxyToClientResponse.end(cache[host + url].join())
    return
  }
  chunks = []
  callback()
})

victorb avatar Nov 25 '15 23:11 victorb

@VictorBjelkholm Yes, that's it. I would just warn you that several requests may be handled in parallel, so having a global chunks array may not be the good solution (it can be erased before the end of the response data by another request).

Instead, you should store the chunks in the context (which sticks to one request):

ctx.chunks = []

Or define the chunks array in the context of each request like in this example: https://github.com/joeferner/node-http-mitm-proxy/blob/master/test/processFullResponseBody.js

felicienfrancois avatar Nov 26 '15 09:11 felicienfrancois

Hah, yeah, learned that the hard way yesterday and did exactly what you said! Thanks for sharing that here though.

On Thu, Nov 26, 2015 at 10:09 AM Félicien FRANCOIS [email protected] wrote:

@VictorBjelkholm https://github.com/VictorBjelkholm Yes, that's it. I would just warn you that several requests may be handled in parallel, so having a global chunks array may not be the good solution (it can be erased before the end of the response data by another request).

Instead, you should store the chunks in the context (which sticks to one request):

ctx.chunks = []

— Reply to this email directly or view it on GitHub https://github.com/joeferner/node-http-mitm-proxy/issues/24#issuecomment-159853748 .

victorb avatar Nov 26 '15 09:11 victorb

@VictorBjelkholm

Or you can try ctx.proxyToClientResponse.writeHead(504, 'Proxy Error');

Also When connection is not stopped onResponseData is still trigged and all data are loaded

So try to download 5Mb file you will see that onResponseEnd is executed even if you cancel connection, and data are still transfer, client don't get data but proxy still transfer it

__ Best solution will be to close socket connection after error, not only on this example but on time-out an on error

My TMP solution:

ctx.proxyToClientResponse.writeHead(504, 'Proxy Error'); ctx.proxyToClientResponse.end(''); ctx.serverToProxyResponse.complete = true;

ghost avatar Nov 03 '16 18:11 ghost