c++node.jsnode.js-addon

how to send data with Readable stream from NodeJS to C++


I'm currently sending data from C++ to NodeJS passing a NodeJS readableStream.push.bind(readableStream) to the C++ binding code and writing onto the stream from C++ using

Napi::Function push = info[0].As<Napi::Function>();
Napi::ThreadSafeFunction push_safe = Napi::ThreadSafeFunction::New(env, push, "push", 0, 1);
push_safe.BlockingCall(data, custom_callback);

Now I want to be able to send data to the C++ binding, using a NodeJS Stream, but I can't figure out if C++ can accept a Stream, and calling a C++ version of .on('data', (data) => {}) on it, something like:

example NodeJS:

const read_stream = fs.createReadStream("./random.bin");
mybinding.myfunc(read_stream);

example C++:

Value myfunc(const CallbackInfo& info) {
  Env env = info.Env();

  auto on_data_lambda = [](data)
  {
    std::cout << "new data arrived from nodejs stream: " << data << std::endl;
  }

  /*some magic*/
}



Solution

  • Implementing this with all the bells and whistles is not trivial. As always, the most complex part is handling all the errors.

    I suggest you go check in Node.js sources src/js_stream.cc which contains something similar and you will understand why a good/fast implementation is difficult.

    If you want to keep it simple, you can start by creating a JS reference to your C++ on_data function.

    Then you can register this function as an event receiver.

    Napi::Value ondata(const Napi::CallbackInfo &info) {
      if (!info[0].IsObject()) printf("Something strange received");
      else printf("received %s\n", info[0].ToString().Utf8Value().c_str());
      return info.Env().Undefined();
    }
    
    Napi::Value stream(const Napi::CallbackInfo &info) {
      Napi::Object stream = info[0].ToObject();
      Napi::Function on_data_ref = Napi::Function::New(info.Env(), ondata, "on_data");
      Napi::Value onValue = stream.Get("on");
      if (!onValue.IsFunction()) throw Napi::Error::New(info.Env(), "This is not an event emitter");
      Napi::Function on = onValue.As<Napi::Function>();
      on.Call(stream, {Napi::String::New(info.Env(), "data"), on_data_ref});
      return info.Env().Undefined();
    }
    

    If you do this, data will start flowing. But you still have to do correctly the error handling and everything else.