* Immediately destroys the stream. Any queued data is discarded. If * an error is given, the closed promise will be rejected with that * error. If no error is given, the closed promise will be resolved. * When destroying with an error, RESET_STREAM and/or STOP_SENDING * are emitted to th
(error, options = kEmptyObject)
| 1960 | * @param {QuicStreamDestroyOptions} [options] |
| 1961 | */ |
| 1962 | destroy(error, options = kEmptyObject) { |
| 1963 | assertIsQuicStream(this); |
| 1964 | const inner = this.#inner; |
| 1965 | // Two distinct guards: |
| 1966 | // * `#destroying` flips synchronously here so any re-entrant call |
| 1967 | // from inside this method's user callbacks hits the guard and |
| 1968 | // returns immediately. |
| 1969 | // * `destroyed` (i.e. `#handle === undefined`) catches the case |
| 1970 | // where the C++ side already finished cleanup via the |
| 1971 | // `onStreamClose -> [kFinishClose]` path - which does NOT go |
| 1972 | // through `destroy()` and therefore never sets `#destroying`. |
| 1973 | // `[kFinishClose]` clears `#handle` at the end of its work. |
| 1974 | if (inner.destroying || this.destroyed) return; |
| 1975 | // Validate options up front so a malformed `options` argument |
| 1976 | // throws before any side effects (mutating `#destroying`, |
| 1977 | // emitting wire frames, invoking `onerror`, settling the closed |
| 1978 | // promise). The caller may retry with valid options. |
| 1979 | validateObject(options, 'options'); |
| 1980 | const { code: optionCode, reason } = options; |
| 1981 | if (optionCode !== undefined && |
| 1982 | typeof optionCode !== 'bigint' && |
| 1983 | typeof optionCode !== 'number') { |
| 1984 | throw new ERR_INVALID_ARG_TYPE('options.code', |
| 1985 | ['bigint', 'number'], optionCode); |
| 1986 | } |
| 1987 | if (reason !== undefined) { |
| 1988 | validateString(reason, 'options.reason'); |
| 1989 | } |
| 1990 | inner.destroying = true; |
| 1991 | // Resolve the wire error code for any RESET_STREAM / STOP_SENDING |
| 1992 | // frames emitted below. |
| 1993 | let abortCode; |
| 1994 | if (optionCode !== undefined) { |
| 1995 | abortCode = BigInt(optionCode); |
| 1996 | } else if (error !== undefined) { |
| 1997 | abortCode = QuicError.isQuicError(error) ? |
| 1998 | error.errorCode : |
| 1999 | getQuicSessionState(inner.session).internalErrorCode; |
| 2000 | } |
| 2001 | // When destroying with an error, ensure the peer stops sending |
| 2002 | // data we are about to discard by emitting STOP_SENDING. The |
| 2003 | // condition gates the emission to error-path destroys with a |
| 2004 | // still-open readable side. The C++ state.readEnded flag is |
| 2005 | // authoritative -- it is set for locally-initiated uni streams |
| 2006 | // (which have no readable side) and when reading completes. |
| 2007 | if (abortCode !== undefined && |
| 2008 | !inner.state.readEnded) { |
| 2009 | this.#handle.stopSending(abortCode); |
| 2010 | } |
| 2011 | // When destroying with an error, ensure the peer learns about |
| 2012 | // it via RESET_STREAM. The writer.fail path inside [kFinishClose] |
| 2013 | // emits RESET_STREAM only when a writer has been created; |
| 2014 | // streams that destroy without ever accessing stream.writer |
| 2015 | // (e.g. used setBody or never wrote at all) need an explicit |
| 2016 | // RESET_STREAM here so the write side does not dangle on the |
| 2017 | // wire. The C++ state.writeEnded flag is authoritative. |
| 2018 | if (abortCode !== undefined && |
| 2019 | inner.writer === undefined && |
no test coverage detected