Skip to content
This repository was archived by the owner on Oct 30, 2022. It is now read-only.

Commit dc4dcd2

Browse files
committed
Parse HTTP headers
1 parent bd08f34 commit dc4dcd2

2 files changed

Lines changed: 97 additions & 2 deletions

File tree

src/index.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,72 @@ export enum HttpProtocol {
2828
HTTPS
2929
}
3030

31+
/** HTTP request or response headers. */
32+
export class HttpHeaders {
33+
private headers: Map<string, string[]>;
34+
35+
constructor(headers?: { [name: string]: string | string[] }) {
36+
if (headers) {
37+
this.headers = new Map<string, string[]>();
38+
Object.keys(headers).forEach(headerName => {
39+
let values: string | string[] = headers[headerName];
40+
if (typeof values === "string") {
41+
values = [values];
42+
}
43+
if (values.length > 0) {
44+
this.headers.set(headerName.toLowerCase(), values);
45+
}
46+
});
47+
} else {
48+
this.headers = new Map<string, string[]>();
49+
}
50+
}
51+
52+
/**
53+
* The first header value for the given header name, if any.
54+
*
55+
* @param headerName Header name.
56+
*
57+
* @returns The first header value, or null if none.
58+
* @see #getAll
59+
*/
60+
get(headerName: string): string | null {
61+
const values = this.headers.get(headerName.toLowerCase());
62+
return values && values.length > 0 ? values[0] : null;
63+
}
64+
65+
/**
66+
* All header values for the given header name.
67+
*
68+
* @param headerName The header name.
69+
*
70+
* @returns an immutable list of header values, or an empty list if none
71+
* @see #get
72+
*/
73+
getAll(headerName: string): string[] {
74+
return this.headers.get(headerName.toLowerCase()) || new Array<string>();
75+
}
76+
}
77+
3178
/** HTTP request. */
3279
export class HttpRequest {
3380
protocol: HttpProtocol;
3481
method: HttpMethod;
82+
headers: HttpHeaders;
3583
body?: string;
3684

3785
public constructor(builder: HttpRequestBuilder) {
3886
this.protocol = builder.protocol;
3987
this.method = builder.method;
88+
this.headers = builder.headers;
4089
this.body = builder.body;
4190
}
4291
}
4392

4493
export class HttpRequestBuilder {
4594
protocol?: HttpProtocol;
4695
method?: HttpMethod;
96+
headers?: HttpHeaders;
4797
body?: string;
4898

4999
withProtocol(protocol: HttpProtocol): this {
@@ -61,30 +111,43 @@ export class HttpRequestBuilder {
61111
return this;
62112
}
63113

114+
withHeaders(headers: HttpHeaders): this {
115+
this.headers = headers;
116+
return this;
117+
}
118+
64119
build(): HttpRequest {
65120
return new HttpRequest(this);
66121
}
67122
}
68123

69124
export class HttpResponse {
70125
statusCode?: number;
126+
headers: HttpHeaders;
71127
body?: string;
72128

73129
constructor(builder: HttpResponseBuilder) {
74130
this.statusCode = builder.statusCode;
131+
this.headers = builder.headers;
75132
this.body = builder.body;
76133
}
77134
}
78135

79136
export class HttpResponseBuilder {
80137
statusCode?: number;
138+
headers?: HttpHeaders;
81139
body?: string;
82140

83141
withStatusCode(statusCode: number): this {
84142
this.statusCode = statusCode;
85143
return this;
86144
}
87145

146+
withHeaders(headers: HttpHeaders): this {
147+
this.headers = headers;
148+
return this;
149+
}
150+
88151
withBody(body: string): this {
89152
this.body = body;
90153
return this;
@@ -112,14 +175,23 @@ export class HttpExchangeReader {
112175
HttpProtocol[(parsedRequest.protocol as string).toUpperCase()];
113176
const method: HttpMethod =
114177
HttpMethod[(parsedRequest.method as string).toUpperCase()];
178+
const requestHeaders: HttpHeaders = new HttpHeaders(parsedRequest.headers);
115179

116180
const request = new HttpRequestBuilder()
117181
.withProtocol(protocol)
118182
.withMethod(method)
183+
.withHeaders(requestHeaders)
184+
.withBody(parsedRequest.body)
119185
.build();
120186

187+
const responseHeaders: HttpHeaders = new HttpHeaders(
188+
parsedResponse.headers
189+
);
190+
121191
const response = new HttpResponseBuilder()
122192
.withStatusCode(parsedResponse.statusCode)
193+
.withHeaders(responseHeaders)
194+
.withBody(parsedResponse.body)
123195
.build();
124196

125197
return new HttpExchange(request, response);

test/index.test.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,38 @@ test("Http exchanges from JSON", () => {
4141
const json = `{
4242
"request": {
4343
"protocol": "https",
44-
"method": "post"
44+
"method": "post",
45+
"headers": {
46+
"accept": "*/*",
47+
"multi-value": ["value1", "value2"]
48+
},
49+
"body": "a request body"
4550
},
4651
"response": {
47-
"statusCode": 404
52+
"statusCode": 404,
53+
"headers": {
54+
"content-length": "15",
55+
"Upper-Case": "yes"
56+
},
57+
"body": "a response body"
4858
}
4959
}`;
5060

5161
const exchange = HttpExchangeReader.fromJson(json);
62+
5263
expect(exchange.request.protocol).toBe(HttpProtocol.HTTPS);
5364
expect(exchange.request.method).toBe(HttpMethod.POST);
65+
expect(exchange.request.headers.get("accept")).toBe("*/*");
66+
expect(exchange.request.headers.get("multi-value")).toBe("value1");
67+
expect(exchange.request.headers.getAll("multi-value")).toEqual([
68+
"value1",
69+
"value2"
70+
]);
71+
expect(exchange.request.body).toBe("a request body");
72+
5473
expect(exchange.response.statusCode).toBe(404);
74+
expect(exchange.response.headers.get("content-length")).toBe("15");
75+
expect(exchange.response.headers.get("Upper-Case")).toBe("yes");
76+
expect(exchange.response.headers.get("upper-case")).toBe("yes");
77+
expect(exchange.response.body).toBe("a response body");
5578
});

0 commit comments

Comments
 (0)