Skip to content

Migration from process.binding  #22064

Description

@jdalton

With #22004 landed to doc deprecate process.binding I was encouraged to open an issue related to my use of it. Now is a great time to starting examining what from process.binding can be exposed in a user-friendly way.

In my own experience I've attempted to use (if available and there were no user-facing options) these bindings. My usage fits into these categories

  1. chrome inspector wiring
  2. command-line checks
  3. custom error stack decoration
  4. custom inspection

The API run down as follows:

  • process.binding("config")

    • experimentalREPLAwait (command line checks)
    • experimentalWorker (command line checks)
    • exposeInternals (command line checks)
    • preserveSymlinks (command line checks)
    • preserveSymlinksMain (command line checks)
  • process.binding("inspector")

    • callAndPauseOnStart (inspector wiring; other utils like ndb use this too)
    • consoleCall (inspector wiring)
    • open (only existence checks, much like Node does)
  • process.binding("util")

    • decorated_private_symbol (error stack decoration)
    • getProxyDetails (custom inspection)
    • setHiddenValue (error stack decoration)
    • safeGetenv

For chrome inspector wiring there has been some work to expose things like originalConsole (#21659), but more in this area is needed.

For command-line checks the answer may be to use process.execArgv, but a more config like object form would be handy.

For custom error stack decoration there is early feelers in #21958.

Custom inspection helpers like getProxyDetails/getPromiseDetails and safeGetenv have nothing simmering at the moment.

Updated:

I crossed through API that have user-facing options.

Update:

Replaced process.binding("inspector").open inferences with process.config.variables.v8_enable_inspector checks.

Activity

  1. devsnek commented on Aug 1, 2018

    @devsnek
    Member

    i have reservations about exposing anything on process.binding('util') publicly... stack decoration shouldn't have a node-specific solution and promise and proxy details can't be equally represented by the engines that might want to run node.

    taking a step back, i think we should look at how the things these apis enabled would be implemented if these apis had never existed, instead of straight-up replacements.

  2. jdalton commented on Aug 1, 2018

    @jdalton
    MemberAuthor

    @devsnek

    stack decoration shouldn't have a node-specific solution

    Cool. As long as there's some way, it would be nice. (#21958 is a step to providing a way)

    and promise and proxy details can't be equally represented by the engines that might want to run node.

    Right, Chakra had to do extra work to support those.

    taking a step back, i think we should look at how the things these apis enabled would be implemented if these apis had never existed, instead of straight-up replacements.

    👏 That's a great exercise!

  3. mcollina commented on Aug 1, 2018

    @mcollina
    SponsorMember

    consoleCall (inspector wiring)

    @jdalton have you seen https://nodejs.org/api/inspector.html#inspector_inspector_console to print messages into the inspector console?

  4. jdalton commented on Aug 1, 2018

    @jdalton
    MemberAuthor

    @mcollina

    have you seen https://nodejs.org/api/inspector.html#inspector_inspector_console to print messages into the inspector console?

    Yes. I believe you're saying I can create my own form of consoleCall that pipes to Node's console and the inspector console with the exposed originalConsole. 👍

  5. mcollina commented on Aug 1, 2018

    @mcollina
    SponsorMember

    It would be great if you could confirm that, so we can rule out one item from that list.

  6. jdalton commented on Aug 2, 2018

    @jdalton
    MemberAuthor

    @mcollina

    It would be great if you could confirm that, so we can rule out one item from that list.

    Confirmed. Looks like consoleCall could be replaced with a plain JS function.

  7. alexkozy commented on Aug 2, 2018

    @alexkozy
    Member

    I am not sure that I am not missing some context, but the vital part of console.log behavior at least on inspector side - it is providing stack trace that points to the place where console.log was called. I am worried that with plain JS function we will get JS stack frame that contains location inside this function.

  8. jdalton commented on Aug 2, 2018

    @jdalton
    MemberAuthor

    @ak239

    I am worried that with plain JS function we will get JS stack frame that contains location inside this function.

    Would you be up for providing a small repro case so I can test it out and see what's what?

  9. devsnek commented on Aug 2, 2018

    @devsnek
    Member

    @jdalton

    wrappedConsole[key] = consoleCall.bind(wrappedConsole,
    originalConsole[key],
    wrappedConsole[key],
    config);

    right here the two functions are wrapped to be called by a native function (consoleCall is c++)

    if (InspectorEnabled(env)) {
    Local<Value> inspector_method = info[0];
    CHECK(inspector_method->IsFunction());
    Local<Value> config_value = info[2];
    CHECK(config_value->IsObject());
    Local<Object> config_object = config_value.As<Object>();
    Local<String> in_call_key = FIXED_ONE_BYTE_STRING(isolate, "in_call");
    if (!config_object->Has(context, in_call_key).FromMaybe(false)) {
    CHECK(config_object->Set(context,
    in_call_key,
    v8::True(isolate)).FromJust());
    CHECK(!inspector_method.As<Function>()->Call(context,
    info.Holder(),
    call_args.size(),
    call_args.data()).IsEmpty());
    }
    CHECK(config_object->Delete(context, in_call_key).FromJust());
    }
    Local<Value> node_method = info[1];
    CHECK(node_method->IsFunction());
    node_method.As<Function>()->Call(context,
    info.Holder(),
    call_args.size(),
    call_args.data()).FromMaybe(Local<Value>());

  10. jdalton commented on Aug 2, 2018

    @jdalton
    MemberAuthor

    @devsnek I wasn't asking the whereabouts of consoleCall in Node core. I was asking for @ak239 to provide an example that showed the potential issue.

  11. devsnek commented on Aug 2, 2018

    @devsnek
    Member

    @jdalton you can try replacing the c++ consoleCall with this if you wanna play around with it.

    // because this is written in js it will be added as an additional frame
    // which doesn't show up in the c++ version
    function consoleCall(inspector_method, node_method, config_object, ...args) {
      const in_call_key = 'in_call';
      if (!(in_call_key in config_object)) {
        config_object[in_call_key] = true;
        inspector_method(...args);
        delete config_object[in_call_key];
      }
      node_method(...args);
    }
  12. jdalton commented on Aug 2, 2018

    @jdalton
    MemberAuthor

    @devsnek

    you can try replacing the c++ consoleCall with this if you wanna play around with it.

    Thanks! I've got something on my end as well. I'm just looking for a test case to run it against is all.

  13. TimothyGu commented on Aug 2, 2018

    @TimothyGu
    Member

    @jdalton

    Screenshot of Chrome Dev Tools running the provided test script, with red highlighting on the console.error line in useCPPWrapped but in consoleCall when it's called from useJSWrapped; also displays the full stack trace in the console where consoleCall is the top frame for useJSWrapped, but useCPPWrapped is the top frame for useCPPWrapped

    Test script:

    'use strict';
    
    const { Console } = require('console');
    const originalConsole = require('inspector').console;
    
    const config = {};
    
    // Pretty straightforward port of InspectorConsoleCall() to JavaScript.
    function consoleCall(inspectorMethod, nodeMethod, config, ...args) {
    	// Assume inspector is always on.
    	if (!('in_call' in config)) {
    		config.in_call = true;
    		inspectorMethod.apply(this, args);
    	}
    	delete config.in_call;
    
    	nodeMethod.apply(this, args);
    }
    
    const jsWrappedConsole = new Console(process.stdout);
    jsWrappedConsole.error = consoleCall.bind(jsWrappedConsole, originalConsole.error, jsWrappedConsole.error, config);
    
    function usesCPPWrapped() {
    	console.error('bleh');
    }
    
    function usesJSWrapped() {
    	jsWrappedConsole.error('bleh');
    }
    
    usesCPPWrapped();
    usesJSWrapped();

    Note how console.error's stack is at the right place, but jsWrappedConsole.error's error symbol is always on the inspectorMethod.apply line.

    Hope this helps.

  14. jdalton commented on Aug 2, 2018

    @jdalton
    MemberAuthor

    Ah yep okay, since console.error doesn't actually throw I can't get creative with Error.captureStackTrace either.

  15. devsnek commented on Aug 2, 2018

    @devsnek
    Member

    @jdalton you can do builtinLibs.includes('inspector') for that open method which would be much more idiomatic anyway.

    if my changes to error stack decoration ever land there will be no need for decorated_private_symbol, setHiddenValue, and getHiddenValue.

    i would recommend creating os.getenv and os.setenv as a replacement for safeGetenv.

    i would strongly disagree with exposing proxy inspection as an api and i will to the best of my ability block exposing promise inspection

  16. 13 remaining items

  17. stevenvachon commented on Sep 25, 2019

    @stevenvachon

    Is #3591 related to this at all? It still has not been resolved.

  18. jasnell commented on Sep 25, 2019

    @jasnell
    Member

    To be honest, I don't really know @stevenvachon, it's been a while since I've looked that issue over.

  19. gajus commented on Sep 25, 2019

    @gajus

    As @bnoordhuis points out in the thread in #21509, the current behavior is intentional as to avoid a very real security vulnerability, and it is working as intended.

    What is the security vulnerability? He doesn't state in that comment the implications.

    What is the workaround for when a remote service returns invalid response, such as in a scenario I describe in #22064 (comment) ?

  20. jasnell commented on Sep 25, 2019

    @jasnell
    Member

    Strict handling of invalid characters is necessary to prevent a variety of request smuggling and response splitting style attacks.

    The correct workaround is to try to get the broken remote service fixed. Not sure what else we can do now that the legacy parser has been removed.

  21. devsnek commented on Sep 25, 2019

    @devsnek
    Member

    was the question here about loosening how llhttp works or allowing the parser to be overridden? the latter seems like a reasonable request to me.

  22. gajus commented on Sep 26, 2019

    @gajus

    The correct workaround is to try to get the broken remote service fixed. Not sure what else we can do now that the legacy parser has been removed.

    That is an insane position to take.

    I run a large data aggregation network. There are literally hundreds of websites misbehaving even within our relatively restricted domain operation.

    This change literally makes Node.js not usable for us.

  23. addaleax commented on Sep 26, 2019

    @addaleax
    Member

    @gajus Although I see the relationship, I feel like this is turning into a separate discussion from what the issue is about, and I’d suggest opening a new issue, ideally with a description of what exact “features” you need from the newer parser that the old one had.

  24. bnoordhuis commented on Sep 26, 2019

    @bnoordhuis
    Member

    allowing the parser to be overridden

    This has been discussed in the past (repeatedly and at length) and the answer has always been 'no' because it turns too many implementation details into frozen-forever public API.

    Custom JS parsers exist (link). You could even WASM-ify http-parser if you want bug-for-bug compatibility. I'm its maintainer and I'd be open to that.

  25. gajus commented on Sep 26, 2019

    @gajus

    @devsnek overriding process.binding('http_parser').HTTPParser doesn't work in Node v12, though – does it?

  26. gajus commented on Nov 21, 2019

    @gajus

    The HTTPParser override does not work in v12 and above.

    I have raised a new issue #30573.

  27. added
    metaIssues and PRs related to the general management of the project.
    processIssues and PRs related to the process subsystem.
    and removed
    metaIssues and PRs related to the general management of the project.
    on Dec 11, 2019
  28. jasnell commented on Jun 26, 2020

    @jasnell
    Member

    For all internal uses, we have migrated away from using process.binding(). I believe we can close this issue now, although there is the follow on question of when/how we can deprecate process.binding()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    processIssues and PRs related to the process subsystem.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions