Skip to content

Commit 90c8de2

Browse files
authored
fix(ext/runtime): expose Deno.createHttpClient (#515)
1 parent 49c0d59 commit 90c8de2

File tree

3 files changed

+171
-0
lines changed

3 files changed

+171
-0
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { assertEquals } from "jsr:@std/assert";
2+
3+
export default {
4+
async fetch(req: Request) {
5+
const port = parseInt(req.headers.get("x-port") ?? "");
6+
if (isNaN(port)) {
7+
return new Response(null, {
8+
status: 500,
9+
});
10+
}
11+
12+
const caCerts = [];
13+
14+
if (req.method === "POST") {
15+
const arr = await req.arrayBuffer();
16+
const dec = new TextDecoder();
17+
const ca = dec.decode(arr);
18+
caCerts.push(ca);
19+
}
20+
21+
const client = Deno.createHttpClient({
22+
caCerts,
23+
});
24+
25+
try {
26+
const resp = await fetch(`https://localhost:${port}`, { client });
27+
assertEquals(resp.status, 200);
28+
return new Response(await resp.text());
29+
} catch (ex) {
30+
if (ex instanceof TypeError) {
31+
return new Response(ex.toString(), {
32+
status: 500,
33+
});
34+
}
35+
36+
return new Response(null, {
37+
status: 500,
38+
});
39+
}
40+
},
41+
};

crates/base/tests/integration_tests.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use base::worker;
3535
use base::worker::TerminationToken;
3636
use base::WorkerKind;
3737
use deno::DenoOptionsBuilder;
38+
use deno_core::error::AnyError;
3839
use deno_core::serde_json::json;
3940
use deno_core::serde_json::{self};
4041
use deno_facade::generate_binary_eszip;
@@ -79,14 +80,17 @@ use tokio::io::AsyncReadExt;
7980
use tokio::io::AsyncWrite;
8081
use tokio::io::AsyncWriteExt;
8182
use tokio::join;
83+
use tokio::net::TcpListener;
8284
use tokio::net::TcpStream;
8385
use tokio::sync::mpsc;
8486
use tokio::sync::oneshot;
8587
use tokio::time::sleep;
8688
use tokio::time::timeout;
89+
use tokio_rustls::rustls;
8790
use tokio_rustls::rustls::pki_types::ServerName;
8891
use tokio_rustls::rustls::ClientConfig;
8992
use tokio_rustls::rustls::RootCertStore;
93+
use tokio_rustls::TlsAcceptor;
9094
use tokio_rustls::TlsConnector;
9195
use tokio_util::compat::TokioAsyncReadCompatExt;
9296
use tokio_util::sync::CancellationToken;
@@ -2309,6 +2313,109 @@ async fn test_declarative_style_fetch_handler() {
23092313
);
23102314
}
23112315

2316+
#[tokio::test]
2317+
#[serial]
2318+
async fn test_issue_208() {
2319+
async fn create_simple_server(
2320+
tls: Option<Tls>,
2321+
token: CancellationToken,
2322+
) -> Result<(), AnyError> {
2323+
let config = Arc::new(tls.server_config());
2324+
let acceptor = TlsAcceptor::from(config);
2325+
let listener =
2326+
TcpListener::bind(format!("127.0.0.1:{}", tls.port())).await?;
2327+
loop {
2328+
let acceptor = acceptor.clone();
2329+
tokio::select! {
2330+
Ok((stream, _)) = listener.accept() => {
2331+
tokio::spawn(async move {
2332+
if let Ok(tls_stream) = acceptor.accept(stream).await {
2333+
let _ = hyper::server::conn::Http::new().serve_connection(
2334+
tls_stream,
2335+
hyper::service::service_fn(|_req: _| async {
2336+
Ok::<_, hyper::Error>(hyper::Response::new(Body::from("meow")))
2337+
})
2338+
)
2339+
.await
2340+
.ok();
2341+
}
2342+
});
2343+
}
2344+
_ = token.cancelled() => {
2345+
break;
2346+
}
2347+
}
2348+
}
2349+
Ok(())
2350+
}
2351+
2352+
let tls = new_localhost_tls(true);
2353+
let port = tls.port();
2354+
let token = CancellationToken::new();
2355+
let server = tokio::spawn({
2356+
let token = token.clone();
2357+
async move {
2358+
create_simple_server(tls, token).await.unwrap();
2359+
}
2360+
});
2361+
2362+
{
2363+
let client = Client::new();
2364+
let builder = client
2365+
.request(
2366+
Method::POST,
2367+
format!("http://localhost:{}/issue-208", NON_SECURE_PORT),
2368+
)
2369+
.header("x-port", port)
2370+
.body(TLS_LOCALHOST_ROOT_CA);
2371+
2372+
integration_test!(
2373+
"./test_cases/main",
2374+
NON_SECURE_PORT,
2375+
"",
2376+
None,
2377+
Some(builder),
2378+
None,
2379+
(|resp| async {
2380+
let resp = resp.unwrap();
2381+
assert!(resp.status().as_u16() == 200);
2382+
assert_eq!(resp.text().await.unwrap(), "meow");
2383+
}),
2384+
TerminationToken::new()
2385+
);
2386+
}
2387+
2388+
// unknown issuer
2389+
{
2390+
let client = Client::new();
2391+
let builder = client
2392+
.request(
2393+
Method::GET,
2394+
format!("http://localhost:{}/issue-208", NON_SECURE_PORT),
2395+
)
2396+
.header("x-port", port);
2397+
2398+
integration_test!(
2399+
"./test_cases/main",
2400+
NON_SECURE_PORT,
2401+
"",
2402+
None,
2403+
Some(builder),
2404+
None,
2405+
(|resp| async {
2406+
let resp = resp.unwrap();
2407+
assert!(resp.status().as_u16() == 500);
2408+
let reason = resp.text().await.unwrap();
2409+
assert!(reason.contains("invalid peer certificate: UnknownIssuer"));
2410+
}),
2411+
TerminationToken::new()
2412+
);
2413+
}
2414+
2415+
token.cancel();
2416+
server.await.unwrap();
2417+
}
2418+
23122419
#[tokio::test]
23132420
#[serial]
23142421
async fn test_issue_420() {
@@ -3713,6 +3820,7 @@ trait TlsExt {
37133820
fn sock_addr(&self) -> SocketAddr;
37143821
fn port(&self) -> u16;
37153822
fn stream(&self) -> BoxFuture<'static, Box<dyn AsyncReadWrite>>;
3823+
fn server_config(&self) -> rustls::ServerConfig;
37163824
}
37173825

37183826
impl TlsExt for Option<Tls> {
@@ -3792,6 +3900,26 @@ impl TlsExt for Option<Tls> {
37923900
}
37933901
.boxed()
37943902
}
3903+
3904+
fn server_config(&self) -> rustls::ServerConfig {
3905+
assert!(self.is_some());
3906+
let certs =
3907+
rustls_pemfile::certs(&mut std::io::BufReader::new(TLS_LOCALHOST_CERT))
3908+
.flatten()
3909+
.collect();
3910+
let key = rustls_pemfile::private_key(&mut std::io::BufReader::new(
3911+
TLS_LOCALHOST_KEY,
3912+
))
3913+
.into_iter()
3914+
.flatten()
3915+
.next()
3916+
.unwrap();
3917+
3918+
rustls::ServerConfig::builder()
3919+
.with_no_client_auth()
3920+
.with_single_cert(certs, key)
3921+
.unwrap()
3922+
}
37953923
}
37963924

37973925
fn new_localhost_tls(secure: bool) -> Option<Tls> {

ext/runtime/js/denoOverrides.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as net from "ext:deno_net/01_net.js";
22
import * as tls from "ext:deno_net/02_tls.js";
33
import * as timers from "ext:deno_web/02_timers.js";
44
import * as fs from "ext:deno_fs/30_fs.js";
5+
import { createHttpClient } from "ext:deno_fetch/22_http_client.js";
56
import { osCalls } from "ext:os/os.js";
67
import * as io from "ext:deno_io/12_io.js";
78
import * as permissions from "ext:runtime/permissions.js";
@@ -95,6 +96,7 @@ const ioVars = {
9596
};
9697

9798
const denoOverrides = {
99+
createHttpClient,
98100
serve,
99101
serveHttp,
100102
upgradeWebSocket,

0 commit comments

Comments
 (0)