-
Notifications
You must be signed in to change notification settings - Fork 165
Expand file tree
/
Copy pathparse.ts
More file actions
84 lines (76 loc) · 2.21 KB
/
Copy pathparse.ts
File metadata and controls
84 lines (76 loc) · 2.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
export interface ParsedHttpRequest {
headers: Record<string, string>;
method: string;
path: string;
rawBody: Uint8Array;
}
const CR = 0x0d;
const LF = 0x0a;
export function mergeHeaderValue(
headers: Record<string, string>,
name: string,
value: string
): void {
const lowerName = name.toLowerCase();
headers[lowerName] = Object.hasOwn(headers, lowerName)
? `${headers[lowerName]}, ${value}`
: value;
}
function findHeaderEnd(bytes: Uint8Array): number {
for (let i = 0; i + 3 < bytes.length; i++) {
if (
bytes[i] === CR &&
bytes[i + 1] === LF &&
bytes[i + 2] === CR &&
bytes[i + 3] === LF
) {
return i;
}
}
return -1;
}
/**
* Parses the HTTP/1.1 wire-format request carried by the WebSocket protobuf
* transport. HTTP JSON deliveries already contain these parsed fields. Headers
* are lowercased; repeated values are joined with ", " (RFC 7230 §3.2.2).
*/
export function parseHttpRequest(bytes: Uint8Array): ParsedHttpRequest {
const headerEnd = findHeaderEnd(bytes);
if (headerEnd < 0) {
throw new Error("fusor: raw_request missing CRLFCRLF header terminator");
}
const headerText = new TextDecoder("utf-8").decode(
bytes.subarray(0, headerEnd)
);
const rawBody = bytes.subarray(headerEnd + 4);
const lines = headerText.split("\r\n");
const requestLine = lines[0];
if (!requestLine) {
throw new Error("fusor: raw_request missing request line");
}
const firstSpace = requestLine.indexOf(" ");
const lastSpace = requestLine.lastIndexOf(" ");
if (firstSpace < 0 || lastSpace <= firstSpace) {
throw new Error(`fusor: malformed request line: ${requestLine}`);
}
const method = requestLine.slice(0, firstSpace);
const path = requestLine.slice(firstSpace + 1, lastSpace);
const headers: Record<string, string> = {};
for (let i = 1; i < lines.length; i++) {
const line = lines[i];
if (!line) {
continue;
}
const colon = line.indexOf(":");
if (colon < 0) {
continue;
}
const key = line.slice(0, colon).trim();
const value = line.slice(colon + 1).trim();
if (!key) {
continue;
}
mergeHeaderValue(headers, key, value);
}
return { method, path, headers, rawBody };
}