Skip to content

Commit 53fd9b4

Browse files
committed
chore(core/protocols): move http binding deser to HttpBindingProtocol
1 parent b15137d commit 53fd9b4

File tree

5 files changed

+207
-101
lines changed

5 files changed

+207
-101
lines changed

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

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { op, SCHEMA, struct } from "@smithy/core/schema";
1+
import { map, op, SCHEMA, struct } from "@smithy/core/schema";
22
import { HttpResponse } from "@smithy/protocol-http";
33
import {
44
Codec,
@@ -8,6 +8,8 @@ import {
88
MetadataBearer,
99
OperationSchema,
1010
ResponseMetadata,
11+
Schema,
12+
SerdeFunctions,
1113
ShapeDeserializer,
1214
ShapeSerializer,
1315
} from "@smithy/types";
@@ -41,9 +43,11 @@ describe(HttpBindingProtocol.name, () => {
4143
public getShapeId(): string {
4244
throw new Error("Method not implemented.");
4345
}
46+
4447
public getPayloadCodec(): Codec<any, any> {
4548
throw new Error("Method not implemented.");
4649
}
50+
4751
protected handleError(
4852
operationSchema: OperationSchema,
4953
context: HandlerExecutionContext,
@@ -157,4 +161,53 @@ describe(HttpBindingProtocol.name, () => {
157161
);
158162
expect(request.path).toEqual("/custom/Operation");
159163
});
164+
165+
it("can deserialize a prefix header binding and header binding from the same header", async () => {
166+
type TestSignature = (
167+
schema: Schema,
168+
context: HandlerExecutionContext & SerdeFunctions,
169+
response: IHttpResponse,
170+
dataObject: any
171+
) => Promise<string[]>;
172+
const deserializeHttpMessage = ((StringRestProtocol.prototype as any).deserializeHttpMessage as TestSignature).bind(
173+
{
174+
deserializer: new FromStringShapeDeserializer({
175+
httpBindings: true,
176+
timestampFormat: {
177+
useTrait: true,
178+
default: SCHEMA.TIMESTAMP_EPOCH_SECONDS,
179+
},
180+
}),
181+
}
182+
);
183+
const httpResponse: IHttpResponse = {
184+
statusCode: 200,
185+
headers: {
186+
"my-header": "header-value",
187+
},
188+
};
189+
190+
const dataObject = {};
191+
await deserializeHttpMessage(
192+
struct(
193+
"",
194+
"Struct",
195+
0,
196+
["prefixHeaders", "header"],
197+
[
198+
[map("", "Map", 0, 0, 0), { httpPrefixHeaders: "my-" }],
199+
[0, { httpHeader: "my-header" }],
200+
]
201+
),
202+
{} as any,
203+
httpResponse,
204+
dataObject
205+
);
206+
expect(dataObject).toEqual({
207+
prefixHeaders: {
208+
header: "header-value",
209+
},
210+
header: "header-value",
211+
});
212+
});
160213
});

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

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import { NormalizedSchema, SCHEMA } from "@smithy/core/schema";
2+
import { splitEvery, splitHeader } from "@smithy/core/serde";
23
import { HttpRequest } from "@smithy/protocol-http";
34
import {
45
Endpoint,
56
EndpointBearer,
7+
EventStreamSerdeContext,
68
HandlerExecutionContext,
79
HttpRequest as IHttpRequest,
810
HttpResponse as IHttpResponse,
911
MetadataBearer,
1012
OperationSchema,
13+
Schema,
1114
SerdeFunctions,
1215
} from "@smithy/types";
16+
import { sdkStreamMixin } from "@smithy/util-stream";
1317

1418
import { collectBody } from "./collect-stream-body";
1519
import { extendedEncodeURIComponent } from "./extended-encode-uri-component";
@@ -226,4 +230,125 @@ export abstract class HttpBindingProtocol extends HttpProtocol {
226230

227231
return output;
228232
}
233+
234+
/**
235+
* The base method ignores HTTP bindings.
236+
*
237+
* @deprecated (only this signature) use signature without headerBindings.
238+
* @override
239+
*/
240+
protected async deserializeHttpMessage(
241+
schema: Schema,
242+
context: HandlerExecutionContext & SerdeFunctions,
243+
response: IHttpResponse,
244+
headerBindings: Set<string>,
245+
dataObject: any
246+
): Promise<string[]>;
247+
protected async deserializeHttpMessage(
248+
schema: Schema,
249+
context: HandlerExecutionContext & SerdeFunctions,
250+
response: IHttpResponse,
251+
dataObject: any
252+
): Promise<string[]>;
253+
protected async deserializeHttpMessage(
254+
schema: Schema,
255+
context: HandlerExecutionContext & SerdeFunctions,
256+
response: IHttpResponse,
257+
arg4: unknown,
258+
arg5?: unknown
259+
): Promise<string[]> {
260+
let dataObject: any;
261+
if (arg4 instanceof Set) {
262+
dataObject = arg5;
263+
} else {
264+
dataObject = arg4;
265+
}
266+
267+
const deserializer = this.deserializer;
268+
const ns = NormalizedSchema.of(schema);
269+
const nonHttpBindingMembers = [] as string[];
270+
271+
for (const [memberName, memberSchema] of ns.structIterator()) {
272+
const memberTraits = memberSchema.getMemberTraits();
273+
274+
if (memberTraits.httpPayload) {
275+
const isStreaming = memberSchema.isStreaming();
276+
if (isStreaming) {
277+
const isEventStream = memberSchema.isStructSchema();
278+
if (isEventStream) {
279+
// streaming event stream (union)
280+
const context = this.serdeContext as unknown as EventStreamSerdeContext;
281+
if (!context.eventStreamMarshaller) {
282+
throw new Error("@smithy/core - HttpProtocol: eventStreamMarshaller missing in serdeContext.");
283+
}
284+
const memberSchemas = memberSchema.getMemberSchemas();
285+
dataObject[memberName] = context.eventStreamMarshaller.deserialize(response.body, async (event) => {
286+
const unionMember =
287+
Object.keys(event).find((key) => {
288+
return key !== "__type";
289+
}) ?? "";
290+
if (unionMember in memberSchemas) {
291+
const eventStreamSchema = memberSchemas[unionMember];
292+
return {
293+
[unionMember]: await deserializer.read(eventStreamSchema, event[unionMember].body),
294+
};
295+
} else {
296+
// this union convention is ignored by the event stream marshaller.
297+
return {
298+
$unknown: event,
299+
};
300+
}
301+
});
302+
} else {
303+
// streaming blob body
304+
dataObject[memberName] = sdkStreamMixin(response.body);
305+
}
306+
} else if (response.body) {
307+
const bytes: Uint8Array = await collectBody(response.body, context as SerdeFunctions);
308+
if (bytes.byteLength > 0) {
309+
dataObject[memberName] = await deserializer.read(memberSchema, bytes);
310+
}
311+
}
312+
} else if (memberTraits.httpHeader) {
313+
const key = String(memberTraits.httpHeader).toLowerCase();
314+
const value = response.headers[key];
315+
if (null != value) {
316+
if (memberSchema.isListSchema()) {
317+
const headerListValueSchema = memberSchema.getValueSchema();
318+
let sections: string[];
319+
if (
320+
headerListValueSchema.isTimestampSchema() &&
321+
headerListValueSchema.getSchema() === SCHEMA.TIMESTAMP_DEFAULT
322+
) {
323+
sections = splitEvery(value, ",", 2);
324+
} else {
325+
sections = splitHeader(value);
326+
}
327+
const list = [];
328+
for (const section of sections) {
329+
list.push(await deserializer.read([headerListValueSchema, { httpHeader: key }], section.trim()));
330+
}
331+
dataObject[memberName] = list;
332+
} else {
333+
dataObject[memberName] = await deserializer.read(memberSchema, value);
334+
}
335+
}
336+
} else if (memberTraits.httpPrefixHeaders !== undefined) {
337+
dataObject[memberName] = {};
338+
for (const [header, value] of Object.entries(response.headers)) {
339+
if (header.startsWith(memberTraits.httpPrefixHeaders)) {
340+
dataObject[memberName][header.slice(memberTraits.httpPrefixHeaders.length)] = await deserializer.read(
341+
[memberSchema.getValueSchema(), { httpHeader: header }],
342+
value
343+
);
344+
}
345+
}
346+
} else if (memberTraits.httpResponseCode) {
347+
dataObject[memberName] = response.statusCode;
348+
} else {
349+
nonHttpBindingMembers.push(memberName);
350+
}
351+
}
352+
return nonHttpBindingMembers;
353+
}
229354
}

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { HttpProtocol } from "./HttpProtocol";
66
import { FromStringShapeDeserializer } from "./serde/FromStringShapeDeserializer";
77

88
describe(HttpProtocol.name, () => {
9-
it("can deserialize a prefix header binding and header binding from the same header", async () => {
9+
it("ignores http bindings (only HttpBindingProtocol uses them)", async () => {
1010
type TestSignature = (
1111
schema: Schema,
1212
context: HandlerExecutionContext & SerdeFunctions,
@@ -46,10 +46,7 @@ describe(HttpProtocol.name, () => {
4646
dataObject
4747
);
4848
expect(dataObject).toEqual({
49-
prefixHeaders: {
50-
header: "header-value",
51-
},
52-
header: "header-value",
49+
// headers were ignored
5350
});
5451
});
5552
});

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

Lines changed: 11 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,9 @@ export abstract class HttpProtocol implements ClientProtocol<IHttpRequest, IHttp
144144
}
145145

146146
/**
147-
* @deprecated use signature without headerBindings.
147+
* For HTTP binding protocols, this method is overridden in {@link HttpBindingProtocol}.
148+
*
149+
* @deprecated only use this for HTTP binding protocols.
148150
*/
149151
protected async deserializeHttpMessage(
150152
schema: Schema,
@@ -166,98 +168,13 @@ export abstract class HttpProtocol implements ClientProtocol<IHttpRequest, IHttp
166168
arg4: unknown,
167169
arg5?: unknown
168170
): Promise<string[]> {
169-
let dataObject: any;
170-
if (arg4 instanceof Set) {
171-
dataObject = arg5;
172-
} else {
173-
dataObject = arg4;
174-
}
175-
176-
const deserializer = this.deserializer;
177-
const ns = NormalizedSchema.of(schema);
178-
const nonHttpBindingMembers = [] as string[];
179-
180-
for (const [memberName, memberSchema] of ns.structIterator()) {
181-
const memberTraits = memberSchema.getMemberTraits();
182-
183-
if (memberTraits.httpPayload) {
184-
const isStreaming = memberSchema.isStreaming();
185-
if (isStreaming) {
186-
const isEventStream = memberSchema.isStructSchema();
187-
if (isEventStream) {
188-
// streaming event stream (union)
189-
const context = this.serdeContext as unknown as EventStreamSerdeContext;
190-
if (!context.eventStreamMarshaller) {
191-
throw new Error("@smithy/core - HttpProtocol: eventStreamMarshaller missing in serdeContext.");
192-
}
193-
const memberSchemas = memberSchema.getMemberSchemas();
194-
dataObject[memberName] = context.eventStreamMarshaller.deserialize(response.body, async (event) => {
195-
const unionMember =
196-
Object.keys(event).find((key) => {
197-
return key !== "__type";
198-
}) ?? "";
199-
if (unionMember in memberSchemas) {
200-
const eventStreamSchema = memberSchemas[unionMember];
201-
return {
202-
[unionMember]: await deserializer.read(eventStreamSchema, event[unionMember].body),
203-
};
204-
} else {
205-
// this union convention is ignored by the event stream marshaller.
206-
return {
207-
$unknown: event,
208-
};
209-
}
210-
});
211-
} else {
212-
// streaming blob body
213-
dataObject[memberName] = sdkStreamMixin(response.body);
214-
}
215-
} else if (response.body) {
216-
const bytes: Uint8Array = await collectBody(response.body, context as SerdeFunctions);
217-
if (bytes.byteLength > 0) {
218-
dataObject[memberName] = await deserializer.read(memberSchema, bytes);
219-
}
220-
}
221-
} else if (memberTraits.httpHeader) {
222-
const key = String(memberTraits.httpHeader).toLowerCase();
223-
const value = response.headers[key];
224-
if (null != value) {
225-
if (memberSchema.isListSchema()) {
226-
const headerListValueSchema = memberSchema.getValueSchema();
227-
let sections: string[];
228-
if (
229-
headerListValueSchema.isTimestampSchema() &&
230-
headerListValueSchema.getSchema() === SCHEMA.TIMESTAMP_DEFAULT
231-
) {
232-
sections = splitEvery(value, ",", 2);
233-
} else {
234-
sections = splitHeader(value);
235-
}
236-
const list = [];
237-
for (const section of sections) {
238-
list.push(await deserializer.read([headerListValueSchema, { httpHeader: key }], section.trim()));
239-
}
240-
dataObject[memberName] = list;
241-
} else {
242-
dataObject[memberName] = await deserializer.read(memberSchema, value);
243-
}
244-
}
245-
} else if (memberTraits.httpPrefixHeaders !== undefined) {
246-
dataObject[memberName] = {};
247-
for (const [header, value] of Object.entries(response.headers)) {
248-
if (header.startsWith(memberTraits.httpPrefixHeaders)) {
249-
dataObject[memberName][header.slice(memberTraits.httpPrefixHeaders.length)] = await deserializer.read(
250-
[memberSchema.getValueSchema(), { httpHeader: header }],
251-
value
252-
);
253-
}
254-
}
255-
} else if (memberTraits.httpResponseCode) {
256-
dataObject[memberName] = response.statusCode;
257-
} else {
258-
nonHttpBindingMembers.push(memberName);
259-
}
260-
}
261-
return nonHttpBindingMembers;
171+
void schema;
172+
void context;
173+
void response;
174+
void arg4;
175+
void arg5;
176+
// This method is preserved for backwards compatibility.
177+
// It should remain unused.
178+
return [];
262179
}
263180
}

0 commit comments

Comments
 (0)