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

Commit 0933216

Browse files
committed
Parse more fields
1 parent 9d6cf47 commit 0933216

2 files changed

Lines changed: 248 additions & 83 deletions

File tree

src/index.ts

Lines changed: 179 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ export enum HttpMethod {
2323
/** HTTP request protocol. */
2424
export enum HttpProtocol {
2525
/** Unencrypted HTTP protocol. */
26-
HTTP,
26+
HTTP = "HTTP",
2727
/** Encrypted HTTPS protocol. */
28-
HTTPS
28+
HTTPS = "HTTPS"
2929
}
3030

3131
/** HTTP request or response headers. */
@@ -75,57 +75,170 @@ export class HttpHeaders {
7575
}
7676
}
7777

78+
/** HTTP request query parameters. */
79+
export class HttpQueryParameters {
80+
private parameters: Map<string, string[]>;
81+
82+
constructor(parameters?: { string: string | string[] }) {
83+
if (parameters) {
84+
this.parameters = new Map<string, string[]>();
85+
Object.keys(parameters).forEach(parameterName => {
86+
let values: string | string[] = parameters[parameterName];
87+
if (typeof values === "string") {
88+
values = [values];
89+
}
90+
if (values.length > 0) {
91+
this.parameters.set(parameterName.toLowerCase(), values);
92+
}
93+
});
94+
} else {
95+
this.parameters = new Map<string, string[]>();
96+
}
97+
}
98+
99+
/**
100+
* The first parameter value for the given parameter name, if any.
101+
*
102+
* @param parameterName The parameter name.
103+
*
104+
* @returns The first parameter value, or null if none.
105+
* @see #getAll
106+
*/
107+
get(parameterName: string): string | null {
108+
const values = this.parameters.get(parameterName.toLowerCase());
109+
return values && values.length > 0 ? values[0] : null;
110+
}
111+
112+
/**
113+
* All parameter values for the given parameter name.
114+
*
115+
* @param parameterName The parameter name.
116+
*
117+
* @returns a list of parameter values, or an empty list if none
118+
* @see #get
119+
*/
120+
getAll(parameterName: string): string[] {
121+
return (
122+
this.parameters.get(parameterName.toLowerCase()) || new Array<string>()
123+
);
124+
}
125+
}
126+
78127
/** HTTP request. */
79-
export class HttpRequest {
128+
export interface HttpRequest {
80129
timestamp?: Date;
81-
protocol: HttpProtocol;
82130
method: HttpMethod;
131+
protocol: HttpProtocol;
132+
host: string;
83133
headers: HttpHeaders;
84134
body?: string;
135+
path: string;
136+
pathname: string;
137+
query: HttpQueryParameters;
138+
}
85139

86-
public constructor(builder: HttpRequestBuilder) {
87-
this.timestamp = builder.timestamp;
88-
this.protocol = builder.protocol;
89-
this.method = builder.method;
90-
this.headers = builder.headers;
91-
this.body = builder.body;
92-
}
140+
/** HTTP request. */
141+
export interface HttpRequestFromPath {
142+
timestamp?: Date;
143+
method: HttpMethod;
144+
protocol: HttpProtocol;
145+
host: string;
146+
headers: HttpHeaders;
147+
body?: string;
148+
path: string;
93149
}
94150

95-
export class HttpRequestBuilder {
151+
/** HTTP request. */
152+
export interface HttpRequestFromPathNameAndQuery {
96153
timestamp?: Date;
97-
protocol?: HttpProtocol;
98-
method?: HttpMethod;
99-
headers?: HttpHeaders;
154+
method: HttpMethod;
155+
protocol: HttpProtocol;
156+
host: string;
157+
headers: HttpHeaders;
100158
body?: string;
159+
pathname: string;
160+
query: { string: string | string[] };
161+
}
101162

102-
withTimestamp(timestamp: Date): this {
103-
this.timestamp = timestamp;
104-
return this;
105-
}
163+
export class HttpRequestBuilder {
164+
static build(requestData: HttpRequestFromPath): HttpRequest {
165+
const url = new URL("file://" + requestData.path);
106166

107-
withProtocol(protocol: HttpProtocol): this {
108-
this.protocol = protocol;
109-
return this;
110-
}
167+
const queryMap = new Object();
168+
const queryString = url.search.substring(1);
169+
for (const entry of queryString.split("&")) {
170+
const pair = entry.split("=");
171+
const parameterName = decodeURIComponent(pair[0]);
172+
const parameterValue = decodeURIComponent(pair[1]);
111173

112-
withMethod(method: HttpMethod): this {
113-
this.method = method;
114-
return this;
115-
}
174+
let existingEntry = queryMap[parameterName] as string[];
175+
if (!existingEntry) {
176+
existingEntry = new Array<string>();
177+
queryMap[parameterName] = existingEntry;
178+
}
179+
existingEntry.push(parameterValue);
180+
}
181+
const query = new HttpQueryParameters(
182+
queryMap as { string: string | string[] }
183+
);
116184

117-
withBody(body: string): this {
118-
this.body = body;
119-
return this;
185+
return {
186+
timestamp: requestData.timestamp,
187+
method: requestData.method,
188+
protocol: requestData.protocol,
189+
host: requestData.host,
190+
headers: requestData.headers,
191+
body: requestData.body,
192+
path: requestData.path,
193+
pathname: url.pathname,
194+
query: query
195+
};
120196
}
197+
static buildFromPathnameAndQuery(
198+
requestData: HttpRequestFromPathNameAndQuery
199+
): HttpRequest {
200+
let path = requestData.pathname;
201+
if (requestData.query) {
202+
path += "?";
203+
let first = true;
204+
for (const key in requestData.query) {
205+
const value = requestData.query[key];
206+
if (value instanceof String) {
207+
if (first) {
208+
first = false;
209+
} else {
210+
path += "&";
211+
}
212+
path +=
213+
encodeURIComponent(key) +
214+
"=" +
215+
encodeURIComponent(value.toString());
216+
} else {
217+
for (const entry of value) {
218+
if (first) {
219+
first = false;
220+
} else {
221+
path += "&";
222+
}
223+
path += encodeURIComponent(key) + "=" + encodeURIComponent(entry);
224+
}
225+
}
226+
}
227+
}
121228

122-
withHeaders(headers: HttpHeaders): this {
123-
this.headers = headers;
124-
return this;
125-
}
229+
const query = new HttpQueryParameters(requestData.query);
126230

127-
build(): HttpRequest {
128-
return new HttpRequest(this);
231+
return {
232+
timestamp: requestData.timestamp,
233+
method: requestData.method,
234+
protocol: requestData.protocol,
235+
host: requestData.host,
236+
headers: requestData.headers,
237+
body: requestData.body,
238+
path: path,
239+
pathname: requestData.pathname,
240+
query: query
241+
};
129242
}
130243
}
131244

@@ -136,11 +249,9 @@ export interface HttpResponse {
136249
body?: string;
137250
}
138251

139-
export class HttpExchange {
140-
constructor(public request: HttpRequest, public response: HttpResponse) {
141-
this.request = request;
142-
this.response = response;
143-
}
252+
export interface HttpExchange {
253+
request: HttpRequest;
254+
response: HttpResponse;
144255
}
145256

146257
export class HttpExchangeReader {
@@ -158,13 +269,32 @@ export class HttpExchangeReader {
158269
HttpMethod[(parsedRequest.method as string).toUpperCase()];
159270
const requestHeaders: HttpHeaders = new HttpHeaders(parsedRequest.headers);
160271

161-
const request = new HttpRequestBuilder()
162-
.withTimestamp(requestTimestamp)
163-
.withProtocol(protocol)
164-
.withMethod(method)
165-
.withHeaders(requestHeaders)
166-
.withBody(parsedRequest.body)
167-
.build();
272+
let request: HttpRequest;
273+
274+
if (parsedRequest.path) {
275+
request = HttpRequestBuilder.build({
276+
timestamp: requestTimestamp,
277+
method: method,
278+
protocol: protocol,
279+
host: parsedRequest.host,
280+
headers: requestHeaders,
281+
body: parsedRequest.body,
282+
path: parsedRequest.path
283+
});
284+
} else if (parsedRequest.pathname) {
285+
request = HttpRequestBuilder.buildFromPathnameAndQuery({
286+
timestamp: requestTimestamp,
287+
method: method,
288+
protocol: protocol,
289+
host: parsedRequest.host,
290+
headers: requestHeaders,
291+
body: parsedRequest.body,
292+
pathname: parsedRequest.pathname,
293+
query: parsedRequest.query
294+
});
295+
} else {
296+
throw new Error("Either 'path' or 'pathname' is required");
297+
}
168298

169299
const responseTimestamp = parsedResponse.timestamp
170300
? new Date(parsedResponse.timestamp)
@@ -180,6 +310,6 @@ export class HttpExchangeReader {
180310
body: parsedResponse.body
181311
};
182312

183-
return new HttpExchange(request, response);
313+
return { request, response };
184314
}
185315
}

0 commit comments

Comments
 (0)