Skip to content

Commit 883d261

Browse files
authored
[refactor, chore](tls): tidy cfg, fix doc, test tls feature (#443)
* refactor(tls): tidy cfg, fix doc, test tls feature * fix test
1 parent 6846acd commit 883d261

6 files changed

Lines changed: 69 additions & 114 deletions

File tree

Taskfile.yaml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ tasks:
1616
- task: test:other
1717
test:core:
1818
deps:
19+
- task: test:tls
1920
- task: test:no_rt
2021
- for: [tokio, async-std, smol, nio, glommio, lambda, worker]
2122
task: test:rt
@@ -29,6 +30,7 @@ tasks:
2930

3031
check:
3132
deps:
33+
- task: check:tls
3234
- task: check:no_rt
3335
- for: [tokio, async-std, smol, nio, glommio, lambda]
3436
task: check:rt-native_target
@@ -61,7 +63,7 @@ tasks:
6163
test:doc:
6264
dir: ./ohkami
6365
cmds:
64-
- cargo test --doc --no-default-features --features DEBUG,rt_tokio,sse,ws,{{.maybe_nightly}}
66+
- cargo test --doc --no-default-features --features DEBUG,rt_tokio,sse,ws,tls,{{.maybe_nightly}}
6567
# not activating `openapi` feature for testability of README sample codes
6668

6769
test:examples:
@@ -92,6 +94,12 @@ tasks:
9294
- cargo test --lib --features rt_{{.rt}},DEBUG,ws,{{.maybe_nightly}}
9395
- cargo test --lib --features rt_{{.rt}},DEBUG,sse,ws,openapi,{{.maybe_nightly}}
9496

97+
test:tls: # currently depending on tokio-rustls and works only on tokio
98+
dir: ./ohkami
99+
cmds:
100+
- cargo test --lib --features rt_tokio,DEBUG,tls,{{.maybe_nightly}}
101+
- cargo test --lib --features rt_tokio,DEBUG,sse,ws,tls,{{.maybe_nightly}}
102+
95103
#### checks ####
96104
# Assure buildability without "DEBUG" feature
97105

@@ -114,6 +122,12 @@ tasks:
114122
- cargo check --lib --features rt_{{.rt}},ws,{{.maybe_nightly}}
115123
- cargo check --lib --features rt_{{.rt}},sse,ws,openapi,{{.maybe_nightly}}
116124

125+
check:tls: # currently depending on tokio-rustls and works only on tokio
126+
dir: ./ohkami
127+
cmds:
128+
- cargo check --lib --features rt_tokio,tls,{{.maybe_nightly}}
129+
- cargo check --lib --features rt_tokio,sse,ws,tls,{{.maybe_nightly}}
130+
117131
check:rt_worker:
118132
dir: ./ohkami
119133
cmds:

ohkami/Cargo.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ nightly = []
9191
openapi = ["dep:ohkami_openapi", "ohkami_macros/openapi"]
9292
sse = ["ohkami_lib/stream"]
9393
ws = ["ohkami_lib/stream", "dep:mews"]
94-
tls = ["__rt_native__", "rt_tokio", "dep:rustls", "dep:tokio-rustls"]
94+
tls = ["rt_tokio", "dep:rustls", "dep:tokio-rustls"] # currently depending on tokio-rustls and works only on tokio
9595

9696
##### internal #####
9797
__rt__ = []
@@ -104,6 +104,7 @@ DEBUG = ["tokio?/rt-multi-thread", "tokio?/macros"]
104104
# "openapi",
105105
# "sse",
106106
# "ws",
107+
# "tls",
107108
# "rt_tokio",
108109
# #"rt_async-std",
109110
# #"rt_smol",
@@ -114,5 +115,7 @@ DEBUG = ["tokio?/rt-multi-thread", "tokio?/macros"]
114115
#]
115116

116117

117-
[dev-dependencies]
118-
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "macros"] }
118+
[dev-dependencies] # for doc test
119+
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "macros"] }
120+
rustls = { version = "0.23", features = ["ring"] }
121+
rustls-pemfile = { version = "2.2" }

ohkami/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ mod __rt__ {
175175
}
176176

177177
pub(crate) const PORT: u16 = {
178-
#[cfg(feature="rt_tokio") ] {3001}
178+
#[cfg(feature="rt_tokio") ] {if cfg!(feature="tls") {9443} else {3001}}
179179
#[cfg(feature="rt_async-std")] {3002}
180180
#[cfg(feature="rt_smol") ] {3003}
181181
#[cfg(feature="rt_nio") ] {3004}

ohkami/src/ohkami/mod.rs

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,9 @@ impl Ohkami {
610610
///
611611
/// ```no_run
612612
/// use ohkami::prelude::*;
613-
/// use rustls::{ServerConfig, Certificate, PrivateKey};
613+
/// use rustls::ServerConfig;
614+
/// use rustls::pki_types::{CertificateDer, PrivateKeyDer};
615+
///
614616
/// use std::fs::File;
615617
/// use std::io::BufReader;
616618
///
@@ -621,33 +623,31 @@ impl Ohkami {
621623
/// #[tokio::main]
622624
/// async fn main() -> std::io::Result<()> {
623625
/// // Initialize rustls crypto provider
624-
/// match rustls::crypto::ring::default_provider().install_default() {
625-
// Ok(_) => println!("Successfully installed rustls crypto provider"),
626-
// Err(e) => {
627-
// eprintln!("Failed to install rustls crypto provider: {:?}", e);
628-
// std::process::exit(1);
629-
// }
630-
// }
626+
/// rustls::crypto::ring::default_provider().install_default()
627+
/// .expect("Failed to install rustls crypto provider");
628+
///
631629
/// // Load certificates and private key
632-
/// let cert_file = File::open("path/to/cert.pem")?;
633-
/// let key_file = File::open("path/to/key.pem")?;
630+
/// let cert_file = File::open("server.crt")?;
631+
/// let key_file = File::open("server.key")?;
634632
///
635633
/// let cert_chain = rustls_pemfile::certs(&mut BufReader::new(cert_file))
636-
/// .map(|certs| certs.into_iter().map(Certificate).collect())
637-
/// .unwrap_or_default();
638-
///
639-
/// let key = rustls_pemfile::pkcs8_private_keys(&mut BufReader::new(key_file))
640-
/// .next()
641-
/// .map(|key| PrivateKey(key))
642-
/// .expect("Failed to load private key");
634+
/// .map(|cd| cd.map(CertificateDer::from))
635+
/// .collect::<Result<Vec<_>, _>>()?;
643636
///
637+
/// let key = rustls_pemfile::read_one(&mut BufReader::new(key_file))?
638+
/// .map(|p| match p {
639+
/// rustls_pemfile::Item::Pkcs1Key(k) => PrivateKeyDer::Pkcs1(k),
640+
/// rustls_pemfile::Item::Pkcs8Key(k) => PrivateKeyDer::Pkcs8(k),
641+
/// _ => panic!("Unexpected private key type"),
642+
/// })
643+
/// .expect("Failed to read private key");
644+
///
644645
/// // Build TLS configuration
645646
/// let tls_config = ServerConfig::builder()
646-
/// .with_safe_defaults()
647647
/// .with_no_client_auth()
648648
/// .with_single_cert(cert_chain, key)
649649
/// .expect("Failed to build TLS configuration");
650-
///
650+
///
651651
/// // Create and run Ohkami with HTTPS
652652
/// Ohkami::new((
653653
/// "/".GET(hello),
@@ -656,6 +656,17 @@ impl Ohkami {
656656
/// Ok(())
657657
/// }
658658
/// ```
659+
///
660+
/// ```sh
661+
/// $ openssl req -x509 -newkey rsa:4096 -nodes -keyout server.key -out server.crt -days 365 -subj "/CN=localhost"
662+
///
663+
/// $ cargo run
664+
/// ```
665+
///
666+
/// ```sh
667+
/// $ curl --insecure https://localhost:8443
668+
/// Hello, secure ohkami!
669+
/// ```
659670
pub async fn howl_tls<T>(self, bind: impl __rt__::IntoTcpListener<T>, tls_config: rustls::ServerConfig) {
660671
let (router, _) = self.into_router().finalize();
661672
let router = Arc::new(router);

ohkami/src/session/mod.rs

Lines changed: 14 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,28 @@ use crate::util::timeout_in;
88
use crate::router::r#final::Router;
99
use crate::{Request, Response};
1010

11-
#[cfg(feature="ws")]
12-
use crate::__rt__::TcpStream;
13-
14-
pub(crate) struct Session<S> {
11+
pub(crate) struct Session<C> {
1512
router: Arc<Router>,
16-
connection: S,
13+
connection: C,
1714
ip: std::net::IpAddr,
1815
}
1916

20-
#[cfg(feature="ws")]
21-
pub(crate) trait WebSocketUpgradeable {
22-
fn into_websocket_stream(self) -> Result<TcpStream, &'static str>;
17+
pub(crate) trait Connection: AsyncRead + AsyncWrite + Unpin {
18+
#[cfg(feature="ws")]
19+
fn into_websocket_stream(self) -> Result<crate::__rt__::TcpStream, &'static str>;
2320
}
2421

25-
#[cfg(feature="ws")]
26-
impl WebSocketUpgradeable for TcpStream {
27-
fn into_websocket_stream(self) -> Result<TcpStream, &'static str> {
22+
impl Connection for crate::__rt__::TcpStream {
23+
#[cfg(feature="ws")]
24+
fn into_websocket_stream(self) -> Result<crate::__rt__::TcpStream, &'static str> {
2825
Ok(self)
2926
}
3027
}
3128

32-
#[cfg(feature="ws")]
33-
impl<S> Session<S>
34-
where
35-
S: AsyncRead + AsyncWrite + Unpin + WebSocketUpgradeable,
36-
{
29+
impl<C: Connection> Session<C> {
3730
pub(crate) fn new(
3831
router: Arc<Router>,
39-
connection: S,
32+
connection: C,
4033
ip: std::net::IpAddr
4134
) -> Self {
4235
Self {
@@ -85,14 +78,15 @@ where
8578
}
8679
}
8780
}).await {
88-
None => crate::WARNING!("[WARNING] \
81+
None => crate::WARNING!("\
8982
Session timeouted. In Ohkami, Keep-Alive timeout \
9083
is set to 42 seconds by default and is configurable \
9184
by `OHKAMI_KEEPALIVE_TIMEOUT` environment variable.\
9285
"),
9386

9487
Some(Upgrade::None) => crate::DEBUG!("about to shutdown connection"),
9588

89+
#[cfg(feature="ws")]
9690
Some(Upgrade::WebSocket(ws)) => {
9791
match self.connection.into_websocket_stream() {
9892
Ok(tcp_stream) => {
@@ -103,7 +97,7 @@ where
10397
tcp_stream
10498
).await;
10599
if aborted {
106-
crate::WARNING!("[WARNING] \
100+
crate::WARNING!("\
107101
WebSocket session aborted by timeout. In Ohkami, \
108102
WebSocket timeout is set to 3600 seconds (1 hour) \
109103
by default and is configurable by `OHKAMI_WEBSOCKET_TIMEOUT` \
@@ -114,78 +108,10 @@ where
114108
crate::DEBUG!("WebSocket session finished");
115109
}
116110
Err(msg) => {
117-
crate::WARNING!("[WARNING] {}", msg);
111+
crate::WARNING!("{msg}");
118112
}
119113
}
120114
}
121115
}
122116
}
123117
}
124-
125-
// There has to be some cleaner implementation to apply the conditional trait bounds in this...
126-
#[cfg(not(feature="ws"))]
127-
impl<S> Session<S>
128-
where
129-
S: AsyncRead + AsyncWrite + Unpin,
130-
{
131-
pub(crate) fn new(
132-
router: Arc<Router>,
133-
connection: S,
134-
ip: std::net::IpAddr
135-
) -> Self {
136-
Self {
137-
router,
138-
connection,
139-
ip
140-
}
141-
}
142-
143-
pub(crate) async fn manage(mut self) {
144-
#[cold] #[inline(never)]
145-
fn panicking(panic: Box<dyn Any + Send>) -> Response {
146-
if let Some(msg) = panic.downcast_ref::<String>() {
147-
crate::WARNING!("[Panicked]: {msg}");
148-
} else if let Some(msg) = panic.downcast_ref::<&str>() {
149-
crate::WARNING!("[Panicked]: {msg}");
150-
} else {
151-
crate::WARNING!("[Panicked]");
152-
}
153-
crate::Response::InternalServerError()
154-
}
155-
156-
match timeout_in(Duration::from_secs(crate::CONFIG.keepalive_timeout()), async {
157-
let mut req = Request::init(self.ip);
158-
let mut req = unsafe {Pin::new_unchecked(&mut req)};
159-
loop {
160-
req.clear();
161-
match req.as_mut().read(&mut self.connection).await {
162-
Ok(Some(())) => {
163-
let close = matches!(req.headers.Connection(), Some("close" | "Close"));
164-
165-
let res = match catch_unwind(AssertUnwindSafe({
166-
let req = req.as_mut();
167-
|| self.router.handle(req.get_mut())
168-
})) {
169-
Ok(future) => future.await,
170-
Err(panic) => panicking(panic),
171-
};
172-
let upgrade = res.send(&mut self.connection).await;
173-
174-
if !upgrade.is_none() {break upgrade}
175-
if close {break Upgrade::None}
176-
}
177-
Ok(None) => break Upgrade::None,
178-
Err(res) => {res.send(&mut self.connection).await;},
179-
}
180-
}
181-
}).await {
182-
None => crate::WARNING!("[WARNING] \
183-
Session timeouted. In Ohkami, Keep-Alive timeout \
184-
is set to 42 seconds by default and is configurable \
185-
by `OHKAMI_KEEPALIVE_TIMEOUT` environment variable.\
186-
"),
187-
188-
Some(Upgrade::None) => crate::DEBUG!("about to shutdown connection"),
189-
}
190-
}
191-
}

ohkami/src/tls/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use tokio::io::{AsyncRead, AsyncWrite};
2+
23
pub struct TlsStream(pub tokio_rustls::server::TlsStream<tokio::net::TcpStream>);
34

4-
#[cfg(feature="ws")]
5-
impl crate::session::WebSocketUpgradeable for TlsStream {
5+
impl crate::session::Connection for TlsStream {
6+
#[cfg(feature="ws")]
67
fn into_websocket_stream(self) -> Result<crate::__rt__::TcpStream, &'static str> {
78
Err("WebSocket connections are not supported over TLS yet")
89
}

0 commit comments

Comments
 (0)