Skip to content

Commit 6221008

Browse files
committed
Merge branch 'main' into ext_node_http_upgrade
2 parents d2897cb + 44bd59c commit 6221008

File tree

7 files changed

+200
-26
lines changed

7 files changed

+200
-26
lines changed

cli/tests/integration/node_unit_tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ util::unit_test_factory!(
1919
_fs_close_test = _fs / _fs_close_test,
2020
_fs_copy_test = _fs / _fs_copy_test,
2121
_fs_dir_test = _fs / _fs_dir_test,
22+
_fs_dirent_test = _fs / _fs_dirent_test,
2223
_fs_exists_test = _fs / _fs_exists_test,
2324
_fs_fdatasync_test = _fs / _fs_fdatasync_test,
2425
_fs_fstat_test = _fs / _fs_fstat_test,
+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2+
import {
3+
assert,
4+
assertEquals,
5+
assertThrows,
6+
} from "../../../../test_util/std/testing/asserts.ts";
7+
import { Dirent as Dirent_ } from "node:fs";
8+
9+
// deno-lint-ignore no-explicit-any
10+
const Dirent = Dirent_ as any;
11+
12+
class DirEntryMock implements Deno.DirEntry {
13+
name = "";
14+
isFile = false;
15+
isDirectory = false;
16+
isSymlink = false;
17+
}
18+
19+
Deno.test({
20+
name: "Directories are correctly identified",
21+
fn() {
22+
const entry: DirEntryMock = new DirEntryMock();
23+
entry.isDirectory = true;
24+
entry.isFile = false;
25+
entry.isSymlink = false;
26+
assert(new Dirent(entry).isDirectory());
27+
assert(!new Dirent(entry).isFile());
28+
assert(!new Dirent(entry).isSymbolicLink());
29+
},
30+
});
31+
32+
Deno.test({
33+
name: "Files are correctly identified",
34+
fn() {
35+
const entry: DirEntryMock = new DirEntryMock();
36+
entry.isDirectory = false;
37+
entry.isFile = true;
38+
entry.isSymlink = false;
39+
assert(!new Dirent(entry).isDirectory());
40+
assert(new Dirent(entry).isFile());
41+
assert(!new Dirent(entry).isSymbolicLink());
42+
},
43+
});
44+
45+
Deno.test({
46+
name: "Symlinks are correctly identified",
47+
fn() {
48+
const entry: DirEntryMock = new DirEntryMock();
49+
entry.isDirectory = false;
50+
entry.isFile = false;
51+
entry.isSymlink = true;
52+
assert(!new Dirent(entry).isDirectory());
53+
assert(!new Dirent(entry).isFile());
54+
assert(new Dirent(entry).isSymbolicLink());
55+
},
56+
});
57+
58+
Deno.test({
59+
name: "File name is correct",
60+
fn() {
61+
const entry: DirEntryMock = new DirEntryMock();
62+
entry.name = "my_file";
63+
assertEquals(new Dirent(entry).name, "my_file");
64+
},
65+
});
66+
67+
Deno.test({
68+
name: "Socket and FIFO pipes aren't yet available",
69+
fn() {
70+
const entry: DirEntryMock = new DirEntryMock();
71+
assertThrows(
72+
() => {
73+
new Dirent(entry).isFIFO();
74+
},
75+
Error,
76+
"does not yet support",
77+
);
78+
assertThrows(
79+
() => {
80+
new Dirent(entry).isSocket();
81+
},
82+
Error,
83+
"does not yet support",
84+
);
85+
},
86+
});

cli/tests/unit_node/http_test.ts

+32
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import https from "node:https";
66
import {
77
assert,
88
assertEquals,
9+
fail,
910
} from "../../../test_util/std/testing/asserts.ts";
1011
import { assertSpyCalls, spy } from "../../../test_util/std/testing/mock.ts";
1112
import { deferred } from "../../../test_util/std/async/deferred.ts";
@@ -618,6 +619,37 @@ Deno.test("[node/http] ClientRequest search params", async () => {
618619
assertEquals(body, "foo=bar");
619620
});
620621

622+
Deno.test("[node/http] HTTPS server", async () => {
623+
const promise = deferred<void>();
624+
const promise2 = deferred<void>();
625+
const client = Deno.createHttpClient({
626+
caCerts: [Deno.readTextFileSync("cli/tests/testdata/tls/RootCA.pem")],
627+
});
628+
const server = https.createServer({
629+
cert: Deno.readTextFileSync("cli/tests/testdata/tls/localhost.crt"),
630+
key: Deno.readTextFileSync("cli/tests/testdata/tls/localhost.key"),
631+
}, (_req, res) => {
632+
res.end("success!");
633+
});
634+
server.listen(() => {
635+
// deno-lint-ignore no-explicit-any
636+
fetch(`https://localhost:${(server.address() as any).port}`, {
637+
client,
638+
}).then(async (res) => {
639+
assertEquals(res.status, 200);
640+
assertEquals(await res.text(), "success!");
641+
server.close();
642+
promise2.resolve();
643+
});
644+
})
645+
.on("error", () => fail());
646+
server.on("close", () => {
647+
promise.resolve();
648+
});
649+
await Promise.all([promise, promise2]);
650+
client.close();
651+
});
652+
621653
Deno.test(
622654
"[node/http] client upgrade",
623655
{ permissions: { net: true } },

ext/fetch/lib.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use deno_core::op;
2323
use deno_core::BufView;
2424
use deno_core::WriteOutcome;
2525

26+
use deno_core::task::spawn;
2627
use deno_core::url::Url;
2728
use deno_core::AsyncRefCell;
2829
use deno_core::AsyncResult;
@@ -58,6 +59,8 @@ use reqwest::RequestBuilder;
5859
use reqwest::Response;
5960
use serde::Deserialize;
6061
use serde::Serialize;
62+
use tokio::io::AsyncReadExt;
63+
use tokio::io::AsyncWriteExt;
6164
use tokio::sync::mpsc;
6265

6366
// Re-export reqwest and data_url
@@ -512,8 +515,6 @@ pub fn op_fetch_response_into_byte_stream(
512515
Ok(rid)
513516
}
514517

515-
use deno_core::task::spawn;
516-
517518
#[op]
518519
pub async fn op_fetch_response_upgrade(
519520
state: Rc<RefCell<OpState>>,
@@ -566,9 +567,6 @@ pub async fn op_fetch_response_upgrade(
566567
)
567568
}
568569

569-
use tokio::io::AsyncReadExt;
570-
use tokio::io::AsyncWriteExt;
571-
572570
struct UpgradeStream {
573571
read: AsyncRefCell<tokio::io::ReadHalf<tokio::io::DuplexStream>>,
574572
write: AsyncRefCell<tokio::io::WriteHalf<tokio::io::DuplexStream>>,

ext/http/http_next.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -376,12 +376,17 @@ pub fn op_http_read_request_body(
376376
}
377377

378378
#[op(fast)]
379-
pub fn op_http_set_response_header(slab_id: SlabId, name: &str, value: &str) {
379+
pub fn op_http_set_response_header(
380+
slab_id: SlabId,
381+
name: ByteString,
382+
value: ByteString,
383+
) {
380384
let mut http = slab_get(slab_id);
381385
let resp_headers = http.response().headers_mut();
382386
// These are valid latin-1 strings
383-
let name = HeaderName::from_bytes(name.as_bytes()).unwrap();
384-
let value = HeaderValue::from_bytes(value.as_bytes()).unwrap();
387+
let name = HeaderName::from_bytes(&name).unwrap();
388+
// SAFETY: These are valid latin-1 strings
389+
let value = unsafe { HeaderValue::from_maybe_shared_unchecked(value) };
385390
resp_headers.append(name, value);
386391
}
387392

@@ -410,7 +415,9 @@ fn op_http_set_response_headers(
410415
let v8_name: ByteString = from_v8(scope, name).unwrap();
411416
let v8_value: ByteString = from_v8(scope, value).unwrap();
412417
let header_name = HeaderName::from_bytes(&v8_name).unwrap();
413-
let header_value = HeaderValue::from_bytes(&v8_value).unwrap();
418+
let header_value =
419+
// SAFETY: These are valid latin-1 strings
420+
unsafe { HeaderValue::from_maybe_shared_unchecked(v8_value) };
414421
resp_headers.append(header_name, header_value);
415422
}
416423
}
@@ -425,7 +432,8 @@ pub fn op_http_set_response_trailers(
425432
for (name, value) in trailers {
426433
// These are valid latin-1 strings
427434
let name = HeaderName::from_bytes(&name).unwrap();
428-
let value = HeaderValue::from_bytes(&value).unwrap();
435+
// SAFETY: These are valid latin-1 strings
436+
let value = unsafe { HeaderValue::from_maybe_shared_unchecked(value) };
429437
trailer_map.append(name, value);
430438
}
431439
*http.trailers().borrow_mut() = Some(trailer_map);

ext/node/polyfills/http.ts

+25-11
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { nextTick } from "ext:deno_node/_next_tick.ts";
1818
import {
1919
validateBoolean,
2020
validateInteger,
21+
validateObject,
2122
validatePort,
2223
} from "ext:deno_node/internal/validators.mjs";
2324
import {
@@ -1507,16 +1508,16 @@ export class IncomingMessageForServer extends NodeReadable {
15071508
}
15081509
}
15091510

1510-
type ServerHandler = (
1511+
export type ServerHandler = (
15111512
req: IncomingMessageForServer,
15121513
res: ServerResponse,
15131514
) => void;
15141515

1515-
export function Server(handler?: ServerHandler): ServerImpl {
1516-
return new ServerImpl(handler);
1516+
export function Server(opts, requestListener?: ServerHandler): ServerImpl {
1517+
return new ServerImpl(opts, requestListener);
15171518
}
15181519

1519-
class ServerImpl extends EventEmitter {
1520+
export class ServerImpl extends EventEmitter {
15201521
#httpConnections: Set<Deno.HttpConn> = new Set();
15211522
#listener?: Deno.Listener;
15221523

@@ -1528,12 +1529,24 @@ class ServerImpl extends EventEmitter {
15281529
#servePromise: Deferred<void>;
15291530
listening = false;
15301531

1531-
constructor(handler?: ServerHandler) {
1532+
constructor(opts, requestListener?: ServerHandler) {
15321533
super();
1534+
1535+
if (typeof opts === "function") {
1536+
requestListener = opts;
1537+
opts = kEmptyObject;
1538+
} else if (opts == null) {
1539+
opts = kEmptyObject;
1540+
} else {
1541+
validateObject(opts, "options");
1542+
}
1543+
1544+
this._opts = opts;
1545+
15331546
this.#servePromise = deferred();
15341547
this.#servePromise.then(() => this.emit("close"));
1535-
if (handler !== undefined) {
1536-
this.on("request", handler);
1548+
if (requestListener !== undefined) {
1549+
this.on("request", requestListener);
15371550
}
15381551
}
15391552

@@ -1562,12 +1575,12 @@ class ServerImpl extends EventEmitter {
15621575
port,
15631576
} as Deno.NetAddr;
15641577
this.listening = true;
1565-
nextTick(() => this.#serve());
1578+
nextTick(() => this._serve());
15661579

15671580
return this;
15681581
}
15691582

1570-
#serve() {
1583+
_serve() {
15711584
const ac = new AbortController();
15721585
const handler = (request: Request, info: Deno.ServeHandlerInfo) => {
15731586
const req = new IncomingMessageForServer(request, info.remoteAddr);
@@ -1600,6 +1613,7 @@ class ServerImpl extends EventEmitter {
16001613
this.#addr!.port = port;
16011614
this.emit("listening");
16021615
},
1616+
...this._additionalServeOptions?.(),
16031617
},
16041618
);
16051619
if (this.#unref) {
@@ -1662,8 +1676,8 @@ class ServerImpl extends EventEmitter {
16621676

16631677
Server.prototype = ServerImpl.prototype;
16641678

1665-
export function createServer(handler?: ServerHandler) {
1666-
return Server(handler);
1679+
export function createServer(opts, requestListener?: ServerHandler) {
1680+
return Server(opts, requestListener);
16671681
}
16681682

16691683
/** Makes an HTTP request. */

ext/node/polyfills/https.ts

+40-5
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,49 @@ import {
1010
} from "ext:deno_node/http.ts";
1111
import { Agent as HttpAgent } from "ext:deno_node/_http_agent.mjs";
1212
import { createHttpClient } from "ext:deno_fetch/22_http_client.js";
13+
import {
14+
type ServerHandler,
15+
ServerImpl as HttpServer,
16+
} from "ext:deno_node/http.ts";
17+
import { validateObject } from "ext:deno_node/internal/validators.mjs";
18+
import { kEmptyObject } from "ext:deno_node/internal/util.mjs";
19+
import { Buffer } from "ext:deno_node/buffer.ts";
20+
21+
export class Server extends HttpServer {
22+
constructor(opts, requestListener?: ServerHandler) {
23+
if (typeof opts === "function") {
24+
requestListener = opts;
25+
opts = kEmptyObject;
26+
} else if (opts == null) {
27+
opts = kEmptyObject;
28+
} else {
29+
validateObject(opts, "options");
30+
}
31+
32+
if (opts.cert && Array.isArray(opts.cert)) {
33+
notImplemented("https.Server.opts.cert array type");
34+
}
1335

14-
export class Server {
15-
constructor() {
16-
notImplemented("https.Server.prototype.constructor");
36+
if (opts.key && Array.isArray(opts.key)) {
37+
notImplemented("https.Server.opts.key array type");
38+
}
39+
40+
super(opts, requestListener);
41+
}
42+
43+
_additionalServeOptions() {
44+
return {
45+
cert: this._opts.cert instanceof Buffer
46+
? this._opts.cert.toString()
47+
: this._opts.cert,
48+
key: this._opts.key instanceof Buffer
49+
? this._opts.key.toString()
50+
: this._opts.key,
51+
};
1752
}
1853
}
19-
export function createServer() {
20-
notImplemented("https.createServer");
54+
export function createServer(opts, requestListener?: ServerHandler) {
55+
return new Server(opts, requestListener);
2156
}
2257

2358
interface HttpsRequestOptions extends RequestOptions {

0 commit comments

Comments
 (0)