h2o2 icon indicating copy to clipboard operation
h2o2 copied to clipboard

Cannot use a body with DELETE proxy method when passThrough is set to true

Open FrankHassanabad opened this issue 3 years ago • 1 comments

Support plan

  • is this issue currently blocking your project? (no):
  • is this issue affecting a production system? (no):

Context

  • node version: 14.15.3
  • module version with issue: 9.0.2
  • last module version without issue: I don't know but I do know the commit when this was added which is https://github.com/hapijs/h2o2/commit/7988c2dd1774be7541b3b54b4fd3ebf860323e82
  • environment (e.g. node, browser, native): node
  • used with (e.g. hapi application, another framework, standalone, ...): hapi application
  • any other relevant information: This is replicable with a simple example. Although I talk about DELETE having the issue, the other methods of GET, HEAD, DELETE, OPTIONS and CONNECT. probably have the same problem.

What are you trying to achieve or the steps to reproduce?

I am trying to use h2o2 to proxy a DELETE method with a body attached to the DELETE method when using passThrough: true. I get back a Bad Request because h2o2 is deleting the content-length header https://github.com/hapijs/h2o2/blob/master/lib/index.js#L111

The content-length looks to have been removed due to this earlier issue https://github.com/hapijs/h2o2/issues/77 from this commit https://github.com/hapijs/h2o2/commit/7988c2dd1774be7541b3b54b4fd3ebf860323e82

Since h2o2 removes the content-length, there is an assumption that all REST methods such as DELETE, OPTIONS, GET, etc... will default to Transfer-Encoding: chunked when they have a body for NodeJS but it looks like only POST does that per this comment from NodeJS: https://github.com/nodejs/node/issues/19179#issuecomment-370924178

Example code below reproduces this issue with curl commands as well as a workaround for anyone facing this currently who are not using this proxy with bassmaster:

'use strict';

const Hapi = require('@hapi/hapi');
const H2o2 = require('@hapi/h2o2');

/**
 * Add this to a index.js and then:
 * npm init
 * npm install @hapi/h2o2
 * npm install @hapi/hapi
 *
 * node index.js
 *
 * Then run any of the curl commands below to see the bug and workaround:
 * curl -H 'Content-Type: application/json' -d '{ "test": "test" }' -X DELETE http://127.0.0.1:5050/bug
 * curl -H 'Content-Type: application/json' -d '{ "test": "test" }' -X DELETE http://127.0.0.1:5050/work_around
 */
const start = async function() {
  const server = Hapi.server({ address: '127.0.0.1', port: 5050 });
  await server.register(H2o2);

  // non-proxy delete route you can send a body through curl without issues:
  // curl -H 'Content-Type: application/json' -d '{ "test": "test" }' -X DELETE http://127.0.0.1:5050/ 
  server.route({
    method: 'DELETE',
    path: '/',
    handler: function () {
      return Promise.resolve({ status: "ok" });
    }
  });

  // proxy route with the bug where you cannot send a body through curl because of the deletion of content-length
  // curl -H 'Content-Type: application/json' -d '{ "test": "test" }' -X DELETE http://127.0.0.1:5050/bug
  server.route({
    method: 'DELETE',
    path: '/bug',
    handler: {
      proxy: {
        passThrough: true,
        uri: 'http://127.0.0.1:5050',
      }
    }
  });    

  // workaround proxy route where I readd the content-length even though it was deleted. 
  // curl -H 'Content-Type: application/json' -d '{ "test": "test" }' -X DELETE http://127.0.0.1:5050/work_around
  server.route({
    method: 'DELETE',
    path: '/work_around',
    handler: {
      proxy: {
        passThrough: true,
        mapUri: function (request) {
          return {
            headers: { 'content-length': request.headers['content-length'] },
            uri: 'http://127.0.0.1:5050'
          };
        },          
      }
    }
  });    

    await server.start();
    console.log(`Direct connection example: curl -H 'Content-Type: application/json' -d '{ "test": "test" }' -X DELETE http://${server.info.address}:${server.info.port}/`);
    console.log(`bug example: curl -H 'Content-Type: application/json' -d '{ "test": "test" }' -X DELETE http://${server.info.address}:${server.info.port}/bug`);
    console.log(`work around example: curl -H 'Content-Type: application/json' -d '{ "test": "test" }' -X DELETE http://${server.info.address}:${server.info.port}/work_around`);
    console.log(`Server started at: http://${server.info.address}:${server.info.port}`);
}

start();

What was the result you got?

I get back invalid Bad Request from trying to proxy a body through DELETE method when passThrough is true.

What result did you expect?

I should be able to use a body with DELETE through the proxy without workarounds.

FrankHassanabad avatar Jan 05 '21 02:01 FrankHassanabad

I have same problem, but DELETE method with body don't work with/without passThrough property. I think h2o2 ignore body property for delete methods.

RG100-EMUX avatar Oct 07 '22 12:10 RG100-EMUX