Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling Sessions with an 'Environment' #246

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ groupable = "*"
mustache = "*"
lazy_static = "*"
modifier = "*"
cookie = "*"
byteorder = "*"

[dependencies.compiletest_rs]
version = "*"
Expand All @@ -40,6 +42,16 @@ path = "examples/example.rs"

[[example]]

name = "cookies_example"
path = "examples/cookies_example.rs"

[[example]]

name = "session_example"
path = "examples/session_example.rs"

[[example]]

name = "example_with_default_router"
path = "examples/example_with_default_router.rs"

Expand Down
46 changes: 46 additions & 0 deletions examples/cookies_example.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#[macro_use] extern crate nickel;
extern crate cookie;

use nickel::{Nickel, HttpRouter, Cookies, QueryString};
use nickel::cookies;
use cookie::Cookie;

struct Data {
secret_key: cookies::SecretKey
}

impl AsRef<cookies::SecretKey> for Data {
fn as_ref(&self) -> &cookies::SecretKey {
&self.secret_key
}
}

fn main() {
let data = Data { secret_key: cookies::SecretKey([0; 32]) };
let mut server = Nickel::with_data(data);

// Try curl -b MyCookie=bar localhost:6767
server.get("/", middleware! { |mut res|
let cookie = res.cookies().find("MyCookie");
format!("MyCookie={:?}", cookie.map(|c| c.value))
});

// Note: Don't use get for login in real applications ;)
// Try http://localhost:6767/login?name=foo
server.get("/login", middleware! { |mut res|
let cookie = {
let name = res.request.query().get("name")
.unwrap_or("default_name");
Cookie::new("MyCookie".to_owned(), name.to_owned())
};

let jar = res.cookies_mut()
// long life cookies!
.permanent();
jar.add(cookie);

"Cookie set!"
});

server.listen("127.0.0.1:6767");
}
40 changes: 21 additions & 19 deletions examples/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use std::collections::BTreeMap;
use std::io::Write;
use nickel::status::StatusCode::{self, NotFound, BadRequest};
use nickel::{
Nickel, NickelError, Continue, Halt, Request,
QueryString, JsonBody, StaticFilesHandler, HttpRouter, Action, MediaType
Nickel, NickelError, Continue, Halt, QueryString, JsonBody,
StaticFilesHandler, HttpRouter, Action, MediaType
};
use regex::Regex;
use rustc_serialize::json::{Json, ToJson};
Expand Down Expand Up @@ -35,31 +35,31 @@ fn main() {

//this is an example middleware function that just logs each request
// middleware is optional and can be registered with `utilize`
server.utilize(middleware! { |request|
println!("logging request: {:?}", request.origin.uri);
server.utilize(middleware! { |response|
println!("logging request: {:?}", response.request.origin.uri);
});

let mut router = Nickel::router();

// go to http://localhost:6767/user/4711 to see this route in action
router.get("/user/:userid", middleware! { |request|
format!("This is user: {}", request.param("userid").unwrap())
router.get("/user/:userid", middleware! { |response|
format!("This is user: {}", response.param("userid").unwrap())
});

// go to http://localhost:6767/bar to see this route in action
router.get("/bar", middleware!("This is the /bar handler"));

// go to http://localhost:6767/content-type to see this route in action
router.get("/content-type", middleware! { |_, mut response|
router.get("/content-type", middleware! { |mut response|
response.set(MediaType::Json);
"{'foo':'bar'}"
});

let hello_regex = Regex::new("/hello/(?P<name>[a-zA-Z]+)").unwrap();

// go to http://localhost:6767/hello/moomah to see this route in action
router.get(hello_regex, middleware! { |request|
format!("Hello {}", request.param("name").unwrap())
router.get(hello_regex, middleware! { |response|
format!("Hello {}", response.param("name").unwrap())
});

// go to http://localhost:6767/some/crazy/route to see this route in action
Expand All @@ -74,8 +74,8 @@ fn main() {

// try it with curl
// curl 'http://localhost:6767/a/post/request' -H 'Content-Type: application/json;charset=UTF-8' --data-binary $'{ "firstname": "John","lastname": "Connor" }'
router.post("/a/post/request", middleware! { |request, response|
let person = request.json_as::<Person>().unwrap();
router.post("/a/post/request", middleware! { |mut response|
let person = response.request.json_as::<Person>().unwrap();
format!("Hello {} {}", person.firstname, person.lastname)
});

Expand All @@ -89,8 +89,8 @@ fn main() {
});

// try calling http://localhost:6767/query?foo=bar
router.get("/query", middleware! { |request|
if let Some(vals) = request.query().all("foo") {
router.get("/query", middleware! { |mut response|
if let Some(vals) = response.request.query().all("foo") {
format!("Your foo values in the query string are: {:?}", vals)
} else {
format!("You didn't provide any foo values!")
Expand All @@ -99,12 +99,14 @@ fn main() {

// try calling http://localhost:6767/strict?state=valid
// then try calling http://localhost:6767/strict?state=invalid
router.get("/strict", middleware! { |request|
if request.query().get("state") != Some("valid") {
router.get("/strict", middleware! { |mut response|
let reply = if response.request.query().get("state") != Some("valid") {
(BadRequest, "Error Parsing JSON")
} else {
(StatusCode::Ok, "Congratulations on conforming!")
}
};

reply
});

server.utilize(router);
Expand All @@ -113,8 +115,8 @@ fn main() {
server.utilize(StaticFilesHandler::new("examples/assets/"));

//this is how to overwrite the default error handler to handle 404 cases with a custom view
fn custom_404<'a>(err: &mut NickelError, _req: &mut Request) -> Action {
if let Some(ref mut res) = err.stream {
fn custom_404<'a, D>(err: &mut NickelError<D>) -> Action {
if let Some(ref mut res) = err.response_mut() {
if res.status() == NotFound {
let _ = res.write_all(b"<h1>Call the police!</h1>");
return Halt(())
Expand All @@ -126,7 +128,7 @@ fn main() {


// issue #20178
let custom_handler: fn(&mut NickelError, &mut Request) -> Action = custom_404;
let custom_handler: fn(&mut NickelError<()>) -> Action = custom_404;

server.handle_error(custom_handler);

Expand Down
2 changes: 1 addition & 1 deletion examples/example_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::collections::HashMap;
fn main() {
let mut server = Nickel::new();

server.get("/", middleware! { |_, res|
server.get("/", middleware! { |res|
let mut data = HashMap::<&str, &str>::new();
data.insert("name", "user");
return res.render("examples/assets/template.tpl", &data)
Expand Down
55 changes: 29 additions & 26 deletions examples/macro_example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ extern crate hyper;
use std::io::Write;
use nickel::status::StatusCode::{self, NotFound};
use nickel::{
Nickel, NickelError, Continue, Halt, Request, Response, MediaType,
QueryString, JsonBody, StaticFilesHandler, MiddlewareResult, HttpRouter, Action
Nickel, NickelError, Continue, Halt, Response, MediaType, QueryString,
JsonBody, StaticFilesHandler, MiddlewareResult, HttpRouter, Action
};
use regex::Regex;
use hyper::header::Location;
Expand All @@ -19,14 +19,14 @@ struct Person {
}

//this is an example middleware function that just logs each request
fn logger<'a>(request: &mut Request, response: Response<'a>) -> MiddlewareResult<'a> {
println!("logging request: {:?}", request.origin.uri);
fn logger<'a, 'k, D>(response: Response<'a, 'k, D>) -> MiddlewareResult<'a, 'k, D> {
println!("logging request: {:?}", response.request.origin.uri);
Ok(Continue(response))
}

//this is how to overwrite the default error handler to handle 404 cases with a custom view
fn custom_404<'a>(err: &mut NickelError, _req: &mut Request) -> Action {
if let Some(ref mut res) = err.stream {
fn custom_404<'a, D>(err: &mut NickelError<D>) -> Action {
if let Some(ref mut res) = err.response_mut() {
if res.status() == NotFound {
let _ = res.write_all(b"<h1>Call the police!</h1>");
return Halt(())
Expand All @@ -50,16 +50,16 @@ fn main() {
// The return type for a route can be anything that implements `Responder`
server.utilize(router!(
// go to http://localhost:6767/user/4711 to see this route in action
get "/user/:userid" => |request| {
get "/user/:userid" => |response| {
// returning a String
format!("This is user: {}", request.param("userid").unwrap())
format!("This is user: {}", response.param("userid").unwrap())
}

// go to http://localhost:6767/no_alloc/4711 to see this route in action
get "/no_alloc/:userid" => |request, response| {
// returning a slice of T where T: Display
&["This is user: ", request.param("userid").unwrap()][..]
}
// get "/no_alloc/:userid" => |response| {
// // returning a slice of T where T: Display
// &["This is user: ", response.param("userid").unwrap()][..]
// }

// go to http://localhost:6767/bar to see this route in action
get "/bar" => {
Expand All @@ -68,18 +68,18 @@ fn main() {
}

// go to http://localhost:6767/content-type to see this route in action
get "/content-type" => |_, mut response| {
get "/content-type" => |mut response| {
response.set(MediaType::Json);
"{'foo':'bar'}"
}

// go to http://localhost:6767/hello/moomah to see this route in action
get hello_regex => |request| {
format!("Hello {}", request.param("name").unwrap())
get hello_regex => |response| {
format!("Hello {}", response.param("name").unwrap())
}

// go to http://localhost:6767/redirect to see this route in action
get "/redirect" => |_, mut response| {
get "/redirect" => |mut response| {
response.set(Location("http://nickel.rs".into()));

StatusCode::PermanentRedirect
Expand All @@ -104,25 +104,28 @@ fn main() {

// try it with curl
// curl 'http://localhost:6767/a/post/request' -H 'Content-Type: application/json;charset=UTF-8' --data-binary $'{ "firstname": "John","lastname": "Connor" }'
post "/a/post/request" => |request| {
let person = request.json_as::<Person>().unwrap();
post "/a/post/request" => |mut response| {
let person = response.request.json_as::<Person>().unwrap();
format!("Hello {} {}", person.firstname, person.lastname)
}

// try calling http://localhost:6767/query?foo=bar
get "/query" => |request| {
let query = request.query();
let foo = query.get("foo").unwrap_or("This is only a default value");
let bar = query.get("bar").unwrap_or("This is only a default value");
let text = format!("<p>Your foo values in the query string are: {:?}\
<p>Your bar values are: {:?}",
foo, bar);
get "/query" => |mut response| {
let text = {
let query = response.request.query();
let foo = query.get("foo").unwrap_or("This is only a default value");
let bar = query.get("bar").unwrap_or("This is only a default value");
format!("<p>Your foo values in the query string are: {:?}\
<p>Your bar values are: {:?}",
foo, bar);
};

text
}
));

// issue #20178
let custom_handler: fn(&mut NickelError, &mut Request) -> Action = custom_404;
let custom_handler: fn(&mut NickelError<()>) -> Action = custom_404;

server.handle_error(custom_handler);

Expand Down
72 changes: 72 additions & 0 deletions examples/session_example.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#[macro_use] extern crate nickel;
extern crate rustc_serialize;
extern crate time;

use std::io::Write;
use nickel::*;
use nickel::status::StatusCode;
use time::Duration;

#[derive(RustcDecodable, RustcEncodable)]
struct User {
name: String,
password: String,
}

struct ServerData;
static SECRET_KEY: &'static cookies::SecretKey = &cookies::SecretKey([0; 32]);
impl AsRef<cookies::SecretKey> for ServerData {
fn as_ref(&self) -> &cookies::SecretKey { SECRET_KEY }
}
impl SessionStore for ServerData {
type Store = Option<String>;

fn timeout() -> Duration {
Duration::seconds(5)
}
}


fn main() {
let mut server = Nickel::with_data(ServerData);

/* Anyone should be able to reach thist route. */
server.get("/", middleware! { |mut res|
format!("You are logged in as: {:?}\n", res.session())
});

server.post("/login", middleware!{|mut res|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may not see the forest for the tree here. How do I fully test this example? Of course I can send the right json to the running server using something like curl or postman and I get the "successfully logged in" text. But I'd like to test out the entire example (session cookie). So I thought, ok, let's just render a small <form> for the / route that can be used for the login. But that's not gonna work as non ajax submitted forms aren't send as JSON so I would have to change other parts of the example, too.

So I wonder, did you actually test the entire example to see if the session cookie is created by the browser after successful login? If so, how did you test that out?

@Ryman

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See nickel-org/nickel-auth#1, it has a test shell script. (but yes we should have a test written in rust!)

if let Ok(u) = res.request.json_as::<User>() {
if u.name == "foo" && u.password == "bar" {
*res.session_mut() = Some(u.name);
return res.send("Successfully logged in.")
}
}
(StatusCode::BadRequest, "Access denied.")
});

server.get("/secret", middleware! { |mut res| <ServerData>
match *res.session() {
Some(ref user) if user == "foo" => (StatusCode::Ok, "Some hidden information!"),
_ => (StatusCode::Forbidden, "Access denied.")
}
});

fn custom_403<'a>(err: &mut NickelError<ServerData>) -> Action {
if let Some(ref mut res) = err.response_mut() {
if res.status() == StatusCode::Forbidden {
let _ = res.write_all(b"Access denied!\n");
return Halt(())
}
}

Continue(())
}

// issue #20178
let custom_handler: fn(&mut NickelError<ServerData>) -> Action = custom_403;

server.handle_error(custom_handler);

server.listen("127.0.0.1:6767");
}
Loading