diff --git a/ext/http/http_next.rs b/ext/http/http_next.rs index 1251f00cc0..7dbac6021a 100644 --- a/ext/http/http_next.rs +++ b/ext/http/http_next.rs @@ -18,6 +18,7 @@ use crate::service::HttpServerState; use crate::service::SignallingRc; use crate::websocket_upgrade::WebSocketUpgrade; use crate::LocalExecutor; +use crate::Options; use cache_control::CacheControl; use deno_core::external; use deno_core::futures::future::poll_fn; @@ -821,10 +822,16 @@ fn serve_http11_unconditional( io: impl HttpServeStream, svc: impl HttpService + 'static, cancel: Rc, + http1_builder_hook: Option http1::Builder>, ) -> impl Future> + 'static { - let conn = http1::Builder::new() - .keep_alive(true) - .writev(*USE_WRITEV) + let mut builder = http1::Builder::new(); + builder.keep_alive(true).writev(*USE_WRITEV); + + if let Some(http1_builder_hook) = http1_builder_hook { + builder = http1_builder_hook(builder); + } + + let conn = builder .serve_connection(TokioIo::new(io), svc) .with_upgrades(); @@ -843,9 +850,17 @@ fn serve_http2_unconditional( io: impl HttpServeStream, svc: impl HttpService + 'static, cancel: Rc, + http2_builder_hook: Option< + fn(http2::Builder) -> http2::Builder, + >, ) -> impl Future> + 'static { - let conn = - http2::Builder::new(LocalExecutor).serve_connection(TokioIo::new(io), svc); + let mut builder = http2::Builder::new(LocalExecutor); + + if let Some(http2_builder_hook) = http2_builder_hook { + builder = http2_builder_hook(builder); + } + + let conn = builder.serve_connection(TokioIo::new(io), svc); async { match conn.or_abort(cancel).await { Err(mut conn) => { @@ -861,15 +876,16 @@ async fn serve_http2_autodetect( io: impl HttpServeStream, svc: impl HttpService + 'static, cancel: Rc, + options: Options, ) -> Result<(), HttpNextError> { let prefix = NetworkStreamPrefixCheck::new(io, HTTP2_PREFIX); let (matches, io) = prefix.match_prefix().await?; if matches { - serve_http2_unconditional(io, svc, cancel) + serve_http2_unconditional(io, svc, cancel, options.http2_builder_hook) .await .map_err(HttpNextError::Hyper) } else { - serve_http11_unconditional(io, svc, cancel) + serve_http11_unconditional(io, svc, cancel, options.http1_builder_hook) .await .map_err(HttpNextError::Hyper) } @@ -880,6 +896,7 @@ fn serve_https( request_info: HttpConnectionProperties, lifetime: HttpLifetime, tx: tokio::sync::mpsc::Sender>, + options: Options, ) -> JoinHandle> { let HttpLifetime { server_state, @@ -891,21 +908,31 @@ fn serve_https( handle_request(req, request_info.clone(), server_state.clone(), tx.clone()) }); spawn( - async { + async move { let handshake = io.handshake().await?; // If the client specifically negotiates a protocol, we will use it. If not, we'll auto-detect // based on the prefix bytes let handshake = handshake.alpn; if Some(TLS_ALPN_HTTP_2) == handshake.as_deref() { - serve_http2_unconditional(io, svc, listen_cancel_handle) - .await - .map_err(HttpNextError::Hyper) + serve_http2_unconditional( + io, + svc, + listen_cancel_handle, + options.http2_builder_hook, + ) + .await + .map_err(HttpNextError::Hyper) } else if Some(TLS_ALPN_HTTP_11) == handshake.as_deref() { - serve_http11_unconditional(io, svc, listen_cancel_handle) - .await - .map_err(HttpNextError::Hyper) + serve_http11_unconditional( + io, + svc, + listen_cancel_handle, + options.http1_builder_hook, + ) + .await + .map_err(HttpNextError::Hyper) } else { - serve_http2_autodetect(io, svc, listen_cancel_handle).await + serve_http2_autodetect(io, svc, listen_cancel_handle, options).await } } .try_or_cancel(connection_cancel_handle), @@ -917,6 +944,7 @@ fn serve_http( request_info: HttpConnectionProperties, lifetime: HttpLifetime, tx: tokio::sync::mpsc::Sender>, + options: Options, ) -> JoinHandle> { let HttpLifetime { server_state, @@ -928,7 +956,7 @@ fn serve_http( handle_request(req, request_info.clone(), server_state.clone(), tx.clone()) }); spawn( - serve_http2_autodetect(io, svc, listen_cancel_handle) + serve_http2_autodetect(io, svc, listen_cancel_handle, options) .try_or_cancel(connection_cancel_handle), ) } @@ -938,6 +966,7 @@ fn serve_http_on( listen_properties: &HttpListenProperties, lifetime: HttpLifetime, tx: tokio::sync::mpsc::Sender>, + options: Options, ) -> JoinHandle> where HTTP: HttpPropertyExtractor, @@ -949,14 +978,14 @@ where match network_stream { NetworkStream::Tcp(conn) => { - serve_http(conn, connection_properties, lifetime, tx) + serve_http(conn, connection_properties, lifetime, tx, options) } NetworkStream::Tls(conn) => { - serve_https(conn, connection_properties, lifetime, tx) + serve_https(conn, connection_properties, lifetime, tx, options) } #[cfg(unix)] NetworkStream::Unix(conn) => { - serve_http(conn, connection_properties, lifetime, tx) + serve_http(conn, connection_properties, lifetime, tx, options) } } } @@ -1045,6 +1074,11 @@ where let lifetime = resource.lifetime(); + let options = { + let state = state.borrow(); + *state.borrow::() + }; + let listen_properties_clone: HttpListenProperties = listen_properties.clone(); let handle = spawn(async move { loop { @@ -1057,6 +1091,7 @@ where &listen_properties_clone, lifetime.clone(), tx.clone(), + options, ); } #[allow(unreachable_code)] @@ -1093,11 +1128,17 @@ where let (tx, rx) = tokio::sync::mpsc::channel(10); let resource: Rc = Rc::new(HttpJoinHandle::new(rx)); + let options = { + let state = state.borrow(); + *state.borrow::() + }; + let handle = serve_http_on::( connection, &listen_properties, resource.lifetime(), tx, + options, ); // Set the handle after we start the future diff --git a/ext/http/lib.rs b/ext/http/lib.rs index 49893b1b92..39b0bbc2af 100644 --- a/ext/http/lib.rs +++ b/ext/http/lib.rs @@ -39,6 +39,8 @@ use deno_net::raw::NetworkStream; use deno_websocket::ws_create_server_stream; use flate2::write::GzEncoder; use flate2::Compression; +use hyper::server::conn::http1; +use hyper::server::conn::http2; use hyper_util::rt::TokioIo; use hyper_v014::body::Bytes; use hyper_v014::body::HttpBody; @@ -96,6 +98,25 @@ pub use request_properties::HttpRequestProperties; pub use service::UpgradeUnavailableError; pub use websocket_upgrade::WebSocketUpgradeError; +#[derive(Debug, Default, Clone, Copy)] +pub struct Options { + /// By passing a hook function, the caller can customize various configuration + /// options for the HTTP/2 server. + /// See [`http2::Builder`] for what parameters can be customized. + /// + /// If `None`, the default configuration provided by hyper will be used. Note + /// that the default configuration is subject to change in future versions. + pub http2_builder_hook: + Option) -> http2::Builder>, + /// By passing a hook function, the caller can customize various configuration + /// options for the HTTP/1 server. + /// See [`http1::Builder`] for what parameters can be customized. + /// + /// If `None`, the default configuration provided by hyper will be used. Note + /// that the default configuration is subject to change in future versions. + pub http1_builder_hook: Option http1::Builder>, +} + deno_core::extension!( deno_http, deps = [deno_web, deno_net, deno_fetch, deno_websocket], @@ -135,6 +156,12 @@ deno_core::extension!( http_next::op_http_cancel, ], esm = ["00_serve.ts", "01_http.js", "02_websocket.ts"], + options = { + options: Options, + }, + state = |state, options| { + state.put::(options.options); + } ); #[derive(Debug, thiserror::Error)] @@ -1117,7 +1144,7 @@ async fn op_http_upgrade_websocket( // Needed so hyper can use non Send futures #[derive(Clone)] -struct LocalExecutor; +pub struct LocalExecutor; impl hyper_v014::rt::Executor for LocalExecutor where diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs index 7f6e6b8ab6..3bf515131f 100644 --- a/runtime/snapshot.rs +++ b/runtime/snapshot.rs @@ -300,7 +300,9 @@ pub fn create_runtime_snapshot( deno_cron::local::LocalCronHandler::new(), ), deno_napi::deno_napi::init_ops_and_esm::(), - deno_http::deno_http::init_ops_and_esm::(), + deno_http::deno_http::init_ops_and_esm::( + deno_http::Options::default(), + ), deno_io::deno_io::init_ops_and_esm(Default::default()), deno_fs::deno_fs::init_ops_and_esm::(fs.clone()), deno_node::deno_node::init_ops_and_esm::(None, fs.clone()), diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 8e0c870d1a..a282d11b9a 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -497,7 +497,9 @@ impl WebWorker { ), deno_cron::deno_cron::init_ops_and_esm(LocalCronHandler::new()), deno_napi::deno_napi::init_ops_and_esm::(), - deno_http::deno_http::init_ops_and_esm::(), + deno_http::deno_http::init_ops_and_esm::( + deno_http::Options::default(), + ), deno_io::deno_io::init_ops_and_esm(Some(options.stdio)), deno_fs::deno_fs::init_ops_and_esm::( services.fs.clone(), diff --git a/runtime/worker.rs b/runtime/worker.rs index 99123463cf..909147df9b 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -407,7 +407,9 @@ impl MainWorker { ), deno_cron::deno_cron::init_ops_and_esm(LocalCronHandler::new()), deno_napi::deno_napi::init_ops_and_esm::(), - deno_http::deno_http::init_ops_and_esm::(), + deno_http::deno_http::init_ops_and_esm::( + deno_http::Options::default(), + ), deno_io::deno_io::init_ops_and_esm(Some(options.stdio)), deno_fs::deno_fs::init_ops_and_esm::( services.fs.clone(),