Skip to content

Commit 9f26ca4

Browse files
authored
feat(ext/http): Make http server parameters configurable (#26785)
This commit makes http server parameters configurable on the extension initialization via two callbacks users can provide. The main motivation behind this change is to allow `deno_http` users to tune the HTTP/2 server to suit their needs, although Deno CLI users will not benefit from it as no JavaScript interface is exposed to set these parameters currently. It is up to users whether to provide hook functions. If not provided, the default configuration from hyper crate will be used.
1 parent df1d363 commit 9f26ca4

File tree

5 files changed

+97
-23
lines changed

5 files changed

+97
-23
lines changed

ext/http/http_next.rs

+60-19
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::service::HttpServerState;
1818
use crate::service::SignallingRc;
1919
use crate::websocket_upgrade::WebSocketUpgrade;
2020
use crate::LocalExecutor;
21+
use crate::Options;
2122
use cache_control::CacheControl;
2223
use deno_core::external;
2324
use deno_core::futures::future::poll_fn;
@@ -821,10 +822,16 @@ fn serve_http11_unconditional(
821822
io: impl HttpServeStream,
822823
svc: impl HttpService<Incoming, ResBody = HttpRecordResponse> + 'static,
823824
cancel: Rc<CancelHandle>,
825+
http1_builder_hook: Option<fn(http1::Builder) -> http1::Builder>,
824826
) -> impl Future<Output = Result<(), hyper::Error>> + 'static {
825-
let conn = http1::Builder::new()
826-
.keep_alive(true)
827-
.writev(*USE_WRITEV)
827+
let mut builder = http1::Builder::new();
828+
builder.keep_alive(true).writev(*USE_WRITEV);
829+
830+
if let Some(http1_builder_hook) = http1_builder_hook {
831+
builder = http1_builder_hook(builder);
832+
}
833+
834+
let conn = builder
828835
.serve_connection(TokioIo::new(io), svc)
829836
.with_upgrades();
830837

@@ -843,9 +850,17 @@ fn serve_http2_unconditional(
843850
io: impl HttpServeStream,
844851
svc: impl HttpService<Incoming, ResBody = HttpRecordResponse> + 'static,
845852
cancel: Rc<CancelHandle>,
853+
http2_builder_hook: Option<
854+
fn(http2::Builder<LocalExecutor>) -> http2::Builder<LocalExecutor>,
855+
>,
846856
) -> impl Future<Output = Result<(), hyper::Error>> + 'static {
847-
let conn =
848-
http2::Builder::new(LocalExecutor).serve_connection(TokioIo::new(io), svc);
857+
let mut builder = http2::Builder::new(LocalExecutor);
858+
859+
if let Some(http2_builder_hook) = http2_builder_hook {
860+
builder = http2_builder_hook(builder);
861+
}
862+
863+
let conn = builder.serve_connection(TokioIo::new(io), svc);
849864
async {
850865
match conn.or_abort(cancel).await {
851866
Err(mut conn) => {
@@ -861,15 +876,16 @@ async fn serve_http2_autodetect(
861876
io: impl HttpServeStream,
862877
svc: impl HttpService<Incoming, ResBody = HttpRecordResponse> + 'static,
863878
cancel: Rc<CancelHandle>,
879+
options: Options,
864880
) -> Result<(), HttpNextError> {
865881
let prefix = NetworkStreamPrefixCheck::new(io, HTTP2_PREFIX);
866882
let (matches, io) = prefix.match_prefix().await?;
867883
if matches {
868-
serve_http2_unconditional(io, svc, cancel)
884+
serve_http2_unconditional(io, svc, cancel, options.http2_builder_hook)
869885
.await
870886
.map_err(HttpNextError::Hyper)
871887
} else {
872-
serve_http11_unconditional(io, svc, cancel)
888+
serve_http11_unconditional(io, svc, cancel, options.http1_builder_hook)
873889
.await
874890
.map_err(HttpNextError::Hyper)
875891
}
@@ -880,6 +896,7 @@ fn serve_https(
880896
request_info: HttpConnectionProperties,
881897
lifetime: HttpLifetime,
882898
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
899+
options: Options,
883900
) -> JoinHandle<Result<(), HttpNextError>> {
884901
let HttpLifetime {
885902
server_state,
@@ -891,21 +908,31 @@ fn serve_https(
891908
handle_request(req, request_info.clone(), server_state.clone(), tx.clone())
892909
});
893910
spawn(
894-
async {
911+
async move {
895912
let handshake = io.handshake().await?;
896913
// If the client specifically negotiates a protocol, we will use it. If not, we'll auto-detect
897914
// based on the prefix bytes
898915
let handshake = handshake.alpn;
899916
if Some(TLS_ALPN_HTTP_2) == handshake.as_deref() {
900-
serve_http2_unconditional(io, svc, listen_cancel_handle)
901-
.await
902-
.map_err(HttpNextError::Hyper)
917+
serve_http2_unconditional(
918+
io,
919+
svc,
920+
listen_cancel_handle,
921+
options.http2_builder_hook,
922+
)
923+
.await
924+
.map_err(HttpNextError::Hyper)
903925
} else if Some(TLS_ALPN_HTTP_11) == handshake.as_deref() {
904-
serve_http11_unconditional(io, svc, listen_cancel_handle)
905-
.await
906-
.map_err(HttpNextError::Hyper)
926+
serve_http11_unconditional(
927+
io,
928+
svc,
929+
listen_cancel_handle,
930+
options.http1_builder_hook,
931+
)
932+
.await
933+
.map_err(HttpNextError::Hyper)
907934
} else {
908-
serve_http2_autodetect(io, svc, listen_cancel_handle).await
935+
serve_http2_autodetect(io, svc, listen_cancel_handle, options).await
909936
}
910937
}
911938
.try_or_cancel(connection_cancel_handle),
@@ -917,6 +944,7 @@ fn serve_http(
917944
request_info: HttpConnectionProperties,
918945
lifetime: HttpLifetime,
919946
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
947+
options: Options,
920948
) -> JoinHandle<Result<(), HttpNextError>> {
921949
let HttpLifetime {
922950
server_state,
@@ -928,7 +956,7 @@ fn serve_http(
928956
handle_request(req, request_info.clone(), server_state.clone(), tx.clone())
929957
});
930958
spawn(
931-
serve_http2_autodetect(io, svc, listen_cancel_handle)
959+
serve_http2_autodetect(io, svc, listen_cancel_handle, options)
932960
.try_or_cancel(connection_cancel_handle),
933961
)
934962
}
@@ -938,6 +966,7 @@ fn serve_http_on<HTTP>(
938966
listen_properties: &HttpListenProperties,
939967
lifetime: HttpLifetime,
940968
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
969+
options: Options,
941970
) -> JoinHandle<Result<(), HttpNextError>>
942971
where
943972
HTTP: HttpPropertyExtractor,
@@ -949,14 +978,14 @@ where
949978

950979
match network_stream {
951980
NetworkStream::Tcp(conn) => {
952-
serve_http(conn, connection_properties, lifetime, tx)
981+
serve_http(conn, connection_properties, lifetime, tx, options)
953982
}
954983
NetworkStream::Tls(conn) => {
955-
serve_https(conn, connection_properties, lifetime, tx)
984+
serve_https(conn, connection_properties, lifetime, tx, options)
956985
}
957986
#[cfg(unix)]
958987
NetworkStream::Unix(conn) => {
959-
serve_http(conn, connection_properties, lifetime, tx)
988+
serve_http(conn, connection_properties, lifetime, tx, options)
960989
}
961990
}
962991
}
@@ -1045,6 +1074,11 @@ where
10451074

10461075
let lifetime = resource.lifetime();
10471076

1077+
let options = {
1078+
let state = state.borrow();
1079+
*state.borrow::<Options>()
1080+
};
1081+
10481082
let listen_properties_clone: HttpListenProperties = listen_properties.clone();
10491083
let handle = spawn(async move {
10501084
loop {
@@ -1057,6 +1091,7 @@ where
10571091
&listen_properties_clone,
10581092
lifetime.clone(),
10591093
tx.clone(),
1094+
options,
10601095
);
10611096
}
10621097
#[allow(unreachable_code)]
@@ -1093,11 +1128,17 @@ where
10931128
let (tx, rx) = tokio::sync::mpsc::channel(10);
10941129
let resource: Rc<HttpJoinHandle> = Rc::new(HttpJoinHandle::new(rx));
10951130

1131+
let options = {
1132+
let state = state.borrow();
1133+
*state.borrow::<Options>()
1134+
};
1135+
10961136
let handle = serve_http_on::<HTTP>(
10971137
connection,
10981138
&listen_properties,
10991139
resource.lifetime(),
11001140
tx,
1141+
options,
11011142
);
11021143

11031144
// Set the handle after we start the future

ext/http/lib.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ use deno_net::raw::NetworkStream;
3939
use deno_websocket::ws_create_server_stream;
4040
use flate2::write::GzEncoder;
4141
use flate2::Compression;
42+
use hyper::server::conn::http1;
43+
use hyper::server::conn::http2;
4244
use hyper_util::rt::TokioIo;
4345
use hyper_v014::body::Bytes;
4446
use hyper_v014::body::HttpBody;
@@ -96,6 +98,25 @@ pub use request_properties::HttpRequestProperties;
9698
pub use service::UpgradeUnavailableError;
9799
pub use websocket_upgrade::WebSocketUpgradeError;
98100

101+
#[derive(Debug, Default, Clone, Copy)]
102+
pub struct Options {
103+
/// By passing a hook function, the caller can customize various configuration
104+
/// options for the HTTP/2 server.
105+
/// See [`http2::Builder`] for what parameters can be customized.
106+
///
107+
/// If `None`, the default configuration provided by hyper will be used. Note
108+
/// that the default configuration is subject to change in future versions.
109+
pub http2_builder_hook:
110+
Option<fn(http2::Builder<LocalExecutor>) -> http2::Builder<LocalExecutor>>,
111+
/// By passing a hook function, the caller can customize various configuration
112+
/// options for the HTTP/1 server.
113+
/// See [`http1::Builder`] for what parameters can be customized.
114+
///
115+
/// If `None`, the default configuration provided by hyper will be used. Note
116+
/// that the default configuration is subject to change in future versions.
117+
pub http1_builder_hook: Option<fn(http1::Builder) -> http1::Builder>,
118+
}
119+
99120
deno_core::extension!(
100121
deno_http,
101122
deps = [deno_web, deno_net, deno_fetch, deno_websocket],
@@ -135,6 +156,12 @@ deno_core::extension!(
135156
http_next::op_http_cancel,
136157
],
137158
esm = ["00_serve.ts", "01_http.js", "02_websocket.ts"],
159+
options = {
160+
options: Options,
161+
},
162+
state = |state, options| {
163+
state.put::<Options>(options.options);
164+
}
138165
);
139166

140167
#[derive(Debug, thiserror::Error)]
@@ -1117,7 +1144,7 @@ async fn op_http_upgrade_websocket(
11171144

11181145
// Needed so hyper can use non Send futures
11191146
#[derive(Clone)]
1120-
struct LocalExecutor;
1147+
pub struct LocalExecutor;
11211148

11221149
impl<Fut> hyper_v014::rt::Executor<Fut> for LocalExecutor
11231150
where

runtime/snapshot.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,9 @@ pub fn create_runtime_snapshot(
300300
deno_cron::local::LocalCronHandler::new(),
301301
),
302302
deno_napi::deno_napi::init_ops_and_esm::<Permissions>(),
303-
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(),
303+
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(
304+
deno_http::Options::default(),
305+
),
304306
deno_io::deno_io::init_ops_and_esm(Default::default()),
305307
deno_fs::deno_fs::init_ops_and_esm::<Permissions>(fs.clone()),
306308
deno_node::deno_node::init_ops_and_esm::<Permissions>(None, fs.clone()),

runtime/web_worker.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,9 @@ impl WebWorker {
497497
),
498498
deno_cron::deno_cron::init_ops_and_esm(LocalCronHandler::new()),
499499
deno_napi::deno_napi::init_ops_and_esm::<PermissionsContainer>(),
500-
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(),
500+
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(
501+
deno_http::Options::default(),
502+
),
501503
deno_io::deno_io::init_ops_and_esm(Some(options.stdio)),
502504
deno_fs::deno_fs::init_ops_and_esm::<PermissionsContainer>(
503505
services.fs.clone(),

runtime/worker.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,9 @@ impl MainWorker {
407407
),
408408
deno_cron::deno_cron::init_ops_and_esm(LocalCronHandler::new()),
409409
deno_napi::deno_napi::init_ops_and_esm::<PermissionsContainer>(),
410-
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(),
410+
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(
411+
deno_http::Options::default(),
412+
),
411413
deno_io::deno_io::init_ops_and_esm(Some(options.stdio)),
412414
deno_fs::deno_fs::init_ops_and_esm::<PermissionsContainer>(
413415
services.fs.clone(),

0 commit comments

Comments
 (0)