diff --git a/.changeset/nervous-lies-film.md b/.changeset/nervous-lies-film.md new file mode 100644 index 00000000000..518324d7e49 --- /dev/null +++ b/.changeset/nervous-lies-film.md @@ -0,0 +1,5 @@ +--- +"@smithy/core": patch +--- + +drain stream in httpBindingProtocol with unit output diff --git a/packages/core/src/submodules/protocols/HttpBindingProtocol.spec.ts b/packages/core/src/submodules/protocols/HttpBindingProtocol.spec.ts index a6e37c1b3dd..88b14c737b7 100644 --- a/packages/core/src/submodules/protocols/HttpBindingProtocol.spec.ts +++ b/packages/core/src/submodules/protocols/HttpBindingProtocol.spec.ts @@ -1,4 +1,5 @@ import { op } from "@smithy/core/schema"; +import { streamCollector } from "@smithy/node-http-handler"; import { HttpResponse } from "@smithy/protocol-http"; import type { $Schema, @@ -20,6 +21,7 @@ import type { TimestampEpochSecondsSchema, } from "@smithy/types"; import { parseUrl } from "@smithy/url-parser/src"; +import { Readable } from "node:stream"; import { describe, expect, test as it } from "vitest"; import { HttpBindingProtocol } from "./HttpBindingProtocol"; @@ -244,4 +246,42 @@ describe(HttpBindingProtocol.name, () => { expect(request.path).toMatch(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/); expect(request.headers?.["header-token"]).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/); }); + + it("should discard response bodies for Unit operation outputs, making no attempt to parse them", async () => { + const protocol = new StringRestProtocol(); + let streamProgress = 0; + const response = await protocol.deserializeResponse( + op("", "", {}, "unit", "unit"), + { + streamCollector: streamCollector, + } as any, + new HttpResponse({ + statusCode: 200, + headers: {}, + body: Readable.from({ + async *[Symbol.asyncIterator]() { + yield "@"; + streamProgress = 25; + yield "#"; + streamProgress = 50; + yield "$"; + streamProgress = 75; + yield "%"; + streamProgress = 100; + }, + }), + }) + ); + + expect(response).toEqual({ + $metadata: { + cfId: undefined, + extendedRequestId: undefined, + httpStatusCode: 200, + requestId: undefined, + }, + }); + + expect(streamProgress).toBe(100); + }); }); diff --git a/packages/core/src/submodules/protocols/HttpBindingProtocol.ts b/packages/core/src/submodules/protocols/HttpBindingProtocol.ts index 77c423d18fe..4badec23e96 100644 --- a/packages/core/src/submodules/protocols/HttpBindingProtocol.ts +++ b/packages/core/src/submodules/protocols/HttpBindingProtocol.ts @@ -229,6 +229,8 @@ export abstract class HttpBindingProtocol extends HttpProtocol { } // Due to Smithy validation, we can assume that the members with no HTTP // bindings DO NOT contain an event stream. + } else if (nonHttpBindingMembers.discardResponseBody) { + await collectBody(response.body, context); } dataObject.$metadata = this.deserializeMetadata(response); @@ -247,20 +249,20 @@ export abstract class HttpBindingProtocol extends HttpProtocol { response: IHttpResponse, headerBindings: Set, dataObject: any - ): Promise; + ): Promise; protected async deserializeHttpMessage( schema: Schema, context: HandlerExecutionContext & SerdeFunctions, response: IHttpResponse, dataObject: any - ): Promise; + ): Promise; protected async deserializeHttpMessage( schema: Schema, context: HandlerExecutionContext & SerdeFunctions, response: IHttpResponse, arg4: unknown, arg5?: unknown - ): Promise { + ): Promise { let dataObject: any; if (arg4 instanceof Set) { dataObject = arg5; @@ -268,14 +270,16 @@ export abstract class HttpBindingProtocol extends HttpProtocol { dataObject = arg4; } + let discardResponseBody = true; const deserializer = this.deserializer; const ns = NormalizedSchema.of(schema); - const nonHttpBindingMembers = [] as string[]; + const nonHttpBindingMembers = [] as string[] & { discardResponseBody?: boolean }; for (const [memberName, memberSchema] of ns.structIterator()) { const memberTraits = memberSchema.getMemberTraits(); if (memberTraits.httpPayload) { + discardResponseBody = false; const isStreaming = memberSchema.isStreaming(); if (isStreaming) { const isEventStream = memberSchema.isStructSchema(); @@ -339,6 +343,7 @@ export abstract class HttpBindingProtocol extends HttpProtocol { nonHttpBindingMembers.push(memberName); } } + nonHttpBindingMembers.discardResponseBody = discardResponseBody; return nonHttpBindingMembers; } }