undici icon indicating copy to clipboard operation
undici copied to clipboard

DispatchInterceptor with POST request doesn't work

Open battall opened this issue 1 year ago • 2 comments

Bug Description

When a POST request is made using a dispatch interceptor and undici.DecoratorHandler, an error is thrown if the request includes a body. However, if the request does not include a body, no error is thrown.

/Users/battal/Desktop/files/projects/win-bot/node_modules/undici/index.js:109
      Error.captureStackTrace(err, this)
            ^
TypeError: fetch failed
    at fetch (/Users/battal/Desktop/files/projects/win-bot/node_modules/undici/index.js:109:13) {
  cause: TypeError: this.handler.onBodySent is not a function
      at undiciInterceptorTesto.onBodySent (/Users/battal/Desktop/files/projects/win-bot/node_modules/undici/lib/handler/DecoratorHandler.js:33:25)
      at Request.onBodySent (/Users/battal/Desktop/files/projects/win-bot/node_modules/undici/lib/core/request.js:205:24)
      at AsyncWriter.write (/Users/battal/Desktop/files/projects/win-bot/node_modules/undici/lib/client.js:1714:13)
      at writeIterable (/Users/battal/Desktop/files/projects/win-bot/node_modules/undici/lib/client.js:1637:19)
}

Reproducible By

import * as undici from "undici";
import { fetch } from "undici";

const undiciInterceptorHeaders = (dispatch: any) => {
  class undiciInterceptorTesto extends undici.DecoratorHandler {
    onHeaders(statusCode: any, headers: any, resume: any) {
      // @ts-ignore
      return super.onHeaders(statusCode, headers, resume);
    }
  }
  return function InterceptedDispatch(opts: any, handler: any) {
    return dispatch(opts, new undiciInterceptorTesto(handler));
  };
};

let dispatcher = new undici.Agent({
  interceptors: {
    Client: [undiciInterceptorHeaders as unknown as undici.Dispatcher],
  },
});

fetch("https://postman-echo.com/post", {
  method: "POST",
  body: new URLSearchParams("amk=5&asdm=6"),
  // body: new URLSearchParams(""), // works fine
  dispatcher,
})
  .then((res) => res.json())
  .then((body) => {
    console.log(body);
  });

Expected Behavior

it should not throw error

Environment

Macos Sonoma Beta 4 Node v20.5.0

battall avatar Aug 08 '23 13:08 battall

after some fiddling, creating a empty onBodySent method on undici.DecoratorHandler makes things work, not sure why.

const undiciInterceptorHeaders = (dispatch: any) => {
  class undiciInterceptorTesto extends undici.DecoratorHandler {
    onHeaders(statusCode: any, headers: any, resume: any) {
      // @ts-ignore
      return super.onHeaders(statusCode, headers, resume);
    }
    onBodySent() {} // <---- here
  }
  return function InterceptedDispatch(opts: any, handler: any) {
    return dispatch(opts, new undiciInterceptorTesto(handler));
  };
};

looks like on request.js:201, even tho "this[kHandler]" doesnt have a onBodySent function, code sees it like there is one, black magic to me how this happens

onBodySent (chunk) {
    if (this[kHandler].onBodySent) {
      // i edited this line
      console.log(this[kHandler], this[kHandler].onBodySent)
      try {
        this[kHandler].onBodySent(chunk)
      } catch (err) {
        this.onError(err)
      }
    }
  }
undiciInterceptorTesto {
  handler: {
    body: null,
    abort: [Function (anonymous)],
    onConnect: [Function: onConnect],
    onHeaders: [Function: onHeaders],
    onData: [Function: onData],
    onComplete: [Function: onComplete],
    onError: [Function: onError],
    onUpgrade: [Function: onUpgrade]
  }
} [Function: onBodySent]

battall avatar Aug 08 '23 14:08 battall

https://github.com/nodejs/undici/blob/c83b084879fa0bb8e0469d31ec61428ac68160d5/docs/api/Dispatcher.md?plain=1#L214

it would appear as though either the docs are wrong or the code should work without an onBodySent, not entirely sure which is more correct.

KhafraDev avatar Aug 09 '23 02:08 KhafraDev