Skip to content

Commit 77c149f

Browse files
authored
fix(core/protocols): drain Unit response streams in HttpBindingProtocol (#1782)
1 parent ca0ee3f commit 77c149f

File tree

3 files changed

+54
-4
lines changed

3 files changed

+54
-4
lines changed

.changeset/nervous-lies-film.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@smithy/core": patch
3+
---
4+
5+
drain stream in httpBindingProtocol with unit output

packages/core/src/submodules/protocols/HttpBindingProtocol.spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { op } from "@smithy/core/schema";
2+
import { streamCollector } from "@smithy/node-http-handler";
23
import { HttpResponse } from "@smithy/protocol-http";
34
import type {
45
$Schema,
@@ -20,6 +21,7 @@ import type {
2021
TimestampEpochSecondsSchema,
2122
} from "@smithy/types";
2223
import { parseUrl } from "@smithy/url-parser/src";
24+
import { Readable } from "node:stream";
2325
import { describe, expect, test as it } from "vitest";
2426

2527
import { HttpBindingProtocol } from "./HttpBindingProtocol";
@@ -244,4 +246,42 @@ describe(HttpBindingProtocol.name, () => {
244246
expect(request.path).toMatch(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);
245247
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}$/);
246248
});
249+
250+
it("should discard response bodies for Unit operation outputs, making no attempt to parse them", async () => {
251+
const protocol = new StringRestProtocol();
252+
let streamProgress = 0;
253+
const response = await protocol.deserializeResponse(
254+
op("", "", {}, "unit", "unit"),
255+
{
256+
streamCollector: streamCollector,
257+
} as any,
258+
new HttpResponse({
259+
statusCode: 200,
260+
headers: {},
261+
body: Readable.from({
262+
async *[Symbol.asyncIterator]() {
263+
yield "@";
264+
streamProgress = 25;
265+
yield "#";
266+
streamProgress = 50;
267+
yield "$";
268+
streamProgress = 75;
269+
yield "%";
270+
streamProgress = 100;
271+
},
272+
}),
273+
})
274+
);
275+
276+
expect(response).toEqual({
277+
$metadata: {
278+
cfId: undefined,
279+
extendedRequestId: undefined,
280+
httpStatusCode: 200,
281+
requestId: undefined,
282+
},
283+
});
284+
285+
expect(streamProgress).toBe(100);
286+
});
247287
});

packages/core/src/submodules/protocols/HttpBindingProtocol.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ export abstract class HttpBindingProtocol extends HttpProtocol {
229229
}
230230
// Due to Smithy validation, we can assume that the members with no HTTP
231231
// bindings DO NOT contain an event stream.
232+
} else if (nonHttpBindingMembers.discardResponseBody) {
233+
await collectBody(response.body, context);
232234
}
233235

234236
dataObject.$metadata = this.deserializeMetadata(response);
@@ -247,35 +249,37 @@ export abstract class HttpBindingProtocol extends HttpProtocol {
247249
response: IHttpResponse,
248250
headerBindings: Set<string>,
249251
dataObject: any
250-
): Promise<string[]>;
252+
): Promise<string[] & { discardResponseBody?: boolean }>;
251253
protected async deserializeHttpMessage(
252254
schema: Schema,
253255
context: HandlerExecutionContext & SerdeFunctions,
254256
response: IHttpResponse,
255257
dataObject: any
256-
): Promise<string[]>;
258+
): Promise<string[] & { discardResponseBody?: boolean }>;
257259
protected async deserializeHttpMessage(
258260
schema: Schema,
259261
context: HandlerExecutionContext & SerdeFunctions,
260262
response: IHttpResponse,
261263
arg4: unknown,
262264
arg5?: unknown
263-
): Promise<string[]> {
265+
): Promise<string[] & { discardResponseBody?: boolean }> {
264266
let dataObject: any;
265267
if (arg4 instanceof Set) {
266268
dataObject = arg5;
267269
} else {
268270
dataObject = arg4;
269271
}
270272

273+
let discardResponseBody = true;
271274
const deserializer = this.deserializer;
272275
const ns = NormalizedSchema.of(schema);
273-
const nonHttpBindingMembers = [] as string[];
276+
const nonHttpBindingMembers = [] as string[] & { discardResponseBody?: boolean };
274277

275278
for (const [memberName, memberSchema] of ns.structIterator()) {
276279
const memberTraits = memberSchema.getMemberTraits();
277280

278281
if (memberTraits.httpPayload) {
282+
discardResponseBody = false;
279283
const isStreaming = memberSchema.isStreaming();
280284
if (isStreaming) {
281285
const isEventStream = memberSchema.isStructSchema();
@@ -339,6 +343,7 @@ export abstract class HttpBindingProtocol extends HttpProtocol {
339343
nonHttpBindingMembers.push(memberName);
340344
}
341345
}
346+
nonHttpBindingMembers.discardResponseBody = discardResponseBody;
342347
return nonHttpBindingMembers;
343348
}
344349
}

0 commit comments

Comments
 (0)