Skip to content

Commit 0b7ec3e

Browse files
committed
Add support for returning trailers in thenCallback handlers
1 parent 02752e5 commit 0b7ec3e

File tree

4 files changed

+63
-3
lines changed

4 files changed

+63
-3
lines changed

src/rules/requests/request-handler-definitions.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { stripIndent } from 'common-tags';
77

88
import {
99
Headers,
10+
Trailers,
1011
CompletedRequest,
1112
CompletedBody,
1213
Explainable,
@@ -180,6 +181,15 @@ export interface CallbackResponseMessageResult {
180181
*/
181182
headers?: Headers;
182183

184+
/**
185+
* The replacement HTTP trailers, as an object of string keys and either
186+
* single string or array of string values. Note that there are not all
187+
* header fields are valid as trailers, and there are other requirements
188+
* such as chunked encoding that must be met for trailers to be sent
189+
* successfully.
190+
*/
191+
trailers?: Trailers;
192+
183193
/**
184194
* A string or buffer, which replaces the response body if set. This will
185195
* be automatically encoded to match the Content-Encoding defined in your

src/rules/requests/request-handlers.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,14 @@ export class SimpleHandler extends SimpleHandlerDefinition {
166166
}
167167
}
168168

169-
async function writeResponseFromCallback(result: CallbackResponseMessageResult, response: OngoingResponse) {
169+
async function writeResponseFromCallback(
170+
result: CallbackResponseMessageResult,
171+
response: OngoingResponse
172+
) {
170173
if (result.json !== undefined) {
171-
result.headers = _.assign(result.headers || {}, { 'Content-Type': 'application/json' });
174+
result.headers = Object.assign(result.headers || {}, {
175+
'Content-Type': 'application/json'
176+
});
172177
result.body = JSON.stringify(result.json);
173178
delete result.json;
174179
}
@@ -193,6 +198,9 @@ async function writeResponseFromCallback(result: CallbackResponseMessageResult,
193198
result.statusMessage,
194199
result.headers
195200
);
201+
202+
if (result.trailers) response.addTrailers(result.trailers);
203+
196204
response.end(result.rawBody || "");
197205
}
198206

src/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ export interface Headers {
3636
[key: string]: undefined | string | string[];
3737
}
3838

39+
export interface Trailers {
40+
// 0+ of any trailer
41+
[key: string]: undefined | string | string[];
42+
}
43+
3944
export type RawHeaders = Array<[key: string, value: string]>;
4045

4146
export interface Request {

test/integration/handlers/callback-response.spec.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
import * as zlib from 'zlib';
2+
import * as http from 'http';
3+
24
import { getLocal } from "../../..";
3-
import { expect, fetch, isNode, isWeb, headersToObject } from "../../test-utils";
5+
import {
6+
expect,
7+
fetch,
8+
isNode,
9+
isWeb,
10+
headersToObject,
11+
nodeOnly
12+
} from "../../test-utils";
413

514
describe("Callback response handlers", function () {
615

@@ -71,6 +80,34 @@ describe("Callback response handlers", function () {
7180
expect(await response.text()).to.equal("Error: Cannot set custom :status pseudoheader values");
7281
});
7382

83+
nodeOnly(() => { // Browsers can't read trailers
84+
it("should allow mocking response trailers with a callback", async () => {
85+
await server.forGet("/mocked-endpoint").thenCallback(() => {
86+
return {
87+
statusCode: 200,
88+
trailers: {
89+
'mock-trailer': 'set'
90+
}
91+
};
92+
});
93+
94+
const response = await new Promise<http.IncomingMessage>((resolve, reject) => {
95+
const req = http.request(server.urlFor("/mocked-endpoint")).end();
96+
req.on('response', (res) => {
97+
// Wait until everything is 100% done, so we definitely have trailers
98+
res.resume();
99+
res.on('end', () => resolve(res));
100+
});
101+
req.on('error', reject);
102+
});
103+
104+
expect(response.statusCode).to.equal(200);
105+
expect(response.trailers).to.deep.equal({
106+
'mock-trailer': 'set'
107+
});
108+
});
109+
});
110+
74111
it("should allow mocking body as json with callback", async () => {
75112
await server.forGet("/mocked-endpoint").thenCallback(() => ({
76113
statusCode: 201,

0 commit comments

Comments
 (0)