mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
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.
This commit is contained in:
parent
df1d36324f
commit
9f26ca4509
5 changed files with 97 additions and 23 deletions
|
@ -18,6 +18,7 @@ use crate::service::HttpServerState;
|
||||||
use crate::service::SignallingRc;
|
use crate::service::SignallingRc;
|
||||||
use crate::websocket_upgrade::WebSocketUpgrade;
|
use crate::websocket_upgrade::WebSocketUpgrade;
|
||||||
use crate::LocalExecutor;
|
use crate::LocalExecutor;
|
||||||
|
use crate::Options;
|
||||||
use cache_control::CacheControl;
|
use cache_control::CacheControl;
|
||||||
use deno_core::external;
|
use deno_core::external;
|
||||||
use deno_core::futures::future::poll_fn;
|
use deno_core::futures::future::poll_fn;
|
||||||
|
@ -821,10 +822,16 @@ fn serve_http11_unconditional(
|
||||||
io: impl HttpServeStream,
|
io: impl HttpServeStream,
|
||||||
svc: impl HttpService<Incoming, ResBody = HttpRecordResponse> + 'static,
|
svc: impl HttpService<Incoming, ResBody = HttpRecordResponse> + 'static,
|
||||||
cancel: Rc<CancelHandle>,
|
cancel: Rc<CancelHandle>,
|
||||||
|
http1_builder_hook: Option<fn(http1::Builder) -> http1::Builder>,
|
||||||
) -> impl Future<Output = Result<(), hyper::Error>> + 'static {
|
) -> impl Future<Output = Result<(), hyper::Error>> + 'static {
|
||||||
let conn = http1::Builder::new()
|
let mut builder = http1::Builder::new();
|
||||||
.keep_alive(true)
|
builder.keep_alive(true).writev(*USE_WRITEV);
|
||||||
.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)
|
.serve_connection(TokioIo::new(io), svc)
|
||||||
.with_upgrades();
|
.with_upgrades();
|
||||||
|
|
||||||
|
@ -843,9 +850,17 @@ fn serve_http2_unconditional(
|
||||||
io: impl HttpServeStream,
|
io: impl HttpServeStream,
|
||||||
svc: impl HttpService<Incoming, ResBody = HttpRecordResponse> + 'static,
|
svc: impl HttpService<Incoming, ResBody = HttpRecordResponse> + 'static,
|
||||||
cancel: Rc<CancelHandle>,
|
cancel: Rc<CancelHandle>,
|
||||||
|
http2_builder_hook: Option<
|
||||||
|
fn(http2::Builder<LocalExecutor>) -> http2::Builder<LocalExecutor>,
|
||||||
|
>,
|
||||||
) -> impl Future<Output = Result<(), hyper::Error>> + 'static {
|
) -> impl Future<Output = Result<(), hyper::Error>> + 'static {
|
||||||
let conn =
|
let mut builder = http2::Builder::new(LocalExecutor);
|
||||||
http2::Builder::new(LocalExecutor).serve_connection(TokioIo::new(io), svc);
|
|
||||||
|
if let Some(http2_builder_hook) = http2_builder_hook {
|
||||||
|
builder = http2_builder_hook(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
let conn = builder.serve_connection(TokioIo::new(io), svc);
|
||||||
async {
|
async {
|
||||||
match conn.or_abort(cancel).await {
|
match conn.or_abort(cancel).await {
|
||||||
Err(mut conn) => {
|
Err(mut conn) => {
|
||||||
|
@ -861,15 +876,16 @@ async fn serve_http2_autodetect(
|
||||||
io: impl HttpServeStream,
|
io: impl HttpServeStream,
|
||||||
svc: impl HttpService<Incoming, ResBody = HttpRecordResponse> + 'static,
|
svc: impl HttpService<Incoming, ResBody = HttpRecordResponse> + 'static,
|
||||||
cancel: Rc<CancelHandle>,
|
cancel: Rc<CancelHandle>,
|
||||||
|
options: Options,
|
||||||
) -> Result<(), HttpNextError> {
|
) -> Result<(), HttpNextError> {
|
||||||
let prefix = NetworkStreamPrefixCheck::new(io, HTTP2_PREFIX);
|
let prefix = NetworkStreamPrefixCheck::new(io, HTTP2_PREFIX);
|
||||||
let (matches, io) = prefix.match_prefix().await?;
|
let (matches, io) = prefix.match_prefix().await?;
|
||||||
if matches {
|
if matches {
|
||||||
serve_http2_unconditional(io, svc, cancel)
|
serve_http2_unconditional(io, svc, cancel, options.http2_builder_hook)
|
||||||
.await
|
.await
|
||||||
.map_err(HttpNextError::Hyper)
|
.map_err(HttpNextError::Hyper)
|
||||||
} else {
|
} else {
|
||||||
serve_http11_unconditional(io, svc, cancel)
|
serve_http11_unconditional(io, svc, cancel, options.http1_builder_hook)
|
||||||
.await
|
.await
|
||||||
.map_err(HttpNextError::Hyper)
|
.map_err(HttpNextError::Hyper)
|
||||||
}
|
}
|
||||||
|
@ -880,6 +896,7 @@ fn serve_https(
|
||||||
request_info: HttpConnectionProperties,
|
request_info: HttpConnectionProperties,
|
||||||
lifetime: HttpLifetime,
|
lifetime: HttpLifetime,
|
||||||
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
|
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
|
||||||
|
options: Options,
|
||||||
) -> JoinHandle<Result<(), HttpNextError>> {
|
) -> JoinHandle<Result<(), HttpNextError>> {
|
||||||
let HttpLifetime {
|
let HttpLifetime {
|
||||||
server_state,
|
server_state,
|
||||||
|
@ -891,21 +908,31 @@ fn serve_https(
|
||||||
handle_request(req, request_info.clone(), server_state.clone(), tx.clone())
|
handle_request(req, request_info.clone(), server_state.clone(), tx.clone())
|
||||||
});
|
});
|
||||||
spawn(
|
spawn(
|
||||||
async {
|
async move {
|
||||||
let handshake = io.handshake().await?;
|
let handshake = io.handshake().await?;
|
||||||
// If the client specifically negotiates a protocol, we will use it. If not, we'll auto-detect
|
// If the client specifically negotiates a protocol, we will use it. If not, we'll auto-detect
|
||||||
// based on the prefix bytes
|
// based on the prefix bytes
|
||||||
let handshake = handshake.alpn;
|
let handshake = handshake.alpn;
|
||||||
if Some(TLS_ALPN_HTTP_2) == handshake.as_deref() {
|
if Some(TLS_ALPN_HTTP_2) == handshake.as_deref() {
|
||||||
serve_http2_unconditional(io, svc, listen_cancel_handle)
|
serve_http2_unconditional(
|
||||||
.await
|
io,
|
||||||
.map_err(HttpNextError::Hyper)
|
svc,
|
||||||
|
listen_cancel_handle,
|
||||||
|
options.http2_builder_hook,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(HttpNextError::Hyper)
|
||||||
} else if Some(TLS_ALPN_HTTP_11) == handshake.as_deref() {
|
} else if Some(TLS_ALPN_HTTP_11) == handshake.as_deref() {
|
||||||
serve_http11_unconditional(io, svc, listen_cancel_handle)
|
serve_http11_unconditional(
|
||||||
.await
|
io,
|
||||||
.map_err(HttpNextError::Hyper)
|
svc,
|
||||||
|
listen_cancel_handle,
|
||||||
|
options.http1_builder_hook,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(HttpNextError::Hyper)
|
||||||
} else {
|
} 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),
|
.try_or_cancel(connection_cancel_handle),
|
||||||
|
@ -917,6 +944,7 @@ fn serve_http(
|
||||||
request_info: HttpConnectionProperties,
|
request_info: HttpConnectionProperties,
|
||||||
lifetime: HttpLifetime,
|
lifetime: HttpLifetime,
|
||||||
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
|
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
|
||||||
|
options: Options,
|
||||||
) -> JoinHandle<Result<(), HttpNextError>> {
|
) -> JoinHandle<Result<(), HttpNextError>> {
|
||||||
let HttpLifetime {
|
let HttpLifetime {
|
||||||
server_state,
|
server_state,
|
||||||
|
@ -928,7 +956,7 @@ fn serve_http(
|
||||||
handle_request(req, request_info.clone(), server_state.clone(), tx.clone())
|
handle_request(req, request_info.clone(), server_state.clone(), tx.clone())
|
||||||
});
|
});
|
||||||
spawn(
|
spawn(
|
||||||
serve_http2_autodetect(io, svc, listen_cancel_handle)
|
serve_http2_autodetect(io, svc, listen_cancel_handle, options)
|
||||||
.try_or_cancel(connection_cancel_handle),
|
.try_or_cancel(connection_cancel_handle),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -938,6 +966,7 @@ fn serve_http_on<HTTP>(
|
||||||
listen_properties: &HttpListenProperties,
|
listen_properties: &HttpListenProperties,
|
||||||
lifetime: HttpLifetime,
|
lifetime: HttpLifetime,
|
||||||
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
|
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
|
||||||
|
options: Options,
|
||||||
) -> JoinHandle<Result<(), HttpNextError>>
|
) -> JoinHandle<Result<(), HttpNextError>>
|
||||||
where
|
where
|
||||||
HTTP: HttpPropertyExtractor,
|
HTTP: HttpPropertyExtractor,
|
||||||
|
@ -949,14 +978,14 @@ where
|
||||||
|
|
||||||
match network_stream {
|
match network_stream {
|
||||||
NetworkStream::Tcp(conn) => {
|
NetworkStream::Tcp(conn) => {
|
||||||
serve_http(conn, connection_properties, lifetime, tx)
|
serve_http(conn, connection_properties, lifetime, tx, options)
|
||||||
}
|
}
|
||||||
NetworkStream::Tls(conn) => {
|
NetworkStream::Tls(conn) => {
|
||||||
serve_https(conn, connection_properties, lifetime, tx)
|
serve_https(conn, connection_properties, lifetime, tx, options)
|
||||||
}
|
}
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
NetworkStream::Unix(conn) => {
|
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 lifetime = resource.lifetime();
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
let state = state.borrow();
|
||||||
|
*state.borrow::<Options>()
|
||||||
|
};
|
||||||
|
|
||||||
let listen_properties_clone: HttpListenProperties = listen_properties.clone();
|
let listen_properties_clone: HttpListenProperties = listen_properties.clone();
|
||||||
let handle = spawn(async move {
|
let handle = spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
|
@ -1057,6 +1091,7 @@ where
|
||||||
&listen_properties_clone,
|
&listen_properties_clone,
|
||||||
lifetime.clone(),
|
lifetime.clone(),
|
||||||
tx.clone(),
|
tx.clone(),
|
||||||
|
options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[allow(unreachable_code)]
|
#[allow(unreachable_code)]
|
||||||
|
@ -1093,11 +1128,17 @@ where
|
||||||
let (tx, rx) = tokio::sync::mpsc::channel(10);
|
let (tx, rx) = tokio::sync::mpsc::channel(10);
|
||||||
let resource: Rc<HttpJoinHandle> = Rc::new(HttpJoinHandle::new(rx));
|
let resource: Rc<HttpJoinHandle> = Rc::new(HttpJoinHandle::new(rx));
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
let state = state.borrow();
|
||||||
|
*state.borrow::<Options>()
|
||||||
|
};
|
||||||
|
|
||||||
let handle = serve_http_on::<HTTP>(
|
let handle = serve_http_on::<HTTP>(
|
||||||
connection,
|
connection,
|
||||||
&listen_properties,
|
&listen_properties,
|
||||||
resource.lifetime(),
|
resource.lifetime(),
|
||||||
tx,
|
tx,
|
||||||
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Set the handle after we start the future
|
// Set the handle after we start the future
|
||||||
|
|
|
@ -39,6 +39,8 @@ use deno_net::raw::NetworkStream;
|
||||||
use deno_websocket::ws_create_server_stream;
|
use deno_websocket::ws_create_server_stream;
|
||||||
use flate2::write::GzEncoder;
|
use flate2::write::GzEncoder;
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
|
use hyper::server::conn::http1;
|
||||||
|
use hyper::server::conn::http2;
|
||||||
use hyper_util::rt::TokioIo;
|
use hyper_util::rt::TokioIo;
|
||||||
use hyper_v014::body::Bytes;
|
use hyper_v014::body::Bytes;
|
||||||
use hyper_v014::body::HttpBody;
|
use hyper_v014::body::HttpBody;
|
||||||
|
@ -96,6 +98,25 @@ pub use request_properties::HttpRequestProperties;
|
||||||
pub use service::UpgradeUnavailableError;
|
pub use service::UpgradeUnavailableError;
|
||||||
pub use websocket_upgrade::WebSocketUpgradeError;
|
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<fn(http2::Builder<LocalExecutor>) -> http2::Builder<LocalExecutor>>,
|
||||||
|
/// 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<fn(http1::Builder) -> http1::Builder>,
|
||||||
|
}
|
||||||
|
|
||||||
deno_core::extension!(
|
deno_core::extension!(
|
||||||
deno_http,
|
deno_http,
|
||||||
deps = [deno_web, deno_net, deno_fetch, deno_websocket],
|
deps = [deno_web, deno_net, deno_fetch, deno_websocket],
|
||||||
|
@ -135,6 +156,12 @@ deno_core::extension!(
|
||||||
http_next::op_http_cancel,
|
http_next::op_http_cancel,
|
||||||
],
|
],
|
||||||
esm = ["00_serve.ts", "01_http.js", "02_websocket.ts"],
|
esm = ["00_serve.ts", "01_http.js", "02_websocket.ts"],
|
||||||
|
options = {
|
||||||
|
options: Options,
|
||||||
|
},
|
||||||
|
state = |state, options| {
|
||||||
|
state.put::<Options>(options.options);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
@ -1117,7 +1144,7 @@ async fn op_http_upgrade_websocket(
|
||||||
|
|
||||||
// Needed so hyper can use non Send futures
|
// Needed so hyper can use non Send futures
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct LocalExecutor;
|
pub struct LocalExecutor;
|
||||||
|
|
||||||
impl<Fut> hyper_v014::rt::Executor<Fut> for LocalExecutor
|
impl<Fut> hyper_v014::rt::Executor<Fut> for LocalExecutor
|
||||||
where
|
where
|
||||||
|
|
|
@ -300,7 +300,9 @@ pub fn create_runtime_snapshot(
|
||||||
deno_cron::local::LocalCronHandler::new(),
|
deno_cron::local::LocalCronHandler::new(),
|
||||||
),
|
),
|
||||||
deno_napi::deno_napi::init_ops_and_esm::<Permissions>(),
|
deno_napi::deno_napi::init_ops_and_esm::<Permissions>(),
|
||||||
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(),
|
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(
|
||||||
|
deno_http::Options::default(),
|
||||||
|
),
|
||||||
deno_io::deno_io::init_ops_and_esm(Default::default()),
|
deno_io::deno_io::init_ops_and_esm(Default::default()),
|
||||||
deno_fs::deno_fs::init_ops_and_esm::<Permissions>(fs.clone()),
|
deno_fs::deno_fs::init_ops_and_esm::<Permissions>(fs.clone()),
|
||||||
deno_node::deno_node::init_ops_and_esm::<Permissions>(None, fs.clone()),
|
deno_node::deno_node::init_ops_and_esm::<Permissions>(None, fs.clone()),
|
||||||
|
|
|
@ -497,7 +497,9 @@ impl WebWorker {
|
||||||
),
|
),
|
||||||
deno_cron::deno_cron::init_ops_and_esm(LocalCronHandler::new()),
|
deno_cron::deno_cron::init_ops_and_esm(LocalCronHandler::new()),
|
||||||
deno_napi::deno_napi::init_ops_and_esm::<PermissionsContainer>(),
|
deno_napi::deno_napi::init_ops_and_esm::<PermissionsContainer>(),
|
||||||
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(),
|
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(
|
||||||
|
deno_http::Options::default(),
|
||||||
|
),
|
||||||
deno_io::deno_io::init_ops_and_esm(Some(options.stdio)),
|
deno_io::deno_io::init_ops_and_esm(Some(options.stdio)),
|
||||||
deno_fs::deno_fs::init_ops_and_esm::<PermissionsContainer>(
|
deno_fs::deno_fs::init_ops_and_esm::<PermissionsContainer>(
|
||||||
services.fs.clone(),
|
services.fs.clone(),
|
||||||
|
|
|
@ -407,7 +407,9 @@ impl MainWorker {
|
||||||
),
|
),
|
||||||
deno_cron::deno_cron::init_ops_and_esm(LocalCronHandler::new()),
|
deno_cron::deno_cron::init_ops_and_esm(LocalCronHandler::new()),
|
||||||
deno_napi::deno_napi::init_ops_and_esm::<PermissionsContainer>(),
|
deno_napi::deno_napi::init_ops_and_esm::<PermissionsContainer>(),
|
||||||
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(),
|
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(
|
||||||
|
deno_http::Options::default(),
|
||||||
|
),
|
||||||
deno_io::deno_io::init_ops_and_esm(Some(options.stdio)),
|
deno_io::deno_io::init_ops_and_esm(Some(options.stdio)),
|
||||||
deno_fs::deno_fs::init_ops_and_esm::<PermissionsContainer>(
|
deno_fs::deno_fs::init_ops_and_esm::<PermissionsContainer>(
|
||||||
services.fs.clone(),
|
services.fs.clone(),
|
||||||
|
|
Loading…
Reference in a new issue