Remove Session in favor of private cookies. New testing API.

Sessions
--------

This commit removes the `Session` type in favor of methods on the
`Cookies` types that allow for adding, removing, and getting private
(signed and encrypted) cookies. These methods provide a superset of
the functionality of `Session` while also being a minimal addition to
the existing API. They can be used to implement the previous `Session`
type as well as other forms of session storage. The new methods are:

  * Cookie::add_private(&mut self, Cookie)
  * Cookie::remove_private(&mut self, Cookie)
  * Cookie::get_private(&self, &str)

Resolves #20

Testing
-------

This commit removes the `rocket::testing` module. It adds the
`rocket::local` module which provides a `Client` type for local
dispatching of requests against a `Rocket` instance. This `local`
package subsumes the previous `testing` package.

Rocket Examples
---------------

The `forms`, `optional_result`, and `hello_alt_methods` examples have
been removed. The following example have been renamed:

  * extended_validation -> form_validation
  * hello_ranks -> ranking
  * from_request -> request_guard
  * hello_tls -> tls

Other Changes
-------------

This commit also includes the following smaller changes:

  * Config::{development, staging, production} constructors have been
    added for easier creation of default `Config` structures.
  * The `Config` type is exported from the root.
  * `Request` implements `Clone` and `Debug`.
  * `Request::new` is no longer exported.
  * A `Response::body_bytes` method was added to easily retrieve a
    response's body as a `Vec<u8>`.
This commit is contained in:
Sergio Benitez 2017-06-06 13:41:04 -07:00
parent 504a7fe583
commit b8ba7b855f
89 changed files with 1243 additions and 1454 deletions

View File

@ -8,28 +8,25 @@ members = [
"contrib/", "contrib/",
"examples/cookies", "examples/cookies",
"examples/errors", "examples/errors",
"examples/extended_validation", "examples/form_validation",
"examples/forms",
"examples/hello_person", "examples/hello_person",
"examples/query_params", "examples/query_params",
"examples/hello_world", "examples/hello_world",
"examples/manual_routes", "examples/manual_routes",
"examples/optional_redirect", "examples/optional_redirect",
"examples/optional_result",
"examples/redirect", "examples/redirect",
"examples/static_files", "examples/static_files",
"examples/todo", "examples/todo",
"examples/content_types", "examples/content_types",
"examples/hello_ranks", "examples/ranking",
"examples/testing", "examples/testing",
"examples/from_request", "examples/request_guard",
"examples/stream", "examples/stream",
"examples/json", "examples/json",
"examples/msgpack", "examples/msgpack",
"examples/handlebars_templates", "examples/handlebars_templates",
"examples/form_kitchen_sink", "examples/form_kitchen_sink",
"examples/config", "examples/config",
"examples/hello_alt_methods",
"examples/raw_upload", "examples/raw_upload",
"examples/pastebin", "examples/pastebin",
"examples/state", "examples/state",
@ -37,6 +34,6 @@ members = [
"examples/uuid", "examples/uuid",
"examples/session", "examples/session",
"examples/raw_sqlite", "examples/raw_sqlite",
"examples/hello_tls", "examples/tls",
"examples/fairings", "examples/fairings",
] ]

View File

@ -1,9 +1,9 @@
use rocket::{self, State}; use rocket::{self, State};
use rocket::fairing::AdHoc; use rocket::fairing::AdHoc;
use rocket::config::{self, Config, Environment}; use rocket::config::{self, Config, Environment};
use rocket::http::{Method, Status}; use rocket::http::Status;
use rocket::LoggingLevel; use rocket::LoggingLevel;
use rocket::testing::MockRequest; use rocket::local::Client;
struct LocalConfig(Config); struct LocalConfig(Config);
@ -55,8 +55,6 @@ pub fn test_config(environment: Environment) {
// environment in `ignite()`. We'll read this back in the handler to config. // environment in `ignite()`. We'll read this back in the handler to config.
::std::env::set_var("ROCKET_ENV", environment.to_string()); ::std::env::set_var("ROCKET_ENV", environment.to_string());
// FIXME: launch fairings aren't run during tests since...the Rocket isn't
// being launch
let rocket = rocket::ignite() let rocket = rocket::ignite()
.attach(AdHoc::on_attach(|rocket| { .attach(AdHoc::on_attach(|rocket| {
println!("Attaching local config."); println!("Attaching local config.");
@ -65,7 +63,7 @@ pub fn test_config(environment: Environment) {
})) }))
.mount("/", routes![check_config]); .mount("/", routes![check_config]);
let mut request = MockRequest::new(Method::Get, "/check_config"); let client = Client::new(rocket).unwrap();
let response = request.dispatch_with(&rocket); let response = client.get("/check_config").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
} }

View File

@ -2,7 +2,7 @@ use super::rocket;
use super::serde_json; use super::serde_json;
use super::Person; use super::Person;
use rocket::http::{Accept, ContentType, Header, MediaType, Method, Status}; use rocket::http::{Accept, ContentType, Header, MediaType, Method, Status};
use rocket::testing::MockRequest; use rocket::local::Client;
fn test<H>(method: Method, uri: &str, header: H, status: Status, body: String) fn test<H>(method: Method, uri: &str, header: H, status: Status, body: String)
where H: Into<Header<'static>> where H: Into<Header<'static>>
@ -10,9 +10,9 @@ fn test<H>(method: Method, uri: &str, header: H, status: Status, body: String)
let rocket = rocket::ignite() let rocket = rocket::ignite()
.mount("/hello", routes![super::get_hello, super::post_hello]) .mount("/hello", routes![super::get_hello, super::post_hello])
.catch(errors![super::not_found]); .catch(errors![super::not_found]);
let mut request = MockRequest::new(method, uri).header(header);
let mut response = request.dispatch_with(&rocket);
let client = Client::new(rocket).unwrap();
let mut response = client.req(method, uri).header(header).dispatch();
assert_eq!(response.status(), status); assert_eq!(response.status(), status);
assert_eq!(response.body_string(), Some(body)); assert_eq!(response.body_string(), Some(body));
} }

View File

@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::*; use rocket::http::*;
use rocket_contrib::Template; use rocket_contrib::Template;
@ -9,11 +9,12 @@ const TEMPLATE_ROOT: &'static str = "templates/";
#[test] #[test]
fn test_submit() { fn test_submit() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut request = MockRequest::new(Method::Post, "/submit") let response = client.post("/submit")
.header(ContentType::Form) .header(ContentType::Form)
.body("message=Hello from Rocket!"); .body("message=Hello from Rocket!")
let response = request.dispatch_with(&rocket); .dispatch();
let cookie_headers: Vec<_> = response.headers().get("Set-Cookie").collect(); let cookie_headers: Vec<_> = response.headers().get("Set-Cookie").collect();
let location_headers: Vec<_> = response.headers().get("Location").collect(); let location_headers: Vec<_> = response.headers().get("Location").collect();
@ -23,33 +24,26 @@ fn test_submit() {
} }
fn test_body(optional_cookie: Option<Cookie<'static>>, expected_body: String) { fn test_body(optional_cookie: Option<Cookie<'static>>, expected_body: String) {
let rocket = rocket();
let mut request = MockRequest::new(Method::Get, "/");
// Attach a cookie if one is given. // Attach a cookie if one is given.
if let Some(cookie) = optional_cookie { let client = Client::new(rocket()).unwrap();
request = request.cookie(cookie); let mut response = match optional_cookie {
} Some(cookie) => client.get("/").cookie(cookie).dispatch(),
None => client.get("/").dispatch(),
};
let mut response = request.dispatch_with(&rocket);
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.body_string(), Some(expected_body)); assert_eq!(response.body_string(), Some(expected_body));
} }
#[test] #[test]
fn test_index() { fn test_index() {
// Render the template with an empty context to test against. // Render the template with an empty context.
let mut context: HashMap<&str, &str> = HashMap::new(); let mut context: HashMap<&str, &str> = HashMap::new();
let template = Template::show(TEMPLATE_ROOT, "index", &context).unwrap(); let template = Template::show(TEMPLATE_ROOT, "index", &context).unwrap();
// Test the route without sending the "message" cookie.
test_body(None, template); test_body(None, template);
// Render the template with a context that contains the message. // Render the template with a context that contains the message.
context.insert("message", "Hello from Rocket!"); context.insert("message", "Hello from Rocket!");
// Test the route with the "message" cookie.
let cookie = Cookie::new("message", "Hello from Rocket!");
let template = Template::show(TEMPLATE_ROOT, "index", &context).unwrap(); let template = Template::show(TEMPLATE_ROOT, "index", &context).unwrap();
test_body(Some(cookie), template); test_body(Some(Cookie::new("message", "Hello from Rocket!")), template);
} }

View File

@ -1,14 +1,14 @@
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::{Method, Status}; use rocket::http::Status;
fn test(uri: &str, status: Status, body: String) { fn test(uri: &str, status: Status, body: String) {
let rocket = rocket::ignite() let rocket = rocket::ignite()
.mount("/", routes![super::hello]) .mount("/", routes![super::hello])
.catch(errors![super::not_found]); .catch(errors![super::not_found]);
let mut req = MockRequest::new(Method::Get, uri);
let mut response = req.dispatch_with(&rocket);
let client = Client::new(rocket).unwrap();
let mut response = client.get(uri).dispatch();
assert_eq!(response.status(), status); assert_eq!(response.status(), status);
assert_eq!(response.body_string(), Some(body)); assert_eq!(response.body_string(), Some(body));
} }

View File

@ -1,8 +0,0 @@
[package]
name = "extended_validation"
version = "0.0.0"
workspace = "../../"
[dependencies]
rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" }

View File

@ -1,46 +1,38 @@
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
#[test] #[test]
fn rewrite_get_put() { fn rewrite_get_put() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut req = MockRequest::new(Get, "/"); let mut response = client.get("/").dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some("Hello, fairings!".into())); assert_eq!(response.body_string(), Some("Hello, fairings!".into()));
} }
#[test] #[test]
fn counts() { fn counts() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
// Issue 1 GET request. // Issue 1 GET request.
let mut req = MockRequest::new(Get, "/"); client.get("/").dispatch();
req.dispatch_with(&rocket);
// Check the GET count, taking into account _this_ GET request. // Check the GET count, taking into account _this_ GET request.
let mut req = MockRequest::new(Get, "/counts"); let mut response = client.get("/counts").dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some("Get: 2\nPost: 0".into())); assert_eq!(response.body_string(), Some("Get: 2\nPost: 0".into()));
// Issue 1 more GET request and a POST. // Issue 1 more GET request and a POST.
let mut req = MockRequest::new(Get, "/"); client.get("/").dispatch();
req.dispatch_with(&rocket); client.post("/").dispatch();
let mut req = MockRequest::new(Post, "/");
req.dispatch_with(&rocket);
// Check the counts. // Check the counts.
let mut req = MockRequest::new(Get, "/counts"); let mut response = client.get("/counts").dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some("Get: 4\nPost: 1".into())); assert_eq!(response.body_string(), Some("Get: 4\nPost: 1".into()));
} }
#[test] #[test]
fn token() { fn token() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
// Ensure the token is '123', which is what we have in `Rocket.toml`. // Ensure the token is '123', which is what we have in `Rocket.toml`.
let mut req = MockRequest::new(Get, "/token"); let mut res = client.get("/token").dispatch();
let mut res = req.dispatch_with(&rocket);
assert_eq!(res.body_string(), Some("123".into())); assert_eq!(res.body_string(), Some("123".into()));
} }

View File

@ -33,19 +33,19 @@ impl<'v> FromFormValue<'v> for FormOption {
} }
#[derive(Debug, FromForm)] #[derive(Debug, FromForm)]
struct FormInput { struct FormInput<'r> {
checkbox: bool, checkbox: bool,
number: usize, number: usize,
#[form(field = "type")] #[form(field = "type")]
radio: FormOption, radio: FormOption,
password: String, password: &'r RawStr,
#[form(field = "textarea")] #[form(field = "textarea")]
text_area: String, text_area: String,
select: FormOption, select: FormOption,
} }
#[post("/", data = "<sink>")] #[post("/", data = "<sink>")]
fn sink(sink: Result<Form<FormInput>, Option<String>>) -> String { fn sink<'r>(sink: Result<Form<'r, FormInput<'r>>, Option<String>>) -> String {
match sink { match sink {
Ok(form) => format!("{:?}", form.get()), Ok(form) => format!("{:?}", form.get()),
Err(Some(f)) => format!("Invalid form input: {}", f), Err(Some(f)) => format!("Invalid form input: {}", f),

View File

@ -1,10 +1,8 @@
use std::fmt; use std::fmt;
use super::{rocket, FormInput, FormOption}; use super::{rocket, FormInput, FormOption};
use rocket::Rocket; use rocket::local::Client;
use rocket::testing::MockRequest;
use rocket::http::ContentType; use rocket::http::ContentType;
use rocket::http::Method::*;
impl fmt::Display for FormOption { impl fmt::Display for FormOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -16,167 +14,169 @@ impl fmt::Display for FormOption {
} }
} }
fn assert_form_eq(rocket: &Rocket, form_str: &str, expected: String) { fn assert_form_eq(client: &Client, form_str: &str, expected: String) {
let mut req = MockRequest::new(Post, "/") let mut res = client.post("/")
.header(ContentType::Form) .header(ContentType::Form)
.body(form_str); .body(form_str)
let mut res = req.dispatch_with(&rocket); .dispatch();
assert_eq!(res.body_string(), Some(expected)); assert_eq!(res.body_string(), Some(expected));
} }
fn assert_valid_form(rocket: &Rocket, input: &FormInput) { fn assert_valid_form(client: &Client, input: &FormInput) {
let f = format!("checkbox={}&number={}&type={}&password={}&textarea={}&select={}", let f = format!("checkbox={}&number={}&type={}&password={}&textarea={}&select={}",
input.checkbox, input.number, input.radio, input.password, input.checkbox, input.number, input.radio, input.password,
input.text_area, input.select); input.text_area, input.select);
assert_form_eq(rocket, &f, format!("{:?}", input)); assert_form_eq(client, &f, format!("{:?}", input));
} }
fn assert_valid_raw_form(rocket: &Rocket, form_str: &str, input: &FormInput) { fn assert_valid_raw_form(client: &Client, form_str: &str, input: &FormInput) {
assert_form_eq(rocket, form_str, format!("{:?}", input)); assert_form_eq(client, form_str, format!("{:?}", input));
} }
#[test] #[test]
fn test_good_forms() { fn test_good_forms() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut input = FormInput { let mut input = FormInput {
checkbox: true, checkbox: true,
number: 310, number: 310,
radio: FormOption::A, radio: FormOption::A,
password: "beep".to_string(), password: "beep".into(),
text_area: "bop".to_string(), text_area: "bop".to_string(),
select: FormOption::B select: FormOption::B
}; };
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.checkbox = false; input.checkbox = false;
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.number = 0; input.number = 0;
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.number = 120; input.number = 120;
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.number = 133; input.number = 133;
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.radio = FormOption::B; input.radio = FormOption::B;
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.radio = FormOption::C; input.radio = FormOption::C;
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.password = "".to_string(); input.password = "".into();
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.password = "----90138490285u2o3hndslkv".to_string(); input.password = "----90138490285u2o3hndslkv".into();
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.password = "hi".to_string(); input.password = "hi".into();
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.text_area = "".to_string(); input.text_area = "".to_string();
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.text_area = "----90138490285u2o3hndslkv".to_string(); input.text_area = "----90138490285u2o3hndslkv".to_string();
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.text_area = "hey".to_string(); input.text_area = "hey".to_string();
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.select = FormOption::A; input.select = FormOption::A;
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
input.select = FormOption::C; input.select = FormOption::C;
assert_valid_form(&rocket, &input); assert_valid_form(&client, &input);
// checkbox need not be present; defaults to false; accepts 'on' and 'off' // checkbox need not be present; defaults to false; accepts 'on' and 'off'
assert_valid_raw_form(&rocket, assert_valid_raw_form(&client,
"number=133&type=c&password=hi&textarea=hey&select=c", "number=133&type=c&password=hi&textarea=hey&select=c",
&input); &input);
assert_valid_raw_form(&rocket, assert_valid_raw_form(&client,
"checkbox=off&number=133&type=c&password=hi&textarea=hey&select=c", "checkbox=off&number=133&type=c&password=hi&textarea=hey&select=c",
&input); &input);
input.checkbox = true; input.checkbox = true;
assert_valid_raw_form(&rocket, assert_valid_raw_form(&client,
"checkbox=on&number=133&type=c&password=hi&textarea=hey&select=c", "checkbox=on&number=133&type=c&password=hi&textarea=hey&select=c",
&input); &input);
} }
fn assert_invalid_form(rocket: &Rocket, vals: &mut [&str; 6]) { fn assert_invalid_form(client: &Client, vals: &mut [&str; 6]) {
let s = format!("checkbox={}&number={}&type={}&password={}&textarea={}&select={}", let s = format!("checkbox={}&number={}&type={}&password={}&textarea={}&select={}",
vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]); vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
assert_form_eq(rocket, &s, format!("Invalid form input: {}", s)); assert_form_eq(client, &s, format!("Invalid form input: {}", s));
*vals = ["true", "1", "a", "hi", "hey", "b"]; *vals = ["true", "1", "a", "hi", "hey", "b"];
} }
fn assert_invalid_raw_form(rocket: &Rocket, form_str: &str) { fn assert_invalid_raw_form(client: &Client, form_str: &str) {
assert_form_eq(rocket, form_str, format!("Invalid form input: {}", form_str)); assert_form_eq(client, form_str, format!("Invalid form input: {}", form_str));
} }
#[test] #[test]
fn check_semantically_invalid_forms() { fn check_semantically_invalid_forms() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut form_vals = ["true", "1", "a", "hi", "hey", "b"]; let mut form_vals = ["true", "1", "a", "hi", "hey", "b"];
form_vals[0] = "not true"; form_vals[0] = "not true";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[0] = "bing"; form_vals[0] = "bing";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[0] = "true0"; form_vals[0] = "true0";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[0] = " false"; form_vals[0] = " false";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[1] = "-1"; form_vals[1] = "-1";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[1] = "1e10"; form_vals[1] = "1e10";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[1] = "-1-1"; form_vals[1] = "-1-1";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[1] = "NaN"; form_vals[1] = "NaN";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[2] = "A"; form_vals[2] = "A";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[2] = "B"; form_vals[2] = "B";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[2] = "d"; form_vals[2] = "d";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[2] = "100"; form_vals[2] = "100";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[2] = ""; form_vals[2] = "";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
// password and textarea are always valid, so we skip them // password and textarea are always valid, so we skip them
form_vals[5] = "A"; form_vals[5] = "A";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[5] = "b "; form_vals[5] = "b ";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[5] = "d"; form_vals[5] = "d";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[5] = "-a"; form_vals[5] = "-a";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
form_vals[5] = ""; form_vals[5] = "";
assert_invalid_form(&rocket, &mut form_vals); assert_invalid_form(&client, &mut form_vals);
// now forms with missing fields // now forms with missing fields
assert_invalid_raw_form(&rocket, "number=10&type=a&password=hi&textarea=hey"); assert_invalid_raw_form(&client, "number=10&type=a&password=hi&textarea=hey");
assert_invalid_raw_form(&rocket, "number=10&radio=a&password=hi&textarea=hey&select=b"); assert_invalid_raw_form(&client, "number=10&radio=a&password=hi&textarea=hey&select=b");
assert_invalid_raw_form(&rocket, "number=10&password=hi&select=b"); assert_invalid_raw_form(&client, "number=10&password=hi&select=b");
assert_invalid_raw_form(&rocket, "number=10&select=b"); assert_invalid_raw_form(&client, "number=10&select=b");
assert_invalid_raw_form(&rocket, "password=hi&select=b"); assert_invalid_raw_form(&client, "password=hi&select=b");
assert_invalid_raw_form(&rocket, "password=hi"); assert_invalid_raw_form(&client, "password=hi");
assert_invalid_raw_form(&rocket, ""); assert_invalid_raw_form(&client, "");
} }
#[test] #[test]
fn check_structurally_invalid_forms() { fn check_structurally_invalid_forms() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
assert_invalid_raw_form(&rocket, "==&&&&&&=="); assert_invalid_raw_form(&client, "==&&&&&&==");
assert_invalid_raw_form(&rocket, "a&=b"); assert_invalid_raw_form(&client, "a&=b");
assert_invalid_raw_form(&rocket, "="); assert_invalid_raw_form(&client, "=");
} }
#[test] #[test]
fn check_bad_utf8() { fn check_bad_utf8() {
let client = Client::new(rocket()).unwrap();
unsafe { unsafe {
let bad_str = ::std::str::from_utf8_unchecked(b"a=\xff"); let bad_str = ::std::str::from_utf8_unchecked(b"a=\xff");
assert_form_eq(&rocket(), bad_str, "Form input was invalid UTF8.".into()); assert_form_eq(&client, bad_str, "Form input was invalid UTF8.".into());
} }
} }

View File

@ -1,5 +1,5 @@
[package] [package]
name = "optional_result" name = "form_validation"
version = "0.0.0" version = "0.0.0"
workspace = "../../" workspace = "../../"

View File

@ -1,24 +1,20 @@
use rocket::testing::MockRequest;
use rocket::http::Method::*;
use rocket::http::{ContentType, Status};
use super::rocket; use super::rocket;
use rocket::local::Client;
use rocket::http::{ContentType, Status};
fn test_login<T>(user: &str, pass: &str, age: &str, status: Status, body: T) fn test_login<T>(user: &str, pass: &str, age: &str, status: Status, body: T)
where T: Into<Option<&'static str>> where T: Into<Option<&'static str>>
{ {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let query = format!("username={}&password={}&age={}", user, pass, age); let query = format!("username={}&password={}&age={}", user, pass, age);
let mut response = client.post("/login")
let mut req = MockRequest::new(Post, "/login")
.header(ContentType::Form) .header(ContentType::Form)
.body(&query); .body(&query)
.dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.status(), status); assert_eq!(response.status(), status);
let body_str = response.body_string();
if let Some(expected_str) = body.into() { if let Some(expected_str) = body.into() {
let body_str = response.body_string();
assert!(body_str.map_or(false, |s| s.contains(expected_str))); assert!(body_str.map_or(false, |s| s.contains(expected_str)));
} }
} }
@ -48,12 +44,12 @@ fn test_invalid_age() {
} }
fn check_bad_form(form_str: &str, status: Status) { fn check_bad_form(form_str: &str, status: Status) {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut req = MockRequest::new(Post, "/login") let response = client.post("/login")
.header(ContentType::Form) .header(ContentType::Form)
.body(form_str); .body(form_str)
.dispatch();
let response = req.dispatch_with(&rocket);
assert_eq!(response.status(), status); assert_eq!(response.status(), status);
} }
@ -82,5 +78,6 @@ fn test_bad_form_missing_fields() {
#[test] #[test]
fn test_bad_form_additional_fields() { fn test_bad_form_additional_fields() {
check_bad_form("username=Sergio&password=pass&age=30&addition=1", Status::UnprocessableEntity); check_bad_form("username=Sergio&password=pass&age=30&addition=1",
Status::UnprocessableEntity);
} }

View File

@ -1,14 +0,0 @@
use rocket::response::NamedFile;
use std::io;
use std::path::{Path, PathBuf};
#[get("/")]
fn index() -> io::Result<NamedFile> {
NamedFile::open("static/index.html")
}
#[get("/<file..>", rank = 5)]
fn files(file: PathBuf) -> io::Result<NamedFile> {
NamedFile::open(Path::new("static/").join(file))
}

View File

@ -1,52 +0,0 @@
#![feature(plugin, custom_derive)]
#![plugin(rocket_codegen)]
extern crate rocket;
mod files;
#[cfg(test)] mod tests;
use rocket::request::Form;
use rocket::response::Redirect;
use rocket::http::RawStr;
#[derive(FromForm)]
struct UserLogin<'r> {
username: &'r RawStr,
password: String,
age: Result<usize, &'r RawStr>,
}
#[post("/login", data = "<user_form>")]
fn login<'a>(user_form: Form<'a, UserLogin<'a>>) -> Result<Redirect, String> {
let user = user_form.get();
match user.age {
Ok(age) if age < 21 => return Err(format!("Sorry, {} is too young!", age)),
Ok(age) if age > 120 => return Err(format!("Are you sure you're {}?", age)),
Err(e) => return Err(format!("'{}' is not a valid integer.", e)),
Ok(_) => { /* Move along, adult. */ }
};
if user.username == "Sergio" {
match user.password.as_str() {
"password" => Ok(Redirect::to("/user/Sergio")),
_ => Err("Wrong password!".to_string())
}
} else {
Err(format!("Unrecognized user, '{}'.", user.username))
}
}
#[get("/user/<username>")]
fn user_page(username: String) -> String {
format!("This is {}'s page.", username)
}
fn rocket() -> rocket::Rocket {
rocket::ignite()
.mount("/", routes![files::index, files::files, user_page, login])
}
fn main() {
rocket().launch();
}

View File

@ -1,62 +0,0 @@
use rocket::testing::MockRequest;
use rocket::http::Method::*;
use rocket::http::{ContentType, Status};
use super::rocket;
fn test_login(username: &str, password: &str, age: isize, status: Status,
body: Option<&'static str>) {
let rocket = rocket();
let mut req = MockRequest::new(Post, "/login")
.header(ContentType::Form)
.body(&format!("username={}&password={}&age={}", username, password, age));
let mut response = req.dispatch_with(&rocket);
let body_str = response.body_string();
println!("Checking: {:?}/{:?}/{:?}/{:?}", username, password, age, body_str);
assert_eq!(response.status(), status);
if let Some(string) = body {
assert!(body_str.map_or(true, |s| s.contains(string)));
}
}
#[test]
fn test_good_login() {
test_login("Sergio", "password", 30, Status::SeeOther, None);
}
const OK: Status = self::Status::Ok;
#[test]
fn test_bad_login() {
test_login("Sergio", "password", 20, OK, Some("Sorry, 20 is too young!"));
test_login("Sergio", "password", 200, OK, Some("Are you sure you're 200?"));
test_login("Sergio", "jk", -100, OK, Some("'-100' is not a valid integer."));
test_login("Sergio", "ok", 30, OK, Some("Wrong password!"));
test_login("Mike", "password", 30, OK, Some("Unrecognized user, 'Mike'."));
}
fn check_bad_form(form_str: &str, status: Status) {
let rocket = rocket();
let mut req = MockRequest::new(Post, "/login")
.header(ContentType::Form)
.body(form_str);
let response = req.dispatch_with(&rocket);
assert_eq!(response.status(), status);
}
#[test]
fn test_bad_form() {
check_bad_form("&", Status::BadRequest);
check_bad_form("=", Status::BadRequest);
check_bad_form("&&&===&", Status::BadRequest);
check_bad_form("username=Sergio", Status::UnprocessableEntity);
check_bad_form("username=Sergio&", Status::UnprocessableEntity);
check_bad_form("username=Sergio&pass=something", Status::UnprocessableEntity);
check_bad_form("user=Sergio&password=something", Status::UnprocessableEntity);
check_bad_form("password=something", Status::UnprocessableEntity);
}

View File

@ -1,8 +0,0 @@
<h1>Login</h1>
<form action="/login" method="post" accept-charset="utf-8">
Username:<input type="text" name="username">
Password:<input type="password" name="password">
Age:<input type="number" name="age">
<input type="submit" value="Login">
</form>

View File

@ -1,8 +0,0 @@
[package]
name = "from_request"
version = "0.0.0"
workspace = "../../"
[dependencies]
rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" }

View File

@ -1,17 +1,15 @@
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::{Client, LocalResponse};
use rocket::http::Method::*; use rocket::http::Method::*;
use rocket::http::Status; use rocket::http::Status;
use rocket::Response;
use rocket_contrib::Template; use rocket_contrib::Template;
const TEMPLATE_ROOT: &'static str = "templates/"; const TEMPLATE_ROOT: &'static str = "templates/";
macro_rules! dispatch { macro_rules! dispatch {
($method:expr, $path:expr, $test_fn:expr) => ({ ($method:expr, $path:expr, $test_fn:expr) => ({
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut req = MockRequest::new($method, $path); $test_fn(client.req($method, $path).dispatch());
$test_fn(req.dispatch_with(&rocket));
}) })
} }
@ -19,7 +17,7 @@ macro_rules! dispatch {
fn test_root() { fn test_root() {
// Check that the redirect works. // Check that the redirect works.
for method in &[Get, Head] { for method in &[Get, Head] {
dispatch!(*method, "/", |mut response: Response| { dispatch!(*method, "/", |mut response: LocalResponse| {
assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.status(), Status::SeeOther);
assert!(response.body().is_none()); assert!(response.body().is_none());
@ -30,7 +28,7 @@ fn test_root() {
// Check that other request methods are not accepted (and instead caught). // Check that other request methods are not accepted (and instead caught).
for method in &[Post, Put, Delete, Options, Trace, Connect, Patch] { for method in &[Post, Put, Delete, Options, Trace, Connect, Patch] {
dispatch!(*method, "/", |mut response: Response| { dispatch!(*method, "/", |mut response: LocalResponse| {
let mut map = ::std::collections::HashMap::new(); let mut map = ::std::collections::HashMap::new();
map.insert("path", "/"); map.insert("path", "/");
let expected = Template::show(TEMPLATE_ROOT, "error/404", &map).unwrap(); let expected = Template::show(TEMPLATE_ROOT, "error/404", &map).unwrap();
@ -44,7 +42,7 @@ fn test_root() {
#[test] #[test]
fn test_name() { fn test_name() {
// Check that the /hello/<name> route works. // Check that the /hello/<name> route works.
dispatch!(Get, "/hello/Jack", |mut response: Response| { dispatch!(Get, "/hello/Jack", |mut response: LocalResponse| {
let context = super::TemplateContext { let context = super::TemplateContext {
name: "Jack".to_string(), name: "Jack".to_string(),
items: vec!["One", "Two", "Three"].iter().map(|s| s.to_string()).collect() items: vec!["One", "Two", "Three"].iter().map(|s| s.to_string()).collect()
@ -59,7 +57,7 @@ fn test_name() {
#[test] #[test]
fn test_404() { fn test_404() {
// Check that the error catcher works. // Check that the error catcher works.
dispatch!(Get, "/hello/", |mut response: Response| { dispatch!(Get, "/hello/", |mut response: LocalResponse| {
let mut map = ::std::collections::HashMap::new(); let mut map = ::std::collections::HashMap::new();
map.insert("path", "/hello/"); map.insert("path", "/hello/");

View File

@ -1,8 +0,0 @@
[package]
name = "hello_alt_methods"
version = "0.0.0"
workspace = "../../"
[dependencies]
rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" }

View File

@ -1,26 +0,0 @@
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
use std::io;
use rocket::response::NamedFile;
#[cfg(test)] mod tests;
#[get("/")]
fn index() -> io::Result<NamedFile> {
NamedFile::open("static/index.html")
}
#[put("/")]
fn put() -> &'static str {
"Hello, PUT request!"
}
fn main() {
rocket::ignite()
.mount("/", routes![index, put])
.launch();
}

View File

@ -1,25 +0,0 @@
use super::rocket;
use rocket::testing::MockRequest;
use rocket::http::Method;
use rocket::http::Status;
fn test(method: Method, status: Status, body_prefix: Option<&str>) {
let rocket = rocket::ignite()
.mount("/", routes![super::index, super::put]);
let mut req = MockRequest::new(method, "/");
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.status(), status);
if let Some(expected_body_string) = body_prefix {
let body_str = response.body_string().unwrap();
assert!(body_str.starts_with(expected_body_string));
}
}
#[test]
fn hello_world_alt_methods() {
test(Method::Get, Status::Ok, Some("<!DOCTYPE html>"));
test(Method::Put, Status::Ok, Some("Hello, PUT request!"));
test(Method::Post, Status::NotFound, None);
}

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Hello Alt Methods</title>
</head>
<body>
<form action="/" method="post" accept-charset="utf-8">
<input type="hidden" name="_method" id="_method" value="put" />
<input type="submit" name="Submit" id="Submit" value="submit" />
</form>
</body>
</html>

View File

@ -1,20 +1,19 @@
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::Status; use rocket::http::Status;
fn client() -> Client {
Client::new(rocket::ignite().mount("/", routes![super::hello, super::hi])).unwrap()
}
fn test(uri: &str, expected: String) { fn test(uri: &str, expected: String) {
let rocket = rocket::ignite().mount("/", routes![super::hello, super::hi]); let client = client();
let mut req = MockRequest::new(Get, uri); assert_eq!(client.get(uri).dispatch().body_string(), Some(expected));
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some(expected));
} }
fn test_404(uri: &str) { fn test_404(uri: &str) {
let rocket = rocket::ignite().mount("/", routes![super::hello, super::hi]); let client = client();
let mut req = MockRequest::new(Get, uri); assert_eq!(client.get(uri).dispatch().status(), Status::NotFound);
let response = req.dispatch_with(&rocket);
assert_eq!(response.status(), Status::NotFound);
} }
#[test] #[test]

View File

@ -1,12 +1,10 @@
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
#[test] #[test]
fn hello_world() { fn hello_world() {
let rocket = rocket::ignite().mount("/", routes![super::hello]); let rocket = rocket::ignite().mount("/", routes![super::hello]);
let mut req = MockRequest::new(Get, "/"); let client = Client::new(rocket).unwrap();
let mut response = req.dispatch_with(&rocket); let mut response = client.get("/").dispatch();
assert_eq!(response.body_string(), Some("Hello, world!".into())); assert_eq!(response.body_string(), Some("Hello, world!".into()));
} }

View File

@ -1,95 +1,72 @@
use rocket; use rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::{Status, ContentType}; use rocket::http::{Status, ContentType};
use rocket::Response;
macro_rules! run_test {
($rocket: expr, $req:expr, $test_fn:expr) => ({
let mut req = $req;
$test_fn(req.dispatch_with($rocket));
})
}
#[test] #[test]
fn bad_get_put() { fn bad_get_put() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
// Try to get a message with an ID that doesn't exist. // Try to get a message with an ID that doesn't exist.
let req = MockRequest::new(Get, "/message/99").header(ContentType::JSON); let mut res = client.get("/message/99").header(ContentType::JSON).dispatch();
run_test!(&rocket, req, |mut response: Response| { assert_eq!(res.status(), Status::NotFound);
assert_eq!(response.status(), Status::NotFound);
let body = response.body().unwrap().into_string().unwrap(); let body = res.body_string().unwrap();
assert!(body.contains("error")); assert!(body.contains("error"));
assert!(body.contains("Resource was not found.")); assert!(body.contains("Resource was not found."));
});
// Try to get a message with an invalid ID. // Try to get a message with an invalid ID.
let req = MockRequest::new(Get, "/message/hi").header(ContentType::JSON); let mut res = client.get("/message/hi").header(ContentType::JSON).dispatch();
run_test!(&rocket, req, |mut response: Response| { let body = res.body_string().unwrap();
assert_eq!(response.status(), Status::NotFound); assert_eq!(res.status(), Status::NotFound);
let body = response.body().unwrap().into_string().unwrap();
assert!(body.contains("error")); assert!(body.contains("error"));
});
// Try to put a message without a proper body. // Try to put a message without a proper body.
let req = MockRequest::new(Put, "/message/80").header(ContentType::JSON); let res = client.put("/message/80").header(ContentType::JSON).dispatch();
run_test!(&rocket, req, |response: Response| { assert_eq!(res.status(), Status::BadRequest);
assert_eq!(response.status(), Status::BadRequest);
});
// Try to put a message for an ID that doesn't exist. // Try to put a message for an ID that doesn't exist.
let req = MockRequest::new(Put, "/message/80") let res = client.put("/message/80")
.header(ContentType::JSON) .header(ContentType::JSON)
.body(r#"{ "contents": "Bye bye, world!" }"#); .body(r#"{ "contents": "Bye bye, world!" }"#)
.dispatch();
run_test!(&rocket, req, |response: Response| { assert_eq!(res.status(), Status::NotFound);
assert_eq!(response.status(), Status::NotFound);
});
} }
#[test] #[test]
fn post_get_put_get() { fn post_get_put_get() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
// Check that a message with ID 1 doesn't exist. // Check that a message with ID 1 doesn't exist.
let req = MockRequest::new(Get, "/message/1").header(ContentType::JSON); let res = client.get("/message/1").header(ContentType::JSON).dispatch();
run_test!(&rocket, req, |response: Response| { assert_eq!(res.status(), Status::NotFound);
assert_eq!(response.status(), Status::NotFound);
});
// Add a new message with ID 1. // Add a new message with ID 1.
let req = MockRequest::new(Post, "/message/1") let res = client.post("/message/1")
.header(ContentType::JSON) .header(ContentType::JSON)
.body(r#"{ "contents": "Hello, world!" }"#); .body(r#"{ "contents": "Hello, world!" }"#)
.dispatch();
run_test!(&rocket, req, |response: Response| { assert_eq!(res.status(), Status::Ok);
assert_eq!(response.status(), Status::Ok);
});
// Check that the message exists with the correct contents. // Check that the message exists with the correct contents.
let req = MockRequest::new(Get, "/message/1") .header(ContentType::JSON); let mut res = client.get("/message/1").header(ContentType::JSON).dispatch();
run_test!(&rocket, req, |mut response: Response| { assert_eq!(res.status(), Status::Ok);
assert_eq!(response.status(), Status::Ok); let body = res.body().unwrap().into_string().unwrap();
let body = response.body().unwrap().into_string().unwrap();
assert!(body.contains("Hello, world!")); assert!(body.contains("Hello, world!"));
});
// Change the message contents. // Change the message contents.
let req = MockRequest::new(Put, "/message/1") let res = client.put("/message/1")
.header(ContentType::JSON) .header(ContentType::JSON)
.body(r#"{ "contents": "Bye bye, world!" }"#); .body(r#"{ "contents": "Bye bye, world!" }"#)
.dispatch();
run_test!(&rocket, req, |response: Response| { assert_eq!(res.status(), Status::Ok);
assert_eq!(response.status(), Status::Ok);
});
// Check that the message exists with the updated contents. // Check that the message exists with the updated contents.
let req = MockRequest::new(Get, "/message/1") .header(ContentType::JSON); let mut res = client.get("/message/1").header(ContentType::JSON).dispatch();
run_test!(&rocket, req, |mut response: Response| { assert_eq!(res.status(), Status::Ok);
assert_eq!(response.status(), Status::Ok); let body = res.body().unwrap().into_string().unwrap();
let body = response.body().unwrap().into_string().unwrap();
assert!(!body.contains("Hello, world!")); assert!(!body.contains("Hello, world!"));
assert!(body.contains("Bye bye, world!")); assert!(body.contains("Bye bye, world!"));
});
} }

View File

@ -1,18 +1,13 @@
use super::rocket; use rocket::local::Client;
use rocket::testing::MockRequest;
use rocket::http::Status; use rocket::http::Status;
use rocket::http::Method::*;
#[test] #[test]
fn test_push_pop() { fn test_push_pop() {
let rocket = rocket(); let client = Client::new(super::rocket()).unwrap();
let mut req = MockRequest::new(Put, "/push?description=test1"); let response = client.put("/push?description=test1").dispatch();
let response = req.dispatch_with(&rocket);
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
let mut req = MockRequest::new(Get, "/pop"); let mut response = client.get("/pop").dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some("test1".to_string())); assert_eq!(response.body_string(), Some("test1".to_string()));
} }

View File

@ -1,13 +1,10 @@
use super::*; use super::*;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::{ContentType, Status}; use rocket::http::{ContentType, Status};
use rocket::http::Method::*;
fn test(uri: &str, content_type: ContentType, status: Status, body: String) { fn test(uri: &str, content_type: ContentType, status: Status, body: String) {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();;
let mut request = MockRequest::new(Get, uri).header(content_type); let mut response = client.get(uri).header(content_type).dispatch();
let mut response = request.dispatch_with(&rocket);
assert_eq!(response.status(), status); assert_eq!(response.status(), status);
assert_eq!(response.body_string(), Some(body)); assert_eq!(response.body_string(), Some(body));
} }
@ -34,21 +31,21 @@ fn test_echo() {
#[test] #[test]
fn test_upload() { fn test_upload() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();;
let expected_body = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ let expected_body = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
sed do eiusmod tempor incididunt ut labore et dolore \ sed do eiusmod tempor incididunt ut labore et dolore \
magna aliqua".to_string(); magna aliqua".to_string();
// Upload the body. // Upload the body.
let mut request = MockRequest::new(Post, "/upload") let response = client.post("/upload")
.header(ContentType::Plain) .header(ContentType::Plain)
.body(&expected_body); .body(&expected_body)
let response = request.dispatch_with(&rocket); .dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
// Ensure we get back the same body. // Ensure we get back the same body.
let mut request = MockRequest::new(Get, "/upload"); let mut response = client.get("/upload").dispatch();
let mut response = request.dispatch_with(&rocket);
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.body_string(), Some(expected_body)); assert_eq!(response.body_string(), Some(expected_body));
} }

View File

@ -1,8 +1,6 @@
use rocket; use rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::{Status, ContentType}; use rocket::http::{Status, ContentType};
use rocket::Response;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct Message { struct Message {
@ -10,35 +8,26 @@ struct Message {
contents: String contents: String
} }
macro_rules! run_test {
($rocket: expr, $req:expr, $test_fn:expr) => ({
let mut req = $req;
$test_fn(req.dispatch_with($rocket));
})
}
#[test] #[test]
fn msgpack_get() { fn msgpack_get() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let req = MockRequest::new(Get, "/message/1").header(ContentType::MsgPack); let mut res = client.get("/message/1").header(ContentType::MsgPack).dispatch();
run_test!(&rocket, req, |mut response: Response| { assert_eq!(res.status(), Status::Ok);
assert_eq!(response.status(), Status::Ok);
let body = response.body().unwrap().into_bytes().unwrap(); // Check that the message is `[1, "Hello, world!"]`
// Represents a message of `[1, "Hello, world!"]` assert_eq!(&res.body_bytes().unwrap(),
assert_eq!(&body, &[146, 1, 173, 72, 101, 108, 108, 111, 44, 32, 119, 111, &[146, 1, 173, 72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33]);
114, 108, 100, 33]);
});
} }
#[test] #[test]
fn msgpack_post() { fn msgpack_post() {
let rocket = rocket(); // Dispatch request with a message of `[2, "Goodbye, world!"]`.
let req = MockRequest::new(Post, "/message") let client = Client::new(rocket()).unwrap();
let mut res = client.post("/message")
.header(ContentType::MsgPack) .header(ContentType::MsgPack)
// Represents a message of `[2, "Goodbye, world!"]` .body(&[146, 2, 175, 71, 111, 111, 100, 98, 121, 101, 44, 32, 119, 111, 114, 108, 100, 33])
.body(&[146, 2, 175, 71, 111, 111, 100, 98, 121, 101, 44, 32, 119, 111, 114, 108, 100, 33]); .dispatch();
run_test!(&rocket, req, |mut response: Response| {
assert_eq!(response.status(), Status::Ok); assert_eq!(res.status(), Status::Ok);
assert_eq!(response.body().unwrap().into_string().unwrap(), "Goodbye, world!"); assert_eq!(res.body_string(), Some("Goodbye, world!".into()));
});
} }

View File

@ -1,24 +1,25 @@
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::{Method, Status}; use rocket::http::Status;
fn test_200(uri: &str, expected_body: &str) { fn client() -> Client {
let rocket = rocket::ignite() let rocket = rocket::ignite()
.mount("/", routes![super::root, super::user, super::login]); .mount("/", routes![super::root, super::user, super::login]);
let mut request = MockRequest::new(Method::Get, uri); Client::new(rocket).unwrap()
let mut response = request.dispatch_with(&rocket);
}
fn test_200(uri: &str, expected_body: &str) {
let client = client();
let mut response = client.get(uri).dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.body_string(), Some(expected_body.to_string())); assert_eq!(response.body_string(), Some(expected_body.to_string()));
} }
fn test_303(uri: &str, expected_location: &str) { fn test_303(uri: &str, expected_location: &str) {
let rocket = rocket::ignite() let client = client();
.mount("/", routes![super::root, super::user, super::login]); let response = client.get(uri).dispatch();
let mut request = MockRequest::new(Method::Get, uri);
let response = request.dispatch_with(&rocket);
let location_headers: Vec<_> = response.headers().get("Location").collect(); let location_headers: Vec<_> = response.headers().get("Location").collect();
assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.status(), Status::SeeOther);
assert_eq!(location_headers, vec![expected_location]); assert_eq!(location_headers, vec![expected_location]);
} }

View File

@ -1,21 +0,0 @@
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
#[cfg(test)] mod tests;
use rocket::http::RawStr;
#[get("/users/<name>")]
fn user(name: &RawStr) -> Option<&'static str> {
if name == "Sergio" {
Some("Hello, Sergio!")
} else {
None
}
}
fn main() {
rocket::ignite().mount("/", routes![user]).launch();
}

View File

@ -1,23 +0,0 @@
use super::rocket;
use rocket::testing::MockRequest;
use rocket::http::{Method, Status};
#[test]
fn test_200() {
let rocket = rocket::ignite().mount("/", routes![super::user]);
let mut request = MockRequest::new(Method::Get, "/users/Sergio");
let mut response = request.dispatch_with(&rocket);
assert_eq!(response.status(), Status::Ok);
assert_eq!(response.body_string(), Some("Hello, Sergio!".into()));
}
#[test]
fn test_404() {
let rocket = rocket::ignite().mount("/", routes![super::user]);
let mut request = MockRequest::new(Method::Get, "/users/unknown");
let response = request.dispatch_with(&rocket);
// Only test the status because the body is the default 404.
assert_eq!(response.status(), Status::NotFound);
}

View File

@ -1,8 +1,6 @@
use super::{rocket, index}; use super::{rocket, index};
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::{Status, ContentType}; use rocket::http::{Status, ContentType};
use rocket::Rocket;
fn extract_id(from: &str) -> Option<String> { fn extract_id(from: &str) -> Option<String> {
from.rfind('/').map(|i| &from[(i + 1)..]).map(|s| s.trim_right().to_string()) from.rfind('/').map(|i| &from[(i + 1)..]).map(|s| s.trim_right().to_string())
@ -10,56 +8,52 @@ fn extract_id(from: &str) -> Option<String> {
#[test] #[test]
fn check_index() { fn check_index() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
// Ensure the index returns what we expect. // Ensure the index returns what we expect.
let mut req = MockRequest::new(Get, "/"); let mut response = client.get("/").dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::Plain)); assert_eq!(response.content_type(), Some(ContentType::Plain));
assert_eq!(response.body_string(), Some(index().into())) assert_eq!(response.body_string(), Some(index().into()))
} }
fn upload_paste(rocket: &Rocket, body: &str) -> String { fn upload_paste(client: &Client, body: &str) -> String {
let mut req = MockRequest::new(Post, "/").body(body); let mut response = client.post("/").body(body).dispatch();
let mut response = req.dispatch_with(rocket);
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::Plain)); assert_eq!(response.content_type(), Some(ContentType::Plain));
extract_id(&response.body_string().unwrap()).unwrap() extract_id(&response.body_string().unwrap()).unwrap()
} }
fn download_paste(client: &Client, id: &str) -> String {
fn download_paste(rocket: &Rocket, id: &str) -> String { let mut response = client.get(format!("/{}", id)).dispatch();
let mut req = MockRequest::new(Get, format!("/{}", id));
let mut response = req.dispatch_with(rocket);
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
response.body_string().unwrap() response.body_string().unwrap()
} }
#[test] #[test]
fn pasting() { fn pasting() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
// Do a trivial upload, just to make sure it works. // Do a trivial upload, just to make sure it works.
let body_1 = "Hello, world!"; let body_1 = "Hello, world!";
let id_1 = upload_paste(&rocket, body_1); let id_1 = upload_paste(&client, body_1);
assert_eq!(download_paste(&rocket, &id_1), body_1); assert_eq!(download_paste(&client, &id_1), body_1);
// Make sure we can keep getting that paste. // Make sure we can keep getting that paste.
assert_eq!(download_paste(&rocket, &id_1), body_1); assert_eq!(download_paste(&client, &id_1), body_1);
assert_eq!(download_paste(&rocket, &id_1), body_1); assert_eq!(download_paste(&client, &id_1), body_1);
assert_eq!(download_paste(&rocket, &id_1), body_1); assert_eq!(download_paste(&client, &id_1), body_1);
// Upload some unicode. // Upload some unicode.
let body_2 = "こんにちは"; let body_2 = "こんにちは";
let id_2 = upload_paste(&rocket, body_2); let id_2 = upload_paste(&client, body_2);
assert_eq!(download_paste(&rocket, &id_2), body_2); assert_eq!(download_paste(&client, &id_2), body_2);
// Make sure we can get both pastes. // Make sure we can get both pastes.
assert_eq!(download_paste(&rocket, &id_1), body_1); assert_eq!(download_paste(&client, &id_1), body_1);
assert_eq!(download_paste(&rocket, &id_2), body_2); assert_eq!(download_paste(&client, &id_2), body_2);
assert_eq!(download_paste(&rocket, &id_1), body_1); assert_eq!(download_paste(&client, &id_1), body_1);
assert_eq!(download_paste(&rocket, &id_2), body_2); assert_eq!(download_paste(&client, &id_2), body_2);
// Now a longer upload. // Now a longer upload.
let body_3 = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed let body_3 = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed
@ -69,8 +63,8 @@ fn pasting() {
in voluptate velit esse cillum dolore eu fugiat nulla pariatur. in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim id est laborum."; officia deserunt mollit anim id est laborum.";
let id_3 = upload_paste(&rocket, body_3); let id_3 = upload_paste(&client, body_3);
assert_eq!(download_paste(&rocket, &id_3), body_3); assert_eq!(download_paste(&client, &id_3), body_3);
assert_eq!(download_paste(&rocket, &id_1), body_1); assert_eq!(download_paste(&client, &id_1), body_1);
assert_eq!(download_paste(&rocket, &id_2), body_2); assert_eq!(download_paste(&client, &id_2), body_2);
} }

View File

@ -1,16 +1,14 @@
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::{Client, LocalResponse as Response};
use rocket::http::Method::*;
use rocket::http::Status; use rocket::http::Status;
use rocket::Response;
macro_rules! run_test { macro_rules! run_test {
($query:expr, $test_fn:expr) => ({ ($query:expr, $test_fn:expr) => ({
let rocket = rocket::ignite() let rocket = rocket::ignite()
.mount("/", routes![super::hello]); .mount("/", routes![super::hello]);
let mut request = MockRequest::new(Get, format!("/hello{}", $query)); let client = Client::new(rocket).unwrap();
$test_fn(request.dispatch_with(&rocket)); $test_fn(client.get(format!("/hello{}", $query)).dispatch());
}) })
} }

View File

@ -1,5 +1,5 @@
[package] [package]
name = "forms" name = "ranking"
version = "0.0.0" version = "0.0.0"
workspace = "../../" workspace = "../../"

View File

@ -1,11 +1,10 @@
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
fn test(uri: &str, expected: String) { fn test(uri: &str, expected: String) {
let rocket = rocket::ignite().mount("/", routes![super::hello, super::hi]); let rocket = rocket::ignite().mount("/", routes![super::hello, super::hi]);
let mut req = MockRequest::new(Get, uri); let client = Client::new(rocket).unwrap();
let mut response = req.dispatch_with(&rocket); let mut response = client.get(uri).dispatch();
assert_eq!(response.body_string(), Some(expected)); assert_eq!(response.body_string(), Some(expected));
} }

View File

@ -1,12 +1,9 @@
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
#[test] #[test]
fn hello() { fn hello() {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut req = MockRequest::new(Get, "/"); let mut response = client.get("/").dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some("Rocketeer".into())); assert_eq!(response.body_string(), Some("Rocketeer".into()));
} }

View File

@ -1,30 +1,30 @@
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::{Status, ContentType}; use rocket::http::{Status, ContentType};
use rocket::http::Method::*;
use std::io::Read; use std::io::Read;
use std::fs::File; use std::fs::{self, File};
const UPLOAD_CONTENTS: &str = "Hey! I'm going to be uploaded. :D Yay!";
#[test] #[test]
fn test_index() { fn test_index() {
let rocket = super::rocket(); let client = Client::new(super::rocket()).unwrap();
let mut req = MockRequest::new(Get, "/"); let mut res = client.get("/").dispatch();
let mut res = req.dispatch_with(&rocket);
assert_eq!(res.body_string(), Some(super::index().to_string())); assert_eq!(res.body_string(), Some(super::index().to_string()));
} }
#[test] #[test]
fn test_raw_upload() { fn test_raw_upload() {
const UPLOAD_CONTENTS: &str = "Hey! I'm going to be uploaded. :D Yay!"; // Delete the upload file before we begin.
let _ = fs::remove_file("/tmp/upload.txt");
let rocket = super::rocket();
let mut req = MockRequest::new(Post, "/upload")
.header(ContentType::Plain)
.body(UPLOAD_CONTENTS);
// Do the upload. Make sure we get the expected results. // Do the upload. Make sure we get the expected results.
let mut res = req.dispatch_with(&rocket); let client = Client::new(super::rocket()).unwrap();
let mut res = client.post("/upload")
.header(ContentType::Plain)
.body(UPLOAD_CONTENTS)
.dispatch();
assert_eq!(res.status(), Status::Ok); assert_eq!(res.status(), Status::Ok);
assert_eq!(res.body_string(), Some(UPLOAD_CONTENTS.len().to_string())); assert_eq!(res.body_string(), Some(UPLOAD_CONTENTS.len().to_string()));

View File

@ -1,21 +1,17 @@
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::Response;
use rocket::http::Method::*;
use rocket::http::Status; use rocket::http::Status;
macro_rules! run_test { fn client() -> Client {
($path:expr, $test_fn:expr) => ({
let rocket = rocket::ignite().mount("/", routes![super::root, super::login]); let rocket = rocket::ignite().mount("/", routes![super::root, super::login]);
let mut request = MockRequest::new(Get, format!($path)); Client::new(rocket).unwrap()
$test_fn(request.dispatch_with(&rocket));
})
} }
#[test] #[test]
fn test_root() { fn test_root() {
run_test!("/", |mut response: Response| { let client = client();
let mut response = client.get("/").dispatch();
assert!(response.body().is_none()); assert!(response.body().is_none());
assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.status(), Status::SeeOther);
for h in response.headers().iter() { for h in response.headers().iter() {
@ -25,14 +21,11 @@ fn test_root() {
_ => { /* let these through */ } _ => { /* let these through */ }
} }
} }
});
} }
#[test] #[test]
fn test_login() { fn test_login() {
run_test!("/login", |mut response: Response| { let client = client();
assert_eq!(response.body_string(), let mut r = client.get("/login").dispatch();
Some("Hi! Please log in before continuing.".to_string())); assert_eq!(r.body_string(), Some("Hi! Please log in before continuing.".into()));
assert_eq!(response.status(), Status::Ok);
});
} }

View File

@ -1,5 +1,5 @@
[package] [package]
name = "hello_ranks" name = "request_guard"
version = "0.0.0" version = "0.0.0"
workspace = "../../" workspace = "../../"

View File

@ -28,27 +28,27 @@ fn header_count(header_count: HeaderCount) -> String {
format!("Your request contained {} headers!", header_count) format!("Your request contained {} headers!", header_count)
} }
fn rocket() -> rocket::Rocket {
rocket::ignite().mount("/", routes![header_count])
}
fn main() { fn main() {
rocket::ignite().mount("/", routes![header_count]).launch(); rocket().launch();
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::rocket; use rocket::local::Client;
use rocket::testing::MockRequest;
use rocket::http::Method::*;
use rocket::http::Header; use rocket::http::Header;
fn test_header_count<'h>(headers: Vec<Header<'static>>) { fn test_header_count<'h>(headers: Vec<Header<'static>>) {
let rocket = rocket::ignite() let client = Client::new(super::rocket()).unwrap();
.mount("/", routes![super::header_count]); let mut req = client.get("/");
let mut req = MockRequest::new(Get, "/");
for header in headers.iter().cloned() { for header in headers.iter().cloned() {
req = req.header(header); req.add_header(header);
} }
let mut response = req.dispatch_with(&rocket); let mut response = req.dispatch();
let expect = format!("Your request contained {} headers!", headers.len()); let expect = format!("Your request contained {} headers!", headers.len());
assert_eq!(response.body_string(), Some(expect)); assert_eq!(response.body_string(), Some(expect));
} }
@ -56,7 +56,8 @@ mod test {
#[test] #[test]
fn test_n_headers() { fn test_n_headers() {
for i in 0..50 { for i in 0..50 {
let headers = (0..i).map(|n| Header::new(n.to_string(), n.to_string())) let headers = (0..i)
.map(|n| Header::new(n.to_string(), n.to_string()))
.collect(); .collect();
test_header_count(headers); test_header_count(headers);

View File

@ -9,7 +9,7 @@ use std::collections::HashMap;
use rocket::Outcome; use rocket::Outcome;
use rocket::request::{self, Form, FlashMessage, FromRequest, Request}; use rocket::request::{self, Form, FlashMessage, FromRequest, Request};
use rocket::response::{Redirect, Flash}; use rocket::response::{Redirect, Flash};
use rocket::http::{Cookie, Session}; use rocket::http::{Cookie, Cookies};
use rocket_contrib::Template; use rocket_contrib::Template;
#[derive(FromForm)] #[derive(FromForm)]
@ -25,8 +25,8 @@ impl<'a, 'r> FromRequest<'a, 'r> for User {
type Error = (); type Error = ();
fn from_request(request: &'a Request<'r>) -> request::Outcome<User, ()> { fn from_request(request: &'a Request<'r>) -> request::Outcome<User, ()> {
let user = request.session() let user = request.cookies()
.get("user_id") .get_private("user_id")
.and_then(|cookie| cookie.value().parse().ok()) .and_then(|cookie| cookie.value().parse().ok())
.map(|id| User(id)); .map(|id| User(id));
@ -38,9 +38,9 @@ impl<'a, 'r> FromRequest<'a, 'r> for User {
} }
#[post("/login", data = "<login>")] #[post("/login", data = "<login>")]
fn login(mut session: Session, login: Form<Login>) -> Flash<Redirect> { fn login(mut cookies: Cookies, login: Form<Login>) -> Flash<Redirect> {
if login.get().username == "Sergio" && login.get().password == "password" { if login.get().username == "Sergio" && login.get().password == "password" {
session.set(Cookie::new("user_id", 1.to_string())); cookies.add_private(Cookie::new("user_id", 1.to_string()));
Flash::success(Redirect::to("/"), "Successfully logged in.") Flash::success(Redirect::to("/"), "Successfully logged in.")
} else { } else {
Flash::error(Redirect::to("/login"), "Invalid username/password.") Flash::error(Redirect::to("/login"), "Invalid username/password.")
@ -48,8 +48,8 @@ fn login(mut session: Session, login: Form<Login>) -> Flash<Redirect> {
} }
#[post("/logout")] #[post("/logout")]
fn logout(mut session: Session) -> Flash<Redirect> { fn logout(mut cookies: Cookies) -> Flash<Redirect> {
session.remove(Cookie::named("user_id")); cookies.remove_private(Cookie::named("user_id"));
Flash::success(Redirect::to("/login"), "Successfully logged out.") Flash::success(Redirect::to("/login"), "Successfully logged out.")
} }
@ -82,6 +82,7 @@ fn index() -> Redirect {
fn main() { fn main() {
rocket::ignite() rocket::ignite()
.attach(Template::fairing())
.mount("/", routes![index, user_index, login, logout, login_user, login_page]) .mount("/", routes![index, user_index, login, logout, login_user, login_page])
.launch(); .launch();
} }

View File

@ -1,32 +1,28 @@
use rocket::Rocket; use rocket::local::Client;
use rocket::testing::MockRequest;
use rocket::http::Method::*;
use rocket::http::Status; use rocket::http::Status;
fn register_hit(rocket: &Rocket) { fn register_hit(client: &Client) {
let mut req = MockRequest::new(Get, "/"); let response = client.get("/").dispatch();;
let response = req.dispatch_with(&rocket);
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
} }
fn get_count(rocket: &Rocket) -> usize { fn get_count(client: &Client) -> usize {
let mut req = MockRequest::new(Get, "/count"); let mut response = client.get("/count").dispatch();
let mut response = req.dispatch_with(&rocket);
response.body_string().and_then(|s| s.parse().ok()).unwrap() response.body_string().and_then(|s| s.parse().ok()).unwrap()
} }
#[test] #[test]
fn test_count() { fn test_count() {
let rocket = super::rocket(); let client = Client::new(super::rocket()).unwrap();
// Count should start at 0. // Count should start at 0.
assert_eq!(get_count(&rocket), 0); assert_eq!(get_count(&client), 0);
for _ in 0..99 { register_hit(&rocket); } for _ in 0..99 { register_hit(&client); }
assert_eq!(get_count(&rocket), 99); assert_eq!(get_count(&client), 99);
register_hit(&rocket); register_hit(&client);
assert_eq!(get_count(&rocket), 100); assert_eq!(get_count(&client), 100);
} }
// Cargo runs each test in parallel on different threads. We use all of these // Cargo runs each test in parallel on different threads. We use all of these

View File

@ -1,8 +1,7 @@
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::Status; use rocket::http::Status;
use super::rocket; use super::rocket;
@ -10,10 +9,8 @@ use super::rocket;
fn test_query_file<T> (path: &str, file: T, status: Status) fn test_query_file<T> (path: &str, file: T, status: Status)
where T: Into<Option<&'static str>> where T: Into<Option<&'static str>>
{ {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut req = MockRequest::new(Get, &path); let mut response = client.get(path).dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.status(), status); assert_eq!(response.status(), status);
let body_data = response.body().and_then(|body| body.into_bytes()); let body_data = response.body().and_then(|body| body.into_bytes());

View File

@ -1,14 +1,12 @@
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::prelude::*; use std::io::prelude::*;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
#[test] #[test]
fn test_root() { fn test_root() {
let rocket = super::rocket(); let client = Client::new(super::rocket()).unwrap();
let mut req = MockRequest::new(Get, "/"); let mut res = client.get("/").dispatch();
let mut res = req.dispatch_with(&rocket);
// Check that we have exactly 25,000 'a'. // Check that we have exactly 25,000 'a'.
let res_str = res.body_string().unwrap(); let res_str = res.body_string().unwrap();
@ -26,9 +24,8 @@ fn test_file() {
file.write_all(CONTENTS.as_bytes()).expect("write to big_file"); file.write_all(CONTENTS.as_bytes()).expect("write to big_file");
// Get the big file contents, hopefully. // Get the big file contents, hopefully.
let rocket = super::rocket(); let client = Client::new(super::rocket()).unwrap();
let mut req = MockRequest::new(Get, "/big_file"); let mut res = client.get("/big_file").dispatch();
let mut res = req.dispatch_with(&rocket);
assert_eq!(res.body_string(), Some(CONTENTS.into())); assert_eq!(res.body_string(), Some(CONTENTS.into()));
// Delete the 'big_file'. // Delete the 'big_file'.

View File

@ -8,22 +8,24 @@ fn hello() -> &'static str {
"Hello, world!" "Hello, world!"
} }
fn rocket() -> rocket::Rocket {
rocket::ignite().mount("/", routes![hello])
}
fn main() { fn main() {
rocket::ignite().mount("/", routes![hello]).launch(); rocket().launch();
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Status; use rocket::http::Status;
use rocket::http::Method::*;
#[test] #[test]
fn test_hello() { fn test_hello() {
let rocket = rocket::ignite().mount("/", routes![super::hello]); let client = Client::new(rocket()).unwrap();
let mut req = MockRequest::new(Get, "/"); let mut response = client.get("/").dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.body_string(), Some("Hello, world!".into())); assert_eq!(response.body_string(), Some("Hello, world!".into()));
} }

View File

@ -1,5 +1,5 @@
[package] [package]
name = "hello_tls" name = "tls"
version = "0.0.0" version = "0.0.0"
workspace = "../../" workspace = "../../"

View File

@ -1,12 +1,10 @@
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
#[test] #[test]
fn hello_world() { fn hello_world() {
let rocket = rocket::ignite().mount("/", routes![super::hello]); let rocket = rocket::ignite().mount("/", routes![super::hello]);
let mut req = MockRequest::new(Get, "/"); let client = Client::new(rocket).unwrap();
let mut response = req.dispatch_with(&rocket); let mut response = client.get("/").dispatch();
assert_eq!(response.body_string(), Some("Hello, world!".into())); assert_eq!(response.body_string(), Some("Hello, world!".into()));
} }

View File

@ -5,8 +5,7 @@ use super::task::Task;
use self::parking_lot::Mutex; use self::parking_lot::Mutex;
use self::rand::{Rng, thread_rng}; use self::rand::{Rng, thread_rng};
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::{Status, ContentType}; use rocket::http::{Status, ContentType};
// We use a lock to synchronize between tests so DB operations don't collide. // We use a lock to synchronize between tests so DB operations don't collide.
@ -15,9 +14,10 @@ use rocket::http::{Status, ContentType};
static DB_LOCK: Mutex<()> = Mutex::new(()); static DB_LOCK: Mutex<()> = Mutex::new(());
macro_rules! run_test { macro_rules! run_test {
(|$rocket:ident, $conn:ident| $block:expr) => ({ (|$client:ident, $conn:ident| $block:expr) => ({
let _lock = DB_LOCK.lock(); let _lock = DB_LOCK.lock();
let ($rocket, db) = super::rocket(); let (rocket, db) = super::rocket();
let $client = Client::new(rocket).expect("Rocket client");
let $conn = db.expect("failed to get database connection for testing"); let $conn = db.expect("failed to get database connection for testing");
$block $block
}) })
@ -25,15 +25,15 @@ macro_rules! run_test {
#[test] #[test]
fn test_insertion_deletion() { fn test_insertion_deletion() {
run_test!(|rocket, conn| { run_test!(|client, conn| {
// Get the tasks before making changes. // Get the tasks before making changes.
let init_tasks = Task::all(&conn); let init_tasks = Task::all(&conn);
// Issue a request to insert a new task. // Issue a request to insert a new task.
let mut req = MockRequest::new(Post, "/todo") client.post("/todo")
.header(ContentType::Form) .header(ContentType::Form)
.body("description=My+first+task"); .body("description=My+first+task")
req.dispatch_with(&rocket); .dispatch();
// Ensure we have one more task in the database. // Ensure we have one more task in the database.
let new_tasks = Task::all(&conn); let new_tasks = Task::all(&conn);
@ -45,8 +45,7 @@ fn test_insertion_deletion() {
// Issue a request to delete the task. // Issue a request to delete the task.
let id = new_tasks[0].id.unwrap(); let id = new_tasks[0].id.unwrap();
let mut req = MockRequest::new(Delete, format!("/todo/{}", id)); client.delete(format!("/todo/{}", id)).dispatch();
req.dispatch_with(&rocket);
// Ensure it's gone. // Ensure it's gone.
let final_tasks = Task::all(&conn); let final_tasks = Task::all(&conn);
@ -59,24 +58,22 @@ fn test_insertion_deletion() {
#[test] #[test]
fn test_toggle() { fn test_toggle() {
run_test!(|rocket, conn| { run_test!(|client, conn| {
// Issue a request to insert a new task; ensure it's not yet completed. // Issue a request to insert a new task; ensure it's not yet completed.
let mut req = MockRequest::new(Post, "/todo") client.post("/todo")
.header(ContentType::Form) .header(ContentType::Form)
.body("description=test_for_completion"); .body("description=test_for_completion")
req.dispatch_with(&rocket); .dispatch();
let task = Task::all(&conn)[0].clone(); let task = Task::all(&conn)[0].clone();
assert_eq!(task.completed, false); assert_eq!(task.completed, false);
// Issue a request to toggle the task; ensure it is completed. // Issue a request to toggle the task; ensure it is completed.
let mut req = MockRequest::new(Put, format!("/todo/{}", task.id.unwrap())); client.put(format!("/todo/{}", task.id.unwrap())).dispatch();
req.dispatch_with(&rocket);
assert_eq!(Task::all(&conn)[0].completed, true); assert_eq!(Task::all(&conn)[0].completed, true);
// Issue a request to toggle the task; ensure it's not completed again. // Issue a request to toggle the task; ensure it's not completed again.
let mut req = MockRequest::new(Put, format!("/todo/{}", task.id.unwrap())); client.put(format!("/todo/{}", task.id.unwrap())).dispatch();
req.dispatch_with(&rocket);
assert_eq!(Task::all(&conn)[0].completed, false); assert_eq!(Task::all(&conn)[0].completed, false);
}) })
} }
@ -86,7 +83,7 @@ fn test_many_insertions() {
const ITER: usize = 100; const ITER: usize = 100;
let mut rng = thread_rng(); let mut rng = thread_rng();
run_test!(|rocket, conn| { run_test!(|client, conn| {
// Get the number of tasks initially. // Get the number of tasks initially.
let init_num = Task::all(&conn).len(); let init_num = Task::all(&conn).len();
let mut descs = Vec::new(); let mut descs = Vec::new();
@ -94,10 +91,10 @@ fn test_many_insertions() {
for i in 0..ITER { for i in 0..ITER {
// Issue a request to insert a new task with a random description. // Issue a request to insert a new task with a random description.
let desc: String = rng.gen_ascii_chars().take(12).collect(); let desc: String = rng.gen_ascii_chars().take(12).collect();
let mut req = MockRequest::new(Post, "/todo") client.post("/todo")
.header(ContentType::Form) .header(ContentType::Form)
.body(format!("description={}", desc)); .body(format!("description={}", desc))
req.dispatch_with(&rocket); .dispatch();
// Record the description we choose for this iteration. // Record the description we choose for this iteration.
descs.insert(0, desc); descs.insert(0, desc);
@ -115,32 +112,34 @@ fn test_many_insertions() {
#[test] #[test]
fn test_bad_form_submissions() { fn test_bad_form_submissions() {
run_test!(|rocket, _conn| { run_test!(|client, _conn| {
// Submit an empty form. We should get a 422 but no flash error. // Submit an empty form. We should get a 422 but no flash error.
let mut req = MockRequest::new(Post, "/todo").header(ContentType::Form); let res = client.post("/todo")
let res = req.dispatch_with(&rocket); .header(ContentType::Form)
assert_eq!(res.status(), Status::UnprocessableEntity); .dispatch();
let mut cookies = res.headers().get("Set-Cookie"); let mut cookies = res.headers().get("Set-Cookie");
assert_eq!(res.status(), Status::UnprocessableEntity);
assert!(!cookies.any(|value| value.contains("error"))); assert!(!cookies.any(|value| value.contains("error")));
// Submit a form with an empty description. // Submit a form with an empty description. We look for 'error' in the
let mut req = MockRequest::new(Post, "/todo") // cookies which corresponds to flash message being set as an error.
let res = client.post("/todo")
.header(ContentType::Form) .header(ContentType::Form)
.body("description="); .body("description=")
.dispatch();
// We look for 'error' in the cookies which corresponds to flash message
// being set as an error.
let res = req.dispatch_with(&rocket);
let mut cookies = res.headers().get("Set-Cookie"); let mut cookies = res.headers().get("Set-Cookie");
assert!(cookies.any(|value| value.contains("error"))); assert!(cookies.any(|value| value.contains("error")));
// Submit a form without a description. Expect a 422 but no flash error. // Submit a form without a description. Expect a 422 but no flash error.
let mut req = MockRequest::new(Post, "/todo") let res = client.post("/todo")
.header(ContentType::Form) .header(ContentType::Form)
.body("evil=smile"); .body("evil=smile")
let res = req.dispatch_with(&rocket); .dispatch();
assert_eq!(res.status(), Status::UnprocessableEntity);
let mut cookies = res.headers().get("Set-Cookie"); let mut cookies = res.headers().get("Set-Cookie");
assert_eq!(res.status(), Status::UnprocessableEntity);
assert!(!cookies.any(|value| value.contains("error"))); assert!(!cookies.any(|value| value.contains("error")));
}) })
} }

View File

@ -39,8 +39,11 @@ fn people(id: UUID) -> Result<String, String> {
.ok_or(format!("Person not found for UUID: {}", id))?) .ok_or(format!("Person not found for UUID: {}", id))?)
} }
fn main() { fn rocket() -> rocket::Rocket {
rocket::ignite() rocket::ignite()
.mount("/", routes![people]) .mount("/", routes![people])
.launch(); }
fn main() {
rocket().launch();
} }

View File

@ -1,23 +1,16 @@
use super::rocket; use super::rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::Status; use rocket::http::Status;
fn test(uri: &str, expected: &str) { fn test(uri: &str, expected: &str) {
let rocket = rocket::ignite().mount("/", routes![super::people]); let client = Client::new(rocket()).unwrap();
let mut res = client.get(uri).dispatch();
let mut req = MockRequest::new(Get, uri);
let mut res = req.dispatch_with(&rocket);
assert_eq!(res.body_string(), Some(expected.into())); assert_eq!(res.body_string(), Some(expected.into()));
} }
fn test_404(uri: &str) { fn test_404(uri: &str) {
let rocket = rocket::ignite().mount("/", routes![super::people]); let client = Client::new(rocket()).unwrap();
let res = client.get(uri).dispatch();
let mut req = MockRequest::new(Get, uri);
let res = req.dispatch_with(&rocket);
assert_eq!(res.status(), Status::NotFound); assert_eq!(res.status(), Status::NotFound);
} }
@ -26,8 +19,7 @@ fn test_people() {
test("/people/7f205202-7ba1-4c39-b2fc-3e630722bf9f", "We found: Lacy"); test("/people/7f205202-7ba1-4c39-b2fc-3e630722bf9f", "We found: Lacy");
test("/people/4da34121-bc7d-4fc1-aee6-bf8de0795333", "We found: Bob"); test("/people/4da34121-bc7d-4fc1-aee6-bf8de0795333", "We found: Bob");
test("/people/ad962969-4e3d-4de7-ac4a-2d86d6d10839", "We found: George"); test("/people/ad962969-4e3d-4de7-ac4a-2d86d6d10839", "We found: George");
test("/people/e18b3a5c-488f-4159-a240-2101e0da19fd",
test("/people/e18b3a5c-488f-4159-a240-2101e0da19fd", "Person not found for UUID: e18b3a5c-488f-4159-a240-2101e0da19fd"); "Person not found for UUID: e18b3a5c-488f-4159-a240-2101e0da19fd");
test_404("/people/invalid_uuid"); test_404("/people/invalid_uuid");
} }

View File

@ -23,7 +23,7 @@ log = "0.3"
url = "1" url = "1"
toml = { version = "0.2", default-features = false } toml = { version = "0.2", default-features = false }
num_cpus = "1" num_cpus = "1"
state = "0.2.1" state = "0.2.2"
time = "0.1" time = "0.1"
memchr = "1" memchr = "1"
base64 = "0.5.2" base64 = "0.5.2"
@ -32,7 +32,7 @@ pear = "0.0.8"
pear_codegen = "0.0.8" pear_codegen = "0.0.8"
rustls = { version = "0.8.0", optional = true } rustls = { version = "0.8.0", optional = true }
cookie = { version = "0.8.1", features = ["percent-encode", "secure"] } cookie = { version = "0.8.1", features = ["percent-encode", "secure"] }
hyper = { version = "0.10.9", default-features = false } hyper = { version = "0.10.11", default-features = false }
ordermap = "0.2" ordermap = "0.2"
[dependencies.hyper-rustls] [dependencies.hyper-rustls]

View File

@ -13,8 +13,7 @@ fn post() -> &'static str { "post" }
fn rocket() -> rocket::Rocket { fn rocket() -> rocket::Rocket {
let config = Config::new(Environment::Production).unwrap(); let config = Config::new(Environment::Production).unwrap();
rocket::custom(config, false) rocket::custom(config, false).mount("/", routes![get, post])
.mount("/", routes![get, post])
} }
mod benches { mod benches {
@ -22,35 +21,34 @@ mod benches {
use super::rocket; use super::rocket;
use self::test::Bencher; use self::test::Bencher;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::{Accept, ContentType}; use rocket::http::{Accept, ContentType};
#[bench] #[bench]
fn accept_format(b: &mut Bencher) { fn accept_format(b: &mut Bencher) {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut request = MockRequest::new(Get, "/").header(Accept::JSON); let mut request = client.get("/").header(Accept::JSON);
b.iter(|| { request.dispatch_with(&rocket); }); b.iter(|| { request.mut_dispatch(); });
} }
#[bench] #[bench]
fn wrong_accept_format(b: &mut Bencher) { fn wrong_accept_format(b: &mut Bencher) {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut request = MockRequest::new(Get, "/").header(Accept::HTML); let mut request = client.get("/").header(Accept::HTML);
b.iter(|| { request.dispatch_with(&rocket); }); b.iter(|| { request.mut_dispatch(); });
} }
#[bench] #[bench]
fn content_type_format(b: &mut Bencher) { fn content_type_format(b: &mut Bencher) {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut request = MockRequest::new(Post, "/").header(ContentType::JSON); let mut request = client.post("/").header(ContentType::JSON);
b.iter(|| { request.dispatch_with(&rocket); }); b.iter(|| { request.mut_dispatch(); });
} }
#[bench] #[bench]
fn wrong_content_type_format(b: &mut Bencher) { fn wrong_content_type_format(b: &mut Bencher) {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut request = MockRequest::new(Post, "/").header(ContentType::Plain); let mut request = client.post("/").header(ContentType::Plain);
b.iter(|| { request.dispatch_with(&rocket); }); b.iter(|| { request.mut_dispatch(); });
} }
} }

View File

@ -35,36 +35,35 @@ mod benches {
use super::rocket; use super::rocket;
use self::test::Bencher; use self::test::Bencher;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::{Accept, ContentType}; use rocket::http::{Accept, ContentType};
#[bench] #[bench]
fn accept_format(b: &mut Bencher) { fn accept_format(b: &mut Bencher) {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut requests = vec![]; let mut requests = vec![];
requests.push(MockRequest::new(Get, "/").header(Accept::JSON)); requests.push(client.get("/").header(Accept::JSON));
requests.push(MockRequest::new(Get, "/").header(Accept::HTML)); requests.push(client.get("/").header(Accept::HTML));
requests.push(MockRequest::new(Get, "/").header(Accept::Plain)); requests.push(client.get("/").header(Accept::Plain));
b.iter(|| { b.iter(|| {
for request in requests.iter_mut() { for request in requests.iter_mut() {
request.dispatch_with(&rocket); request.mut_dispatch();
} }
}); });
} }
#[bench] #[bench]
fn content_type_format(b: &mut Bencher) { fn content_type_format(b: &mut Bencher) {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut requests = vec![]; let mut requests = vec![];
requests.push(MockRequest::new(Post, "/").header(ContentType::JSON)); requests.push(client.post("/").header(ContentType::JSON));
requests.push(MockRequest::new(Post, "/").header(ContentType::HTML)); requests.push(client.post("/").header(ContentType::HTML));
requests.push(MockRequest::new(Post, "/").header(ContentType::Plain)); requests.push(client.post("/").header(ContentType::Plain));
b.iter(|| { b.iter(|| {
for request in requests.iter_mut() { for request in requests.iter_mut() {
request.dispatch_with(&rocket); request.mut_dispatch();
} }
}); });
} }

View File

@ -49,82 +49,82 @@ mod benches {
use super::{hello_world_rocket, rocket}; use super::{hello_world_rocket, rocket};
use self::test::Bencher; use self::test::Bencher;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*; use rocket::http::Method::*;
#[bench] #[bench]
fn bench_hello_world(b: &mut Bencher) { fn bench_hello_world(b: &mut Bencher) {
let rocket = hello_world_rocket(); let client = Client::new(hello_world_rocket()).unwrap();
let mut request = MockRequest::new(Get, "/"); let mut request = client.get("/");
b.iter(|| { b.iter(|| {
request.dispatch_with(&rocket); request.mut_dispatch();
}); });
} }
#[bench] #[bench]
fn bench_single_get_index(b: &mut Bencher) { fn bench_single_get_index(b: &mut Bencher) {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut request = MockRequest::new(Get, "/"); let mut request = client.get("/");
b.iter(|| { b.iter(|| {
request.dispatch_with(&rocket); request.mut_dispatch();
}); });
} }
#[bench] #[bench]
fn bench_get_put_post_index(b: &mut Bencher) { fn bench_get_put_post_index(b: &mut Bencher) {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
// Hold all of the requests we're going to make during the benchmark. // Hold all of the requests we're going to make during the benchmark.
let mut requests = vec![]; let mut requests = vec![];
requests.push(MockRequest::new(Get, "/")); requests.push(client.get("/"));
requests.push(MockRequest::new(Put, "/")); requests.push(client.put("/"));
requests.push(MockRequest::new(Post, "/")); requests.push(client.post("/"));
b.iter(|| { b.iter(|| {
for request in requests.iter_mut() { for request in requests.iter_mut() {
request.dispatch_with(&rocket); request.mut_dispatch();
} }
}); });
} }
#[bench] #[bench]
fn bench_dynamic(b: &mut Bencher) { fn bench_dynamic(b: &mut Bencher) {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
// Hold all of the requests we're going to make during the benchmark. // Hold all of the requests we're going to make during the benchmark.
let mut requests = vec![]; let mut requests = vec![];
requests.push(MockRequest::new(Get, "/abc")); requests.push(client.get("/abc"));
requests.push(MockRequest::new(Get, "/abcdefg")); requests.push(client.get("/abcdefg"));
requests.push(MockRequest::new(Get, "/123")); requests.push(client.get("/123"));
b.iter(|| { b.iter(|| {
for request in requests.iter_mut() { for request in requests.iter_mut() {
request.dispatch_with(&rocket); request.mut_dispatch();
} }
}); });
} }
#[bench] #[bench]
fn bench_simple_routing(b: &mut Bencher) { fn bench_simple_routing(b: &mut Bencher) {
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
// Hold all of the requests we're going to make during the benchmark. // Hold all of the requests we're going to make during the benchmark.
let mut requests = vec![]; let mut requests = vec![];
for route in rocket.routes() { for route in client.rocket().routes() {
let request = MockRequest::new(route.method, route.path.path()); let request = client.req(route.method, route.path.path());
requests.push(request); requests.push(request);
} }
// A few more for the dynamic route. // A few more for the dynamic route.
requests.push(MockRequest::new(Get, "/abc")); requests.push(client.get("/abc"));
requests.push(MockRequest::new(Get, "/abcdefg")); requests.push(client.get("/abcdefg"));
requests.push(MockRequest::new(Get, "/123")); requests.push(client.get("/123"));
b.iter(|| { b.iter(|| {
for request in requests.iter_mut() { for request in requests.iter_mut() {
request.dispatch_with(&rocket); request.mut_dispatch();
} }
}); });
} }

View File

@ -112,6 +112,69 @@ impl Config {
Config::default(env, cwd.as_path().join("Rocket.custom.toml")) Config::default(env, cwd.as_path().join("Rocket.custom.toml"))
} }
/// Returns a builder for `Config` structure where the default parameters
/// are set to those of the development environment. The root configuration
/// directory is set to the current working directory.
///
/// # Errors
///
/// If the current directory cannot be retrieved, a `BadCWD` error is
/// returned.
///
/// # Example
///
/// ```rust
/// use rocket::config::{Config, Environment};
///
/// let mut my_config = Config::development().unwrap();
/// my_config.set_port(1001);
/// ```
pub fn development() -> Result<Config> {
Config::new(Environment::Development)
}
/// Creates a new configuration using the default parameters from the
/// staging environment. The root configuration directory is set to the
/// current working directory.
///
/// # Errors
///
/// If the current directory cannot be retrieved, a `BadCWD` error is
/// returned.
///
/// # Example
///
/// ```rust
/// use rocket::config::{Config, Environment};
///
/// let mut my_config = Config::staging().expect("cwd");
/// my_config.set_port(1001);
/// ```
pub fn staging() -> Result<Config> {
Config::new(Environment::Staging)
}
/// Creates a new configuration using the default parameters from the
/// production environment. The root configuration directory is set to the
/// current working directory.
///
/// # Errors
///
/// If the current directory cannot be retrieved, a `BadCWD` error is
/// returned.
///
/// # Example
///
/// ```rust
/// use rocket::config::{Config, Environment};
///
/// let mut my_config = Config::production().expect("cwd");
/// my_config.set_port(1001);
/// ```
pub fn production() -> Result<Config> {
Config::new(Environment::Production)
}
/// Returns the default configuration for the environment `env` given that /// Returns the default configuration for the environment `env` given that
/// the configuration was stored at `config_path`. If `config_path` is not /// the configuration was stored at `config_path`. If `config_path` is not
/// an absolute path, an `Err` of `ConfigError::BadFilePath` is returned. /// an absolute path, an `Err` of `ConfigError::BadFilePath` is returned.

View File

@ -66,8 +66,7 @@ impl Data {
/// the data in a request. /// the data in a request.
pub fn open(mut self) -> DataStream { pub fn open(mut self) -> DataStream {
let buffer = ::std::mem::replace(&mut self.buffer, vec![]); let buffer = ::std::mem::replace(&mut self.buffer, vec![]);
let empty_stream = Cursor::new(vec![]) let empty_stream = Cursor::new(vec![]).chain(NetStream::Empty);
.chain(NetStream::Local(Cursor::new(vec![])));
// FIXME: Insert a `BufReader` in front of the `NetStream` with capacity // FIXME: Insert a `BufReader` in front of the `NetStream` with capacity
// 4096. We need the new `Chain` methods to get the inner reader to // 4096. We need the new `Chain` methods to get the inner reader to
@ -205,9 +204,9 @@ impl Data {
} }
/// This creates a `data` object from a local data source `data`. /// This creates a `data` object from a local data source `data`.
#[inline]
pub(crate) fn local(data: Vec<u8>) -> Data { pub(crate) fn local(data: Vec<u8>) -> Data {
let empty_stream = Cursor::new(vec![]) let empty_stream = Cursor::new(vec![]).chain(NetStream::Empty);
.chain(NetStream::Local(Cursor::new(vec![])));
Data { Data {
buffer: data, buffer: data,

View File

@ -1,4 +1,4 @@
use std::io::{self, Cursor}; use std::io;
use std::net::{SocketAddr, Shutdown}; use std::net::{SocketAddr, Shutdown};
use std::time::Duration; use std::time::Duration;
@ -16,7 +16,7 @@ pub enum NetStream {
Http(HttpStream), Http(HttpStream),
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
Https(HttpsStream), Https(HttpsStream),
Local(Cursor<Vec<u8>>) Empty,
} }
impl io::Read for NetStream { impl io::Read for NetStream {
@ -25,9 +25,10 @@ impl io::Read for NetStream {
trace_!("NetStream::read()"); trace_!("NetStream::read()");
let res = match *self { let res = match *self {
Http(ref mut stream) => stream.read(buf), Http(ref mut stream) => stream.read(buf),
Local(ref mut stream) => stream.read(buf), #[cfg(feature = "tls")] Https(ref mut stream) => stream.read(buf),
#[cfg(feature = "tls")] Https(ref mut stream) => stream.read(buf) Empty => Ok(0),
}; };
trace_!("NetStream::read() -- complete"); trace_!("NetStream::read() -- complete");
res res
} }
@ -39,8 +40,8 @@ impl io::Write for NetStream {
trace_!("NetStream::write()"); trace_!("NetStream::write()");
match *self { match *self {
Http(ref mut stream) => stream.write(buf), Http(ref mut stream) => stream.write(buf),
Local(ref mut stream) => stream.write(buf), #[cfg(feature = "tls")] Https(ref mut stream) => stream.write(buf),
#[cfg(feature = "tls")] Https(ref mut stream) => stream.write(buf) Empty => Ok(0),
} }
} }
@ -48,8 +49,8 @@ impl io::Write for NetStream {
fn flush(&mut self) -> io::Result<()> { fn flush(&mut self) -> io::Result<()> {
match *self { match *self {
Http(ref mut stream) => stream.flush(), Http(ref mut stream) => stream.flush(),
Local(ref mut stream) => stream.flush(), #[cfg(feature = "tls")] Https(ref mut stream) => stream.flush(),
#[cfg(feature = "tls")] Https(ref mut stream) => stream.flush() Empty => Ok(()),
} }
} }
} }
@ -60,7 +61,7 @@ impl NetworkStream for NetStream {
match *self { match *self {
Http(ref mut stream) => stream.peer_addr(), Http(ref mut stream) => stream.peer_addr(),
#[cfg(feature = "tls")] Https(ref mut stream) => stream.peer_addr(), #[cfg(feature = "tls")] Https(ref mut stream) => stream.peer_addr(),
Local(_) => Err(io::Error::from(io::ErrorKind::AddrNotAvailable)), Empty => Err(io::Error::from(io::ErrorKind::AddrNotAvailable)),
} }
} }
@ -69,7 +70,7 @@ impl NetworkStream for NetStream {
match *self { match *self {
Http(ref stream) => stream.set_read_timeout(dur), Http(ref stream) => stream.set_read_timeout(dur),
#[cfg(feature = "tls")] Https(ref stream) => stream.set_read_timeout(dur), #[cfg(feature = "tls")] Https(ref stream) => stream.set_read_timeout(dur),
Local(_) => Ok(()), Empty => Ok(()),
} }
} }
@ -78,7 +79,7 @@ impl NetworkStream for NetStream {
match *self { match *self {
Http(ref stream) => stream.set_write_timeout(dur), Http(ref stream) => stream.set_write_timeout(dur),
#[cfg(feature = "tls")] Https(ref stream) => stream.set_write_timeout(dur), #[cfg(feature = "tls")] Https(ref stream) => stream.set_write_timeout(dur),
Local(_) => Ok(()), Empty => Ok(()),
} }
} }
@ -87,7 +88,7 @@ impl NetworkStream for NetStream {
match *self { match *self {
Http(ref mut stream) => stream.close(how), Http(ref mut stream) => stream.close(how),
#[cfg(feature = "tls")] Https(ref mut stream) => stream.close(how), #[cfg(feature = "tls")] Https(ref mut stream) => stream.close(how),
Local(_) => Ok(()), Empty => Ok(()),
} }
} }
} }

View File

@ -1,28 +1,23 @@
use http::Header; use std::fmt;
use std::cell::RefMut; use std::cell::RefMut;
pub use cookie::{Cookie, CookieJar, Iter, CookieBuilder, Delta}; pub use cookie::{Cookie, Key, CookieJar};
use cookie::{SameSite, Delta};
use cookie::{PrivateJar, Key}; use http::Header;
impl<'a, 'c> From<&'a Cookie<'c>> for Header<'static> {
fn from(cookie: &Cookie) -> Header<'static> {
Header::new("Set-Cookie", cookie.encoded().to_string())
}
}
#[derive(Debug)]
pub enum Cookies<'a> { pub enum Cookies<'a> {
Jarred(RefMut<'a, CookieJar>), Jarred(RefMut<'a, CookieJar>, &'a Key),
Empty(CookieJar) Empty(CookieJar)
} }
impl<'a> Cookies<'a> { impl<'a> Cookies<'a> {
pub(crate) fn new(jar: RefMut<'a, CookieJar>) -> Cookies<'a> { #[inline]
Cookies::Jarred(jar) pub(crate) fn new(jar: RefMut<'a, CookieJar>, key: &'a Key) -> Cookies<'a> {
Cookies::Jarred(jar, key)
} }
#[inline]
pub(crate) fn empty() -> Cookies<'static> { pub(crate) fn empty() -> Cookies<'static> {
Cookies::Empty(CookieJar::new()) Cookies::Empty(CookieJar::new())
} }
@ -34,41 +29,82 @@ impl<'a> Cookies<'a> {
pub fn get(&self, name: &str) -> Option<&Cookie<'static>> { pub fn get(&self, name: &str) -> Option<&Cookie<'static>> {
match *self { match *self {
Cookies::Jarred(ref jar) => jar.get(name), Cookies::Jarred(ref jar, _) => jar.get(name),
Cookies::Empty(_) => None
}
}
pub fn get_private(&mut self, name: &str) -> Option<Cookie<'static>> {
match *self {
Cookies::Jarred(ref mut jar, key) => jar.private(key).get(name),
Cookies::Empty(_) => None Cookies::Empty(_) => None
} }
} }
pub fn add(&mut self, cookie: Cookie<'static>) { pub fn add(&mut self, cookie: Cookie<'static>) {
if let Cookies::Jarred(ref mut jar) = *self { if let Cookies::Jarred(ref mut jar, _) = *self {
jar.add(cookie) jar.add(cookie)
} }
} }
pub fn add_private(&mut self, mut cookie: Cookie<'static>) {
cookie.set_http_only(true);
if cookie.path().is_none() {
cookie.set_path("/");
}
if cookie.same_site().is_none() {
cookie.set_same_site(SameSite::Strict);
}
if let Cookies::Jarred(ref mut jar, key) = *self {
jar.private(key).add(cookie)
}
}
pub fn remove(&mut self, cookie: Cookie<'static>) { pub fn remove(&mut self, cookie: Cookie<'static>) {
if let Cookies::Jarred(ref mut jar) = *self { if let Cookies::Jarred(ref mut jar, _) = *self {
jar.remove(cookie) jar.remove(cookie)
} }
} }
pub(crate) fn private(&mut self, key: &Key) -> PrivateJar { pub fn remove_private(&mut self, mut cookie: Cookie<'static>) {
match *self { if cookie.path().is_none() {
Cookies::Jarred(ref mut jar) => jar.private(key), cookie.set_path("/");
Cookies::Empty(ref mut jar) => jar.private(key) }
if let Cookies::Jarred(ref mut jar, key) = *self {
jar.private(key).remove(cookie)
} }
} }
pub fn iter(&self) -> Iter { pub fn iter<'s>(&'s self) -> impl Iterator<Item=&'s Cookie<'static>> {
match *self { match *self {
Cookies::Jarred(ref jar) => jar.iter(), Cookies::Jarred(ref jar, _) => jar.iter(),
Cookies::Empty(ref jar) => jar.iter() Cookies::Empty(ref jar) => jar.iter()
} }
} }
pub(crate) fn delta(&self) -> Delta { pub(crate) fn delta(&self) -> Delta {
match *self { match *self {
Cookies::Jarred(ref jar) => jar.delta(), Cookies::Jarred(ref jar, _) => jar.delta(),
Cookies::Empty(ref jar) => jar.delta() Cookies::Empty(ref jar) => jar.delta()
} }
} }
} }
impl<'a> fmt::Debug for Cookies<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Cookies::Jarred(ref jar, _) => write!(f, "{:?}", jar),
Cookies::Empty(ref jar) => write!(f, "{:?}", jar)
}
}
}
impl<'a, 'c> From<&'a Cookie<'c>> for Header<'static> {
fn from(cookie: &Cookie) -> Header<'static> {
Header::new("Set-Cookie", cookie.encoded().to_string())
}
}

View File

@ -11,7 +11,6 @@ pub mod uri;
#[macro_use] #[macro_use]
mod known_media_types; mod known_media_types;
mod cookies; mod cookies;
mod session;
mod method; mod method;
mod media_type; mod media_type;
mod content_type; mod content_type;
@ -36,5 +35,5 @@ pub use self::header::{Header, HeaderMap};
pub use self::raw_str::RawStr; pub use self::raw_str::RawStr;
pub use self::media_type::MediaType; pub use self::media_type::MediaType;
pub use self::cookies::*; pub use self::cookies::{Cookie, Cookies};
pub use self::session::*; pub(crate) use self::cookies::{Key, CookieJar};

View File

@ -1,75 +0,0 @@
use std::cell::{RefCell, RefMut};
use time::{self, Duration};
use cookie::{Cookie, CookieJar, Delta};
pub use cookie::Key;
use http::{Header, Cookies};
const SESSION_PREFIX: &'static str = "__sess_";
pub struct Session<'a> {
cookies: RefCell<Cookies<'a>>,
key: &'a Key
}
impl<'a> Session<'a> {
#[inline(always)]
pub(crate) fn new(jar: RefMut<'a, CookieJar>, key: &'a Key) -> Session<'a> {
Session { cookies: RefCell::new(Cookies::new(jar)), key: key }
}
#[inline(always)]
pub(crate) fn empty(key: &'a Key) -> Session<'a> {
Session { cookies: RefCell::new(Cookies::empty()), key: key }
}
#[inline(always)]
pub(crate) fn header_for(cookie: &Cookie) -> Header<'static> {
Header::new("Set-Cookie", format!("{}{}", SESSION_PREFIX, cookie))
}
#[inline(always)]
pub(crate) fn parse_cookie(cookie_str: &str) -> Option<Cookie<'static>> {
if !cookie_str.starts_with(SESSION_PREFIX) {
return None;
}
Cookie::parse(&cookie_str[SESSION_PREFIX.len()..]).ok()
.map(|c| c.into_owned())
}
pub fn get(&self, name: &str) -> Option<Cookie<'static>> {
self.cookies.borrow_mut().private(&self.key).get(name)
}
pub fn set(&mut self, mut cookie: Cookie<'static>) {
cookie.set_http_only(true);
if cookie.path().is_none() {
cookie.set_path("/");
}
// TODO: Should this be configurable?
if cookie.max_age().is_none() && cookie.expires().is_none() {
let session_lifetime = Duration::hours(3);
cookie.set_max_age(session_lifetime);
cookie.set_expires(time::now() + session_lifetime);
}
self.cookies.get_mut().private(&self.key).add(cookie)
}
pub fn remove(&mut self, mut cookie: Cookie<'static>) {
if cookie.path().is_none() {
cookie.set_path("/");
}
self.cookies.get_mut().private(&self.key).remove(cookie)
}
#[inline(always)]
pub(crate) fn delta(&mut self) -> Delta {
self.cookies.get_mut().delta()
}
}

View File

@ -117,7 +117,7 @@ extern crate ordermap;
#[cfg(test)] #[macro_use] extern crate lazy_static; #[cfg(test)] #[macro_use] extern crate lazy_static;
#[doc(hidden)] #[macro_use] pub mod logger; #[doc(hidden)] #[macro_use] pub mod logger;
pub mod testing; pub mod local;
pub mod http; pub mod http;
pub mod request; pub mod request;
pub mod response; pub mod response;
@ -140,6 +140,7 @@ mod ext;
#[doc(hidden)] pub use codegen::{StaticRouteInfo, StaticCatchInfo}; #[doc(hidden)] pub use codegen::{StaticRouteInfo, StaticCatchInfo};
#[doc(inline)] pub use outcome::Outcome; #[doc(inline)] pub use outcome::Outcome;
#[doc(inline)] pub use data::Data; #[doc(inline)] pub use data::Data;
#[doc(inline)] pub use config::Config;
pub use router::Route; pub use router::Route;
pub use request::{Request, State}; pub use request::{Request, State};
pub use error::{Error, LaunchError}; pub use error::{Error, LaunchError};

78
lib/src/local/client.rs Normal file
View File

@ -0,0 +1,78 @@
use {Rocket, Request};
use local::LocalRequest;
use http::Method;
use http::uri::URI;
use error::LaunchError;
pub struct Client {
rocket: Rocket,
}
impl Client {
#[inline]
pub fn new(rocket: Rocket) -> Result<Client, LaunchError> {
if let Some(err) = rocket.prelaunch_check() {
return Err(err);
}
Ok(Client {
rocket: rocket,
})
}
#[inline(always)]
pub fn rocket(&self) -> &Rocket {
&self.rocket
}
#[inline(always)]
pub fn req<'c, 'u: 'c, U>(&'c self, method: Method, uri: U) -> LocalRequest<'c>
where U: Into<URI<'u>>
{
let request = Request::new(&self.rocket, method, uri);
LocalRequest::new(&self.rocket, request)
}
#[inline(always)]
pub fn get<'c, 'u: 'c, U: Into<URI<'u>>>(&'c self, uri: U) -> LocalRequest<'c> {
self.req(Method::Get, uri)
}
#[inline(always)]
pub fn put<'c, 'u: 'c, U: Into<URI<'u>>>(&'c self, uri: U) -> LocalRequest<'c> {
self.req(Method::Put, uri)
}
#[inline(always)]
pub fn post<'c, 'u: 'c, U: Into<URI<'u>>>(&'c self, uri: U) -> LocalRequest<'c> {
self.req(Method::Post, uri)
}
#[inline(always)]
pub fn delete<'c, 'u: 'c, U>(&'c self, uri: U) -> LocalRequest<'c>
where U: Into<URI<'u>>
{
self.req(Method::Delete, uri)
}
#[inline(always)]
pub fn options<'c, 'u: 'c, U>(&'c self, uri: U) -> LocalRequest<'c>
where U: Into<URI<'u>>
{
self.req(Method::Options, uri)
}
#[inline(always)]
pub fn head<'c, 'u: 'c, U>(&'c self, uri: U) -> LocalRequest<'c>
where U: Into<URI<'u>>
{
self.req(Method::Head, uri)
}
#[inline(always)]
pub fn patch<'c, 'u: 'c, U>(&'c self, uri: U) -> LocalRequest<'c>
where U: Into<URI<'u>>
{
self.req(Method::Patch, uri)
}
}

107
lib/src/local/mod.rs Normal file
View File

@ -0,0 +1,107 @@
//! Structures for local dispatching of requests, primarily for testing.
//!
//! This module allows for simple request dispatching against a local,
//! non-networked instance of Rocket. The primary use of this module is to unit
//! and integration test Rocket applications by crafting requests, dispatching
//! them, and verifying the response.
//!
//! # Usage
//!
//! This module contains a [`Client`] structure that is used to create
//! [`LocalRequest`] structures that can be dispatched against a given
//! [`Rocket`] instance. Usage is straightforward:
//!
//! 1. Construct a `Rocket` instance that represents the application.
//!
//! ```rust
//! let rocket = rocket::ignite();
//! # let _ = rocket;
//! ```
//!
//! 2. Construct a `Client` using the `Rocket` instance.
//!
//! ```rust
//! # use rocket::local::Client;
//! # let rocket = rocket::ignite();
//! let client = Client::new(rocket).expect("valid rocket instance");
//! # let _ = client;
//! ```
//!
//! 3. Construct requests using the `Client` instance.
//!
//! ```rust
//! # use rocket::local::Client;
//! # let rocket = rocket::ignite();
//! # let client = Client::new(rocket).unwrap();
//! let req = client.get("/");
//! # let _ = req;
//! ```
//!
//! 3. Dispatch the request to retrieve the response.
//!
//! ```rust
//! # use rocket::local::Client;
//! # let rocket = rocket::ignite();
//! # let client = Client::new(rocket).unwrap();
//! # let req = client.get("/");
//! let response = req.dispatch();
//! # let _ = response;
//! ```
//!
//! All together and in idiomatic fashion, this might look like:
//!
//! ```rust
//! use rocket::local::Client;
//!
//! let client = Client::new(rocket::ignite()).expect("valid rocket");
//! let response = client.post("/")
//! .body("Hello, world!")
//! .dispatch();
//! # let _ = response;
//! ```
//!
//! # Unit/Integration Testing
//!
//! This module can be used to test a Rocket application by constructing
//! requests via `Client` and validating the resulting response. As an example,
//! consider the following complete "Hello, world!" application, with testing.
//!
//! ```rust
//! #![feature(plugin)]
//! #![plugin(rocket_codegen)]
//!
//! extern crate rocket;
//!
//! #[get("/")]
//! fn hello() -> &'static str {
//! "Hello, world!"
//! }
//!
//! # fn main() { }
//! #[cfg(test)]
//! mod test {
//! use super::{rocket, hello};
//! use rocket::local::Client;
//!
//! #[test]
//! fn test_hello_world() {
//! // Construct a client to use for dispatching requests.
//! let client = Client::new(rocket::ignite().mount("/", routes![hello]));
//!
//! // Dispatch a request to 'GET /' and validate the response.
//! let mut response = client.get("/").dispatch();
//! assert_eq!(response.body_string(), Some("Hello, world!".into()));
//! }
//! }
//! ```
//!
//! [`Client`]: /rocket/local/struct.Client.html
//! [`LocalRequest`]: /rocket/local/struct.LocalRequest.html
//! [`Rocket`]: /rocket/struct.Rocket.html
//!
mod request;
mod client;
pub use self::request::{LocalResponse, LocalRequest};
pub use self::client::Client;

265
lib/src/local/request.rs Normal file
View File

@ -0,0 +1,265 @@
use std::fmt;
use std::rc::Rc;
use std::mem::transmute;
use std::net::SocketAddr;
use std::ops::{Deref, DerefMut};
use {Rocket, Request, Response, Data};
use http::{Header, Cookie};
pub struct LocalRequest<'c> {
rocket: &'c Rocket,
ptr: *mut Request<'c>,
request: Rc<Request<'c>>,
data: Vec<u8>
}
pub struct LocalResponse<'c> {
_request: Rc<Request<'c>>,
response: Response<'c>,
}
impl<'c> Deref for LocalResponse<'c> {
type Target = Response<'c>;
#[inline(always)]
fn deref(&self) -> &Response<'c> {
&self.response
}
}
impl<'c> DerefMut for LocalResponse<'c> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Response<'c> {
&mut self.response
}
}
impl<'c> LocalRequest<'c> {
#[inline(always)]
pub fn new(rocket: &'c Rocket, request: Request<'c>) -> LocalRequest<'c> {
let mut req = Rc::new(request);
let ptr = Rc::get_mut(&mut req).unwrap() as *mut Request;
LocalRequest { rocket: rocket, ptr: ptr, request: req, data: vec![] }
}
#[inline]
pub fn inner(&self) -> &Request<'c> {
&*self.request
}
#[inline(always)]
fn request(&mut self) -> &mut Request<'c> {
unsafe { &mut *self.ptr }
}
#[inline(always)]
pub fn dispatch(mut self) -> LocalResponse<'c> {
let req = unsafe { transmute(self.request()) };
let response = self.rocket.dispatch(req, Data::local(self.data));
LocalResponse {
_request: self.request,
response: response
}
}
#[inline(always)]
pub fn mut_dispatch(&mut self) -> LocalResponse<'c> {
let data = ::std::mem::replace(&mut self.data, vec![]);
let req = unsafe { transmute(self.request()) };
let response = self.rocket.dispatch(req, Data::local(data));
LocalResponse {
_request: self.request.clone(),
response: response
}
}
#[inline(always)]
pub fn cloned_dispatch(&self) -> LocalResponse<'c> {
let cloned = (*self.request).clone();
let mut req = LocalRequest::new(self.rocket, cloned);
req.data = self.data.clone();
req.dispatch()
}
/// Add a header to this request.
///
/// # Examples
///
/// Add the Content-Type header:
///
/// ```rust
/// use rocket::local::Client;
/// use rocket::http::ContentType;
///
/// # #[allow(unused_variables)]
/// let client = Client::new(rocket::ignite()).unwrap();
/// let req = client.get("/").header(ContentType::JSON);
/// ```
#[inline]
pub fn header<H: Into<Header<'static>>>(mut self, header: H) -> Self {
self.request().add_header(header.into());
self
}
/// Adds a header to this request without consuming `self`.
///
/// # Examples
///
/// Add the Content-Type header:
///
/// ```rust
/// use rocket::local::Client;
/// use rocket::http::ContentType;
///
/// let client = Client::new(rocket::ignite()).unwrap();
/// let mut req = client.get("/");
/// req.add_header(ContentType::JSON);
/// ```
#[inline]
pub fn add_header<H: Into<Header<'static>>>(&mut self, header: H) {
self.request().add_header(header.into());
}
/// Set the remote address of this request.
///
/// # Examples
///
/// Set the remote address to "8.8.8.8:80":
///
/// ```rust
/// use rocket::local::Client;
///
/// let client = Client::new(rocket::ignite()).unwrap();
/// let address = "8.8.8.8:80".parse().unwrap();
/// let req = client.get("/").remote(address);
/// ```
#[inline]
pub fn remote(mut self, address: SocketAddr) -> Self {
self.request().set_remote(address);
self
}
/// Add a cookie to this request.
///
/// # Examples
///
/// Add `user_id` cookie:
///
/// ```rust
/// use rocket::local::Client;
/// use rocket::http::Cookie;
///
/// let client = Client::new(rocket::ignite()).unwrap();
/// # #[allow(unused_variables)]
/// let req = client.get("/")
/// .cookie(Cookie::new("username", "sb"))
/// .cookie(Cookie::new("user_id", format!("{}", 12)));
/// ```
#[inline]
pub fn cookie(self, cookie: Cookie<'static>) -> Self {
self.request.cookies().add(cookie);
self
}
// TODO: For CGI, we want to be able to set the body to be stdin without
// actually reading everything into a vector. Can we allow that here while
// keeping the simplicity? Looks like it would require us to reintroduce a
// NetStream::Local(Box<Read>) or something like that.
/// Set the body (data) of the request.
///
/// # Examples
///
/// Set the body to be a JSON structure; also sets the Content-Type.
///
/// ```rust
/// use rocket::local::Client;
/// use rocket::http::ContentType;
///
/// let client = Client::new(rocket::ignite()).unwrap();
/// # #[allow(unused_variables)]
/// let req = client.post("/")
/// .header(ContentType::JSON)
/// .body(r#"{ "key": "value", "array": [1, 2, 3], }"#);
/// ```
#[inline]
pub fn body<S: AsRef<[u8]>>(mut self, body: S) -> Self {
self.data = body.as_ref().into();
self
}
}
impl<'c> fmt::Debug for LocalRequest<'c> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.request, f)
}
}
impl<'c> fmt::Debug for LocalResponse<'c> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.response, f)
}
}
// fn test() {
// use local::Client;
// let rocket = Rocket::ignite();
// let res = {
// let mut client = Client::new(rocket).unwrap();
// client.get("/").dispatch()
// };
// // let client = Client::new(rocket).unwrap();
// // let res1 = client.get("/").dispatch();
// // let res2 = client.get("/").dispatch();
// }
// fn test() {
// use local::Client;
// let rocket = Rocket::ignite();
// let res = {
// Client::new(rocket).unwrap()
// .get("/").dispatch();
// };
// // let client = Client::new(rocket).unwrap();
// // let res1 = client.get("/").dispatch();
// // let res2 = client.get("/").dispatch();
// }
// fn test() {
// use local::Client;
// let rocket = Rocket::ignite();
// let client = Client::new(rocket).unwrap();
// let res = {
// let x = client.get("/").dispatch();
// let y = client.get("/").dispatch();
// };
// let x = client;
// }
// fn test() {
// use local::Client;
// let rocket1 = Rocket::ignite();
// let rocket2 = Rocket::ignite();
// let client1 = Client::new(rocket1).unwrap();
// let client2 = Client::new(rocket2).unwrap();
// let res = {
// let mut res1 = client1.get("/");
// res1.set_client(&client2);
// res1
// };
// drop(client1);
// }

View File

@ -6,7 +6,7 @@ use request::Request;
use outcome::{self, IntoOutcome}; use outcome::{self, IntoOutcome};
use outcome::Outcome::*; use outcome::Outcome::*;
use http::{Status, ContentType, Accept, Method, Cookies, Session}; use http::{Status, ContentType, Accept, Method, Cookies};
use http::uri::URI; use http::uri::URI;
/// Type alias for the `Outcome` of a `FromRequest` conversion. /// Type alias for the `Outcome` of a `FromRequest` conversion.
@ -236,14 +236,6 @@ impl<'a, 'r> FromRequest<'a, 'r> for Cookies<'a> {
} }
} }
impl<'a, 'r> FromRequest<'a, 'r> for Session<'a> {
type Error = ();
fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
Success(request.session())
}
}
impl<'a, 'r> FromRequest<'a, 'r> for &'a Accept { impl<'a, 'r> FromRequest<'a, 'r> for &'a Accept {
type Error = (); type Error = ();

View File

@ -4,32 +4,26 @@ use std::fmt;
use std::str; use std::str;
use yansi::Paint; use yansi::Paint;
use state::{Container, Storage}; use state::{Container, Storage};
use error::Error;
use super::{FromParam, FromSegments, FromRequest, Outcome}; use super::{FromParam, FromSegments, FromRequest, Outcome};
use rocket::Rocket;
use router::Route; use router::Route;
use config::{Config, Limits}; use config::{Config, Limits};
use http::uri::{URI, Segments}; use http::uri::{URI, Segments};
use http::{Method, Header, HeaderMap, Cookies, Session, CookieJar}; use error::Error;
use http::{Method, Header, HeaderMap, Cookies, CookieJar};
use http::{RawStr, ContentType, Accept, MediaType}; use http::{RawStr, ContentType, Accept, MediaType};
use http::hyper; use http::hyper;
struct PresetState<'r> { #[derive(Clone)]
// The running Rocket instances configuration.
config: &'r Config,
// The managed state of the running Rocket instance.
state: &'r Container,
}
struct RequestState<'r> { struct RequestState<'r> {
preset: Option<PresetState<'r>>, config: &'r Config,
state: &'r Container,
params: RefCell<Vec<(usize, usize)>>, params: RefCell<Vec<(usize, usize)>>,
route: Cell<Option<&'r Route>>, route: Cell<Option<&'r Route>>,
cookies: RefCell<CookieJar>, cookies: RefCell<CookieJar>,
session: RefCell<CookieJar>,
accept: Storage<Option<Accept>>, accept: Storage<Option<Accept>>,
content_type: Storage<Option<ContentType>>, content_type: Storage<Option<ContentType>>,
} }
@ -41,6 +35,7 @@ struct RequestState<'r> {
/// [FromRequest](/rocket/request/trait.FromRequest.html) implementations. It /// [FromRequest](/rocket/request/trait.FromRequest.html) implementations. It
/// contains all of the information for a given web request except for the body /// contains all of the information for a given web request except for the body
/// data. This includes the HTTP method, URI, cookies, headers, and more. /// data. This includes the HTTP method, URI, cookies, headers, and more.
#[derive(Clone)]
pub struct Request<'r> { pub struct Request<'r> {
method: Method, method: Method,
uri: URI<'r>, uri: URI<'r>,
@ -53,45 +48,45 @@ impl<'r> Request<'r> {
/// Create a new `Request` with the given `method` and `uri`. The `uri` /// Create a new `Request` with the given `method` and `uri`. The `uri`
/// parameter can be of any type that implements `Into<URI>` including /// parameter can be of any type that implements `Into<URI>` including
/// `&str` and `String`; it must be a valid absolute URI. /// `&str` and `String`; it must be a valid absolute URI.
///
/// # Example
///
/// ```rust
/// use rocket::Request;
/// use rocket::http::Method;
///
/// # #[allow(unused_variables)]
/// let request = Request::new(Method::Get, "/uri");
/// ```
#[inline(always)] #[inline(always)]
pub fn new<'s: 'r, U: Into<URI<'s>>>(method: Method, uri: U) -> Request<'r> { pub(crate) fn new<'s: 'r, U: Into<URI<'s>>>(rocket: &'r Rocket,
method: Method,
uri: U) -> Request<'r> {
Request { Request {
method: method, method: method,
uri: uri.into(), uri: uri.into(),
headers: HeaderMap::new(), headers: HeaderMap::new(),
remote: None, remote: None,
state: RequestState { state: RequestState {
preset: None, config: &rocket.config,
state: &rocket.state,
route: Cell::new(None), route: Cell::new(None),
params: RefCell::new(Vec::new()), params: RefCell::new(Vec::new()),
cookies: RefCell::new(CookieJar::new()), cookies: RefCell::new(CookieJar::new()),
session: RefCell::new(CookieJar::new()),
accept: Storage::new(), accept: Storage::new(),
content_type: Storage::new(), content_type: Storage::new(),
} }
} }
} }
#[doc(hidden)]
pub fn example<F: Fn(&mut Request)>(method: Method, uri: &str, f: F) {
let rocket = Rocket::custom(Config::development().unwrap(), true);
let mut request = Request::new(&rocket, method, uri);
f(&mut request);
}
/// Retrieve the method from `self`. /// Retrieve the method from `self`.
/// ///
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rocket::Request; /// # use rocket::Request;
/// use rocket::http::Method; /// use rocket::http::Method;
/// ///
/// let request = Request::new(Method::Get, "/uri"); /// # Request::example(Method::Get, "/uri", |request| {
/// assert_eq!(request.method(), Method::Get); /// assert_eq!(request.method(), Method::Get);
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn method(&self) -> Method { pub fn method(&self) -> Method {
@ -103,14 +98,15 @@ impl<'r> Request<'r> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rocket::Request; /// # use rocket::Request;
/// use rocket::http::Method; /// use rocket::http::Method;
/// ///
/// let mut request = Request::new(Method::Get, "/uri"); /// # Request::example(Method::Get, "/uri", |request| {
/// assert_eq!(request.method(), Method::Get); /// assert_eq!(request.method(), Method::Get);
/// ///
/// request.set_method(Method::Post); /// request.set_method(Method::Post);
/// assert_eq!(request.method(), Method::Post); /// assert_eq!(request.method(), Method::Post);
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_method(&mut self, method: Method) { pub fn set_method(&mut self, method: Method) {
@ -122,11 +118,11 @@ impl<'r> Request<'r> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rocket::Request; /// # use rocket::Request;
/// use rocket::http::Method; /// # use rocket::http::Method;
/// /// # Request::example(Method::Get, "/uri", |request| {
/// let request = Request::new(Method::Get, "/uri");
/// assert_eq!(request.uri().as_str(), "/uri"); /// assert_eq!(request.uri().as_str(), "/uri");
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn uri(&self) -> &URI { pub fn uri(&self) -> &URI {
@ -140,13 +136,12 @@ impl<'r> Request<'r> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rocket::Request; /// # use rocket::Request;
/// use rocket::http::Method; /// # use rocket::http::Method;
/// /// # Request::example(Method::Get, "/uri", |mut request| {
/// let mut request = Request::new(Method::Get, "/uri");
///
/// request.set_uri("/hello/Sergio?type=greeting"); /// request.set_uri("/hello/Sergio?type=greeting");
/// assert_eq!(request.uri().as_str(), "/hello/Sergio?type=greeting"); /// assert_eq!(request.uri().as_str(), "/hello/Sergio?type=greeting");
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_uri<'u: 'r, U: Into<URI<'u>>>(&mut self, uri: U) { pub fn set_uri<'u: 'r, U: Into<URI<'u>>>(&mut self, uri: U) {
@ -161,11 +156,11 @@ impl<'r> Request<'r> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rocket::Request; /// # use rocket::Request;
/// use rocket::http::Method; /// # use rocket::http::Method;
/// /// # Request::example(Method::Get, "/uri", |request| {
/// let request = Request::new(Method::Get, "/uri");
/// assert!(request.remote().is_none()); /// assert!(request.remote().is_none());
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn remote(&self) -> Option<SocketAddr> { pub fn remote(&self) -> Option<SocketAddr> {
@ -179,17 +174,17 @@ impl<'r> Request<'r> {
/// Set the remote address to be 127.0.0.1:8000: /// Set the remote address to be 127.0.0.1:8000:
/// ///
/// ```rust /// ```rust
/// use rocket::Request; /// # use rocket::Request;
/// use rocket::http::Method; /// # use rocket::http::Method;
/// use std::net::{SocketAddr, IpAddr, Ipv4Addr}; /// use std::net::{SocketAddr, IpAddr, Ipv4Addr};
/// ///
/// let mut request = Request::new(Method::Get, "/uri"); /// # Request::example(Method::Get, "/uri", |mut request| {
///
/// let (ip, port) = (IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8000); /// let (ip, port) = (IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8000);
/// let localhost = SocketAddr::new(ip, port); /// let localhost = SocketAddr::new(ip, port);
/// request.set_remote(localhost); /// request.set_remote(localhost);
/// ///
/// assert_eq!(request.remote(), Some(localhost)); /// assert_eq!(request.remote(), Some(localhost));
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_remote(&mut self, address: SocketAddr) { pub fn set_remote(&mut self, address: SocketAddr) {
@ -202,12 +197,12 @@ impl<'r> Request<'r> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rocket::Request; /// # use rocket::Request;
/// use rocket::http::Method; /// # use rocket::http::Method;
/// /// # Request::example(Method::Get, "/uri", |request| {
/// let request = Request::new(Method::Get, "/uri");
/// let header_map = request.headers(); /// let header_map = request.headers();
/// assert!(header_map.is_empty()); /// assert!(header_map.is_empty());
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn headers(&self) -> &HeaderMap<'r> { pub fn headers(&self) -> &HeaderMap<'r> {
@ -219,18 +214,20 @@ impl<'r> Request<'r> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rocket::Request; /// # use rocket::Request;
/// use rocket::http::{Method, ContentType}; /// # use rocket::http::Method;
/// use rocket::http::ContentType;
/// ///
/// let mut request = Request::new(Method::Get, "/uri"); /// # Request::example(Method::Get, "/uri", |mut request| {
/// assert!(request.headers().is_empty()); /// assert!(request.headers().is_empty());
/// ///
/// request.add_header(ContentType::HTML); /// request.add_header(ContentType::HTML);
/// assert!(request.headers().contains("Content-Type")); /// assert!(request.headers().contains("Content-Type"));
/// assert_eq!(request.headers().len(), 1); /// assert_eq!(request.headers().len(), 1);
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn add_header<H: Into<Header<'r>>>(&mut self, header: H) { pub fn add_header<'h: 'r, H: Into<Header<'h>>>(&mut self, header: H) {
self.headers.add(header.into()); self.headers.add(header.into());
} }
@ -240,10 +237,11 @@ impl<'r> Request<'r> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rocket::Request; /// # use rocket::Request;
/// use rocket::http::{Method, ContentType}; /// # use rocket::http::Method;
/// use rocket::http::ContentType;
/// ///
/// let mut request = Request::new(Method::Get, "/uri"); /// # Request::example(Method::Get, "/uri", |mut request| {
/// assert!(request.headers().is_empty()); /// assert!(request.headers().is_empty());
/// ///
/// request.add_header(ContentType::Any); /// request.add_header(ContentType::Any);
@ -251,13 +249,14 @@ impl<'r> Request<'r> {
/// ///
/// request.replace_header(ContentType::PNG); /// request.replace_header(ContentType::PNG);
/// assert_eq!(request.headers().get_one("Content-Type"), Some("image/png")); /// assert_eq!(request.headers().get_one("Content-Type"), Some("image/png"));
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn replace_header<H: Into<Header<'r>>>(&mut self, header: H) { pub fn replace_header<'h: 'r, H: Into<Header<'h>>>(&mut self, header: H) {
self.headers.replace(header.into()); self.headers.replace(header.into());
} }
/// Returns a borrow to the cookies in `self`. /// Returns a wrapped borrow to the cookies in `self`.
/// ///
/// Note that `Cookies` implements internal mutability, so this method /// Note that `Cookies` implements internal mutability, so this method
/// allows you to get _and_ set cookies in `self`. /// allows you to get _and_ set cookies in `self`.
@ -267,17 +266,18 @@ impl<'r> Request<'r> {
/// Add a new cookie to a request's cookies: /// Add a new cookie to a request's cookies:
/// ///
/// ```rust /// ```rust
/// use rocket::Request; /// # use rocket::Request;
/// use rocket::http::{Cookie, Method}; /// # use rocket::http::Method;
/// use rocket::http::Cookie;
/// ///
/// let request = Request::new(Method::Get, "/uri"); /// # Request::example(Method::Get, "/uri", |mut request| {
/// request.cookies().add(Cookie::new("key", "val")); /// request.cookies().add(Cookie::new("key", "val"));
/// request.cookies().add(Cookie::new("ans", format!("life: {}", 38 + 4))); /// request.cookies().add(Cookie::new("ans", format!("life: {}", 38 + 4)));
/// # });
/// ``` /// ```
#[inline]
pub fn cookies(&self) -> Cookies { pub fn cookies(&self) -> Cookies {
match self.state.cookies.try_borrow_mut() { match self.state.cookies.try_borrow_mut() {
Ok(jar) => Cookies::new(jar), Ok(jar) => Cookies::new(jar, self.state.config.secret_key()),
Err(_) => { Err(_) => {
error_!("Multiple `Cookies` instances are active at once."); error_!("Multiple `Cookies` instances are active at once.");
info_!("An instance of `Cookies` must be dropped before another \ info_!("An instance of `Cookies` must be dropped before another \
@ -288,21 +288,6 @@ impl<'r> Request<'r> {
} }
} }
#[inline]
pub fn session(&self) -> Session {
let key = self.preset().config.secret_key();
match self.state.session.try_borrow_mut() {
Ok(jar) => Session::new(jar, key),
Err(_) => {
error_!("Multiple `Session` instances are active at once.");
info_!("An instance of `Session` must be dropped before another \
can be retrieved.");
warn_!("The retrieved `Session` instance will be empty.");
Session::empty(key)
}
}
}
/// Returns the Content-Type header of `self`. If the header is not present, /// Returns the Content-Type header of `self`. If the header is not present,
/// returns `None`. The Content-Type header is cached after the first call /// returns `None`. The Content-Type header is cached after the first call
/// to this function. As a result, subsequent calls will always return the /// to this function. As a result, subsequent calls will always return the
@ -311,20 +296,22 @@ impl<'r> Request<'r> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rocket::Request; /// # use rocket::Request;
/// use rocket::http::{Method, ContentType}; /// # use rocket::http::Method;
/// /// # Request::example(Method::Get, "/uri", |mut request| {
/// let mut request = Request::new(Method::Get, "/uri");
/// assert_eq!(request.content_type(), None); /// assert_eq!(request.content_type(), None);
/// # });
/// ``` /// ```
/// ///
/// ```rust /// ```rust
/// use rocket::Request; /// # use rocket::Request;
/// use rocket::http::{Method, ContentType}; /// # use rocket::http::Method;
/// use rocket::http::ContentType;
/// ///
/// let mut request = Request::new(Method::Get, "/uri"); /// # Request::example(Method::Get, "/uri", |mut request| {
/// request.add_header(ContentType::JSON); /// request.add_header(ContentType::JSON);
/// assert_eq!(request.content_type(), Some(&ContentType::JSON)); /// assert_eq!(request.content_type(), Some(&ContentType::JSON));
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn content_type(&self) -> Option<&ContentType> { pub fn content_type(&self) -> Option<&ContentType> {
@ -361,7 +348,7 @@ impl<'r> Request<'r> {
/// Get the limits. /// Get the limits.
pub fn limits(&self) -> &'r Limits { pub fn limits(&self) -> &'r Limits {
&self.preset().config.limits &self.state.config.limits
} }
/// Get the current route, if any. /// Get the current route, if any.
@ -463,17 +450,6 @@ impl<'r> Request<'r> {
Some(Segments(&path[i..j])) Some(Segments(&path[i..j]))
} }
#[inline(always)]
fn preset(&self) -> &PresetState<'r> {
match self.state.preset {
Some(ref state) => state,
None => {
error_!("Internal Rocket error: preset state is unset!");
panic!("Please report this error to the GitHub issue tracker.");
}
}
}
/// Set `self`'s parameters given that the route used to reach this request /// Set `self`'s parameters given that the route used to reach this request
/// was `route`. This should only be used internally by `Rocket` as improper /// was `route`. This should only be used internally by `Rocket` as improper
/// use may result in out of bounds indexing. /// use may result in out of bounds indexing.
@ -490,12 +466,6 @@ impl<'r> Request<'r> {
self.state.cookies = RefCell::new(jar); self.state.cookies = RefCell::new(jar);
} }
/// Replace all of the session cookie in `self` with those in `jar`.
#[inline]
pub(crate) fn set_session(&mut self, jar: CookieJar) {
self.state.session = RefCell::new(jar);
}
/// Try to derive some guarded value from `self`. /// Try to derive some guarded value from `self`.
#[inline(always)] #[inline(always)]
pub fn guard<'a, T: FromRequest<'a, 'r>>(&'a self) -> Outcome<T, T::Error> { pub fn guard<'a, T: FromRequest<'a, 'r>>(&'a self) -> Outcome<T, T::Error> {
@ -505,17 +475,12 @@ impl<'r> Request<'r> {
/// Get the managed state T, if it exists. For internal use only! /// Get the managed state T, if it exists. For internal use only!
#[inline(always)] #[inline(always)]
pub(crate) fn get_state<T: Send + Sync + 'static>(&self) -> Option<&'r T> { pub(crate) fn get_state<T: Send + Sync + 'static>(&self) -> Option<&'r T> {
self.preset().state.try_get() self.state.state.try_get()
}
/// Set the precomputed state. For internal use only!
#[inline(always)]
pub(crate) fn set_preset(&mut self, config: &'r Config, state: &'r Container) {
self.state.preset = Some(PresetState { config, state });
} }
/// Convert from Hyper types into a Rocket Request. /// Convert from Hyper types into a Rocket Request.
pub(crate) fn from_hyp(h_method: hyper::Method, pub(crate) fn from_hyp(rocket: &'r Rocket,
h_method: hyper::Method,
h_headers: hyper::header::Headers, h_headers: hyper::header::Headers,
h_uri: hyper::RequestUri, h_uri: hyper::RequestUri,
h_addr: SocketAddr, h_addr: SocketAddr,
@ -533,13 +498,12 @@ impl<'r> Request<'r> {
}; };
// Construct the request object. // Construct the request object.
let mut request = Request::new(method, uri); let mut request = Request::new(rocket, method, uri);
request.set_remote(h_addr); request.set_remote(h_addr);
// Set the request cookies, if they exist. // Set the request cookies, if they exist.
if let Some(cookie_headers) = h_headers.get_raw("Cookie") { if let Some(cookie_headers) = h_headers.get_raw("Cookie") {
let mut cookie_jar = CookieJar::new(); let mut cookie_jar = CookieJar::new();
let mut session_jar = CookieJar::new();
for header in cookie_headers { for header in cookie_headers {
let raw_str = match ::std::str::from_utf8(header) { let raw_str = match ::std::str::from_utf8(header) {
Ok(string) => string, Ok(string) => string,
@ -547,16 +511,13 @@ impl<'r> Request<'r> {
}; };
for cookie_str in raw_str.split(";").map(|s| s.trim()) { for cookie_str in raw_str.split(";").map(|s| s.trim()) {
if let Some(cookie) = Session::parse_cookie(cookie_str) { if let Some(cookie) = Cookies::parse_cookie(cookie_str) {
session_jar.add_original(cookie);
} else if let Some(cookie) = Cookies::parse_cookie(cookie_str) {
cookie_jar.add_original(cookie); cookie_jar.add_original(cookie);
} }
} }
} }
request.set_cookies(cookie_jar); request.set_cookies(cookie_jar);
request.set_session(session_jar);
} }
// Set the rest of the headers. // Set the rest of the headers.
@ -575,6 +536,17 @@ impl<'r> Request<'r> {
} }
} }
impl<'r> fmt::Debug for Request<'r> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Request")
.field("method", &self.method)
.field("uri", &self.uri)
.field("headers", &self.headers())
.field("remote", &self.remote())
.finish()
}
}
impl<'r> fmt::Display for Request<'r> { impl<'r> fmt::Display for Request<'r> {
/// Pretty prints a Request. This is primarily used by Rocket's logging /// Pretty prints a Request. This is primarily used by Rocket's logging
/// infrastructure. /// infrastructure.

View File

@ -1,7 +1,7 @@
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::collections::HashMap; use std::collections::HashMap;
use Request; use {Rocket, Request, Config};
use http::hyper; use http::hyper;
macro_rules! assert_headers { macro_rules! assert_headers {
@ -20,7 +20,9 @@ macro_rules! assert_headers {
$(expected.entry($key).or_insert(vec![]).append(&mut vec![$($value),+]);)+ $(expected.entry($key).or_insert(vec![]).append(&mut vec![$($value),+]);)+
// Dispatch the request and check that the headers are what we expect. // Dispatch the request and check that the headers are what we expect.
let req = Request::from_hyp(h_method, h_headers, h_uri, h_addr).unwrap(); let config = Config::development().unwrap();
let r = Rocket::custom(config, true);
let req = Request::from_hyp(&r, h_method, h_headers, h_uri, h_addr).unwrap();
let actual_headers = req.headers(); let actual_headers = req.headers();
for (key, values) in expected.iter() { for (key, values) in expected.iter() {
let actual: Vec<_> = actual_headers.get(key).collect(); let actual: Vec<_> = actual_headers.get(key).collect();

View File

@ -903,7 +903,29 @@ impl<'r> Response<'r> {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn body_string(&mut self) -> Option<String> { pub fn body_string(&mut self) -> Option<String> {
self.take_body().and_then(|b| b.into_string()) self.take_body().and_then(Body::into_string)
}
/// Consumes `self's` body and reads it into a `Vec` of `u8` bytes. If
/// `self` doesn't have a body or reading fails returns `None`. Note that
/// `self`'s `body` is consumed after a call to this method.
///
/// # Example
///
/// ```rust
/// use std::io::Cursor;
/// use rocket::Response;
///
/// let mut response = Response::new();
/// assert!(response.body().is_none());
///
/// response.set_sized_body(Cursor::new("hi!"));
/// assert_eq!(response.body_bytes(), Some(vec![0x68, 0x69, 0x21]));
/// assert!(response.body().is_none());
/// ```
#[inline(always)]
pub fn body_bytes(&mut self) -> Option<Vec<u8>> {
self.take_body().and_then(Body::into_bytes)
} }
/// Moves the body of `self` out and returns it, if there is one, leaving no /// Moves the body of `self` out and returns it, if there is one, leaving no

View File

@ -21,19 +21,19 @@ use outcome::Outcome;
use error::{Error, LaunchError, LaunchErrorKind}; use error::{Error, LaunchError, LaunchErrorKind};
use fairing::{Fairing, Fairings}; use fairing::{Fairing, Fairings};
use http::{Method, Status, Header, Session}; use http::{Method, Status, Header};
use http::hyper::{self, header}; use http::hyper::{self, header};
use http::uri::URI; use http::uri::URI;
/// The main `Rocket` type: used to mount routes and catchers and launch the /// The main `Rocket` type: used to mount routes and catchers and launch the
/// application. /// application.
pub struct Rocket { pub struct Rocket {
config: Config, pub(crate) config: Config,
router: Router, router: Router,
default_catchers: HashMap<u16, Catcher>, default_catchers: HashMap<u16, Catcher>,
catchers: HashMap<u16, Catcher>, catchers: HashMap<u16, Catcher>,
state: Container, pub(crate) state: Container,
fairings: Fairings fairings: Fairings,
} }
#[doc(hidden)] #[doc(hidden)]
@ -50,11 +50,12 @@ impl hyper::Handler for Rocket {
let (h_addr, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct(); let (h_addr, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct();
// Convert the Hyper request into a Rocket request. // Convert the Hyper request into a Rocket request.
let mut req = match Request::from_hyp(h_method, h_headers, h_uri, h_addr) { let req_res = Request::from_hyp(self, h_method, h_headers, h_uri, h_addr);
let mut req = match req_res {
Ok(req) => req, Ok(req) => req,
Err(e) => { Err(e) => {
error!("Bad incoming request: {}", e); error!("Bad incoming request: {}", e);
let dummy = Request::new(Method::Get, URI::new("<unknown>")); let dummy = Request::new(self, Method::Get, URI::new("<unknown>"));
let r = self.handle_error(Status::InternalServerError, &dummy); let r = self.handle_error(Status::InternalServerError, &dummy);
return self.issue_response(r, res); return self.issue_response(r, res);
} }
@ -209,16 +210,12 @@ impl Rocket {
} }
} }
// TODO: Explain this `UnsafeCell` business at a macro level.
#[inline] #[inline]
pub(crate) fn dispatch<'s, 'r>(&'s self, pub(crate) fn dispatch<'s, 'r>(&'s self,
request: &'r mut Request<'s>, request: &'r mut Request<'s>,
data: Data) -> Response<'r> { data: Data) -> Response<'r> {
info!("{}:", request); info!("{}:", request);
// Inform the request about all of the precomputed state.
request.set_preset(&self.config, &self.state);
// Do a bit of preprocessing before routing; run the attached fairings. // Do a bit of preprocessing before routing; run the attached fairings.
self.preprocess_request(request, &data); self.preprocess_request(request, &data);
self.fairings.handle_request(request, &data); self.fairings.handle_request(request, &data);
@ -226,16 +223,11 @@ impl Rocket {
// Route the request to get a response. // Route the request to get a response.
let mut response = match self.route(request, data) { let mut response = match self.route(request, data) {
Outcome::Success(mut response) => { Outcome::Success(mut response) => {
// A user's route responded! Set the regular cookies. // A user's route responded! Set the cookies.
for cookie in request.cookies().delta() { for cookie in request.cookies().delta() {
response.adjoin_header(cookie); response.adjoin_header(cookie);
} }
// And now the session cookies.
for cookie in request.session().delta() {
response.adjoin_header(Session::header_for(cookie));
}
response response
} }
Outcome::Forward(data) => { Outcome::Forward(data) => {

View File

@ -131,6 +131,8 @@ mod tests {
use std::str::FromStr; use std::str::FromStr;
use super::Collider; use super::Collider;
use rocket::Rocket;
use config::Config;
use request::Request; use request::Request;
use data::Data; use data::Data;
use handler::Outcome; use handler::Outcome;
@ -368,7 +370,8 @@ mod tests {
fn req_route_mt_collide<S1, S2>(m: Method, mt1: S1, mt2: S2) -> bool fn req_route_mt_collide<S1, S2>(m: Method, mt1: S1, mt2: S2) -> bool
where S1: Into<Option<&'static str>>, S2: Into<Option<&'static str>> where S1: Into<Option<&'static str>>, S2: Into<Option<&'static str>>
{ {
let mut req = Request::new(m, "/"); let rocket = Rocket::custom(Config::development().unwrap(), true);
let mut req = Request::new(&rocket, m, "/");
if let Some(mt_str) = mt1.into() { if let Some(mt_str) = mt1.into() {
if m.supports_payload() { if m.supports_payload() {
req.replace_header(mt_str.parse::<ContentType>().unwrap()); req.replace_header(mt_str.parse::<ContentType>().unwrap());
@ -425,7 +428,8 @@ mod tests {
} }
fn req_route_path_collide(a: &'static str, b: &'static str) -> bool { fn req_route_path_collide(a: &'static str, b: &'static str) -> bool {
let req = Request::new(Get, a.to_string()); let rocket = Rocket::custom(Config::development().unwrap(), true);
let req = Request::new(&rocket, Get, a.to_string());
let route = Route::ranked(0, Get, b.to_string(), dummy_handler); let route = Route::ranked(0, Get, b.to_string(), dummy_handler);
route.collides_with(&req) route.collides_with(&req)
} }

View File

@ -70,6 +70,8 @@ impl Router {
mod test { mod test {
use super::{Router, Route}; use super::{Router, Route};
use rocket::Rocket;
use config::Config;
use http::Method; use http::Method;
use http::Method::*; use http::Method::*;
use http::uri::URI; use http::uri::URI;
@ -159,7 +161,8 @@ mod test {
} }
fn route<'a>(router: &'a Router, method: Method, uri: &str) -> Option<&'a Route> { fn route<'a>(router: &'a Router, method: Method, uri: &str) -> Option<&'a Route> {
let request = Request::new(method, URI::new(uri)); let rocket = Rocket::custom(Config::development().unwrap(), true);
let request = Request::new(&rocket, method, URI::new(uri));
let matches = router.route(&request); let matches = router.route(&request);
if matches.len() > 0 { if matches.len() > 0 {
Some(matches[0]) Some(matches[0])
@ -169,7 +172,8 @@ mod test {
} }
fn matches<'a>(router: &'a Router, method: Method, uri: &str) -> Vec<&'a Route> { fn matches<'a>(router: &'a Router, method: Method, uri: &str) -> Vec<&'a Route> {
let request = Request::new(method, URI::new(uri)); let rocket = Rocket::custom(Config::development().unwrap(), true);
let request = Request::new(&rocket, method, URI::new(uri));
router.route(&request) router.route(&request)
} }

View File

@ -1,274 +0,0 @@
//! A tiny module for testing Rocket applications.
//!
//! # Usage
//!
//! The testing methadology is simple:
//!
//! 1. Construct a `Rocket` instance.
//! 2. Construct a request.
//! 3. Dispatch the request using the Rocket instance.
//! 4. Inspect, validate, and verify the response.
//!
//! ## Construct a `Rocket` Instance
//!
//! Constructing a `Rocket` instance for testing is identical to constructing
//! one for launching, except you should not call the `launch` method. That is,
//! use `rocket::ignite`, then mount routes and catchers. That's it!
//!
//! ## Construct a (Mock)Request
//!
//! The [MockRequest](struct.MockRequest.html) object enables the creation of an
//! HTTP request without using any networking. A `MockRequest` object is
//! constructed using the builder pattern. For example, the following code
//! builds a request for submitting a login form with three fields:
//!
//! ```rust
//! use rocket::http::Method::*;
//! use rocket::http::ContentType;
//! use rocket::testing::MockRequest;
//!
//! let (username, password, age) = ("user", "password", 32);
//! MockRequest::new(Post, "/login")
//! .header(ContentType::Form)
//! .body(&format!("username={}&password={}&age={}", username, password, age));
//! ```
//!
//! ## Dispatch and Validate
//!
//! Finally, requests can be dispatched using the
//! [dispatch_with](struct.MockRequest.html#method.dispatch_with) method on the
//! contructed `MockRequest` instance. The method returns the body of the
//! response. At present, the API does not allow for headers in the response to
//! be examined.
//!
//! # Example
//!
//! The following is an example of a complete application with testing.
//!
//! ```rust
//! #![feature(plugin)]
//! #![plugin(rocket_codegen)]
//!
//! extern crate rocket;
//!
//! #[get("/")]
//! fn hello() -> &'static str {
//! "Hello, world!"
//! }
//!
//! # fn main() { }
//! #[cfg(test)]
//! mod test {
//! use super::rocket;
//! use rocket::testing::MockRequest;
//! use rocket::http::Method::*;
//!
//! #[test]
//! fn test_hello_world() {
//! let rocket = rocket::ignite().mount("/", routes![super::hello]);
//! let mut req = MockRequest::new(Get, "/");
//! let mut response = req.dispatch_with(&rocket);
//!
//! // Check that the body contains the string we expect.
//! assert_eq!(response.body_string(), Some("Hello, world!".into()));
//! }
//! }
//! ```
use ::{Rocket, Request, Response, Data};
use error::LaunchError;
use http::{Method, Status, Header, Cookie};
use std::net::SocketAddr;
/// A type for mocking requests for testing Rocket applications.
pub struct MockRequest<'r> {
prechecked: Option<&'r Rocket>,
request: Request<'r>,
data: Data
}
impl<'r> MockRequest<'r> {
/// Constructs a new mocked request with the given `method` and `uri`.
#[inline]
pub fn new<S: AsRef<str>>(method: Method, uri: S) -> Self {
MockRequest {
prechecked: None,
request: Request::new(method, uri.as_ref().to_string()),
data: Data::local(vec![])
}
}
/// Add a header to this request.
///
/// # Examples
///
/// Add the Content-Type header:
///
/// ```rust
/// use rocket::http::Method::*;
/// use rocket::testing::MockRequest;
/// use rocket::http::ContentType;
///
/// # #[allow(unused_variables)]
/// let req = MockRequest::new(Get, "/").header(ContentType::JSON);
/// ```
#[inline]
pub fn header<H: Into<Header<'static>>>(mut self, header: H) -> Self {
self.request.add_header(header.into());
self
}
/// Adds a header to this request without consuming `self`.
///
/// # Examples
///
/// Add the Content-Type header:
///
/// ```rust
/// use rocket::http::Method::*;
/// use rocket::testing::MockRequest;
/// use rocket::http::ContentType;
///
/// let mut req = MockRequest::new(Get, "/");
/// req.add_header(ContentType::JSON);
/// ```
#[inline]
pub fn add_header<H: Into<Header<'static>>>(&mut self, header: H) {
self.request.add_header(header.into());
}
/// Set the remote address of this request.
///
/// # Examples
///
/// Set the remote address to "8.8.8.8:80":
///
/// ```rust
/// use rocket::http::Method::*;
/// use rocket::testing::MockRequest;
///
/// let address = "8.8.8.8:80".parse().unwrap();
/// # #[allow(unused_variables)]
/// let req = MockRequest::new(Get, "/").remote(address);
/// ```
#[inline]
pub fn remote(mut self, address: SocketAddr) -> Self {
self.request.set_remote(address);
self
}
/// Add a cookie to this request.
///
/// # Examples
///
/// Add `user_id` cookie:
///
/// ```rust
/// use rocket::http::Method::*;
/// use rocket::testing::MockRequest;
/// use rocket::http::Cookie;
///
/// # #[allow(unused_variables)]
/// let req = MockRequest::new(Get, "/")
/// .cookie(Cookie::new("username", "sb"))
/// .cookie(Cookie::new("user_id", format!("{}", 12)));
/// ```
#[inline]
pub fn cookie(self, cookie: Cookie<'static>) -> Self {
self.request.cookies().add(cookie);
self
}
/// Set the body (data) of the request.
///
/// # Examples
///
/// Set the body to be a JSON structure; also sets the Content-Type.
///
/// ```rust
/// use rocket::http::Method::*;
/// use rocket::testing::MockRequest;
/// use rocket::http::ContentType;
///
/// # #[allow(unused_variables)]
/// let req = MockRequest::new(Post, "/")
/// .header(ContentType::JSON)
/// .body(r#"{ "key": "value", "array": [1, 2, 3], }"#);
/// ```
#[inline]
pub fn body<S: AsRef<[u8]>>(mut self, body: S) -> Self {
self.data = Data::local(body.as_ref().into());
self
}
/// Returns `Some` if there an error checking `rocket`. Returns `None` if
/// there's no error and dispatching can continue.
fn precheck(&mut self, rocket: &'r Rocket) -> Option<LaunchError> {
// Check if we've already prechecked some `Rocket` instance.
if let Some(r) = self.prechecked {
// Check if the one we've prechecked is indeed `rocket`. This does a
// straight pointer comparison. If they're the same, then we know
// that the instance must not have changed since we kept an
// immutable borrow to it from the precheck.
if (r as *const Rocket) == (rocket as *const Rocket) {
return None
}
}
if let Some(err) = rocket.prelaunch_check() {
return Some(err);
}
self.prechecked = Some(rocket);
None
}
/// Dispatch this request using a given instance of Rocket.
///
/// It is possible that the supplied `rocket` instance contains malformed
/// input such as colliding or invalid routes or failed fairings. When this
/// is the case, the returned `Response` will contain a status of
/// `InternalServerError`, and the body will contain the error that
/// occurred. In all other cases, the returned `Response` will be that of
/// the application.
///
/// # Examples
///
/// Dispatch to a Rocket instance with the `"Hello, world!"` example
/// mounted:
///
/// ```rust
/// # #![feature(plugin)]
/// # #![plugin(rocket_codegen)]
/// # extern crate rocket;
/// #
/// #[get("/")]
/// fn hello() -> &'static str {
/// "Hello, world!"
/// }
///
/// use rocket::testing::MockRequest;
/// use rocket::http::Method::*;
///
/// # fn main() {
/// let rocket = rocket::ignite().mount("/", routes![hello]);
/// let mut req = MockRequest::new(Get, "/");
/// let mut response = req.dispatch_with(&rocket);
///
/// assert_eq!(response.body_string(), Some("Hello, world!".into()));
/// # }
/// ```
#[inline]
pub fn dispatch_with<'s>(&'s mut self, rocket: &'r Rocket) -> Response<'s> {
if let Some(err) = self.precheck(rocket) {
return Response::build()
.status(Status::InternalServerError)
.sized_body(::std::io::Cursor::new(err.to_string()))
.finalize()
}
let data = ::std::mem::replace(&mut self.data, Data::local(vec![]));
rocket.dispatch(&mut self.request, data)
}
}

View File

@ -18,31 +18,28 @@ fn bug(form_data: Form<FormData>) -> &'static str {
mod tests { mod tests {
use super::*; use super::*;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::{Status, ContentType}; use rocket::http::{Status, ContentType};
#[test] #[test]
fn method_eval() { fn method_eval() {
let rocket = rocket::ignite().mount("/", routes![bug]); let client = Client::new(rocket::ignite().mount("/", routes![bug])).unwrap();
let mut response = client.post("/")
let mut req = MockRequest::new(Post, "/")
.header(ContentType::Form) .header(ContentType::Form)
.body("_method=patch&form_data=Form+data"); .body("_method=patch&form_data=Form+data")
.dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some("OK".into())); assert_eq!(response.body_string(), Some("OK".into()));
} }
#[test] #[test]
fn get_passes_through() { fn get_passes_through() {
let rocket = rocket::ignite().mount("/", routes![bug]); let client = Client::new(rocket::ignite().mount("/", routes![bug])).unwrap();
let response = client.get("/")
let mut req = MockRequest::new(Get, "/")
.header(ContentType::Form) .header(ContentType::Form)
.body("_method=patch&form_data=Form+data"); .body("_method=patch&form_data=Form+data")
.dispatch();
let response = req.dispatch_with(&rocket);
assert_eq!(response.status(), Status::NotFound); assert_eq!(response.status(), Status::NotFound);
} }
} }

View File

@ -17,18 +17,17 @@ fn bug(form_data: Form<FormData>) -> String {
mod tests { mod tests {
use super::*; use super::*;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::ContentType; use rocket::http::ContentType;
use rocket::http::Status; use rocket::http::Status;
fn check_decoding(raw: &str, decoded: &str) { fn check_decoding(raw: &str, decoded: &str) {
let rocket = rocket::ignite().mount("/", routes![bug]); let client = Client::new(rocket::ignite().mount("/", routes![bug])).unwrap();
let mut req = MockRequest::new(Post, "/") let mut response = client.post("/")
.header(ContentType::Form) .header(ContentType::Form)
.body(format!("form_data={}", raw)); .body(format!("form_data={}", raw))
.dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(Some(decoded.to_string()), response.body_string()); assert_eq!(Some(decoded.to_string()), response.body_string());
} }

View File

@ -24,8 +24,7 @@ mod tests {
use super::*; use super::*;
use rocket::Route; use rocket::Route;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::{Status, ContentType}; use rocket::http::{Status, ContentType};
use rocket::response::Body; use rocket::response::Body;
@ -35,12 +34,10 @@ mod tests {
#[test] #[test]
fn auto_head() { fn auto_head() {
let rocket = rocket::ignite().mount("/", routes()); let client = Client::new(rocket::ignite().mount("/", routes())).unwrap();
let mut response = client.head("/").dispatch();
let mut req = MockRequest::new(Head, "/");
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
if let Some(body) = response.body() { if let Some(body) = response.body() {
match body { match body {
Body::Sized(_, n) => assert_eq!(n, "Hello, world!".len() as u64), Body::Sized(_, n) => assert_eq!(n, "Hello, world!".len() as u64),
@ -49,27 +46,23 @@ mod tests {
assert_eq!(body.into_string(), Some("".to_string())); assert_eq!(body.into_string(), Some("".to_string()));
} else { } else {
panic!("Expected an empty body!") panic!("Expected a non-empty body!")
} }
let content_type: Vec<_> = response.headers().get("Content-Type").collect(); let content_type: Vec<_> = response.headers().get("Content-Type").collect();
assert_eq!(content_type, vec![ContentType::Plain.to_string()]); assert_eq!(content_type, vec![ContentType::Plain.to_string()]);
let mut req = MockRequest::new(Head, "/empty"); let response = client.head("empty").dispatch();
let response = req.dispatch_with(&rocket);
assert_eq!(response.status(), Status::NoContent); assert_eq!(response.status(), Status::NoContent);
} }
#[test] #[test]
fn user_head() { fn user_head() {
let rocket = rocket::ignite().mount("/", routes()); let client = Client::new(rocket::ignite().mount("/", routes())).unwrap();
let mut req = MockRequest::new(Head, "/other"); let response = client.head("/other").dispatch();
let response = req.dispatch_with(&rocket);
assert_eq!(response.status(), Status::Ok);
let content_type: Vec<_> = response.headers().get("Content-Type").collect(); let content_type: Vec<_> = response.headers().get("Content-Type").collect();
assert_eq!(response.status(), Status::Ok);
assert_eq!(content_type, vec![ContentType::JSON.to_string()]); assert_eq!(content_type, vec![ContentType::JSON.to_string()]);
} }
} }

View File

@ -18,8 +18,7 @@ fn index(form: Form<Simple>) -> String {
mod limits_tests { mod limits_tests {
use rocket; use rocket;
use rocket::config::{Environment, Config, Limits}; use rocket::config::{Environment, Config, Limits};
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::{Status, ContentType}; use rocket::http::{Status, ContentType};
fn rocket_with_forms_limit(limit: u64) -> rocket::Rocket { fn rocket_with_forms_limit(limit: u64) -> rocket::Rocket {
@ -32,45 +31,45 @@ mod limits_tests {
#[test] #[test]
fn large_enough() { fn large_enough() {
let rocket = rocket_with_forms_limit(128); let client = Client::new(rocket_with_forms_limit(128)).unwrap();
let mut req = MockRequest::new(Post, "/") let mut response = client.post("/")
.body("value=Hello+world") .body("value=Hello+world")
.header(ContentType::Form); .header(ContentType::Form)
.dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some("Hello world".into())); assert_eq!(response.body_string(), Some("Hello world".into()));
} }
#[test] #[test]
fn just_large_enough() { fn just_large_enough() {
let rocket = rocket_with_forms_limit(17); let client = Client::new(rocket_with_forms_limit(17)).unwrap();
let mut req = MockRequest::new(Post, "/") let mut response = client.post("/")
.body("value=Hello+world") .body("value=Hello+world")
.header(ContentType::Form); .header(ContentType::Form)
.dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some("Hello world".into())); assert_eq!(response.body_string(), Some("Hello world".into()));
} }
#[test] #[test]
fn much_too_small() { fn much_too_small() {
let rocket = rocket_with_forms_limit(4); let client = Client::new(rocket_with_forms_limit(4)).unwrap();
let mut req = MockRequest::new(Post, "/") let response = client.post("/")
.body("value=Hello+world") .body("value=Hello+world")
.header(ContentType::Form); .header(ContentType::Form)
.dispatch();
let response = req.dispatch_with(&rocket);
assert_eq!(response.status(), Status::BadRequest); assert_eq!(response.status(), Status::BadRequest);
} }
#[test] #[test]
fn contracted() { fn contracted() {
let rocket = rocket_with_forms_limit(10); let client = Client::new(rocket_with_forms_limit(10)).unwrap();
let mut req = MockRequest::new(Post, "/") let mut response = client.post("/")
.body("value=Hello+world") .body("value=Hello+world")
.header(ContentType::Form); .header(ContentType::Form)
.dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some("Hell".into())); assert_eq!(response.body_string(), Some("Hell".into()));
} }
} }

View File

@ -27,8 +27,7 @@ mod tests {
use super::*; use super::*;
use rocket::Rocket; use rocket::Rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::{Status, ContentType}; use rocket::http::{Status, ContentType};
fn rocket() -> Rocket { fn rocket() -> Rocket {
@ -39,14 +38,14 @@ mod tests {
macro_rules! check_dispatch { macro_rules! check_dispatch {
($mount:expr, $ct:expr, $body:expr) => ( ($mount:expr, $ct:expr, $body:expr) => (
let rocket = rocket(); let client = Client::new(rocket()).unwrap();
let mut req = MockRequest::new(Post, $mount); let mut req = client.post($mount);
let ct: Option<ContentType> = $ct; let ct: Option<ContentType> = $ct;
if let Some(ct) = ct { if let Some(ct) = ct {
req.add_header(ct); req.add_header(ct);
} }
let mut response = req.dispatch_with(&rocket); let mut response = req.dispatch();
let body_str = response.body_string(); let body_str = response.body_string();
let body: Option<&'static str> = $body; let body: Option<&'static str> = $body;
match body { match body {

View File

@ -20,27 +20,24 @@ fn second() -> &'static str {
mod tests { mod tests {
use super::*; use super::*;
use rocket::Rocket; use rocket::Rocket;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
fn assert_no_collision(rocket: &Rocket) { fn assert_no_collision(rocket: Rocket) {
let mut req = MockRequest::new(Get, "/?field=query"); let client = Client::new(rocket).unwrap();
let mut response = req.dispatch_with(&rocket); let mut response = client.get("/?field=query").dispatch();
assert_eq!(response.body_string(), Some("query".into())); assert_eq!(response.body_string(), Some("query".into()));
let mut req = MockRequest::new(Get, "/"); let mut response = client.get("/").dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some("no query".into())); assert_eq!(response.body_string(), Some("no query".into()));
} }
#[test] #[test]
fn check_query_collisions() { fn check_query_collisions() {
let rocket = rocket::ignite().mount("/", routes![first, second]); let rocket = rocket::ignite().mount("/", routes![first, second]);
assert_no_collision(&rocket); assert_no_collision(rocket);
let rocket = rocket::ignite().mount("/", routes![second, first]); let rocket = rocket::ignite().mount("/", routes![second, first]);
assert_no_collision(&rocket); assert_no_collision(rocket);
} }
} }

View File

@ -12,15 +12,13 @@ fn not_found() -> Redirect {
mod tests { mod tests {
use super::*; use super::*;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::Status; use rocket::http::Status;
#[test] #[test]
fn error_catcher_redirect() { fn error_catcher_redirect() {
let rocket = rocket::ignite().catch(errors![not_found]); let client = Client::new(rocket::ignite().catch(errors![not_found])).unwrap();
let mut req = MockRequest::new(Get, "/unknown"); let response = client.get("/unknown").dispatch();
let response = req.dispatch_with(&rocket);
println!("Response:\n{:?}", response); println!("Response:\n{:?}", response);
let location: Vec<_> = response.headers().get("location").collect(); let location: Vec<_> = response.headers().get("location").collect();

View File

@ -12,8 +12,7 @@ fn get_ip(remote: SocketAddr) -> String {
mod remote_rewrite_tests { mod remote_rewrite_tests {
use super::*; use super::*;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
use rocket::http::{Header, Status}; use rocket::http::{Header, Status};
use std::net::SocketAddr; use std::net::SocketAddr;
@ -21,21 +20,19 @@ mod remote_rewrite_tests {
const KNOWN_IP: &'static str = "127.0.0.1:8000"; const KNOWN_IP: &'static str = "127.0.0.1:8000";
fn check_ip(header: Option<Header<'static>>, ip: Option<String>) { fn check_ip(header: Option<Header<'static>>, ip: Option<String>) {
let address: SocketAddr = KNOWN_IP.parse().unwrap(); let addr: SocketAddr = KNOWN_IP.parse().unwrap();
let port = address.port();
let rocket = rocket::ignite().mount("/", routes![get_ip]); let c = Client::new(rocket::ignite().mount("/", routes![get_ip])).unwrap();
let mut req = MockRequest::new(Get, "/").remote(address); let mut response = match header {
if let Some(header) = header { Some(header) => c.get("/").header(header).remote(addr).dispatch(),
req.add_header(header); None => c.get("/").remote(addr).dispatch()
} };
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
let body_str = response.body_string(); let body = response.body_string();
match ip { match ip {
Some(ip) => assert_eq!(body_str, Some(format!("{}:{}", ip, port))), Some(ip) => assert_eq!(body, Some(format!("{}:{}", ip, addr.port()))),
None => assert_eq!(body_str, Some(KNOWN_IP.into())) None => assert_eq!(body, Some(KNOWN_IP.into()))
} }
} }

View File

@ -13,14 +13,10 @@ fn files(route: &Route, path: PathBuf) -> String {
mod route_guard_tests { mod route_guard_tests {
use super::*; use super::*;
use rocket::local::Client;
use rocket::Rocket; fn assert_path(client: &Client, path: &str) {
use rocket::testing::MockRequest; let mut res = client.get(path).dispatch();
use rocket::http::Method::*;
fn assert_path(rocket: &Rocket, path: &str) {
let mut req = MockRequest::new(Get, path);
let mut res = req.dispatch_with(&rocket);
assert_eq!(res.body_string(), Some(path.into())); assert_eq!(res.body_string(), Some(path.into()));
} }
@ -30,9 +26,10 @@ mod route_guard_tests {
.mount("/first", routes![files]) .mount("/first", routes![files])
.mount("/second", routes![files]); .mount("/second", routes![files]);
assert_path(&rocket, "/first/some/path"); let client = Client::new(rocket).unwrap();
assert_path(&rocket, "/second/some/path"); assert_path(&client, "/first/some/path");
assert_path(&rocket, "/first/second/b/c"); assert_path(&client, "/second/some/path");
assert_path(&rocket, "/second/a/b/c"); assert_path(&client, "/first/second/b/c");
assert_path(&client, "/second/a/b/c");
} }
} }

View File

@ -32,14 +32,14 @@ fn dual(user: String, path: Segments) -> String {
mod tests { mod tests {
use super::*; use super::*;
use rocket::testing::MockRequest; use rocket::local::Client;
use rocket::http::Method::*;
#[test] #[test]
fn segments_works() { fn segments_works() {
let rocket = rocket::ignite() let rocket = rocket::ignite()
.mount("/", routes![test, two, one_two, none, dual]) .mount("/", routes![test, two, one_two, none, dual])
.mount("/point", routes![test, two, one_two, dual]); .mount("/point", routes![test, two, one_two, dual]);
let client = Client::new(rocket).unwrap();
// We construct a path that matches each of the routes above. We ensure the // We construct a path that matches each of the routes above. We ensure the
// prefix is stripped, confirming that dynamic segments are working. // prefix is stripped, confirming that dynamic segments are working.
@ -48,9 +48,7 @@ mod tests {
"/static", "/point/static"] "/static", "/point/static"]
{ {
let path = "this/is/the/path/we/want"; let path = "this/is/the/path/we/want";
let mut req = MockRequest::new(Get, format!("{}/{}", prefix, path)); let mut response = client.get(format!("{}/{}", prefix, path)).dispatch();
let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some(path.into())); assert_eq!(response.body_string(), Some(path.into()));
} }
} }