Skip to content

Commit 58e6fda

Browse files
committed
sessions!
1 parent ba65ca4 commit 58e6fda

File tree

6 files changed

+117
-1
lines changed

6 files changed

+117
-1
lines changed

Cargo.toml

+5-1
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ features = ["docs"]
2424
rustdoc-args = ["--cfg", "feature=\"docs\""]
2525

2626
[features]
27-
default = ["h1-server"]
27+
default = ["h1-server", "sessions"]
2828
h1-server = ["async-h1"]
2929
docs = ["unstable"]
30+
sessions = ["async-session"]
3031
unstable = []
3132
# DO NOT USE. Only exists to expose internals so they can be benchmarked.
3233
__internal__bench = []
@@ -44,6 +45,7 @@ route-recognizer = "0.2.0"
4445
logtest = "2.0.0"
4546
async-trait = "0.1.36"
4647
futures-util = "0.3.5"
48+
async-session = { version = "1.0.2", optional = true }
4749

4850
[dev-dependencies]
4951
async-std = { version = "1.6.0", features = ["unstable", "attributes"] }
@@ -63,3 +65,5 @@ required-features = ["unstable"]
6365
name = "router"
6466
harness = false
6567

68+
[patch.crates-io]
69+
async-session = { git = "https://github.com/jbr/async-session", branch = "tide" }

examples/sessions.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use tide::prelude::*;
2+
use tide::sessions::SessionMiddleware;
3+
use tide::{Body, Request, Response};
4+
5+
#[async_std::main]
6+
async fn main() -> Result<(), std::io::Error> {
7+
tide::log::start();
8+
let mut app = tide::new();
9+
app.middleware(SessionMiddleware::mem());
10+
11+
app.at("/")
12+
.get(|req: Request<()>| async move { Ok(Body::from_json(req.session())?) });
13+
14+
app.at("/:key")
15+
.get(|req: Request<()>| async move {
16+
let key: String = req.param("key")?;
17+
Ok(if let Some(value) = req.session_get(&key) {
18+
Body::from_string(value.to_owned()).into()
19+
} else {
20+
Response::new(404)
21+
})
22+
})
23+
.post(|mut req: Request<()>| async move {
24+
let value_now = req.body_string().await?;
25+
let key: String = req.param("key")?;
26+
let value_before = req.session_insert(key, value_now.clone());
27+
28+
Ok(json!({
29+
"value_before": value_before,
30+
"value_now": value_now
31+
}))
32+
});
33+
34+
app.listen("127.0.0.1:8080").await?;
35+
36+
Ok(())
37+
}

src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ pub mod security;
215215
pub mod sse;
216216
pub mod utils;
217217

218+
#[cfg(feature = "sessions")]
219+
pub mod sessions;
220+
218221
pub use endpoint::Endpoint;
219222
pub use middleware::{Middleware, Next};
220223
pub use redirect::Redirect;

src/request.rs

+17
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,23 @@ impl<State> Request<State> {
507507
.and_then(|cookie_data| cookie_data.content.read().unwrap().get(name).cloned())
508508
}
509509

510+
#[cfg(feature = "sessions")]
511+
pub fn session(&self) -> &crate::sessions::Session {
512+
self.ext::<crate::sessions::Session>().expect(
513+
"request session not initialized, did you enable tide::sessions::SessionMiddleware?",
514+
)
515+
}
516+
517+
#[cfg(feature = "sessions")]
518+
pub fn session_get(&self, key: impl AsRef<str>) -> Option<String> {
519+
self.session().get(key.as_ref())
520+
}
521+
522+
#[cfg(feature = "sessions")]
523+
pub fn session_insert(&self, key: String, value: String) -> Option<String> {
524+
self.session().insert(key, value)
525+
}
526+
510527
/// Get the length of the body stream, if it has been set.
511528
///
512529
/// This value is set when passing a fixed-size object into as the body. E.g. a string, or a

src/sessions/middleware.rs

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use super::{mem::MemoryStore, SessionStore};
2+
use crate::{http::cookies::Cookie, utils::async_trait, Middleware, Next, Request};
3+
4+
#[derive(Debug)]
5+
pub struct SessionMiddleware<Store> {
6+
store: Store,
7+
cookie_name: String,
8+
}
9+
10+
impl<Store: SessionStore> SessionMiddleware<Store> {
11+
pub fn new(store: Store) -> Self {
12+
Self {
13+
store,
14+
cookie_name: "session_id".into(),
15+
}
16+
}
17+
18+
pub fn with_cookie_name(mut self, cookie_name: String) -> Self {
19+
self.cookie_name = cookie_name;
20+
self
21+
}
22+
}
23+
24+
impl SessionMiddleware<MemoryStore> {
25+
pub fn mem() -> Self {
26+
Self::new(MemoryStore::new())
27+
}
28+
}
29+
30+
#[async_trait]
31+
impl<Store, State> Middleware<State> for SessionMiddleware<Store>
32+
where
33+
Store: SessionStore,
34+
State: Send + Sync + 'static,
35+
{
36+
async fn handle(&self, mut request: Request<State>, next: Next<'_, State>) -> crate::Result {
37+
let cookie = request.cookie(&self.cookie_name);
38+
let session = self.store.load_or_create(cookie).await?;
39+
request.set_ext(session.clone());
40+
41+
let mut response = next.run(request).await;
42+
43+
let session_cookie_value = self.store.store_session(session).await?;
44+
let session_cookie = Cookie::build(self.cookie_name.clone(), session_cookie_value)
45+
.path("/")
46+
.finish();
47+
response.insert_cookie(session_cookie);
48+
49+
Ok(response)
50+
}
51+
}

src/sessions/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
mod middleware;
2+
3+
pub use async_session::*;
4+
pub use middleware::SessionMiddleware;

0 commit comments

Comments
 (0)