Skip to content

Commit 5ea496f

Browse files
Merge pull request #168 from illicitonion/cookiesexamples
Add examples using sessions
2 parents fb7091b + 05fb3f9 commit 5ea496f

File tree

9 files changed

+528
-0
lines changed

9 files changed

+528
-0
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ members = [
2626

2727
# cookies
2828

29+
# sessions
30+
"examples/sessions/introduction",
31+
"examples/sessions/custom_data_type",
32+
2933
# headers
3034
"examples/headers/setting",
3135

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ information on functionality and ordering.
3535
| [Path](path) | Extracting data from the `Request` path ensuring type safety. | 1 |
3636
| [Query String](query_string) | Extracting data from the `Request` query string whilst ensuring type safety. | 1 |
3737
| [Cookies](cookies) | Working with Cookies. | 0 |
38+
| [Sessions](sessions) | Working with Sessions. | 2 |
3839
| [Headers](headers) | Working with HTTP Headers. | 1 |
3940
| [Handlers](handlers) | Developing application logic that responds to web requests. | 0 |
4041
| [Middleware](middleware) | Developing custom middleware for your application. | 1 |

examples/sessions/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Sessions Examples
2+
3+
A collection of crates showing how to store and retrieve session data with the Gotham web framework.
4+
5+
## Ordering
6+
7+
We recommend reviewing our sessions examples in the order shown below:
8+
9+
1. [Introduction](introduction) - Shows how to store and retrieve session data.
10+
2. [Custom data type](custom_data_type) - Shows how to store and retrieve session data with a custom data type.
11+
12+
## Help
13+
14+
You can get help for the Gotham web framework at:
15+
16+
* [The Gotham web framework website](https://gotham.rs)
17+
* [Gotham web framework API documentation](https://docs.rs/gotham/)
18+
* [Gitter chatroom](https://gitter.im/gotham-rs/gotham)
19+
* [Twitter](https://twitter.com/gotham_rs)
20+
21+
## License
22+
23+
Licensed under your option of:
24+
25+
* [MIT License](../LICENSE-MIT)
26+
* [Apache License, Version 2.0](../LICENSE-APACHE)
27+
28+
## Community
29+
30+
The following policies guide participation in our project and our community:
31+
32+
* [Code of conduct](../../CODE_OF_CONDUCT.md)
33+
* [Contributing](../../CONTRIBUTING.md)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "gotham_examples_session_custom_data_type"
3+
description = "Storing and retrieving session data with a custom data type, in a type safe way, with the Gotham web framework."
4+
version = "0.0.0"
5+
authors = ["Daniel Wagner-Hall <[email protected]>"]
6+
publish = false
7+
8+
[dependencies]
9+
gotham = { path = "../../../gotham" }
10+
gotham_derive = { path = "../../../gotham_derive" }
11+
12+
hyper = "0.11"
13+
mime = "0.3"
14+
serde = "1"
15+
serde_derive = "1"
16+
time = "0.1.39"
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Custom session data types
2+
3+
Storing and retrieving session data with a custom data type, in a type safe way, with the Gotham web framework.
4+
5+
## Running
6+
7+
From the `examples/session/custom_data_type` directory:
8+
9+
```
10+
Terminal 1:
11+
$ cargo run 130 ↵
12+
Compiling gotham_examples_session_custom_data_type v0.0.0 (file://.../gotham/examples/session/custom_data_type)
13+
Finished dev [unoptimized + debuginfo] target(s) in 2.49 secs
14+
Running `.../gotham/target/debug/gotham_examples_cookies_custom_data_type`
15+
Listening for requests at http://127.0.0.1:7878
16+
17+
Terminal 2:
18+
$ curl -v -c /tmp/cookiejar http://localhost:7878
19+
* Rebuilt URL to: http://localhost:7878/
20+
* Trying ::1...
21+
* TCP_NODELAY set
22+
* Connection failed
23+
* connect to ::1 port 7878 failed: Connection refused
24+
* Trying 127.0.0.1...
25+
* TCP_NODELAY set
26+
* Connected to localhost (127.0.0.1) port 7878 (#0)
27+
> GET / HTTP/1.1
28+
> Host: localhost:7878
29+
> User-Agent: curl/7.54.0
30+
> Accept: */*
31+
>
32+
< HTTP/1.1 200 OK
33+
< Content-Length: 41
34+
< Content-Type: text/plain
35+
< X-Request-ID: 5fdd0a88-4b23-4c68-8f6b-91d3c6a69fd4
36+
< X-Frame-Options: DENY
37+
< X-XSS-Protection: 1; mode=block
38+
< X-Content-Type-Options: nosniff
39+
* Added cookie _gotham_session="op-CELe5R-mEJ3zxhcak4eElI5EUtslBLbZ6chyUvAWzEwkvAkPUBzsHj014xHW1tWq0RG4vyXSnXDZneqfxyA" for domain localhost, path /, expire 0
40+
< Set-Cookie: _gotham_session=op-CELe5R-mEJ3zxhcak4eElI5EUtslBLbZ6chyUvAWzEwkvAkPUBzsHj014xHW1tWq0RG4vyXSnXDZneqfxyA; HttpOnly; SameSite=Lax; Path=/
41+
< X-Runtime-Microseconds: 1143
42+
< Date: Thu, 01 Mar 2018 00:29:10 GMT
43+
<
44+
You have never visited this page before.
45+
* Connection #0 to host localhost left intact
46+
47+
$ curl -v -b /tmp/cookiejar http://localhost:7878
48+
* Rebuilt URL to: http://localhost:7878/
49+
* Trying ::1...
50+
* TCP_NODELAY set
51+
* Connection failed
52+
* connect to ::1 port 7878 failed: Connection refused
53+
* Trying 127.0.0.1...
54+
* TCP_NODELAY set
55+
* Connected to localhost (127.0.0.1) port 7878 (#0)
56+
> GET / HTTP/1.1
57+
> Host: localhost:7878
58+
> User-Agent: curl/7.54.0
59+
> Accept: */*
60+
> Cookie: _gotham_session=op-CELe5R-mEJ3zxhcak4eElI5EUtslBLbZ6chyUvAWzEwkvAkPUBzsHj014xHW1tWq0RG4vyXSnXDZneqfxyA
61+
>
62+
< HTTP/1.1 200 OK
63+
< Content-Length: 87
64+
< Content-Type: text/plain
65+
< X-Request-ID: 0a202d40-2c89-418e-82b0-5854c0041665
66+
< X-Frame-Options: DENY
67+
< X-XSS-Protection: 1; mode=block
68+
< X-Content-Type-Options: nosniff
69+
< X-Runtime-Microseconds: 400
70+
< Date: Thu, 01 Mar 2018 00:29:13 GMT
71+
<
72+
You have visited this page 1 time(s) before. Your last visit was 2018-03-01T00:29:10Z.
73+
* Connection #0 to host localhost left intact
74+
75+
```
76+
77+
## License
78+
79+
Licensed under your option of:
80+
81+
* [MIT License](../../LICENSE-MIT)
82+
* [Apache License, Version 2.0](../../LICENSE-APACHE)
83+
84+
## Community
85+
86+
The following policies guide participation in our project and our community:
87+
88+
* [Code of conduct](../../CODE_OF_CONDUCT.md)
89+
* [Contributing](../../CONTRIBUTING.md)
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//! Storing and retrieving session data with a custom data type, in a type safe
2+
//! way, with the Gotham web framework.
3+
4+
extern crate gotham;
5+
#[macro_use]
6+
extern crate gotham_derive;
7+
extern crate hyper;
8+
extern crate mime;
9+
extern crate serde;
10+
#[macro_use]
11+
extern crate serde_derive;
12+
extern crate time;
13+
14+
use hyper::{Response, StatusCode};
15+
16+
use gotham::http::response::create_response;
17+
use gotham::pipeline::new_pipeline;
18+
use gotham::pipeline::single::single_pipeline;
19+
use gotham::router::Router;
20+
use gotham::router::builder::*;
21+
use gotham::state::{FromState, State};
22+
use gotham::middleware::session::{NewSessionMiddleware, SessionData};
23+
24+
// A custom type for storing data associated with the user's session.
25+
#[derive(Clone, Deserialize, Serialize, StateData)]
26+
struct VisitData {
27+
count: usize,
28+
last_visit: String,
29+
}
30+
31+
/// Handler function for `GET` requests directed to `/`
32+
///
33+
/// Each request made will update state about your recent visits, and report it back.
34+
fn get_handler(mut state: State) -> (State, Response) {
35+
let maybe_visit_data = {
36+
let visit_data: &Option<VisitData> = SessionData::<Option<VisitData>>::borrow_from(&state);
37+
visit_data.clone()
38+
};
39+
40+
let body = match &maybe_visit_data {
41+
&Some(ref visit_data) => format!(
42+
"You have visited this page {} time(s) before. Your last visit was {}.\n",
43+
visit_data.count, visit_data.last_visit,
44+
),
45+
&None => "You have never visited this page before.\n".to_owned(),
46+
};
47+
let res = {
48+
create_response(
49+
&state,
50+
StatusCode::Ok,
51+
Some((body.as_bytes().to_vec(), mime::TEXT_PLAIN)),
52+
)
53+
};
54+
{
55+
let visit_data: &mut Option<VisitData> =
56+
SessionData::<Option<VisitData>>::borrow_mut_from(&mut state);
57+
let old_count = maybe_visit_data.map(|v| v.count).unwrap_or(0);
58+
*visit_data = Some(VisitData {
59+
count: old_count + 1,
60+
last_visit: format!("{}", time::now().rfc3339()),
61+
});
62+
}
63+
(state, res)
64+
}
65+
66+
/// Create a `Router`
67+
fn router() -> Router {
68+
let middleware = NewSessionMiddleware::default()
69+
.with_session_type::<Option<VisitData>>()
70+
// By default, the cookies used are only sent over secure connections. For our test server,
71+
// we don't set up an HTTPS certificate, so we allow the cookies to be sent over insecure
72+
// connections. This should not be done in real applications.
73+
.insecure();
74+
let (chain, pipelines) = single_pipeline(new_pipeline().add(middleware).build());
75+
build_router(chain, pipelines, |route| {
76+
route.get("/").to(get_handler);
77+
})
78+
}
79+
80+
/// Start a server and use a `Router` to dispatch requests
81+
pub fn main() {
82+
let addr = "127.0.0.1:7878";
83+
println!("Listening for requests at http://{}", addr);
84+
gotham::start(addr, router())
85+
}
86+
87+
#[cfg(test)]
88+
mod tests {
89+
use super::*;
90+
use gotham::test::TestServer;
91+
use hyper::header::{Cookie, SetCookie};
92+
use std::borrow::Cow;
93+
94+
#[test]
95+
fn cookie_is_set_and_updates_response() {
96+
let test_server = TestServer::new(router()).unwrap();
97+
let response = test_server
98+
.client()
99+
.get("http://localhost/")
100+
.perform()
101+
.unwrap();
102+
103+
assert_eq!(response.status(), StatusCode::Ok);
104+
105+
let set_cookie: Vec<String> = {
106+
let cookie_header = response.headers().get::<SetCookie>();
107+
assert!(cookie_header.is_some());
108+
cookie_header.unwrap().0.clone()
109+
};
110+
assert!(set_cookie.len() == 1);
111+
112+
let body = response.read_body().unwrap();
113+
assert_eq!(
114+
&body[..],
115+
"You have never visited this page before.\n".as_bytes()
116+
);
117+
118+
let cookie = {
119+
let mut cookie = Cookie::new();
120+
121+
let only_cookie: String = set_cookie.get(0).unwrap().clone();
122+
let cookie_components: Vec<_> = only_cookie.split(";").collect();
123+
let cookie_str_parts: Vec<_> = cookie_components.get(0).unwrap().split("=").collect();
124+
cookie.append(
125+
Cow::Owned(cookie_str_parts.get(0).unwrap().to_string()),
126+
Cow::Owned(cookie_str_parts.get(1).unwrap().to_string()),
127+
);
128+
cookie
129+
};
130+
131+
let response = test_server
132+
.client()
133+
.get("http://localhost/")
134+
.with_header(cookie)
135+
.perform()
136+
.unwrap();
137+
138+
assert_eq!(response.status(), StatusCode::Ok);
139+
let body = response.read_body().unwrap();
140+
let body_string = String::from_utf8(body).unwrap();
141+
assert!(
142+
body_string
143+
.starts_with("You have visited this page 1 time(s) before. Your last visit was ",),
144+
"Wrong body: {}",
145+
body_string
146+
);
147+
}
148+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "gotham_examples_session_introduction"
3+
description = "An introduction to storing and retrieving session data, in a type safe way, with the Gotham web framework."
4+
version = "0.0.0"
5+
authors = ["Daniel Wagner-Hall <[email protected]>"]
6+
publish = false
7+
8+
[dependencies]
9+
gotham = { path = "../../../gotham" }
10+
11+
hyper = "0.11"
12+
mime = "0.3"

0 commit comments

Comments
 (0)