0

I'm trying to get the HTTP headers and the HTTP body from the IncomingMessage instance provided by the async hook HTTPINCOMINGMESSAGE:

import asyncHooks = require("async_hooks");
import { IncomingMessage } from "http";

asyncHooks.createHook({
  init: (
    asyncId: number,
    type: string,
    triggerAsyncId: number,
    req: IncomingMessage
  ) => {
    if (type !== "HTTPINCOMINGMESSAGE") return;

    // Where are the HTTP headers?
    console.log(req.headers); // prints `undefined`

    // How do I get the HTTP body?
    console.log(req.on); // prints `undefined`

    // Yet, `req` is an `IncomingMessage`, the same than following `req`:
    //
    //   const http = require('http');
    //   http.createServer((req, res) => {
    //     // The HTTP request headers are available here!
    //     console.log(req.headers);
    //     // `req` is an `IncomingMessage` as well
    //     console.log(req.constructor===IncomingMessage); // prints `true`
    //   });
    //
    console.log(req.constructor===IncomingMessage); // prints `true`
  },
})
.enable();

I've digged into the Node.js source code, but no luck. I can't find the code that populates req.headers, nor the code that creates the HTTP body stream.

Edit

The overarching goal is to enable Wildcard API to provide a context:

import { context } from '@wildcard-api/server';

// Wildcard saves a cookie on behaf of the Wildcard user, and exposes
// the value saved in the cookie over an ES6 proxy `context`.

function called_somewhere_within_an_http_request_lifecycle() {
  // Wildcard uses Async Hooks to be able to know the HTTP request associated
  // with the following `context.userName` ES6 proxy getter call, and therefore
  // can provide the value `userName` saved in the cookie of the HTTP request.
  console.log(context.userName);
}
brillout
  • 7,804
  • 11
  • 72
  • 84
  • Provide more info about what you're trying to reach. Highly likely your problem has another solution. – Paul Rumkin Jan 11 '21 at 16:54
  • @PaulRumkin I just added the concrete use case in the question. There are solutions that don't need Async Hooks but they are considerably less user friendly. – brillout Jan 11 '21 at 21:53
  • Relatively to console.log here is the paragraph prescribing not to use async operations in async hooks: https://nodejs.org/dist/latest-v14.x/docs/api/async_hooks.html#async_hooks_printing_in_asynchooks_callbacks – Paul Rumkin Jan 11 '21 at 22:15
  • 1
    `called_somewhere_within_an_http_request_lifecycle` is user code, not Wildcard code. So there is no infinite recursion here. – brillout Jan 11 '21 at 23:15

1 Answers1

-1

Async hooks should not be used to modify program behavior. This API has been created to track async resources, e.g. to measure performance. Trying to modify the program in that way leads to undefined behavior and unpredictable errors.

You can monkey-patch NodeJS' HTTP library to do so, but I strongly suggest you to avoid it.

Paul Rumkin
  • 6,737
  • 2
  • 25
  • 35
  • `Async hooks should not be used to modify program behavior` why is that? I don't see the problem. – brillout Jan 11 '21 at 21:55
  • Because 1. It's some kind of monkey-patching and there is a lot of detailed information in the Internet about why it's a bad practice. In short words it make code which has a stable well-known behavior to work in unexpected way and could produce errors where should be none. 2. Async hooks goal is to mark context to track errors and measure performance, it should be lightweight and provide context information. It can be used as replacement for domains. But not as a plugin or extension system. – Paul Rumkin Jan 11 '21 at 22:29
  • Wildcard uses Async Hooks merely to map async calls to the current pending HTTP request. That's the only thing the Wildcard Async Hook code does. There are no side effects here. So I really don't see any potential problem. Wildcard is heavily tested against, and after test I'm ensuring that the async hook code don't leak memory. Performance shouldn't be an issue. – brillout Jan 11 '21 at 23:15
  • 1
    Could you provide complete code example of how you plan to tie request to context? Because I think there could be leaks, e.g. on delayed jobs scheduled to be done after request ends. – Paul Rumkin Jan 12 '21 at 03:56
  • https://github.com/reframejs/wildcard-api/blob/9ac10fcd8a673515c854a143362c9ac4fced6100/src/server/context/async-hook-management.ts#L42-L84 There shouldn't be any leak. And I wouldn't know why async hooks would be a problem here? – brillout Jan 12 '21 at 23:25
  • I posted a new SO question about async hooks performance: https://stackoverflow.com/questions/65702716/performance-implications-of-using-async-hooks – brillout Jan 13 '21 at 13:21
  • Turns out what I want to do is a valid use case that Node.js thought to support. They even released `AsyncLocalStorage` in Node 12 which is pretty close to what I'm doing. FYI, I talked to the author who implemented `AsyncLocalStorage` and they are looking to make the performance hit so small to be a no-brainer. Feel free to contradict me, and I'll un-downvote your answer. Thanks for the disucssion! :-) – brillout Jan 17 '21 at 08:00
  • @brillout I've read the conversation and agree that my answer is partially wrong. Thanks for letting know! – Paul Rumkin Jan 17 '21 at 08:19
  • @brillout I still think there is mighty be issues with scheduled jobs. In a few words there might be several contexts which schedule same job, after that some context could be preserved in memory for a longer time, then developer could expect. – Paul Rumkin Jan 17 '21 at 08:27
  • @Paul_Rumkin Thanks for being honest and acknowledging. I gave you the bounty while maintaining the downvote. Hope you see it as a good a comprise as well. – brillout Jan 17 '21 at 14:34
  • @Paul_Rumkin My plan about `destroy` not being called is to regularly manually gargabe collect; if there is an old context, Wildcard removes it. This will ensure that they are no leaks. This, with a proper `server.timeout`, should work. – brillout Jan 17 '21 at 14:39
  • @brillout Thanks for the bounty points. I understand what you try to do and I want it to be solved in JS. As you probably know there already was such think as domains in nodejs which should solve the same issue, but failed. Due to low predictability of such behavior in complex apps. I'd suggest you to discuss it in nodejs discussions: https://github.com/nodejs/node/discussions Probably it will help you to mitigate edge cases and find solution for this. – Paul Rumkin Jan 21 '21 at 09:04
  • @Paul_Rumkin Makes sense. Actuallly (partially thanks to you) I've developed a fallback plan in case Async Hooks don't work out for Wildcard. I'll try it out and see how it goes; if it fails I'll have the fallback plan :-). – brillout Jan 21 '21 at 14:03