Repository: node
Issue: `allowHalfOpen` option from `net` has no effect due to `autoDestroy = true`
Labels: net

### Version

v20.5.0 and v18.17.0 and more

### Platform

`Linux futpib-desktop 6.1.39-3-lts #1 SMP PREEMPT_DYNAMIC Wed, 02 Aug 2023 10:12:55 +0000 x86_64 GNU/Linux`

### Subsystem

net

### What steps will reproduce the bug?

```js
// net-allowHalfOpen.js

const net = require('net');

async function main() {
    let resolveServerSocket;
    const serverSocketPromise = new Promise((resolve, reject) => {
        resolveServerSocket = resolve;
    });

    const server = net.createServer({
        allowHalfOpen: true,
    }, (socket) => {
        resolveServerSocket(socket);
    }).listen();

    const clientSocket = await new Promise(resolve => {
        const socket = net.createConnection({
            allowHalfOpen: true,
            port: server.address().port,
            host: server.address().address,
        }, () => {
            resolve(socket);
        });
    });

    const serverSocket = await serverSocketPromise;

    await new Promise((resolve, reject) => {
        clientSocket.write('data written to client socket', (error) => {
            if (error) {
                reject(error);
            } else {
                resolve();
            }
        });
    });

    await new Promise(resolve => {
        clientSocket.end(resolve);
    });

    for await (const chunk of serverSocket) {
        console.log('read from server socket:', chunk.toString());
    }

    console.log('server socket ended');

    if (serverSocket.destroyed) {
        console.error('server socket is already destroyed 😿');
    }

    await new Promise((resolve, reject) => {
        serverSocket.write('data written to server socket', (error) => {
            if (error) {
                reject(error);
            } else {
                resolve();
            }
        });
    });

    await new Promise(resolve => {
        serverSocket.end(resolve);
    });

    for await (const chunk of clientSocket) {
        console.log('read from client socket:', chunk.toString());
    }

    console.log('client socket ended');

    server.close();
}

main();
```

```
$ node net-allowHalfOpen.js

read from server socket: data written to client socket
server socket ended
server socket is already destroyed 😿
node:internal/errors:496
    ErrorCaptureStackTrace(err);
    ^

Error [ERR_STREAM_DESTROYED]: Cannot call write after a stream was destroyed
    at new NodeError (node:internal/errors:405:5)
    at _write (node:internal/streams/writable:331:11)
    at Writable.write (node:internal/streams/writable:344:10)
    at /home/futpib/code/tmp/net-allowHalfOpen.js:52:22
    at new Promise (<anonymous>)
    at main (/home/futpib/code/tmp/net-allowHalfOpen.js:51:11)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  code: 'ERR_STREAM_DESTROYED'
}

Node.js v20.5.0
```

### How often does it reproduce? Is there a required condition?

Reproduces every time.

### What is the expected behavior? Why is that the expected behavior?

Patch the example above with this workaround/hack to get what I consider to be expected behaviour:
```diff
--- net-allowHalfOpen.js        2023-08-09 00:52:47.771904675 +0400
+++ net-allowHalfOpen-hack.js   2023-08-09 00:52:42.848526177 +0400
@@ -24,6 +24,11 @@
 
     const serverSocket = await serverSocketPromise;
 
+    clientSocket._writableState.autoDestroy = false;
+    clientSocket._readableState.autoDestroy = false;
+    serverSocket._writableState.autoDestroy = false;
+    serverSocket._readableState.autoDestroy = false;
+
     await new Promise((resolve, reject) => {
         clientSocket.write('data written to client socket', (error) => {
             if (error) {

```

<details>
  <summary>Full file after the hack</summary>

```js
// net-allowHalfOpen-hack.js

const net = require('net');

async function main() {
    let resolveServerSocket;
    const serverSocketPromise = new Promise((resolve, reject) => {
        resolveServerSocket = resolve;
    });

    const server = net.createServer({
        allowHalfOpen: true,
    }, (socket) => {
        resolveServerSocket(socket);
    }).listen();

    const clientSocket = await new Promise(resolve => {
        const socket = net.createConnection({
            allowHalfOpen: true,
            port: server.address().port,
            host: server.address().address,
        }, () => {
            resolve(socket);
        });
    });

    const serverSocket = await serverSocketPromise;

    clientSocket._writableState.autoDestroy = false;
    clientSocket._readableState.autoDestroy = false;
    serverSocket._writableState.autoDestroy = false;
    serverSocket._readableState.autoDestroy = false;

    await new Promise((resolve, reject) => {
        clientSocket.write('data written to client socket', (error) => {
            if (error) {
                reject(error);
            } else {
                resolve();
            }
        });
    });

    await new Promise(resolve => {
        clientSocket.end(resolve);
    });

    for await (const chunk of serverSocket) {
        console.log('read from server socket:', chunk.toString());
    }

    console.log('server socket ended');

    if (serverSocket.destroyed) {
        console.error('server socket is already destroyed 😿');
    }

    await new Promise((resolve, reject) => {
        serverSocket.write('data written to server socket', (error) => {
            if (error) {
                reject(error);
            } else {
                resolve();
            }
        });
    });

    await new Promise(resolve => {
        serverSocket.end(resolve);
    });

    for await (const chunk of clientSocket) {
        console.log('read from client socket:', chunk.toString());
    }

    console.log('client socket ended');

    server.close();
}

main();
```

</details>

```
$ node net-allowHalfOpen-hack.js

read from server socket: data written to client socket
server socket ended
read from client socket: data written to server socket
client socket ended
```

This is the expected behavior because this allows for half-open sockets to actually be used, otherwise there is no point in having a `allowHalfOpen` option, it just does not work. More precisely, the socket is closed (destroyed) when only one of it's directions has ended. 

### What do you see instead?

The server socket is closed regardless of `allowHalfOpen` option due to `autoDestroy` option hard set to `true`.

https://github.com/nodejs/node/blob/6432060c7ce1670467c230d2c0c925465dd311f8/lib/net.js#L405

```
$ node net-allowHalfOpen.js

read from server socket: data written to client socket
server socket ended
server socket is already destroyed 😿
node:internal/errors:496
    ErrorCaptureStackTrace(err);
    ^

Error [ERR_STREAM_DESTROYED]: Cannot call write after a stream was destroyed
    at new NodeError (node:internal/errors:405:5)
    at _write (node:internal/streams/writable:331:11)
    at Writable.write (node:internal/streams/writable:344:10)
    at /home/futpib/code/tmp/net-allowHalfOpen.js:52:22
    at new Promise (<anonymous>)
    at main (/home/futpib/code/tmp/net-allowHalfOpen.js:51:11)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  code: 'ERR_STREAM_DESTROYED'
}

Node.js v20.5.0
```

### Additional information

_No response_