Skip to content

Commit b3c5c76

Browse files
committed
feat(core): add http debug utility to core/protocols
1 parent 5aea94e commit b3c5c76

File tree

10 files changed

+657
-13
lines changed

10 files changed

+657
-13
lines changed

.changeset/hot-points-type.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@smithy/core": minor
3+
---
4+
5+
add http debug utility

packages/core/debug.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
/**
3+
* Do not edit:
4+
* This is a compatibility redirect for contexts that do not understand package.json exports field.
5+
*/
6+
declare module "@smithy/core/debug" {
7+
export * from "@smithy/core/dist-types/submodules/debug/index.d";
8+
}

packages/core/debug.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
/**
3+
* Do not edit:
4+
* This is a compatibility redirect for contexts that do not understand package.json exports field.
5+
*/
6+
module.exports = require("./dist-cjs/submodules/debug/index.js");

packages/core/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@
5252
"import": "./dist-es/submodules/serde/index.js",
5353
"require": "./dist-cjs/submodules/serde/index.js",
5454
"types": "./dist-types/submodules/serde/index.d.ts"
55+
},
56+
"./debug": {
57+
"module": "./dist-es/submodules/debug/index.js",
58+
"node": "./dist-cjs/submodules/debug/index.js",
59+
"import": "./dist-es/submodules/debug/index.js",
60+
"require": "./dist-cjs/submodules/debug/index.js",
61+
"types": "./dist-types/submodules/debug/index.d.ts"
5562
}
5663
},
5764
"author": {
@@ -83,6 +90,8 @@
8390
"files": [
8491
"./cbor.d.ts",
8592
"./cbor.js",
93+
"./debug.d.ts",
94+
"./debug.js",
8695
"./protocols.d.ts",
8796
"./protocols.js",
8897
"./serde.d.ts",
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
import { HttpRequest, HttpResponse } from "@smithy/protocol-http/src";
2+
import { Readable } from "node:stream";
3+
import { describe, expect, test as it, vi } from "vitest";
4+
5+
import { addHttpDebugLogMiddleware } from "./httpDebugLogging";
6+
7+
describe(addHttpDebugLogMiddleware.name, () => {
8+
describe("JSON", () => {
9+
const httpRequest = new HttpRequest({
10+
headers: {
11+
header1: "headerValue1",
12+
},
13+
query: {
14+
query1: "queryValue1",
15+
},
16+
protocol: "https:",
17+
hostname: "localhost",
18+
path: "/path",
19+
body: JSON.stringify({ requestBody: "OK" }),
20+
});
21+
22+
const httpResponse = new HttpResponse({
23+
headers: {
24+
responseHeader1: "responseHeaderValue1",
25+
},
26+
statusCode: 200,
27+
body: Readable.from('{ "responseBody": "OK" }'),
28+
});
29+
30+
it("should pass http request and response data through a logger", async () => {
31+
const next = () => ({
32+
response: httpResponse,
33+
});
34+
const args = {
35+
request: httpRequest,
36+
};
37+
38+
const mwStack = {
39+
addRelativeTo(middleware: (next: Function, context: any) => (args) => Promise<any>): void {
40+
middleware(next, {})(args);
41+
},
42+
} as any;
43+
44+
const logger = {
45+
error: vi.fn(),
46+
warn: vi.fn(),
47+
info: vi.fn(),
48+
debug: vi.fn(),
49+
};
50+
51+
addHttpDebugLogMiddleware(mwStack, {
52+
request: {
53+
client: true,
54+
command: true,
55+
method: true,
56+
url: true,
57+
headers: true,
58+
formatBody: true,
59+
body: true,
60+
},
61+
response: {
62+
statusCode: true,
63+
headers: true,
64+
body: true,
65+
formatBody: true,
66+
},
67+
logger: logger,
68+
});
69+
70+
// inner tested fn is un-awaitable.
71+
await new Promise((r) => setTimeout(r, 100));
72+
73+
expect(vi.mocked(logger.debug).mock.calls.flat()).toEqual([
74+
"200 GET UnknownClient UnknownCommand",
75+
" https://localhost/path",
76+
` >> Request URL queryParams: {
77+
"query1": "queryValue1"
78+
}`,
79+
` >>== Request Headers: {
80+
"header1": "headerValue1"
81+
}`,
82+
" >>>=== Request Body Start ======",
83+
` {
84+
"requestBody": "OK"
85+
}`,
86+
" >>>=== Request Body End ======",
87+
` <<== Response Headers: {
88+
"responseHeader1": "responseHeaderValue1"
89+
}`,
90+
" <<<=== Response Body Start ======",
91+
` {
92+
"responseBody": "OK"
93+
}`,
94+
" <<<=== Response Body End ======",
95+
]);
96+
});
97+
});
98+
99+
describe("XML", () => {
100+
const httpRequest = new HttpRequest({
101+
headers: {
102+
header1: "headerValue1",
103+
},
104+
query: {
105+
query1: "queryValue1",
106+
},
107+
protocol: "https:",
108+
hostname: "localhost",
109+
path: "/path",
110+
body: `<?xml version="1.0" encoding="UTF-8"?><WebsiteConfiguration xmlns="https://prod.company.com/doc/2006-03-01/"><ErrorDocument><Key>string</Key></ErrorDocument><IndexDocument><Suffix>string</Suffix></IndexDocument><RedirectAllRequestsTo><HostName>string</HostName><Protocol>string</Protocol></RedirectAllRequestsTo><RoutingRules><RoutingRule><Condition><HttpErrorCodeReturnedEquals>string</HttpErrorCodeReturnedEquals><KeyPrefixEquals>string</KeyPrefixEquals></Condition><Redirect><HostName>string</HostName><HttpRedirectCode>string</HttpRedirectCode><Protocol>string</Protocol><ReplaceKeyPrefixWith>string</ReplaceKeyPrefixWith><ReplaceKeyWith>string</ReplaceKeyWith></Redirect></RoutingRule></RoutingRules></WebsiteConfiguration>`,
111+
});
112+
113+
const httpResponse = new HttpResponse({
114+
headers: {
115+
responseHeader1: "responseHeaderValue1",
116+
},
117+
statusCode: 200,
118+
body: Readable.from(
119+
`<?xml version="1.0" encoding="UTF-8"?><BucketLoggingStatus><LoggingEnabled><TargetBucket>string</TargetBucket><TargetGrants><Grant><Grantee><DisplayName>string</DisplayName><EmailAddress>string</EmailAddress><ID>string</ID><xsi:type>string</xsi:type><URI>string</URI></Grantee><Permission>string</Permission></Grant></TargetGrants><TargetObjectKeyFormat><PartitionedPrefix><PartitionDateSource>string</PartitionDateSource></PartitionedPrefix><SimplePrefix></SimplePrefix></TargetObjectKeyFormat><TargetPrefix>string</TargetPrefix></LoggingEnabled></BucketLoggingStatus>`
120+
),
121+
});
122+
123+
it("should pass http request and response data through a logger", async () => {
124+
const next = () => ({
125+
response: httpResponse,
126+
});
127+
const args = {
128+
request: httpRequest,
129+
};
130+
131+
const mwStack = {
132+
addRelativeTo(middleware: (next: Function, context: any) => (args) => Promise<any>): void {
133+
middleware(next, {})(args);
134+
},
135+
} as any;
136+
137+
addHttpDebugLogMiddleware(mwStack, "lines headers formatted");
138+
const spy = vi.spyOn(console, "debug").mockImplementation(() => {});
139+
140+
// inner tested fn is un-awaitable.
141+
await new Promise((r) => setTimeout(r, 100));
142+
143+
expect(vi.mocked(spy).mock.calls.flat()).toEqual([
144+
"200 GET UnknownClient UnknownCommand",
145+
" https://localhost/path",
146+
` >> Request URL queryParams: {
147+
"query1": "queryValue1"
148+
}`,
149+
` >>== Request Headers: {
150+
"header1": "headerValue1"
151+
}`,
152+
" >>>=== Request Body Start ======",
153+
` <?xml version="1.0" encoding="UTF-8"?>
154+
<WebsiteConfiguration xmlns="https://prod.company.com/doc/2006-03-01/">
155+
<ErrorDocument>
156+
<Key>
157+
string
158+
</Key>
159+
</ErrorDocument>
160+
<IndexDocument>
161+
<Suffix>
162+
string
163+
</Suffix>
164+
</IndexDocument>
165+
<RedirectAllRequestsTo>
166+
<HostName>
167+
string
168+
</HostName>
169+
<Protocol>
170+
string
171+
</Protocol>
172+
</RedirectAllRequestsTo>
173+
<RoutingRules>
174+
<RoutingRule>
175+
<Condition>
176+
<HttpErrorCodeReturnedEquals>
177+
string
178+
</HttpErrorCodeReturnedEquals>
179+
<KeyPrefixEquals>
180+
string
181+
</KeyPrefixEquals>
182+
</Condition>
183+
<Redirect>
184+
<HostName>
185+
string
186+
</HostName>
187+
<HttpRedirectCode>
188+
string
189+
</HttpRedirectCode>
190+
<Protocol>
191+
string
192+
</Protocol>
193+
<ReplaceKeyPrefixWith>
194+
string
195+
</ReplaceKeyPrefixWith>
196+
<ReplaceKeyWith>
197+
string
198+
</ReplaceKeyWith>
199+
</Redirect>
200+
</RoutingRule>
201+
</RoutingRules>
202+
</WebsiteConfiguration>`,
203+
" >>>=== Request Body End ======",
204+
` <<== Response Headers: {
205+
"responseHeader1": "responseHeaderValue1"
206+
}`,
207+
" <<<=== Response Body Start ======",
208+
` <?xml version="1.0" encoding="UTF-8"?>
209+
<BucketLoggingStatus>
210+
<LoggingEnabled>
211+
<TargetBucket>
212+
string
213+
</TargetBucket>
214+
<TargetGrants>
215+
<Grant>
216+
<Grantee>
217+
<DisplayName>
218+
string
219+
</DisplayName>
220+
<EmailAddress>
221+
string
222+
</EmailAddress>
223+
<ID>
224+
string
225+
</ID>
226+
<xsi:type>
227+
string
228+
</xsi:type>
229+
<URI>
230+
string
231+
</URI>
232+
</Grantee>
233+
<Permission>
234+
string
235+
</Permission>
236+
</Grant>
237+
</TargetGrants>
238+
<TargetObjectKeyFormat>
239+
<PartitionedPrefix>
240+
<PartitionDateSource>
241+
string
242+
</PartitionDateSource>
243+
</PartitionedPrefix>
244+
<SimplePrefix>
245+
</SimplePrefix>
246+
</TargetObjectKeyFormat>
247+
<TargetPrefix>
248+
string
249+
</TargetPrefix>
250+
</LoggingEnabled>
251+
</BucketLoggingStatus>`,
252+
" <<<=== Response Body End ======",
253+
]);
254+
});
255+
});
256+
});

0 commit comments

Comments
 (0)