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

Cookies #2 #235

Closed
wants to merge 8 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
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ groupable = "*"
mustache = "*"
lazy_static = "*"
modifier = "*"
cookie = "*"

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

[[example]]

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

[[example]]

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

Expand Down
45 changes: 45 additions & 0 deletions examples/cookies_example.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#[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! { |req|
let cookie = req.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! { |req, mut res|
let jar = res.cookies_mut()
// long life cookies!
.permanent();

let name = req.query().get("name")
.unwrap_or("default_name");
let cookie = Cookie::new("MyCookie".to_owned(),
name.to_owned());
jar.add(cookie);

"Cookie set!"
});

server.listen("127.0.0.1:6767");
}
4 changes: 2 additions & 2 deletions examples/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ 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 {
fn custom_404<'a, D>(err: &mut NickelError<D>, _req: &mut Request<D>) -> Action {
if let Some(ref mut res) = err.stream {
if res.status() == NotFound {
let _ = res.write_all(b"<h1>Call the police!</h1>");
Expand All @@ -126,7 +126,7 @@ fn main() {


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

server.handle_error(custom_handler);

Expand Down
6 changes: 3 additions & 3 deletions examples/macro_example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ struct Person {
}

//this is an example middleware function that just logs each request
fn logger<'a>(request: &mut Request, response: Response<'a>) -> MiddlewareResult<'a> {
fn logger<'a, D>(request: &mut Request<D>, response: Response<'a, D>) -> MiddlewareResult<'a, D> {
println!("logging request: {:?}", 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 {
fn custom_404<'a, D>(err: &mut NickelError<D>, _req: &mut Request<D>) -> Action {
if let Some(ref mut res) = err.stream {
if res.status() == NotFound {
let _ = res.write_all(b"<h1>Call the police!</h1>");
Expand Down Expand Up @@ -122,7 +122,7 @@ fn main() {
));

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

server.handle_error(custom_handler);

Expand Down
91 changes: 91 additions & 0 deletions src/cookies.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use {Request, Response};
use plugin::{Plugin, Pluggable};
use typemap::Key;
use hyper::header;

use cookie::CookieJar;

pub struct SecretKey(pub [u8; 32]);

// Plugin boilerplate
pub struct CookiePlugin;
impl Key for CookiePlugin { type Value = CookieJar<'static>; }

impl<'a, 'b, 'k, D> Plugin<Request<'a, 'b, 'k, D>> for CookiePlugin
where D: AsRef<SecretKey> {
type Error = ();

fn eval(req: &mut Request<D>) -> Result<CookieJar<'static>, ()> {
let key = req.data().as_ref();
let jar = match req.origin.headers.get::<header::Cookie>() {
Some(c) => c.to_cookie_jar(&key.0),
None => CookieJar::new(&key.0)
};

Ok(jar)
}
}

impl<'a, 'b, 'k, D> Plugin<Response<'a, D>> for CookiePlugin
where D: AsRef<SecretKey> {
type Error = ();

fn eval(res: &mut Response<'a, D>) -> Result<CookieJar<'static>, ()> {
// Schedule the cookie to be written when headers are being sent
res.on_send(|res| {
let header = {
let jar = res.get_ref::<CookiePlugin>().unwrap();
header::SetCookie::from_cookie_jar(jar)
};
res.set(header);
});

let key = res.data().as_ref();
Ok(CookieJar::new(&key.0))
}
}

/// Trait to whitelist access to `&'mut CookieJar` via the `Cookies` trait.
pub trait AllowMutCookies {}
impl<'a, D> AllowMutCookies for Response<'a, D> {}

/// Provides access to a `CookieJar`.
///
/// Access to cookies for a `Request` is read-only and represents the cookies
/// sent from the client.
///
/// The `Response` has access to a mutable `CookieJar` when first accessed.
/// Any cookies added to this jar will be sent as `Set-Cookie` response headers
/// when the `Response` sends it's `Headers` to the client.
///
/// #Examples
/// See `examples/cookies_example.rs`.
pub trait Cookies {
/// Provides access to an immutable CookieJar.
///
/// Currently requires a mutable reciever, hopefully this can change in future.
fn cookies(&mut self) -> &CookieJar<'static>;

/// Provides access to a mutable CookieJar.
fn cookies_mut(&mut self) -> &mut CookieJar<'static> where Self: AllowMutCookies;
}

impl<'a, 'b, 'k, D: AsRef<SecretKey>> Cookies for Request<'a, 'b, 'k, D> {
fn cookies(&mut self) -> &<CookiePlugin as Key>::Value {
self.get_ref::<CookiePlugin>().unwrap()
}

fn cookies_mut(&mut self) -> &mut <CookiePlugin as Key>::Value where Self: AllowMutCookies {
unreachable!()
}
}

impl<'a, D: AsRef<SecretKey>> Cookies for Response<'a, D> {
fn cookies(&mut self) -> &<CookiePlugin as Key>::Value {
self.get_ref::<CookiePlugin>().unwrap()
}

fn cookies_mut(&mut self) -> &mut <CookiePlugin as Key>::Value where Self: AllowMutCookies {
self.get_mut::<CookiePlugin>().unwrap()
}
}
4 changes: 2 additions & 2 deletions src/default_error_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use std::io::Write;
#[derive(Clone, Copy)]
pub struct DefaultErrorHandler;

impl ErrorHandler for DefaultErrorHandler {
fn handle_error(&self, err: &mut NickelError, _req: &mut Request) -> Action {
impl<D> ErrorHandler<D> for DefaultErrorHandler {
fn handle_error(&self, err: &mut NickelError<D>, _req: &mut Request<D>) -> Action {
if let Some(ref mut res) = err.stream {
let msg : &[u8] = match res.status() {
NotFound => b"Not Found",
Expand Down
13 changes: 6 additions & 7 deletions src/favicon_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use hyper::uri::RequestUri::AbsolutePath;
use hyper::method::Method::{Get, Head, Options};
use hyper::status::StatusCode;
use hyper::header;
use hyper::net;

use request::Request;
use response::Response;
Expand All @@ -18,9 +17,9 @@ pub struct FaviconHandler {
icon_path: PathBuf, // Is it useful to log where in-memory favicon came from every request?
}

impl Middleware for FaviconHandler {
fn invoke<'a, 'b>(&'a self, req: &mut Request<'b, 'a, 'b>, res: Response<'a, net::Fresh>)
-> MiddlewareResult<'a> {
impl<D> Middleware<D> for FaviconHandler {
fn invoke<'a, 'b>(&'a self, req: &mut Request<'b, 'a, 'b, D>, res: Response<'a, D>)
-> MiddlewareResult<'a, D> {
if FaviconHandler::is_favicon_request(req) {
self.handle_request(req, res)
} else {
Expand Down Expand Up @@ -53,14 +52,14 @@ impl FaviconHandler {
}

#[inline]
pub fn is_favicon_request(req: &Request) -> bool {
pub fn is_favicon_request<D>(req: &Request<D>) -> bool {
match req.origin.uri {
AbsolutePath(ref path) => &**path == "/favicon.ico",
_ => false
}
}

pub fn handle_request<'a>(&self, req: &Request, mut res: Response<'a>) -> MiddlewareResult<'a> {
pub fn handle_request<'a, D>(&self, req: &Request<D>, mut res: Response<'a, D>) -> MiddlewareResult<'a, D> {
match req.origin.method {
Get | Head => {
self.send_favicon(req, res)
Expand All @@ -78,7 +77,7 @@ impl FaviconHandler {
}
}

pub fn send_favicon<'a, 'b>(&self, req: &Request, mut res: Response<'a>) -> MiddlewareResult<'a> {
pub fn send_favicon<'a, 'b, D>(&self, req: &Request<D>, mut res: Response<'a, D>) -> MiddlewareResult<'a, D> {
debug!("{:?} {:?}", req.origin.method, self.icon_path.display());
res.set(MediaType::Ico);
res.send(&*self.icon)
Expand Down
6 changes: 3 additions & 3 deletions src/json_body_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ use std::io::{Read, ErrorKind};
// Plugin boilerplate
struct JsonBodyParser;
impl Key for JsonBodyParser { type Value = String; }
impl<'a, 'b, 'k> Plugin<Request<'a, 'b, 'k>> for JsonBodyParser {
impl<'a, 'b, 'k, D> Plugin<Request<'a, 'b, 'k, D>> for JsonBodyParser {
type Error = io::Error;

fn eval(req: &mut Request) -> Result<String, io::Error> {
fn eval(req: &mut Request<D>) -> Result<String, io::Error> {
let mut s = String::new();
try!(req.origin.read_to_string(&mut s));
Ok(s)
Expand All @@ -22,7 +22,7 @@ pub trait JsonBody {
fn json_as<T: Decodable>(&mut self) -> Result<T, io::Error>;
}

impl<'a, 'b, 'k> JsonBody for Request<'a, 'b, 'k> {
impl<'a, 'b, 'k, D> JsonBody for Request<'a, 'b, 'k, D> {
fn json_as<T: Decodable>(&mut self) -> Result<T, io::Error> {
self.get_ref::<JsonBodyParser>().and_then(|parsed|
json::decode::<T>(&*parsed).map_err(|err|
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ extern crate url;
extern crate mustache;
extern crate groupable;
extern crate modifier;
extern crate cookie;

#[macro_use] extern crate log;
#[macro_use] extern crate lazy_static;
Expand All @@ -39,6 +40,7 @@ pub use router::{Router, Route, RouteResult, HttpRouter};
pub use nickel_error::NickelError;
pub use mimes::MediaType;
pub use responder::Responder;
pub use cookies::Cookies;

#[macro_use] pub mod macros;

Expand All @@ -57,6 +59,7 @@ mod query_string;
mod urlencoded;
mod nickel_error;
mod default_error_handler;
pub mod cookies;

pub mod status {
pub use hyper::status::StatusCode;
Expand Down
10 changes: 5 additions & 5 deletions src/macros/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,18 @@ macro_rules! _middleware_inner {
use $crate::{MiddlewareResult,Responder, Response, Request};

#[inline(always)]
fn restrict<'a, R: Responder>(r: R, res: Response<'a>)
-> MiddlewareResult<'a> {
fn restrict<'a, D, R: Responder<D>>(r: R, res: Response<'a, D>)
-> MiddlewareResult<'a, D> {
res.send(r)
}

// Inference fails due to thinking it's a (&Request, Response) with
// different mutability requirements
#[inline(always)]
fn restrict_closure<F>(f: F) -> F
fn restrict_closure<F, D>(f: F) -> F
where F: for<'r, 'b, 'a>
Fn(&'r mut Request<'b, 'a, 'b>, Response<'a>)
-> MiddlewareResult<'a> + Send + Sync { f }
Fn(&'r mut Request<'b, 'a, 'b, D>, Response<'a, D>)
-> MiddlewareResult<'a, D> + Send + Sync { f }

restrict_closure(move |as_pat!($req), $res_binding| {
restrict(as_block!({$($b)+}), $res)
Expand Down
Loading