pub(crate) fn close(&mut self) -> Result<(), std::io::Error> {
match self {
Stream::NamedTcp(ref mut tcp_stream) => {
- tcp_stream.inner_mut().tcp_stream.shutdown(Shutdown::Both)
+ let result = tcp_stream.inner_mut().tcp_stream.shutdown(Shutdown::Both);
+ // Release the underlying socket immediately instead of waiting
+ // for the arena to be garbage-collected.
+ tcp_stream.drop_payload();
+ result
}
#[cfg(feature = "tls")]
Stream::NamedTls(ref mut tls_stream) => {
let inner = tls_stream.inner_mut();
inner.tls_stream.send_close_notify();
- inner.tls_stream.flush()
+ let result = inner.tls_stream.flush();
+ // Release the rustls connection state (buffers, ServerConfig)
+ // immediately instead of waiting for the arena to be
+ // garbage-collected.
+ tls_stream.drop_payload();
+ result
}
#[cfg(feature = "http")]
Stream::HttpRead(ref mut http_stream) => {
#[cfg(feature = "tls")]
#[inline(always)]
pub(crate) fn tls_accept_client(&mut self) -> CallResult {
- let cert_pem = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet"));
- let key_pem = self.string_encoding_bytes(self.machine_st.registers[2], atom!("octet"));
+ use std::cell::RefCell;
- let cert_chain: Vec<CertificateDer<'static>> =
- match rustls_pemfile::certs(&mut &cert_pem[..]).collect::<Result<Vec<_>, _>>() {
- Ok(certs) if !certs.is_empty() => certs,
- _ => {
- return Err(self.machine_st.open_permission_error(
- self.machine_st.registers[1],
- atom!("tls_server_negotiate"),
- 3,
- ));
- }
- };
+ // A new TLS connection is negotiated for every incoming request. Building
+ // a fresh ServerConfig each time means re-parsing the certificate chain
+ // and (potentially large) private key on every connection. Cache the most
+ // recently built config, keyed by the raw cert/key bytes, and reuse it.
+ thread_local! {
+ static CONFIG_CACHE: RefCell<Option<(Vec<u8>, Vec<u8>, Arc<ServerConfig>)>> =
+ RefCell::new(None);
+ }
- let private_key: PrivateKeyDer<'static> =
- match rustls_pemfile::private_key(&mut &key_pem[..]) {
- Ok(Some(key)) => key,
- _ => {
- return Err(self.machine_st.open_permission_error(
- self.machine_st.registers[2],
- atom!("tls_server_negotiate"),
- 3,
- ));
- }
- };
+ let cert_pem = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet"));
+ let key_pem = self.string_encoding_bytes(self.machine_st.registers[2], atom!("octet"));
let stream0 = self.machine_st.get_stream_or_alias(
self.machine_st.registers[3],
3,
)?;
- let config = match ServerConfig::builder()
- .with_client_cert_verifier(Arc::new(GeminiClientVerifier))
- .with_single_cert(cert_chain, private_key)
- {
- Ok(config) => config,
- Err(_) => {
- return Err(self.machine_st.open_permission_error(
- self.machine_st.registers[1],
- atom!("tls_server_negotiate"),
- 3,
- ));
+ let cached_config = CONFIG_CACHE.with(|cache| {
+ cache.borrow().as_ref().and_then(|(cert, key, config)| {
+ (cert == &cert_pem && key == &key_pem).then(|| config.clone())
+ })
+ });
+
+ let config = match cached_config {
+ Some(config) => config,
+ None => {
+ let cert_chain: Vec<CertificateDer<'static>> =
+ match rustls_pemfile::certs(&mut &cert_pem[..]).collect::<Result<Vec<_>, _>>() {
+ Ok(certs) if !certs.is_empty() => certs,
+ _ => {
+ return Err(self.machine_st.open_permission_error(
+ self.machine_st.registers[1],
+ atom!("tls_server_negotiate"),
+ 3,
+ ));
+ }
+ };
+
+ let private_key: PrivateKeyDer<'static> =
+ match rustls_pemfile::private_key(&mut &key_pem[..]) {
+ Ok(Some(key)) => key,
+ _ => {
+ return Err(self.machine_st.open_permission_error(
+ self.machine_st.registers[2],
+ atom!("tls_server_negotiate"),
+ 3,
+ ));
+ }
+ };
+
+ let config = match ServerConfig::builder()
+ .with_client_cert_verifier(Arc::new(GeminiClientVerifier))
+ .with_single_cert(cert_chain, private_key)
+ {
+ Ok(config) => Arc::new(config),
+ Err(_) => {
+ return Err(self.machine_st.open_permission_error(
+ self.machine_st.registers[1],
+ atom!("tls_server_negotiate"),
+ 3,
+ ));
+ }
+ };
+
+ CONFIG_CACHE.with(|cache| {
+ *cache.borrow_mut() =
+ Some((cert_pem.clone(), key_pem.clone(), config.clone()));
+ });
+
+ config
}
};
- let mut conn = match ServerConnection::new(Arc::new(config)) {
+ let mut conn = match ServerConnection::new(config) {
Ok(conn) => conn,
Err(_) => {
return Err(self.machine_st.open_permission_error(