-
-
Notifications
You must be signed in to change notification settings - Fork 454
Expand file tree
/
Copy pathheaders.ts
More file actions
123 lines (105 loc) · 3.47 KB
/
headers.ts
File metadata and controls
123 lines (105 loc) · 3.47 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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import {toBase64} from "@lodestar/utils";
export enum WireFormat {
json = "json",
ssz = "ssz",
}
export enum MediaType {
json = "application/json",
ssz = "application/octet-stream",
}
export function getWireFormat(mediaType: MediaType): WireFormat {
switch (mediaType) {
case MediaType.json:
return WireFormat.json;
case MediaType.ssz:
return WireFormat.ssz;
}
}
export const supportedMediaTypes = Object.values(MediaType);
function isSupportedMediaType(mediaType: string | null): mediaType is MediaType {
return mediaType !== null && supportedMediaTypes.includes(mediaType as MediaType);
}
export function parseContentTypeHeader(contentType?: string): MediaType | null {
if (!contentType) {
return null;
}
const mediaType = contentType.split(";", 1)[0].trim().toLowerCase();
return isSupportedMediaType(mediaType) ? mediaType : null;
}
export function parseAcceptHeader(accept?: string): MediaType | null {
if (!accept) {
return null;
}
// Respect Quality Values per RFC-9110
// Acceptable mime-types are comma separated with optional whitespace
return accept
.toLowerCase()
.split(",")
.map((x) => x.trim())
.reduce(
(best: [number, MediaType | null], current: string): [number, MediaType | null] => {
// An optional `;` delimiter is used to separate the mime-type from the weight
// Normalize here, using 1 as the default qvalue
const quality = current.includes(";") ? current.split(";") : [current, "q=1"];
const mediaType = quality[0].trim();
// If the mime type isn't acceptable, move on to the next entry
if (!isSupportedMediaType(mediaType)) {
return best;
}
// Otherwise, the portion after the semicolon has optional whitespace and the constant prefix "q="
const weight = quality[1].trim();
if (!weight.startsWith("q=")) {
// If the format is invalid simply move on to the next entry
return best;
}
const qvalue = +weight.replace("q=", "");
if (isNaN(qvalue) || qvalue > 1 || qvalue <= 0) {
// If we can't convert the qvalue to a valid number, move on
return best;
}
if (qvalue < best[0]) {
// This mime type is not preferred
return best;
}
// This mime type is preferred
return [qvalue, mediaType];
},
[0, null]
)[1];
}
export function setAuthorizationHeader(url: URL, headers: Headers, {bearerToken}: {bearerToken?: string}): void {
if (bearerToken && !headers.has("Authorization")) {
headers.set("Authorization", `Bearer ${bearerToken}`);
}
if (url.username || url.password) {
if (!headers.has("Authorization")) {
headers.set("Authorization", `Basic ${toBase64(`${url.username}:${url.password}`)}`);
}
// Remove the username and password from the URL
url.username = "";
url.password = "";
}
}
export function mergeHeaders(a: HeadersInit | undefined, b: HeadersInit | undefined): Headers {
if (!a) {
return new Headers(b);
}
const headers = new Headers(a);
if (!b) {
return headers;
}
if (Array.isArray(b)) {
for (const [key, value] of b) {
headers.set(key, value);
}
} else if (b instanceof Headers) {
for (const [key, value] of b as unknown as Iterable<[string, string]>) {
headers.set(key, value);
}
} else {
for (const [key, value] of Object.entries(b)) {
headers.set(key, value);
}
}
return headers;
}