Skip to content

Commit 28caf45

Browse files
authored
Merge pull request #36 from yossydev/feat/fetch-headers
feat: Headers constructor and get method
2 parents c09644e + 515a915 commit 28caf45

File tree

11 files changed

+193
-33
lines changed

11 files changed

+193
-33
lines changed

examples/atob.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ const validBase64 = "YQ==";
22
console.log("validBase64: ", atob(validBase64)); // "a"
33

44
const validBase64Multiple = "SGVsbG8sIEFuZHJvbWVkYSE=";
5-
console.log("validBase64Multiple: ", atob(validBase64Multiple)); // "Hello, Andromeda!"
5+
console.log("validBase64Multiple: ", atob(validBase64Multiple)); // "Hello, Andromeda!"

examples/headers.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const httpHeaders = {
2+
"Content-Type": "image/jpeg",
3+
"X-My-Custom-Header": "Zeke are cool",
4+
};
5+
const myHeaders = new Headers(httpHeaders);
6+
console.log("myHeaders", myHeaders.get("Content-Type")); // image/jpeg
7+
console.log("myHeaders", myHeaders.get("X-My-Custom-Header")); // Zeke are cool
8+
9+
const headers2 = [
10+
["Set-Cookie", "greeting=hello"],
11+
["Set-Cookie", "name=world"],
12+
];
13+
const myHeaders2 = new Headers(headers2);
14+
console.log("myHeaders2", myHeaders2); // TODO Headers { 'Set-Cookie': 'greeting=hello, name=world' } but [object Object]
15+
console.log("myHeaders", myHeaders2.get("Set-Cookie")); // greeting=hellogreeting=hello,name=worldname=world

examples/url.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
const baseUrl = "https://developer.mozilla.org";
44

55
const A = new URL("/", baseUrl); // 'https://developer.mozilla.org/'
6-
console.log(A)
6+
console.log(A);
77

88
const B = new URL(baseUrl); // 'https://developer.mozilla.org/'
9-
console.log(B)
9+
console.log(B);
1010

1111
console.log(new URL("en-US/docs", B)); // 'https://developer.mozilla.org/en-US/docs'
1212

1313
const D = new URL("/en-US/docs", B); // 'https://developer.mozilla.org/en-US/docs'
14-
console.log(D)
14+
console.log(D);
1515

1616
console.log(new URL("/en-US/docs", D)); // 'https://developer.mozilla.org/en-US/docs'
1717

@@ -50,4 +50,4 @@ if ("parse" in URL) {
5050
// console.log(`[5]: ${result}`);
5151
} else {
5252
console.log("URL.parse() not supported");
53-
}
53+
}

namespace/mod.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,20 +298,20 @@ function alert(message: string) {
298298
}
299299

300300
/**
301-
* Takes the input data, in the form of a Unicode string containing only characters in the range U+0000 to U+00FF,
302-
* each representing a binary byte with values 0x00 to 0xFF respectively, and converts it to its base64 representation,
301+
* Takes the input data, in the form of a Unicode string containing only characters in the range U+0000 to U+00FF,
302+
* each representing a binary byte with values 0x00 to 0xFF respectively, and converts it to its base64 representation,
303303
* which it returns.
304304
*/
305305
function btoa(input: string): string {
306306
return internal_btoa(input);
307307
}
308308

309309
/**
310-
* Takes the input data, in the form of a Unicode string containing base64-encoded binary data,
311-
* decodes it, and returns a string consisting of characters in the range U+0000 to U+00FF,
312-
* each representing a binary byte with values 0x00 to 0xFF respectively,
310+
* Takes the input data, in the form of a Unicode string containing base64-encoded binary data,
311+
* decodes it, and returns a string consisting of characters in the range U+0000 to U+00FF,
312+
* each representing a binary byte with values 0x00 to 0xFF respectively,
313313
* corresponding to that binary data.
314314
*/
315315
function atob(input: string): string {
316316
return internal_atob(input);
317-
}
317+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use andromeda_core::Extension;
2+
3+
#[derive(Default)]
4+
pub struct HeadersExt;
5+
6+
impl HeadersExt {
7+
pub fn new_extension() -> Extension {
8+
Extension {
9+
name: "headers",
10+
ops: vec![],
11+
storage: None,
12+
files: vec![include_str!("./mod.ts")],
13+
}
14+
}
15+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
class Headers {
2+
// TODO: Private properties
3+
// #guard
4+
// #headerList
5+
6+
// TODO: this is HeaderList type
7+
// https://fetch.spec.whatwg.org/#headers-class
8+
constructor(init = undefined) {
9+
// @ts-ignore
10+
this.guard = "none";
11+
// @ts-ignore
12+
this.headerList = [];
13+
fillHeaders(this, init);
14+
}
15+
16+
// https://fetch.spec.whatwg.org/#dom-headers-get
17+
get(name: string) {
18+
// @ts-ignore
19+
return getHeader(this.headerList, name);
20+
}
21+
}
22+
23+
function fillHeaders(headers: Headers, object) {
24+
if (Array.isArray(object)) {
25+
for (let i = 0; i < object.length; ++i) {
26+
const header = object[i];
27+
if (header.length !== 2) {
28+
throw new TypeError(
29+
`Invalid header: length must be 2, but is ${header.length}`,
30+
);
31+
}
32+
appendHeader(headers, header[0], header[1]);
33+
}
34+
} else {
35+
for (const key in object) {
36+
if (!Object.hasOwn(object, key)) {
37+
continue;
38+
}
39+
appendHeader(headers, key, object[key]);
40+
}
41+
}
42+
}
43+
44+
function byteLowerCase(s) {
45+
// NOTE: correct since all callers convert to ByteString first
46+
// TODO(@AaronO): maybe prefer a ByteString_Lower webidl converter
47+
return s;
48+
}
49+
50+
// https://fetch.spec.whatwg.org/#concept-headers-append
51+
function appendHeader(headers, name, value) {
52+
// 1.
53+
value = normalizeHeaderValue(value);
54+
55+
// 2. TODO
56+
// if (!checkHeaderNameForHttpTokenCodePoint(name)) {
57+
// throw new TypeError(`Invalid header name: "${name}"`);
58+
// }
59+
// if (!checkForInvalidValueChars(value)) {
60+
// throw new TypeError(`Invalid header value: "${value}"`);
61+
// }
62+
63+
// 3
64+
if (headers.guard == "immutable") {
65+
throw new TypeError("Cannot change header: headers are immutable");
66+
}
67+
68+
// 7.
69+
const list = headers.headerList;
70+
const lowercaseName = byteLowerCase(name);
71+
for (let i = 0; i < list.length; i++) {
72+
if (byteLowerCase(list[i][0]) === lowercaseName) {
73+
name = list[i][0];
74+
break;
75+
}
76+
}
77+
list.push([name, value]);
78+
}
79+
80+
function normalizeHeaderValue(potentialValue) {
81+
return httpTrim(potentialValue);
82+
}
83+
84+
// TODO: move to web
85+
function isHttpWhitespace(char) {
86+
switch (char) {
87+
case "\u0009":
88+
case "\u000A":
89+
case "\u000D":
90+
case "\u0020":
91+
return true;
92+
default:
93+
return false;
94+
}
95+
}
96+
97+
// const HTTP_BETWEEN_WHITESPACE = new SafeRegExp(
98+
// `^[${HTTP_WHITESPACE_MATCHER}]*(.*?)[${HTTP_WHITESPACE_MATCHER}]*$`,
99+
// );
100+
// TODO: move to web
101+
function httpTrim(s) {
102+
if (!isHttpWhitespace(s[0]) && !isHttpWhitespace(s[s.length - 1])) {
103+
return s;
104+
}
105+
// return String.prototype.match(s, HTTP_BETWEEN_WHITESPACE)?.[1] ?? "";
106+
// TODO: implement to nova RegExp
107+
return s;
108+
}
109+
110+
// https://fetch.spec.whatwg.org/#concept-header-list-get
111+
function getHeader(list, name) {
112+
const lowercaseName = byteLowerCase(name);
113+
const entries = [];
114+
for (let i = 0; i < list.length; i++) {
115+
if (byteLowerCase(list[i][0]) === lowercaseName) {
116+
entries.push(list[i][1]);
117+
}
118+
}
119+
120+
if (entries.length === 0) {
121+
return null;
122+
} else {
123+
return entries.join(entries, "\x2C\x20");
124+
}
125+
}

runtime/src/ext/fetch/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mod headers;
2+
3+
pub use headers::*;

runtime/src/ext/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
mod console;
2+
mod fetch;
23
mod fs;
34
mod process;
45
mod time;
56
mod url;
67
mod web;
78

89
pub use console::*;
10+
pub use fetch::*;
911
pub use fs::*;
1012
pub use process::*;
1113
pub use time::*;

runtime/src/ext/url/mod.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
// deno-lint-ignore-file no-unused-vars
22
class URL {
3-
// TODO: Need to return a URL object.
4-
constructor(url: string, base?: string) {
5-
// @ts-ignore - this is a hack to make the URL object work
6-
this.url = url;
7-
// @ts-ignore - this is a hack to make the Base URL object work
8-
this.base = base;
9-
// @ts-ignore - this is a hack to make the URL object work
10-
this.serialized = base
11-
? internal_url_parse(url, base)
12-
: internal_url_parse_no_base(url);
13-
}
3+
// TODO: Need to return a URL object.
4+
constructor(url: string, base?: string) {
5+
// @ts-ignore - this is a hack to make the URL object work
6+
this.url = url;
7+
// @ts-ignore - this is a hack to make the Base URL object work
8+
this.base = base;
9+
// @ts-ignore - this is a hack to make the URL object work
10+
this.serialized = base
11+
? internal_url_parse(url, base)
12+
: internal_url_parse_no_base(url);
13+
}
1414

15-
toString() {
16-
// @ts-ignore - this is a hack to make the URL object work
17-
return this.serialized;
18-
}
15+
toString() {
16+
// @ts-ignore - this is a hack to make the URL object work
17+
return this.serialized;
18+
}
1919

20-
static parse(url: string, base?: string) {
21-
return new this(url, base)
22-
}
23-
}
20+
static parse(url: string, base?: string) {
21+
return new this(url, base);
22+
}
23+
}

runtime/src/recommended.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use andromeda_core::{Extension, HostData};
22
use nova_vm::ecmascript::execution::agent::{GcAgent, RealmRoot};
33

4-
use crate::{ConsoleExt, FsExt, ProcessExt, RuntimeMacroTask, TimeExt, URLExt, WebExt};
4+
use crate::{ConsoleExt, FsExt, HeadersExt, ProcessExt, RuntimeMacroTask, TimeExt, URLExt, WebExt};
55

66
pub fn recommended_extensions() -> Vec<Extension> {
77
vec![
@@ -11,6 +11,7 @@ pub fn recommended_extensions() -> Vec<Extension> {
1111
ProcessExt::new_extension(),
1212
URLExt::new_extension(),
1313
WebExt::new_extension(),
14+
HeadersExt::new_extension(),
1415
]
1516
}
1617

0 commit comments

Comments
 (0)