Skip to content

Windows anonymous pipes do not work with createReadStream #57288

Open
@mgirolet-gl

Description

@mgirolet-gl

Version

Tested with 22.14.0 and 20.18.1

Platform

Microsoft Windows NT 10.0.22000.0 x64

Subsystem

No response

What steps will reproduce the bug?

C# example for the anonymous pipe generator:

using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Threading.Tasks;

namespace DotNetSide
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using var pipeWriter = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable);
            using var pipeReader = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable);

            Process client = new Process();

            client.StartInfo.FileName = "node";
            client.StartInfo.Arguments = "yourscript.js " + pipeWriter.GetClientHandleAsString() + " " + pipeReader.GetClientHandleAsString();
            client.StartInfo.UseShellExecute = false;
            client.Start();

            pipeWriter.DisposeLocalCopyOfClientHandle();
            pipeReader.DisposeLocalCopyOfClientHandle();

            _ = StartReadingAsync(pipeReader);

            using var sw = new StreamWriter(pipeWriter)
            {
                AutoFlush = true
            };

            string message = Console.ReadLine();

            while (message != "exit")
            {
                await sw.WriteAsync(message);
                message = Console.ReadLine();
            }

            client.Close();
        }

        private static async Task StartReadingAsync(AnonymousPipeServerStream pipeReader)
        {
            try
            {
                StreamReader sr = new StreamReader(pipeReader);

                while (true)
                {
                    var message = await sr.ReadLineAsync();

                    if (message != null)
                    {
                        Console.WriteLine(message);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
    }
}

JS code that should be able to read and write from the pipes' file descriptors:

const fs = require('fs');
const reader = fs.createReadStream(null, {fd: parseInt(process.argv[2], 10)});
const writer = fs.createWriteStream(null, {fd: parseInt(process.argv[3], 10)});

reader.on('data', data => writer.write('echo: ' + data + '\n'));

setInterval(()=> {}, 1000 * 60 * 60);

Source, should work according to the documentation and other examples I've seen.

How often does it reproduce? Is there a required condition?

Everytime I try to open an inherited anonymous pipe with NodeJS

What is the expected behavior? Why is that the expected behavior?

'data' events should be emitted when read pipes have new data, and it should be possible to send data to write pipes too.

What do you see instead?

Whenever I bind a callback to a ReadStream created from a pipe's FD:

node:events:496
      throw er; // Unhandled 'error' event
      ^

Error: EBADF: bad file descriptor, close
Emitted 'error' event on ReadStream instance at:
    at emitErrorNT (node:internal/streams/destroy:169:8)
    at emitErrorCloseNT (node:internal/streams/destroy:128:3)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
  errno: -4083,
  code: 'EBADF',
  syscall: 'close'
}

Whenever I try to write to a WriteStream created from a pipe's FD:

node:events:496
      throw er; // Unhandled 'error' event
      ^

Error: EBADF: bad file descriptor, close
Emitted 'error' event on WriteStream instance at:
    at emitErrorNT (node:internal/streams/destroy:169:8)
    at emitErrorCloseNT (node:internal/streams/destroy:128:3)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
  errno: -4083,
  code: 'EBADF',
  syscall: 'close'
}

Additional information

The same Anonymous pipe generator C# program given as an example works well with C++ using the Windows API, so it's not a C# problem:

#include <iostream>
#include <string>
#include <windows.h>
#include <array>

int main(int argc, const char** argv)
{
    std::string pipeHandleString = argv[1];
    int pipeHandleInt = std::stoi(pipeHandleString);

    HANDLE pipeHandle = (void*)pipeHandleInt;

    std::array<char, 256> buffer = {};

    DWORD numberOfBytesRead;

    BOOL result = false;

    while (result = ReadFile(pipeHandle, &buffer, 256, &numberOfBytesRead, nullptr)) {
        buffer[numberOfBytesRead] = '\0';
        std::cout << "echo: " << buffer.data() << std::endl;
    }
}

Of course, it also works with C# clients.

Metadata

Metadata

Assignees

No one assigned

    Labels

    libuvIssues and PRs related to the libuv dependency or the uv binding.streamIssues and PRs related to the stream subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions