in packages/dubbo-node/src/node-universal-client.ts [275:343]
function h2Request(
sentinel: Sentinel,
sm: NodeHttp2ClientSessionManager,
url: string,
method: string,
headers: http2.OutgoingHttpHeaders,
options: Omit<http2.ClientSessionRequestOptions, "signal">,
onStream: (stream: http2.ClientHttp2Stream) => void
): void {
const requestUrl = new URL(url, sm.authority);
if (requestUrl.origin !== sm.authority) {
const message = `cannot make a request to ${requestUrl.origin}: the http2 session is connected to ${sm.authority}`;
sentinel.reject(new ConnectError(message, Code.Internal));
return;
}
sm.request(method, requestUrl.pathname + requestUrl.search, headers, {}).then(
(stream) => {
stream.session.on("error", sentinel.reject);
sentinel
.catch((reason) => {
if (stream.closed) {
return;
}
// Node.js http2 streams that are aborted via an AbortSignal close with
// an RST_STREAM with code INTERNAL_ERROR.
// To comply with the mapping between gRPC and HTTP/2 codes, we need to
// close with code CANCEL.
// See https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#errors
// See https://www.rfc-editor.org/rfc/rfc7540#section-7
const rstCode =
reason instanceof ConnectError && reason.code == Code.Canceled
? H2Code.CANCEL
: H2Code.INTERNAL_ERROR;
return new Promise<void>((resolve) => stream.close(rstCode, resolve));
})
.finally(() => {
stream.session.off("error", sentinel.reject);
})
.catch(() => {
// We intentionally swallow sentinel rejection - errors must
// propagate through the request or response iterables.
});
stream.on("error", function h2StreamError(e: unknown) {
if (
stream.writableEnded &&
unwrapNodeErrorChain(e)
.map(getNodeErrorProps)
.some((p) => p.code == "ERR_STREAM_WRITE_AFTER_END")
) {
return;
}
sentinel.reject(e);
});
stream.on("close", function h2StreamClose() {
const err = connectErrorFromH2ResetCode(stream.rstCode);
if (err) {
sentinel.reject(err);
}
});
onStream(stream);
},
(reason) => {
sentinel.reject(reason);
}
);
}