joeferner / node-http-mitm-proxy

HTTP Man In The Middle (MITM) Proxy
654 stars 162 forks source link

What is the good way to cancel the default behavior? #24

Open felicienfrancois opened 8 years ago

felicienfrancois commented 8 years ago

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 commented 8 years ago

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

victorb commented 8 years ago

@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 commented 8 years ago

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()
})
felicienfrancois commented 8 years ago

@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

victorb commented 8 years ago

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 notifications@github.com 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 .

ghost commented 7 years ago

@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;