fix(client): replace body.cancel() with text() to prevent hanging (#1320)

Co-authored-by: Konstantin Konstantinov <KKonstantinov@users.noreply.github.com>
This commit is contained in:
Grimmer Kang 2026-01-09 19:38:54 +08:00 • committed by GitHub
commit 09a658b1c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 13 additions and 13 deletions

View file

@ -670,12 +670,12 @@ export async function discoverOAuthProtectedResourceMetadata(
});
if (!response || response.status === 404) {
await response?.body?.cancel();
await response?.text?.().catch(() => {});
throw new Error(`Resource server does not implement OAuth 2.0 Protected Resource Metadata.`);
}
if (!response.ok) {
await response.body?.cancel();
await response.text?.().catch(() => {});
throw new Error(`HTTP ${response.status} trying to load well-known OAuth protected resource metadata.`);
}
return OAuthProtectedResourceMetadataSchema.parse(await response.json());
@ -803,12 +803,12 @@ export async function discoverOAuthMetadata(
});
if (!response || response.status === 404) {
await response?.body?.cancel();
await response?.text?.().catch(() => {});
return undefined;
}
if (!response.ok) {
await response.body?.cancel();
await response.text?.().catch(() => {});
throw new Error(`HTTP ${response.status} trying to load well-known OAuth metadata`);
}
@ -918,7 +918,7 @@ export async function discoverAuthorizationServerMetadata(
}
if (!response.ok) {
await response.body?.cancel();
await response.text?.().catch(() => {});
// Continue looking for any 4xx response code.
if (response.status >= 400 && response.status < 500) {
continue; // Try next URL

View file

@ -260,7 +260,7 @@ export class SSEClientTransport implements Transport {
const response = await (this._fetch ?? fetch)(this._endpoint, init);
if (!response.ok) {
const text = await response.text().catch(() => null);
const text = await response.text?.().catch(() => null);
if (response.status === 401 && this._authProvider) {
const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response);
@ -285,7 +285,7 @@ export class SSEClientTransport implements Transport {
}
// Release connection - POST responses don't have content we need
await response.body?.cancel();
await response.text?.().catch(() => {});
} catch (error) {
this.onerror?.(error as Error);
throw error;

View file

@ -235,7 +235,7 @@ export class StreamableHTTPClientTransport implements Transport {
});
if (!response.ok) {
await response.body?.cancel();
await response.text?.().catch(() => {});
if (response.status === 401 && this._authProvider) {
// Need to authenticate
@ -495,7 +495,7 @@ export class StreamableHTTPClientTransport implements Transport {
}
if (!response.ok) {
const text = await response.text().catch(() => null);
const text = await response.text?.().catch(() => null);
if (response.status === 401 && this._authProvider) {
// Prevent infinite recursion when server returns 401 after successful auth
@ -568,7 +568,7 @@ export class StreamableHTTPClientTransport implements Transport {
// If the response is 202 Accepted, there's no body to process
if (response.status === 202) {
await response.body?.cancel();
await response.text?.().catch(() => {});
// if the accepted notification is initialized, we start the SSE stream
// if it's supported by the server
if (isInitializedNotification(message)) {
@ -603,12 +603,12 @@ export class StreamableHTTPClientTransport implements Transport {
this.onmessage?.(msg);
}
} else {
await response.body?.cancel();
await response.text?.().catch(() => {});
throw new StreamableHTTPError(-1, `Unexpected content type: ${contentType}`);
}
} else {
// No requests in message but got 200 OK - still need to release connection
await response.body?.cancel();
await response.text?.().catch(() => {});
}
} catch (error) {
this.onerror?.(error as Error);
@ -647,7 +647,7 @@ export class StreamableHTTPClientTransport implements Transport {
};
const response = await (this._fetch ?? fetch)(this._url, init);
await response.body?.cancel();
await response.text?.().catch(() => {});
// We specifically handle 405 as a valid response according to the spec,
// meaning the server does not support explicit session termination