0
# HTTP Utilities
1
2
Async HTTP/HTTPS request utilities and server configuration helpers for test environments.
3
4
## Capabilities
5
6
### HTTP Request Utilities
7
8
Async wrappers for making HTTP and HTTPS requests.
9
10
```typescript { .api }
11
/**
12
* Async wrapper for making HTTP GET requests
13
* @param urlString - URL to request
14
* @param agent - Optional HTTP agent for connection pooling
15
* @returns Promise resolving to IncomingMessage response
16
*/
17
function httpGetAsync(
18
urlString: string,
19
agent?: http.Agent
20
): Promise<IncomingMessage>;
21
22
/**
23
* Async wrapper for making HTTPS GET requests
24
* Uses a default agent that accepts self-signed certificates
25
* @param urlString - HTTPS URL to request
26
* @param agent - Optional HTTPS agent, defaults to permissive agent
27
* @returns Promise resolving to IncomingMessage response
28
*/
29
function httpsGetAsync(
30
urlString: string,
31
agent?: https.Agent
32
): Promise<IncomingMessage>;
33
```
34
35
**Usage Examples:**
36
37
```typescript
38
import { httpGetAsync, httpsGetAsync, expect } from "@loopback/testlab";
39
import http from "http";
40
import https from "https";
41
42
// Basic HTTP request
43
const response = await httpGetAsync("http://localhost:3000/api/status");
44
expect(response.statusCode).to.equal(200);
45
46
// Read response body
47
let body = "";
48
response.on("data", (chunk) => {
49
body += chunk;
50
});
51
response.on("end", () => {
52
const data = JSON.parse(body);
53
expect(data.status).to.equal("ok");
54
});
55
56
// HTTPS request (accepts self-signed certificates by default)
57
const httpsResponse = await httpsGetAsync("https://localhost:3443/secure-endpoint");
58
expect(httpsResponse.statusCode).to.equal(200);
59
60
// Custom HTTP agent for connection pooling
61
const customAgent = new http.Agent({
62
keepAlive: true,
63
maxSockets: 5
64
});
65
66
const pooledResponse = await httpGetAsync("http://api.example.com/data", customAgent);
67
68
// Custom HTTPS agent with specific SSL options
69
const customHttpsAgent = new https.Agent({
70
rejectUnauthorized: true, // Require valid certificates
71
keepAlive: true
72
});
73
74
const secureResponse = await httpsGetAsync("https://api.example.com/secure", customHttpsAgent);
75
```
76
77
### HTTP Server Configuration
78
79
Helper functions for creating HTTP/HTTPS server configurations suitable for testing.
80
81
```typescript { .api }
82
/**
83
* Create an HTTP-server configuration that works well in test environments
84
* - Assigns ephemeral port (port 0)
85
* - Uses IPv4 localhost (127.0.0.1) to avoid IPv6 issues
86
* - Provides default TLS configuration for HTTPS
87
* @param customConfig - Additional configuration options
88
* @returns Complete server configuration with host and port
89
*/
90
function givenHttpServerConfig<T extends HttpOptions | HttpsOptions>(
91
customConfig?: T
92
): HostPort & T;
93
94
/**
95
* HTTP server configuration options
96
*/
97
interface HttpOptions extends ListenOptions {
98
protocol?: 'http';
99
}
100
101
/**
102
* HTTPS server configuration options
103
*/
104
interface HttpsOptions extends ListenOptions, HttpsServerOptions {
105
protocol: 'https';
106
}
107
108
/**
109
* Interface requiring host and port properties
110
*/
111
interface HostPort {
112
host: string;
113
port: number;
114
}
115
```
116
117
**Usage Examples:**
118
119
```typescript
120
import { givenHttpServerConfig, expect } from "@loopback/testlab";
121
import http from "http";
122
import https from "https";
123
124
// Basic HTTP server configuration
125
const httpConfig = givenHttpServerConfig();
126
expect(httpConfig.host).to.equal("127.0.0.1");
127
expect(httpConfig.port).to.equal(0); // Ephemeral port
128
129
// Create HTTP server with test config
130
const httpServer = http.createServer((req, res) => {
131
res.writeHead(200, {"Content-Type": "application/json"});
132
res.end(JSON.stringify({message: "Hello"}));
133
});
134
135
httpServer.listen(httpConfig, () => {
136
const address = httpServer.address() as any;
137
console.log(`HTTP server listening on ${address.address}:${address.port}`);
138
});
139
140
// HTTPS server configuration with defaults
141
const httpsConfig = givenHttpServerConfig({protocol: "https"});
142
expect(httpsConfig.protocol).to.equal("https");
143
expect(httpsConfig.host).to.equal("127.0.0.1");
144
expect(httpsConfig.port).to.equal(0);
145
expect(httpsConfig).to.have.property("key"); // Default TLS key
146
expect(httpsConfig).to.have.property("cert"); // Default TLS cert
147
148
// Create HTTPS server with test config
149
const httpsServer = https.createServer(httpsConfig, (req, res) => {
150
res.writeHead(200, {"Content-Type": "application/json"});
151
res.end(JSON.stringify({secure: true}));
152
});
153
154
httpsServer.listen(httpsConfig, () => {
155
const address = httpsServer.address() as any;
156
console.log(`HTTPS server listening on ${address.address}:${address.port}`);
157
});
158
159
// Custom HTTP configuration
160
const customHttpConfig = givenHttpServerConfig({
161
host: "0.0.0.0", // Accept from any interface
162
port: 8080 // Specific port
163
});
164
165
// Custom HTTPS configuration with own certificates
166
const customHttpsConfig = givenHttpServerConfig({
167
protocol: "https",
168
key: fs.readFileSync("/path/to/private-key.pem"),
169
cert: fs.readFileSync("/path/to/certificate.pem"),
170
host: "localhost",
171
port: 8443
172
});
173
```
174
175
### Error Logging
176
177
HTTP error logging utilities for debugging test failures.
178
179
```typescript { .api }
180
/**
181
* Creates a logger that logs HTTP errors when status code is unexpected
182
* @param expectedStatusCode - Status code that should not be logged
183
* @returns Error logging function
184
*/
185
function createUnexpectedHttpErrorLogger(
186
expectedStatusCode?: number
187
): LogError;
188
189
/**
190
* Error logging function type
191
*/
192
type LogError = (err: Error, statusCode: number, request: Request) => void;
193
```
194
195
**Usage Examples:**
196
197
```typescript
198
import { createUnexpectedHttpErrorLogger, expect } from "@loopback/testlab";
199
import { Request } from "express";
200
201
// Create logger that ignores 404 errors
202
const logger = createUnexpectedHttpErrorLogger(404);
203
204
// Simulate different error scenarios
205
const mockRequest = {method: "GET", url: "/api/test"} as Request;
206
207
// This will be logged (unexpected error)
208
logger(new Error("Database connection failed"), 500, mockRequest);
209
// Output: "Unhandled error in GET /api/test: 500 Error: Database connection failed..."
210
211
// This will NOT be logged (expected 404)
212
logger(new Error("Not found"), 404, mockRequest);
213
// No output
214
215
// Logger for successful requests (ignore 200)
216
const successLogger = createUnexpectedHttpErrorLogger(200);
217
218
// This will be logged (unexpected error)
219
successLogger(new Error("Validation failed"), 400, mockRequest);
220
221
// This will NOT be logged (expected success)
222
successLogger(new Error("Should not happen"), 200, mockRequest);
223
224
// Use in Express error handler
225
function errorHandler(err: Error, req: Request, res: Response, next: NextFunction) {
226
const logger = createUnexpectedHttpErrorLogger();
227
logger(err, res.statusCode || 500, req);
228
229
res.status(500).json({error: "Internal server error"});
230
}
231
```
232
233
### Integration with Test Servers
234
235
Complete examples of using HTTP utilities in test scenarios.
236
237
**Usage Examples:**
238
239
```typescript
240
import {
241
givenHttpServerConfig,
242
httpGetAsync,
243
httpsGetAsync,
244
expect
245
} from "@loopback/testlab";
246
import http from "http";
247
import https from "https";
248
249
// Test HTTP server lifecycle
250
describe("HTTP Server Tests", () => {
251
let server: http.Server;
252
let serverUrl: string;
253
254
beforeEach(async () => {
255
const config = givenHttpServerConfig();
256
257
server = http.createServer((req, res) => {
258
if (req.url === "/ping") {
259
res.writeHead(200, {"Content-Type": "application/json"});
260
res.end(JSON.stringify({pong: true}));
261
} else {
262
res.writeHead(404);
263
res.end("Not found");
264
}
265
});
266
267
await new Promise<void>((resolve) => {
268
server.listen(config, () => {
269
const address = server.address() as any;
270
serverUrl = `http://${address.address}:${address.port}`;
271
resolve();
272
});
273
});
274
});
275
276
afterEach(async () => {
277
await new Promise<void>((resolve) => {
278
server.close(() => resolve());
279
});
280
});
281
282
it("should respond to ping", async () => {
283
const response = await httpGetAsync(`${serverUrl}/ping`);
284
expect(response.statusCode).to.equal(200);
285
286
let body = "";
287
response.on("data", chunk => body += chunk);
288
await new Promise(resolve => response.on("end", resolve));
289
290
expect(JSON.parse(body)).to.eql({pong: true});
291
});
292
293
it("should return 404 for unknown routes", async () => {
294
const response = await httpGetAsync(`${serverUrl}/unknown`);
295
expect(response.statusCode).to.equal(404);
296
});
297
});
298
299
// Test HTTPS server with custom certificates
300
describe("HTTPS Server Tests", () => {
301
let server: https.Server;
302
let serverUrl: string;
303
304
beforeEach(async () => {
305
const config = givenHttpServerConfig({protocol: "https"});
306
307
server = https.createServer(config, (req, res) => {
308
res.writeHead(200, {"Content-Type": "application/json"});
309
res.end(JSON.stringify({secure: true, url: req.url}));
310
});
311
312
await new Promise<void>((resolve) => {
313
server.listen(config, () => {
314
const address = server.address() as any;
315
serverUrl = `https://${address.address}:${address.port}`;
316
resolve();
317
});
318
});
319
});
320
321
afterEach(async () => {
322
await new Promise<void>((resolve) => {
323
server.close(() => resolve());
324
});
325
});
326
327
it("should handle HTTPS requests", async () => {
328
// Uses default permissive agent for self-signed certificates
329
const response = await httpsGetAsync(`${serverUrl}/secure`);
330
expect(response.statusCode).to.equal(200);
331
332
let body = "";
333
response.on("data", chunk => body += chunk);
334
await new Promise(resolve => response.on("end", resolve));
335
336
const data = JSON.parse(body);
337
expect(data.secure).to.be.true();
338
expect(data.url).to.equal("/secure");
339
});
340
});
341
342
// Test external API integration
343
describe("External API Tests", () => {
344
it("should handle API responses", async () => {
345
// Mock external service (you would replace with actual service in tests)
346
const mockServer = http.createServer((req, res) => {
347
res.writeHead(200, {"Content-Type": "application/json"});
348
res.end(JSON.stringify({data: "mock response"}));
349
});
350
351
const config = givenHttpServerConfig();
352
await new Promise<void>(resolve => mockServer.listen(config, resolve));
353
354
const address = mockServer.address() as any;
355
const mockUrl = `http://${address.address}:${address.port}`;
356
357
// Test API call
358
const response = await httpGetAsync(`${mockUrl}/api/data`);
359
expect(response.statusCode).to.equal(200);
360
361
await new Promise<void>(resolve => mockServer.close(() => resolve()));
362
});
363
});
364
```