1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-24 07:53:00 +01:00

Merge and fix PR comments

This commit is contained in:
Sven-Hendrik Haase 2018-04-29 14:02:50 +02:00
commit de222fe33b
71 changed files with 1884 additions and 2779 deletions

View File

@ -1,7 +1,7 @@
max_width = 89 max_width = 89
reorder_imports = true reorder_imports = true
reorder_imports_in_group = true #reorder_imports_in_group = true
reorder_imported_names = true #reorder_imported_names = true
wrap_comments = true wrap_comments = true
fn_args_density = "Compressed" fn_args_density = "Compressed"
#use_small_heuristics = false use_small_heuristics = false

View File

@ -1,6 +1,5 @@
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::mem;
use std::rc::Rc; use std::rc::Rc;
use handler::Reply; use handler::Reply;
@ -74,7 +73,7 @@ impl<S: 'static> HttpApplication<S> {
if m { if m {
let path: &'static str = unsafe { let path: &'static str = unsafe {
mem::transmute(&req.path()[inner.prefix + prefix.len()..]) &*(&req.path()[inner.prefix + prefix.len()..] as *const _)
}; };
if path.is_empty() { if path.is_empty() {
req.match_info_mut().add("tail", ""); req.match_info_mut().add("tail", "");
@ -112,12 +111,7 @@ impl<S: 'static> HttpHandler for HttpApplication<S> {
let mut req = req.with_state(Rc::clone(&self.state), self.router.clone()); let mut req = req.with_state(Rc::clone(&self.state), self.router.clone());
let tp = self.get_handler(&mut req); let tp = self.get_handler(&mut req);
let inner = Rc::clone(&self.inner); let inner = Rc::clone(&self.inner);
Ok(Box::new(Pipeline::new( Ok(Box::new(Pipeline::new(req, Rc::clone(&self.middlewares), inner, tp)))
req,
Rc::clone(&self.middlewares),
inner,
tp,
)))
} else { } else {
Err(req) Err(req)
} }
@ -280,7 +274,7 @@ where
{ {
{ {
let parts: &mut ApplicationParts<S> = unsafe { let parts: &mut ApplicationParts<S> = unsafe {
mem::transmute(self.parts.as_mut().expect("Use after finish")) &mut *(self.parts.as_mut().expect("Use after finish") as *mut _)
}; };
// get resource handler // get resource handler
@ -455,20 +449,14 @@ where
} }
let parts = self.parts.as_mut().expect("Use after finish"); let parts = self.parts.as_mut().expect("Use after finish");
parts parts.handlers.push((path, Box::new(WrapHandler::new(handler))));
.handlers
.push((path, Box::new(WrapHandler::new(handler))));
} }
self self
} }
/// Register a middleware. /// Register a middleware.
pub fn middleware<M: Middleware<S>>(mut self, mw: M) -> App<S> { pub fn middleware<M: Middleware<S>>(mut self, mw: M) -> App<S> {
self.parts self.parts.as_mut().expect("Use after finish").middlewares.push(Box::new(mw));
.as_mut()
.expect("Use after finish")
.middlewares
.push(Box::new(mw));
self self
} }
@ -623,9 +611,8 @@ mod tests {
#[test] #[test]
fn test_default_resource() { fn test_default_resource() {
let mut app = App::new() let mut app =
.resource("/test", |r| r.f(|_| HttpResponse::Ok())) App::new().resource("/test", |r| r.f(|_| HttpResponse::Ok())).finish();
.finish();
let req = TestRequest::with_uri("/test").finish(); let req = TestRequest::with_uri("/test").finish();
let resp = app.run(req); let resp = app.run(req);
@ -633,20 +620,14 @@ mod tests {
let req = TestRequest::with_uri("/blah").finish(); let req = TestRequest::with_uri("/blah").finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!( assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
resp.as_response().unwrap().status(),
StatusCode::NOT_FOUND
);
let mut app = App::new() let mut app = App::new()
.default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed())) .default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed()))
.finish(); .finish();
let req = TestRequest::with_uri("/blah").finish(); let req = TestRequest::with_uri("/blah").finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!( assert_eq!(resp.as_response().unwrap().status(), StatusCode::METHOD_NOT_ALLOWED);
resp.as_response().unwrap().status(),
StatusCode::METHOD_NOT_ALLOWED
);
} }
#[test] #[test]
@ -660,9 +641,8 @@ mod tests {
#[test] #[test]
fn test_state() { fn test_state() {
let mut app = App::with_state(10) let mut app =
.resource("/", |r| r.f(|_| HttpResponse::Ok())) App::with_state(10).resource("/", |r| r.f(|_| HttpResponse::Ok())).finish();
.finish();
let req = let req =
HttpRequest::default().with_state(Rc::clone(&app.state), app.router.clone()); HttpRequest::default().with_state(Rc::clone(&app.state), app.router.clone());
let resp = app.run(req); let resp = app.run(req);
@ -694,9 +674,7 @@ mod tests {
#[test] #[test]
fn test_handler() { fn test_handler() {
let mut app = App::new() let mut app = App::new().handler("/test", |_| HttpResponse::Ok()).finish();
.handler("/test", |_| HttpResponse::Ok())
.finish();
let req = TestRequest::with_uri("/test").finish(); let req = TestRequest::with_uri("/test").finish();
let resp = app.run(req); let resp = app.run(req);
@ -712,24 +690,16 @@ mod tests {
let req = TestRequest::with_uri("/testapp").finish(); let req = TestRequest::with_uri("/testapp").finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!( assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
resp.as_response().unwrap().status(),
StatusCode::NOT_FOUND
);
let req = TestRequest::with_uri("/blah").finish(); let req = TestRequest::with_uri("/blah").finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!( assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
resp.as_response().unwrap().status(),
StatusCode::NOT_FOUND
);
} }
#[test] #[test]
fn test_handler2() { fn test_handler2() {
let mut app = App::new() let mut app = App::new().handler("test", |_| HttpResponse::Ok()).finish();
.handler("test", |_| HttpResponse::Ok())
.finish();
let req = TestRequest::with_uri("/test").finish(); let req = TestRequest::with_uri("/test").finish();
let resp = app.run(req); let resp = app.run(req);
@ -745,17 +715,11 @@ mod tests {
let req = TestRequest::with_uri("/testapp").finish(); let req = TestRequest::with_uri("/testapp").finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!( assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
resp.as_response().unwrap().status(),
StatusCode::NOT_FOUND
);
let req = TestRequest::with_uri("/blah").finish(); let req = TestRequest::with_uri("/blah").finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!( assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
resp.as_response().unwrap().status(),
StatusCode::NOT_FOUND
);
} }
#[test] #[test]
@ -779,68 +743,41 @@ mod tests {
let req = TestRequest::with_uri("/prefix/testapp").finish(); let req = TestRequest::with_uri("/prefix/testapp").finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!( assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
resp.as_response().unwrap().status(),
StatusCode::NOT_FOUND
);
let req = TestRequest::with_uri("/prefix/blah").finish(); let req = TestRequest::with_uri("/prefix/blah").finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!( assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
resp.as_response().unwrap().status(),
StatusCode::NOT_FOUND
);
} }
#[test] #[test]
fn test_route() { fn test_route() {
let mut app = App::new() let mut app = App::new()
.route("/test", Method::GET, |_: HttpRequest| { .route("/test", Method::GET, |_: HttpRequest| HttpResponse::Ok())
HttpResponse::Ok() .route("/test", Method::POST, |_: HttpRequest| HttpResponse::Created())
})
.route("/test", Method::POST, |_: HttpRequest| {
HttpResponse::Created()
})
.finish(); .finish();
let req = TestRequest::with_uri("/test") let req = TestRequest::with_uri("/test").method(Method::GET).finish();
.method(Method::GET)
.finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK); assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK);
let req = TestRequest::with_uri("/test") let req = TestRequest::with_uri("/test").method(Method::POST).finish();
.method(Method::POST)
.finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!( assert_eq!(resp.as_response().unwrap().status(), StatusCode::CREATED);
resp.as_response().unwrap().status(),
StatusCode::CREATED
);
let req = TestRequest::with_uri("/test") let req = TestRequest::with_uri("/test").method(Method::HEAD).finish();
.method(Method::HEAD)
.finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!( assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
resp.as_response().unwrap().status(),
StatusCode::NOT_FOUND
);
} }
#[test] #[test]
fn test_handler_prefix() { fn test_handler_prefix() {
let mut app = App::new() let mut app =
.prefix("/app") App::new().prefix("/app").handler("/test", |_| HttpResponse::Ok()).finish();
.handler("/test", |_| HttpResponse::Ok())
.finish();
let req = TestRequest::with_uri("/test").finish(); let req = TestRequest::with_uri("/test").finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!( assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
resp.as_response().unwrap().status(),
StatusCode::NOT_FOUND
);
let req = TestRequest::with_uri("/app/test").finish(); let req = TestRequest::with_uri("/app/test").finish();
let resp = app.run(req); let resp = app.run(req);
@ -856,16 +793,10 @@ mod tests {
let req = TestRequest::with_uri("/app/testapp").finish(); let req = TestRequest::with_uri("/app/testapp").finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!( assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
resp.as_response().unwrap().status(),
StatusCode::NOT_FOUND
);
let req = TestRequest::with_uri("/app/blah").finish(); let req = TestRequest::with_uri("/app/blah").finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!( assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
resp.as_response().unwrap().status(),
StatusCode::NOT_FOUND
);
} }
} }

View File

@ -258,9 +258,7 @@ impl Responder for Binary {
type Error = Error; type Error = Error;
fn respond_to(self, _: HttpRequest) -> Result<HttpResponse, Error> { fn respond_to(self, _: HttpRequest) -> Result<HttpResponse, Error> {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok().content_type("application/octet-stream").body(self))
.content_type("application/octet-stream")
.body(self))
} }
} }

View File

@ -94,13 +94,17 @@ pub struct Pause {
impl Pause { impl Pause {
/// Create message with pause duration parameter /// Create message with pause duration parameter
pub fn new(time: Duration) -> Pause { pub fn new(time: Duration) -> Pause {
Pause { time: Some(time) } Pause {
time: Some(time),
}
} }
} }
impl Default for Pause { impl Default for Pause {
fn default() -> Pause { fn default() -> Pause {
Pause { time: None } Pause {
time: None,
}
} }
} }
@ -427,8 +431,7 @@ impl ClientConnector {
} else { } else {
0 0
}; };
self.acquired_per_host self.acquired_per_host.insert(key.clone(), per_host + 1);
.insert(key.clone(), per_host + 1);
} }
fn release_key(&mut self, key: &Key) { fn release_key(&mut self, key: &Key) {
@ -439,8 +442,7 @@ impl ClientConnector {
return; return;
}; };
if per_host > 1 { if per_host > 1 {
self.acquired_per_host self.acquired_per_host.insert(key.clone(), per_host - 1);
.insert(key.clone(), per_host - 1);
} else { } else {
self.acquired_per_host.remove(key); self.acquired_per_host.remove(key);
} }
@ -516,9 +518,7 @@ impl ClientConnector {
fn collect_periodic(&mut self, ctx: &mut Context<Self>) { fn collect_periodic(&mut self, ctx: &mut Context<Self>) {
self.collect(true); self.collect(true);
// re-schedule next collect period // re-schedule next collect period
ctx.run_later(Duration::from_secs(1), |act, ctx| { ctx.run_later(Duration::from_secs(1), |act, ctx| act.collect_periodic(ctx));
act.collect_periodic(ctx)
});
// send stats // send stats
let stats = mem::replace(&mut self.stats, ClientConnectorStats::default()); let stats = mem::replace(&mut self.stats, ClientConnectorStats::default());
@ -570,7 +570,7 @@ impl ClientConnector {
} }
fn wait_for( fn wait_for(
&mut self, key: Key, wait: Duration, conn_timeout: Duration &mut self, key: Key, wait: Duration, conn_timeout: Duration,
) -> oneshot::Receiver<Result<Connection, ClientConnectorError>> { ) -> oneshot::Receiver<Result<Connection, ClientConnectorError>> {
// connection is not available, wait // connection is not available, wait
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
@ -583,10 +583,7 @@ impl ClientConnector {
wait, wait,
conn_timeout, conn_timeout,
}; };
self.waiters self.waiters.entry(key).or_insert_with(VecDeque::new).push_back(waiter);
.entry(key)
.or_insert_with(VecDeque::new)
.push_back(waiter);
rx rx
} }
} }
@ -810,7 +807,7 @@ impl fut::ActorFuture for Maintenance {
type Actor = ClientConnector; type Actor = ClientConnector;
fn poll( fn poll(
&mut self, act: &mut ClientConnector, ctx: &mut Context<ClientConnector> &mut self, act: &mut ClientConnector, ctx: &mut Context<ClientConnector>,
) -> Poll<Self::Item, Self::Error> { ) -> Poll<Self::Item, Self::Error> {
// check pause duration // check pause duration
let done = if let Some(Some(ref pause)) = act.paused { let done = if let Some(Some(ref pause)) = act.paused {
@ -1105,10 +1102,7 @@ impl Pool {
if self.to_close.borrow().is_empty() { if self.to_close.borrow().is_empty() {
None None
} else { } else {
Some(mem::replace( Some(mem::replace(&mut *self.to_close.borrow_mut(), Vec::new()))
&mut *self.to_close.borrow_mut(),
Vec::new(),
))
} }
} }
@ -1116,10 +1110,7 @@ impl Pool {
if self.to_release.borrow().is_empty() { if self.to_release.borrow().is_empty() {
None None
} else { } else {
Some(mem::replace( Some(mem::replace(&mut *self.to_release.borrow_mut(), Vec::new()))
&mut *self.to_release.borrow_mut(),
Vec::new(),
))
} }
} }

View File

@ -7,18 +7,18 @@ use std::mem;
use error::{ParseError, PayloadError}; use error::{ParseError, PayloadError};
use server::h1::{chunked, Decoder}; use server::h1decoder::EncodingDecoder;
use server::{utils, IoStream}; use server::{utils, IoStream};
use super::ClientResponse;
use super::response::ClientMessage; use super::response::ClientMessage;
use super::ClientResponse;
const MAX_BUFFER_SIZE: usize = 131_072; const MAX_BUFFER_SIZE: usize = 131_072;
const MAX_HEADERS: usize = 96; const MAX_HEADERS: usize = 96;
#[derive(Default)] #[derive(Default)]
pub struct HttpResponseParser { pub struct HttpResponseParser {
decoder: Option<Decoder>, decoder: Option<EncodingDecoder>,
} }
#[derive(Debug, Fail)] #[derive(Debug, Fail)]
@ -32,7 +32,7 @@ pub enum HttpResponseParserError {
impl HttpResponseParser { impl HttpResponseParser {
pub fn parse<T>( pub fn parse<T>(
&mut self, io: &mut T, buf: &mut BytesMut &mut self, io: &mut T, buf: &mut BytesMut,
) -> Poll<ClientResponse, HttpResponseParserError> ) -> Poll<ClientResponse, HttpResponseParserError>
where where
T: IoStream, T: IoStream,
@ -75,7 +75,7 @@ impl HttpResponseParser {
} }
pub fn parse_payload<T>( pub fn parse_payload<T>(
&mut self, io: &mut T, buf: &mut BytesMut &mut self, io: &mut T, buf: &mut BytesMut,
) -> Poll<Option<Bytes>, PayloadError> ) -> Poll<Option<Bytes>, PayloadError>
where where
T: IoStream, T: IoStream,
@ -113,8 +113,8 @@ impl HttpResponseParser {
} }
fn parse_message( fn parse_message(
buf: &mut BytesMut buf: &mut BytesMut,
) -> Poll<(ClientResponse, Option<Decoder>), ParseError> { ) -> Poll<(ClientResponse, Option<EncodingDecoder>), ParseError> {
// Parse http message // Parse http message
let bytes_ptr = buf.as_ref().as_ptr() as usize; let bytes_ptr = buf.as_ref().as_ptr() as usize;
let mut headers: [httparse::Header; MAX_HEADERS] = let mut headers: [httparse::Header; MAX_HEADERS] =
@ -160,12 +160,12 @@ impl HttpResponseParser {
} }
let decoder = if status == StatusCode::SWITCHING_PROTOCOLS { let decoder = if status == StatusCode::SWITCHING_PROTOCOLS {
Some(Decoder::eof()) Some(EncodingDecoder::eof())
} else if let Some(len) = hdrs.get(header::CONTENT_LENGTH) { } else if let Some(len) = hdrs.get(header::CONTENT_LENGTH) {
// Content-Length // Content-Length
if let Ok(s) = len.to_str() { if let Ok(s) = len.to_str() {
if let Ok(len) = s.parse::<u64>() { if let Ok(len) = s.parse::<u64>() {
Some(Decoder::length(len)) Some(EncodingDecoder::length(len))
} else { } else {
debug!("illegal Content-Length: {:?}", len); debug!("illegal Content-Length: {:?}", len);
return Err(ParseError::Header); return Err(ParseError::Header);
@ -176,7 +176,7 @@ impl HttpResponseParser {
} }
} else if chunked(&hdrs)? { } else if chunked(&hdrs)? {
// Chunked encoding // Chunked encoding
Some(Decoder::chunked()) Some(EncodingDecoder::chunked())
} else { } else {
None None
}; };
@ -204,3 +204,16 @@ impl HttpResponseParser {
} }
} }
} }
/// Check if request has chunked transfer encoding
pub fn chunked(headers: &HeaderMap) -> Result<bool, ParseError> {
if let Some(encodings) = headers.get(header::TRANSFER_ENCODING) {
if let Ok(s) = encodings.to_str() {
Ok(s.to_lowercase().contains("chunked"))
} else {
Err(ParseError::Header)
}
} else {
Ok(false)
}
}

View File

@ -18,9 +18,9 @@ use error::Error;
use error::PayloadError; use error::PayloadError;
use header::ContentEncoding; use header::ContentEncoding;
use httpmessage::HttpMessage; use httpmessage::HttpMessage;
use server::WriterState;
use server::encoding::PayloadStream; use server::encoding::PayloadStream;
use server::shared::SharedBytes; use server::shared::SharedBytes;
use server::WriterState;
/// A set of errors that can occur during request sending and response reading /// A set of errors that can occur during request sending and response reading
#[derive(Fail, Debug)] #[derive(Fail, Debug)]
@ -80,7 +80,7 @@ impl SendRequest {
} }
pub(crate) fn with_connector( pub(crate) fn with_connector(
req: ClientRequest, conn: Addr<Unsync, ClientConnector> req: ClientRequest, conn: Addr<Unsync, ClientConnector>,
) -> SendRequest { ) -> SendRequest {
SendRequest { SendRequest {
req, req,
@ -269,11 +269,7 @@ impl Pipeline {
#[inline] #[inline]
fn parse(&mut self) -> Poll<ClientResponse, HttpResponseParserError> { fn parse(&mut self) -> Poll<ClientResponse, HttpResponseParserError> {
if let Some(ref mut conn) = self.conn { if let Some(ref mut conn) = self.conn {
match self.parser match self.parser.as_mut().unwrap().parse(conn, &mut self.parser_buf) {
.as_mut()
.unwrap()
.parse(conn, &mut self.parser_buf)
{
Ok(Async::Ready(resp)) => { Ok(Async::Ready(resp)) => {
// check content-encoding // check content-encoding
if self.should_decompress { if self.should_decompress {
@ -469,9 +465,7 @@ impl Pipeline {
} }
// flush io but only if we need to // flush io but only if we need to
match self.writer match self.writer.poll_completed(self.conn.as_mut().unwrap(), false) {
.poll_completed(self.conn.as_mut().unwrap(), false)
{
Ok(Async::Ready(_)) => { Ok(Async::Ready(_)) => {
if self.disconnected if self.disconnected
|| (self.body_completed && self.writer.is_completed()) || (self.body_completed && self.writer.is_completed())

View File

@ -499,10 +499,7 @@ impl ClientRequestBuilder {
jar.add(cookie.into_owned()); jar.add(cookie.into_owned());
self.cookies = Some(jar) self.cookies = Some(jar)
} else { } else {
self.cookies self.cookies.as_mut().unwrap().add(cookie.into_owned());
.as_mut()
.unwrap()
.add(cookie.into_owned());
} }
self self
} }
@ -594,11 +591,7 @@ impl ClientRequestBuilder {
if self.default_headers { if self.default_headers {
// enable br only for https // enable br only for https
let https = if let Some(parts) = parts(&mut self.request, &self.err) { let https = if let Some(parts) = parts(&mut self.request, &self.err) {
parts parts.uri.scheme_part().map(|s| s == &uri::Scheme::HTTPS).unwrap_or(true)
.uri
.scheme_part()
.map(|s| s == &uri::Scheme::HTTPS)
.unwrap_or(true)
} else { } else {
true true
}; };
@ -610,9 +603,7 @@ impl ClientRequestBuilder {
} }
} }
let mut request = self.request let mut request = self.request.take().expect("cannot reuse request builder");
.take()
.expect("cannot reuse request builder");
// set cookies // set cookies
if let Some(ref mut jar) = self.cookies { if let Some(ref mut jar) = self.cookies {
@ -657,9 +648,7 @@ impl ClientRequestBuilder {
S: Stream<Item = Bytes, Error = E> + 'static, S: Stream<Item = Bytes, Error = E> + 'static,
E: Into<Error>, E: Into<Error>,
{ {
self.body(Body::Streaming(Box::new( self.body(Body::Streaming(Box::new(stream.map_err(|e| e.into()))))
stream.map_err(|e| e.into()),
)))
} }
/// Set an empty body and generate `ClientRequest` /// Set an empty body and generate `ClientRequest`
@ -682,7 +671,7 @@ impl ClientRequestBuilder {
#[inline] #[inline]
fn parts<'a>( fn parts<'a>(
parts: &'a mut Option<ClientRequest>, err: &Option<HttpError> parts: &'a mut Option<ClientRequest>, err: &Option<HttpError>,
) -> Option<&'a mut ClientRequest> { ) -> Option<&'a mut ClientRequest> {
if err.is_some() { if err.is_some() {
return None; return None;

View File

@ -103,12 +103,7 @@ impl ClientResponse {
impl fmt::Debug for ClientResponse { impl fmt::Debug for ClientResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let res = writeln!( let res = writeln!(f, "\nClientResponse {:?} {}", self.version(), self.status());
f,
"\nClientResponse {:?} {}",
self.version(),
self.status()
);
let _ = writeln!(f, " headers:"); let _ = writeln!(f, " headers:");
for (key, val) in self.headers().iter() { for (key, val) in self.headers().iter() {
let _ = writeln!(f, " {:?}: {:?}", key, val); let _ = writeln!(f, " {:?}: {:?}", key, val);
@ -138,14 +133,12 @@ mod tests {
#[test] #[test]
fn test_debug() { fn test_debug() {
let resp = ClientResponse::new(ClientMessage::default()); let resp = ClientResponse::new(ClientMessage::default());
resp.as_mut().headers.insert( resp.as_mut()
header::COOKIE, .headers
HeaderValue::from_static("cookie1=value1"), .insert(header::COOKIE, HeaderValue::from_static("cookie1=value1"));
); resp.as_mut()
resp.as_mut().headers.insert( .headers
header::COOKIE, .insert(header::COOKIE, HeaderValue::from_static("cookie2=value2"));
HeaderValue::from_static("cookie2=value2"),
);
let dbg = format!("{:?}", resp); let dbg = format!("{:?}", resp);
assert!(dbg.contains("ClientResponse")); assert!(dbg.contains("ClientResponse"));

View File

@ -114,10 +114,7 @@ impl HttpClientWriter {
self.buffer, self.buffer,
"{} {} {:?}\r", "{} {} {:?}\r",
msg.method(), msg.method(),
msg.uri() msg.uri().path_and_query().map(|u| u.as_str()).unwrap_or("/"),
.path_and_query()
.map(|u| u.as_str())
.unwrap_or("/"),
msg.version() msg.version()
)?; )?;
@ -253,10 +250,8 @@ fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder
} }
let mut b = BytesMut::new(); let mut b = BytesMut::new();
let _ = write!(b, "{}", bytes.len()); let _ = write!(b, "{}", bytes.len());
req.headers_mut().insert( req.headers_mut()
CONTENT_LENGTH, .insert(CONTENT_LENGTH, HeaderValue::try_from(b.freeze()).unwrap());
HeaderValue::try_from(b.freeze()).unwrap(),
);
TransferEncoding::eof(buf) TransferEncoding::eof(buf)
} }
Body::Streaming(_) | Body::Actor(_) => { Body::Streaming(_) | Body::Actor(_) => {
@ -279,10 +274,8 @@ fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder
}; };
if encoding.is_compression() { if encoding.is_compression() {
req.headers_mut().insert( req.headers_mut()
CONTENT_ENCODING, .insert(CONTENT_ENCODING, HeaderValue::from_static(encoding.as_str()));
HeaderValue::from_static(encoding.as_str()),
);
} }
req.replace_body(body); req.replace_body(body);

View File

@ -3,7 +3,6 @@ use futures::unsync::oneshot;
use futures::{Async, Future, Poll}; use futures::{Async, Future, Poll};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem;
use actix::dev::{ContextImpl, SyncEnvelope, ToEnvelope}; use actix::dev::{ContextImpl, SyncEnvelope, ToEnvelope};
use actix::fut::ActorFuture; use actix::fut::ActorFuture;
@ -174,7 +173,9 @@ where
if self.stream.is_none() { if self.stream.is_none() {
self.stream = Some(SmallVec::new()); self.stream = Some(SmallVec::new());
} }
self.stream.as_mut().map(|s| s.push(frame)); if let Some(s) = self.stream.as_mut() {
s.push(frame)
}
self.inner.modify(); self.inner.modify();
} }
@ -199,7 +200,7 @@ where
fn poll(&mut self) -> Poll<Option<SmallVec<[Frame; 4]>>, Error> { fn poll(&mut self) -> Poll<Option<SmallVec<[Frame; 4]>>, Error> {
let ctx: &mut HttpContext<A, S> = let ctx: &mut HttpContext<A, S> =
unsafe { mem::transmute(self as &mut HttpContext<A, S>) }; unsafe { &mut *(self as &mut HttpContext<A, S> as *mut _) };
if self.inner.alive() { if self.inner.alive() {
match self.inner.poll(ctx) { match self.inner.poll(ctx) {
@ -261,7 +262,7 @@ impl<A: Actor> ActorFuture for Drain<A> {
#[inline] #[inline]
fn poll( fn poll(
&mut self, _: &mut A, _: &mut <Self::Actor as Actor>::Context &mut self, _: &mut A, _: &mut <Self::Actor as Actor>::Context,
) -> Poll<Self::Item, Self::Error> { ) -> Poll<Self::Item, Self::Error> {
self.fut.poll().map_err(|_| ()) self.fut.poll().map_err(|_| ())
} }

View File

@ -41,7 +41,9 @@ pub struct PathDeserializer<'de, S: 'de> {
impl<'de, S: 'de> PathDeserializer<'de, S> { impl<'de, S: 'de> PathDeserializer<'de, S> {
pub fn new(req: &'de HttpRequest<S>) -> Self { pub fn new(req: &'de HttpRequest<S>) -> Self {
PathDeserializer { req } PathDeserializer {
req,
}
} }
} }
@ -59,7 +61,7 @@ impl<'de, S: 'de> Deserializer<'de> for PathDeserializer<'de, S> {
} }
fn deserialize_struct<V>( fn deserialize_struct<V>(
self, _: &'static str, _: &'static [&'static str], visitor: V self, _: &'static str, _: &'static [&'static str], visitor: V,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
@ -75,7 +77,7 @@ impl<'de, S: 'de> Deserializer<'de> for PathDeserializer<'de, S> {
} }
fn deserialize_unit_struct<V>( fn deserialize_unit_struct<V>(
self, _: &'static str, visitor: V self, _: &'static str, visitor: V,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
@ -84,7 +86,7 @@ impl<'de, S: 'de> Deserializer<'de> for PathDeserializer<'de, S> {
} }
fn deserialize_newtype_struct<V>( fn deserialize_newtype_struct<V>(
self, _: &'static str, visitor: V self, _: &'static str, visitor: V,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
@ -93,7 +95,7 @@ impl<'de, S: 'de> Deserializer<'de> for PathDeserializer<'de, S> {
} }
fn deserialize_tuple<V>( fn deserialize_tuple<V>(
self, len: usize, visitor: V self, len: usize, visitor: V,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
@ -114,7 +116,7 @@ impl<'de, S: 'de> Deserializer<'de> for PathDeserializer<'de, S> {
} }
fn deserialize_tuple_struct<V>( fn deserialize_tuple_struct<V>(
self, _: &'static str, len: usize, visitor: V self, _: &'static str, len: usize, visitor: V,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
@ -135,7 +137,7 @@ impl<'de, S: 'de> Deserializer<'de> for PathDeserializer<'de, S> {
} }
fn deserialize_enum<V>( fn deserialize_enum<V>(
self, _: &'static str, _: &'static [&'static str], _: V self, _: &'static str, _: &'static [&'static str], _: V,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
@ -202,11 +204,12 @@ impl<'de> de::MapAccess<'de> for ParamsDeserializer<'de> {
where where
K: de::DeserializeSeed<'de>, K: de::DeserializeSeed<'de>,
{ {
self.current = self.params self.current =
.next() self.params.next().map(|&(ref k, ref v)| (k.as_ref(), v.as_ref()));
.map(|&(ref k, ref v)| (k.as_ref(), v.as_ref()));
match self.current { match self.current {
Some((key, _)) => Ok(Some(seed.deserialize(Key { key })?)), Some((key, _)) => Ok(Some(seed.deserialize(Key {
key,
})?)),
None => Ok(None), None => Ok(None),
} }
} }
@ -216,7 +219,9 @@ impl<'de> de::MapAccess<'de> for ParamsDeserializer<'de> {
V: de::DeserializeSeed<'de>, V: de::DeserializeSeed<'de>,
{ {
if let Some((_, value)) = self.current.take() { if let Some((_, value)) = self.current.take() {
seed.deserialize(Value { value }) seed.deserialize(Value {
value,
})
} else { } else {
Err(de::value::Error::custom("unexpected item")) Err(de::value::Error::custom("unexpected item"))
} }
@ -301,7 +306,7 @@ impl<'de> Deserializer<'de> for Value<'de> {
} }
fn deserialize_unit_struct<V>( fn deserialize_unit_struct<V>(
self, _: &'static str, visitor: V self, _: &'static str, visitor: V,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
@ -331,7 +336,7 @@ impl<'de> Deserializer<'de> for Value<'de> {
} }
fn deserialize_enum<V>( fn deserialize_enum<V>(
self, _: &'static str, _: &'static [&'static str], visitor: V self, _: &'static str, _: &'static [&'static str], visitor: V,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
@ -342,7 +347,7 @@ impl<'de> Deserializer<'de> for Value<'de> {
} }
fn deserialize_newtype_struct<V>( fn deserialize_newtype_struct<V>(
self, _: &'static str, visitor: V self, _: &'static str, visitor: V,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
@ -358,7 +363,7 @@ impl<'de> Deserializer<'de> for Value<'de> {
} }
fn deserialize_struct<V>( fn deserialize_struct<V>(
self, _: &'static str, _: &'static [&'static str], _: V self, _: &'static str, _: &'static [&'static str], _: V,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
@ -367,14 +372,12 @@ impl<'de> Deserializer<'de> for Value<'de> {
} }
fn deserialize_tuple_struct<V>( fn deserialize_tuple_struct<V>(
self, _: &'static str, _: usize, _: V self, _: &'static str, _: usize, _: V,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
Err(de::value::Error::custom( Err(de::value::Error::custom("unsupported type: tuple struct"))
"unsupported type: tuple struct",
))
} }
unsupported_type!(deserialize_any, "any"); unsupported_type!(deserialize_any, "any");
@ -416,7 +419,9 @@ impl<'de> de::EnumAccess<'de> for ValueEnum<'de> {
V: de::DeserializeSeed<'de>, V: de::DeserializeSeed<'de>,
{ {
Ok(( Ok((
seed.deserialize(Key { key: self.value })?, seed.deserialize(Key {
key: self.value,
})?,
UnitVariant, UnitVariant,
)) ))
} }
@ -446,7 +451,7 @@ impl<'de> de::VariantAccess<'de> for UnitVariant {
} }
fn struct_variant<V>( fn struct_variant<V>(
self, _: &'static [&'static str], _: V self, _: &'static [&'static str], _: V,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,

View File

@ -68,12 +68,7 @@ impl fmt::Debug for Error {
if let Some(bt) = self.cause.backtrace() { if let Some(bt) = self.cause.backtrace() {
write!(f, "{:?}\n\n{:?}", &self.cause, bt) write!(f, "{:?}\n\n{:?}", &self.cause, bt)
} else { } else {
write!( write!(f, "{:?}\n\n{:?}", &self.cause, self.backtrace.as_ref().unwrap())
f,
"{:?}\n\n{:?}",
&self.cause,
self.backtrace.as_ref().unwrap()
)
} }
} }
} }
@ -298,17 +293,16 @@ pub enum HttpRangeError {
/// Returned if first-byte-pos of all of the byte-range-spec /// Returned if first-byte-pos of all of the byte-range-spec
/// values is greater than the content size. /// values is greater than the content size.
/// See `https://github.com/golang/go/commit/aa9b3d7` /// See `https://github.com/golang/go/commit/aa9b3d7`
#[fail(display = "First-byte-pos of all of the byte-range-spec values is greater than the content size")] #[fail(
display = "First-byte-pos of all of the byte-range-spec values is greater than the content size"
)]
NoOverlap, NoOverlap,
} }
/// Return `BadRequest` for `HttpRangeError` /// Return `BadRequest` for `HttpRangeError`
impl ResponseError for HttpRangeError { impl ResponseError for HttpRangeError {
fn error_response(&self) -> HttpResponse { fn error_response(&self) -> HttpResponse {
HttpResponse::with_body( HttpResponse::with_body(StatusCode::BAD_REQUEST, "Invalid Range header provided")
StatusCode::BAD_REQUEST,
"Invalid Range header provided",
)
} }
} }

View File

@ -110,7 +110,9 @@ where
result( result(
de::Deserialize::deserialize(PathDeserializer::new(&req)) de::Deserialize::deserialize(PathDeserializer::new(&req))
.map_err(|e| e.into()) .map_err(|e| e.into())
.map(|inner| Path { inner }), .map(|inner| Path {
inner,
}),
) )
} }
} }
@ -246,12 +248,7 @@ where
#[inline] #[inline]
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result { fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
Box::new( Box::new(UrlEncoded::new(req.clone()).limit(cfg.limit).from_err().map(Form))
UrlEncoded::new(req.clone())
.limit(cfg.limit)
.from_err()
.map(Form),
)
} }
} }
@ -296,7 +293,9 @@ impl FormConfig {
impl Default for FormConfig { impl Default for FormConfig {
fn default() -> Self { fn default() -> Self {
FormConfig { limit: 262_144 } FormConfig {
limit: 262_144,
}
} }
} }
@ -337,11 +336,7 @@ impl<S: 'static> FromRequest<S> for Bytes {
return Either::A(result(Err(e))); return Either::A(result(Err(e)));
} }
Either::B(Box::new( Either::B(Box::new(MessageBody::new(req.clone()).limit(cfg.limit).from_err()))
MessageBody::new(req.clone())
.limit(cfg.limit)
.from_err(),
))
} }
} }
@ -387,18 +382,14 @@ impl<S: 'static> FromRequest<S> for String {
// check charset // check charset
let encoding = match req.encoding() { let encoding = match req.encoding() {
Err(_) => { Err(_) => {
return Either::A(result(Err(ErrorBadRequest( return Either::A(result(Err(ErrorBadRequest("Unknown request charset"))))
"Unknown request charset",
))))
} }
Ok(encoding) => encoding, Ok(encoding) => encoding,
}; };
Either::B(Box::new( Either::B(Box::new(
MessageBody::new(req.clone()) MessageBody::new(req.clone()).limit(cfg.limit).from_err().and_then(
.limit(cfg.limit) move |body| {
.from_err()
.and_then(move |body| {
let enc: *const Encoding = encoding as *const Encoding; let enc: *const Encoding = encoding as *const Encoding;
if enc == UTF_8 { if enc == UTF_8 {
Ok(str::from_utf8(body.as_ref()) Ok(str::from_utf8(body.as_ref())
@ -409,7 +400,8 @@ impl<S: 'static> FromRequest<S> for String {
.decode(&body, DecoderTrap::Strict) .decode(&body, DecoderTrap::Strict)
.map_err(|_| ErrorBadRequest("Can not decode body"))?) .map_err(|_| ErrorBadRequest("Can not decode body"))?)
} }
}), },
),
)) ))
} }
} }
@ -485,8 +477,7 @@ mod tests {
fn test_bytes() { fn test_bytes() {
let cfg = PayloadConfig::default(); let cfg = PayloadConfig::default();
let mut req = TestRequest::with_header(header::CONTENT_LENGTH, "11").finish(); let mut req = TestRequest::with_header(header::CONTENT_LENGTH, "11").finish();
req.payload_mut() req.payload_mut().unread_data(Bytes::from_static(b"hello=world"));
.unread_data(Bytes::from_static(b"hello=world"));
match Bytes::from_request(&req, &cfg).poll().unwrap() { match Bytes::from_request(&req, &cfg).poll().unwrap() {
Async::Ready(s) => { Async::Ready(s) => {
@ -500,8 +491,7 @@ mod tests {
fn test_string() { fn test_string() {
let cfg = PayloadConfig::default(); let cfg = PayloadConfig::default();
let mut req = TestRequest::with_header(header::CONTENT_LENGTH, "11").finish(); let mut req = TestRequest::with_header(header::CONTENT_LENGTH, "11").finish();
req.payload_mut() req.payload_mut().unread_data(Bytes::from_static(b"hello=world"));
.unread_data(Bytes::from_static(b"hello=world"));
match String::from_request(&req, &cfg).poll().unwrap() { match String::from_request(&req, &cfg).poll().unwrap() {
Async::Ready(s) => { Async::Ready(s) => {
@ -518,8 +508,7 @@ mod tests {
"application/x-www-form-urlencoded", "application/x-www-form-urlencoded",
).header(header::CONTENT_LENGTH, "11") ).header(header::CONTENT_LENGTH, "11")
.finish(); .finish();
req.payload_mut() req.payload_mut().unread_data(Bytes::from_static(b"hello=world"));
.unread_data(Bytes::from_static(b"hello=world"));
let mut cfg = FormConfig::default(); let mut cfg = FormConfig::default();
cfg.limit(4096); cfg.limit(4096);
@ -573,17 +562,11 @@ mod tests {
let mut resource = ResourceHandler::<()>::default(); let mut resource = ResourceHandler::<()>::default();
resource.name("index"); resource.name("index");
let mut routes = Vec::new(); let mut routes = Vec::new();
routes.push(( routes.push((Resource::new("index", "/{key}/{value}/"), Some(resource)));
Resource::new("index", "/{key}/{value}/"),
Some(resource),
));
let (router, _) = Router::new("", ServerSettings::default(), routes); let (router, _) = Router::new("", ServerSettings::default(), routes);
assert!(router.recognize(&mut req).is_some()); assert!(router.recognize(&mut req).is_some());
match Path::<MyStruct>::from_request(&req, &()) match Path::<MyStruct>::from_request(&req, &()).poll().unwrap() {
.poll()
.unwrap()
{
Async::Ready(s) => { Async::Ready(s) => {
assert_eq!(s.key, "name"); assert_eq!(s.key, "name");
assert_eq!(s.value, "user1"); assert_eq!(s.value, "user1");
@ -591,10 +574,7 @@ mod tests {
_ => unreachable!(), _ => unreachable!(),
} }
match Path::<(String, String)>::from_request(&req, &()) match Path::<(String, String)>::from_request(&req, &()).poll().unwrap() {
.poll()
.unwrap()
{
Async::Ready(s) => { Async::Ready(s) => {
assert_eq!(s.0, "name"); assert_eq!(s.0, "name");
assert_eq!(s.1, "user1"); assert_eq!(s.1, "user1");
@ -620,10 +600,7 @@ mod tests {
_ => unreachable!(), _ => unreachable!(),
} }
match Path::<(String, u8)>::from_request(&req, &()) match Path::<(String, u8)>::from_request(&req, &()).poll().unwrap() {
.poll()
.unwrap()
{
Async::Ready(s) => { Async::Ready(s) => {
assert_eq!(s.0, "name"); assert_eq!(s.0, "name");
assert_eq!(s.1, 32); assert_eq!(s.1, 32);
@ -631,15 +608,9 @@ mod tests {
_ => unreachable!(), _ => unreachable!(),
} }
match Path::<Vec<String>>::from_request(&req, &()) match Path::<Vec<String>>::from_request(&req, &()).poll().unwrap() {
.poll()
.unwrap()
{
Async::Ready(s) => { Async::Ready(s) => {
assert_eq!( assert_eq!(s.into_inner(), vec!["name".to_owned(), "32".to_owned()]);
s.into_inner(),
vec!["name".to_owned(), "32".to_owned()]
);
} }
_ => unreachable!(), _ => unreachable!(),
} }

187
src/fs.rs
View File

@ -14,7 +14,8 @@ use std::os::unix::fs::MetadataExt;
use bytes::{BufMut, Bytes, BytesMut}; use bytes::{BufMut, Bytes, BytesMut};
use futures::{Async, Future, Poll, Stream}; use futures::{Async, Future, Poll, Stream};
use futures_cpupool::{CpuFuture, CpuPool}; use futures_cpupool::{CpuFuture, CpuPool};
use mime_guess::get_mime_type; use mime;
use mime_guess::{guess_mime_type, get_mime_type};
use error::Error; use error::Error;
use handler::{Handler, Reply, Responder, RouteHandler, WrapHandler}; use handler::{Handler, Reply, Responder, RouteHandler, WrapHandler};
@ -202,18 +203,24 @@ impl Responder for NamedFile {
if self.status_code != StatusCode::OK { if self.status_code != StatusCode::OK {
let mut resp = HttpResponse::build(self.status_code); let mut resp = HttpResponse::build(self.status_code);
resp.if_some(self.path().extension(), |ext, resp| { resp.if_some(self.path().extension(), |ext, resp| {
resp.set(header::ContentType(get_mime_type( resp.set(header::ContentType(get_mime_type(&ext.to_string_lossy())));
&ext.to_string_lossy(),
)));
}).if_some(self.path().file_name(), |file_name, resp| { }).if_some(self.path().file_name(), |file_name, resp| {
resp.header("Content-Disposition", let mime_type = guess_mime_type(self.path());
format!("attachment; filename={}", file_name.to_string_lossy())); let inline_or_attachment = match mime_type.type_() {
mime::IMAGE | mime::TEXT => "inline",
_ => "attachment",
};
resp.header(
"Content-Disposition",
format!("{inline_or_attachment}; filename={filename}",
inline_or_attachment=inline_or_attachment,
filename=file_name.to_string_lossy())
);
}); });
let reader = ChunkedReadFile { let reader = ChunkedReadFile {
size: self.md.len(), size: self.md.len(),
offset: 0, offset: 0,
cpu_pool: self.cpu_pool cpu_pool: self.cpu_pool.unwrap_or_else(|| req.cpu_pool().clone()),
.unwrap_or_else(|| req.cpu_pool().clone()),
file: Some(self.file), file: Some(self.file),
fut: None, fut: None,
}; };
@ -256,12 +263,19 @@ impl Responder for NamedFile {
let mut resp = HttpResponse::build(self.status_code); let mut resp = HttpResponse::build(self.status_code);
resp.if_some(self.path().extension(), |ext, resp| { resp.if_some(self.path().extension(), |ext, resp| {
resp.set(header::ContentType(get_mime_type( resp.set(header::ContentType(get_mime_type(&ext.to_string_lossy())));
&ext.to_string_lossy(),
)));
}).if_some(self.path().file_name(), |file_name, resp| { }).if_some(self.path().file_name(), |file_name, resp| {
resp.header("Content-Disposition", let mime_type = guess_mime_type(self.path());
format!("attachment; filename={}", file_name.to_string_lossy())); let inline_or_attachment = match mime_type.type_() {
mime::IMAGE | mime::TEXT => "inline",
_ => "attachment",
};
resp.header(
"Content-Disposition",
format!("{inline_or_attachment}; filename={filename}",
inline_or_attachment=inline_or_attachment,
filename=file_name.to_string_lossy())
);
}).if_some(last_modified, |lm, resp| { }).if_some(last_modified, |lm, resp| {
resp.set(header::LastModified(lm)); resp.set(header::LastModified(lm));
}).if_some(etag, |etag, resp| { }).if_some(etag, |etag, resp| {
@ -280,8 +294,7 @@ impl Responder for NamedFile {
let reader = ChunkedReadFile { let reader = ChunkedReadFile {
size: self.md.len(), size: self.md.len(),
offset: 0, offset: 0,
cpu_pool: self.cpu_pool cpu_pool: self.cpu_pool.unwrap_or_else(|| req.cpu_pool().clone()),
.unwrap_or_else(|| req.cpu_pool().clone()),
file: Some(self.file), file: Some(self.file),
fut: None, fut: None,
}; };
@ -349,7 +362,10 @@ pub struct Directory {
impl Directory { impl Directory {
pub fn new(base: PathBuf, path: PathBuf) -> Directory { pub fn new(base: PathBuf, path: PathBuf) -> Directory {
Directory { base, path } Directory {
base,
path,
}
} }
fn can_list(&self, entry: &io::Result<DirEntry>) -> bool { fn can_list(&self, entry: &io::Result<DirEntry>) -> bool {
@ -419,9 +435,7 @@ impl Responder for Directory {
</ul></body>\n</html>", </ul></body>\n</html>",
index_of, index_of, body index_of, index_of, body
); );
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok().content_type("text/html; charset=utf-8").body(html))
.content_type("text/html; charset=utf-8")
.body(html))
} }
} }
@ -546,13 +560,12 @@ impl<S: 'static> Handler<S> for StaticFiles<S> {
if !self.accessible { if !self.accessible {
Ok(self.default.handle(req)) Ok(self.default.handle(req))
} else { } else {
let relpath = match req.match_info() let relpath =
.get("tail") match req.match_info().get("tail").map(|tail| PathBuf::from_param(tail))
.map(|tail| PathBuf::from_param(tail)) {
{ Some(Ok(path)) => path,
Some(Ok(path)) => path, _ => return Ok(self.default.handle(req)),
_ => return Ok(self.default.handle(req)), };
};
// full filepath // full filepath
let path = self.directory.join(&relpath).canonicalize()?; let path = self.directory.join(&relpath).canonicalize()?;
@ -600,11 +613,10 @@ mod tests {
use test::{self, TestRequest}; use test::{self, TestRequest};
#[test] #[test]
fn test_named_file() { fn test_named_file_text() {
assert!(NamedFile::open("test--").is_err()); assert!(NamedFile::open("test--").is_err());
let mut file = NamedFile::open("Cargo.toml") let mut file =
.unwrap() NamedFile::open("Cargo.toml").unwrap().set_cpu_pool(CpuPool::new(1));
.set_cpu_pool(CpuPool::new(1));
{ {
file.file(); file.file();
let _f: &File = &file; let _f: &File = &file;
@ -614,18 +626,55 @@ mod tests {
} }
let resp = file.respond_to(HttpRequest::default()).unwrap(); let resp = file.respond_to(HttpRequest::default()).unwrap();
assert_eq!( assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), "text/x-toml");
resp.headers().get(header::CONTENT_TYPE).unwrap(),
"text/x-toml"
);
assert_eq!( assert_eq!(
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
"attachment; filename=Cargo.toml" "inline; filename=Cargo.toml"
); );
} }
#[test] #[test]
fn test_named_file_status_code() { fn test_named_file_image() {
let mut file =
NamedFile::open("tests/test.png").unwrap().set_cpu_pool(CpuPool::new(1));
{
file.file();
let _f: &File = &file;
}
{
let _f: &mut File = &mut file;
}
let resp = file.respond_to(HttpRequest::default()).unwrap();
assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), "image/png");
assert_eq!(
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
"inline; filename=test.png"
);
}
#[test]
fn test_named_file_binary() {
let mut file =
NamedFile::open("tests/test.binary").unwrap().set_cpu_pool(CpuPool::new(1));
{
file.file();
let _f: &File = &file;
}
{
let _f: &mut File = &mut file;
}
let resp = file.respond_to(HttpRequest::default()).unwrap();
assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), "application/octet-stream");
assert_eq!(
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
"attachment; filename=test.binary"
);
}
#[test]
fn test_named_file_status_code_text() {
let mut file = NamedFile::open("Cargo.toml") let mut file = NamedFile::open("Cargo.toml")
.unwrap() .unwrap()
.set_status_code(StatusCode::NOT_FOUND) .set_status_code(StatusCode::NOT_FOUND)
@ -639,13 +688,10 @@ mod tests {
} }
let resp = file.respond_to(HttpRequest::default()).unwrap(); let resp = file.respond_to(HttpRequest::default()).unwrap();
assert_eq!( assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), "text/x-toml");
resp.headers().get(header::CONTENT_TYPE).unwrap(),
"text/x-toml"
);
assert_eq!( assert_eq!(
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
"attachment; filename=Cargo.toml" "inline; filename=Cargo.toml"
); );
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
} }
@ -689,9 +735,7 @@ mod tests {
req.match_info_mut().add("tail", ""); req.match_info_mut().add("tail", "");
st.show_index = true; st.show_index = true;
let resp = st.handle(req) let resp = st.handle(req).respond_to(HttpRequest::default()).unwrap();
.respond_to(HttpRequest::default())
.unwrap();
let resp = resp.as_response().expect("HTTP Response"); let resp = resp.as_response().expect("HTTP Response");
assert_eq!( assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(), resp.headers().get(header::CONTENT_TYPE).unwrap(),
@ -707,28 +751,18 @@ mod tests {
let mut req = HttpRequest::default(); let mut req = HttpRequest::default();
req.match_info_mut().add("tail", "tests"); req.match_info_mut().add("tail", "tests");
let resp = st.handle(req) let resp = st.handle(req).respond_to(HttpRequest::default()).unwrap();
.respond_to(HttpRequest::default())
.unwrap();
let resp = resp.as_response().expect("HTTP Response"); let resp = resp.as_response().expect("HTTP Response");
assert_eq!(resp.status(), StatusCode::FOUND); assert_eq!(resp.status(), StatusCode::FOUND);
assert_eq!( assert_eq!(resp.headers().get(header::LOCATION).unwrap(), "/tests/index.html");
resp.headers().get(header::LOCATION).unwrap(),
"/tests/index.html"
);
let mut req = HttpRequest::default(); let mut req = HttpRequest::default();
req.match_info_mut().add("tail", "tests/"); req.match_info_mut().add("tail", "tests/");
let resp = st.handle(req) let resp = st.handle(req).respond_to(HttpRequest::default()).unwrap();
.respond_to(HttpRequest::default())
.unwrap();
let resp = resp.as_response().expect("HTTP Response"); let resp = resp.as_response().expect("HTTP Response");
assert_eq!(resp.status(), StatusCode::FOUND); assert_eq!(resp.status(), StatusCode::FOUND);
assert_eq!( assert_eq!(resp.headers().get(header::LOCATION).unwrap(), "/tests/index.html");
resp.headers().get(header::LOCATION).unwrap(),
"/tests/index.html"
);
} }
#[test] #[test]
@ -737,9 +771,7 @@ mod tests {
let mut req = HttpRequest::default(); let mut req = HttpRequest::default();
req.match_info_mut().add("tail", "tools/wsload"); req.match_info_mut().add("tail", "tools/wsload");
let resp = st.handle(req) let resp = st.handle(req).respond_to(HttpRequest::default()).unwrap();
.respond_to(HttpRequest::default())
.unwrap();
let resp = resp.as_response().expect("HTTP Response"); let resp = resp.as_response().expect("HTTP Response");
assert_eq!(resp.status(), StatusCode::FOUND); assert_eq!(resp.status(), StatusCode::FOUND);
assert_eq!( assert_eq!(
@ -759,23 +791,13 @@ mod tests {
let request = srv.get().uri(srv.url("/public")).finish().unwrap(); let request = srv.get().uri(srv.url("/public")).finish().unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::FOUND); assert_eq!(response.status(), StatusCode::FOUND);
let loc = response let loc = response.headers().get(header::LOCATION).unwrap().to_str().unwrap();
.headers()
.get(header::LOCATION)
.unwrap()
.to_str()
.unwrap();
assert_eq!(loc, "/public/Cargo.toml"); assert_eq!(loc, "/public/Cargo.toml");
let request = srv.get().uri(srv.url("/public/")).finish().unwrap(); let request = srv.get().uri(srv.url("/public/")).finish().unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::FOUND); assert_eq!(response.status(), StatusCode::FOUND);
let loc = response let loc = response.headers().get(header::LOCATION).unwrap().to_str().unwrap();
.headers()
.get(header::LOCATION)
.unwrap()
.to_str()
.unwrap();
assert_eq!(loc, "/public/Cargo.toml"); assert_eq!(loc, "/public/Cargo.toml");
} }
@ -788,23 +810,13 @@ mod tests {
let request = srv.get().uri(srv.url("/test")).finish().unwrap(); let request = srv.get().uri(srv.url("/test")).finish().unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::FOUND); assert_eq!(response.status(), StatusCode::FOUND);
let loc = response let loc = response.headers().get(header::LOCATION).unwrap().to_str().unwrap();
.headers()
.get(header::LOCATION)
.unwrap()
.to_str()
.unwrap();
assert_eq!(loc, "/test/Cargo.toml"); assert_eq!(loc, "/test/Cargo.toml");
let request = srv.get().uri(srv.url("/test/")).finish().unwrap(); let request = srv.get().uri(srv.url("/test/")).finish().unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::FOUND); assert_eq!(response.status(), StatusCode::FOUND);
let loc = response let loc = response.headers().get(header::LOCATION).unwrap().to_str().unwrap();
.headers()
.get(header::LOCATION)
.unwrap()
.to_str()
.unwrap();
assert_eq!(loc, "/test/Cargo.toml"); assert_eq!(loc, "/test/Cargo.toml");
} }
@ -814,10 +826,7 @@ mod tests {
App::new().handler("test", StaticFiles::new(".").index_file("Cargo.toml")) App::new().handler("test", StaticFiles::new(".").index_file("Cargo.toml"))
}); });
let request = srv.get() let request = srv.get().uri(srv.url("/test/%43argo.toml")).finish().unwrap();
.uri(srv.url("/test/%43argo.toml"))
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::OK); assert_eq!(response.status(), StatusCode::OK);
} }

View File

@ -1,5 +1,5 @@
use futures::Poll;
use futures::future::{err, ok, Future, FutureResult}; use futures::future::{err, ok, Future, FutureResult};
use futures::Poll;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Deref; use std::ops::Deref;
@ -296,14 +296,13 @@ where
#[inline] #[inline]
fn respond_to(self, req: HttpRequest) -> Result<Reply, Error> { fn respond_to(self, req: HttpRequest) -> Result<Reply, Error> {
let fut = self.map_err(|e| e.into()) let fut = self.map_err(|e| e.into()).then(move |r| match r.respond_to(req) {
.then(move |r| match r.respond_to(req) { Ok(reply) => match reply.into().0 {
Ok(reply) => match reply.into().0 { ReplyItem::Message(resp) => ok(resp),
ReplyItem::Message(resp) => ok(resp), _ => panic!("Nested async replies are not supported"),
_ => panic!("Nested async replies are not supported"), },
}, Err(e) => err(e),
Err(e) => err(e), });
});
Ok(Reply::async(fut)) Ok(Reply::async(fut))
} }
} }

View File

@ -57,7 +57,10 @@ impl EntityTag {
/// If the tag contains invalid characters. /// If the tag contains invalid characters.
pub fn new(weak: bool, tag: String) -> EntityTag { pub fn new(weak: bool, tag: String) -> EntityTag {
assert!(check_slice_validity(&tag), "Invalid tag: {:?}", tag); assert!(check_slice_validity(&tag), "Invalid tag: {:?}", tag);
EntityTag { weak, tag } EntityTag {
weak,
tag,
}
} }
/// Constructs a new weak EntityTag. /// Constructs a new weak EntityTag.
@ -196,11 +199,7 @@ mod tests {
fn test_etag_parse_failures() { fn test_etag_parse_failures() {
// Expected failures // Expected failures
assert!("no-dquotes".parse::<EntityTag>().is_err()); assert!("no-dquotes".parse::<EntityTag>().is_err());
assert!( assert!("w/\"the-first-w-is-case-sensitive\"".parse::<EntityTag>().is_err());
"w/\"the-first-w-is-case-sensitive\""
.parse::<EntityTag>()
.is_err()
);
assert!("".parse::<EntityTag>().is_err()); assert!("".parse::<EntityTag>().is_err());
assert!("\"unmatched-dquotes1".parse::<EntityTag>().is_err()); assert!("\"unmatched-dquotes1".parse::<EntityTag>().is_err());
assert!("unmatched-dquotes2\"".parse::<EntityTag>().is_err()); assert!("unmatched-dquotes2\"".parse::<EntityTag>().is_err());
@ -209,26 +208,14 @@ mod tests {
#[test] #[test]
fn test_etag_fmt() { fn test_etag_fmt() {
assert_eq!( assert_eq!(format!("{}", EntityTag::strong("foobar".to_owned())), "\"foobar\"");
format!("{}", EntityTag::strong("foobar".to_owned())), assert_eq!(format!("{}", EntityTag::strong("".to_owned())), "\"\"");
"\"foobar\""
);
assert_eq!(
format!("{}", EntityTag::strong("".to_owned())),
"\"\""
);
assert_eq!( assert_eq!(
format!("{}", EntityTag::weak("weak-etag".to_owned())), format!("{}", EntityTag::weak("weak-etag".to_owned())),
"W/\"weak-etag\"" "W/\"weak-etag\""
); );
assert_eq!( assert_eq!(format!("{}", EntityTag::weak("\u{0065}".to_owned())), "W/\"\x65\"");
format!("{}", EntityTag::weak("\u{0065}".to_owned())), assert_eq!(format!("{}", EntityTag::weak("".to_owned())), "W/\"\"");
"W/\"\x65\""
);
assert_eq!(
format!("{}", EntityTag::weak("".to_owned())),
"W/\"\""
);
} }
#[test] #[test]

View File

@ -64,11 +64,7 @@ impl IntoHeaderValue for HttpDate {
fn try_into(self) -> Result<HeaderValue, Self::Error> { fn try_into(self) -> Result<HeaderValue, Self::Error> {
let mut wrt = BytesMut::with_capacity(29).writer(); let mut wrt = BytesMut::with_capacity(29).writer();
write!(wrt, "{}", self.0.rfc822()).unwrap(); write!(wrt, "{}", self.0.rfc822()).unwrap();
unsafe { unsafe { Ok(HeaderValue::from_shared_unchecked(wrt.get_mut().take().freeze())) }
Ok(HeaderValue::from_shared_unchecked(
wrt.get_mut().take().freeze(),
))
}
} }
} }
@ -104,24 +100,12 @@ mod tests {
#[test] #[test]
fn test_date() { fn test_date() {
assert_eq!("Sun, 07 Nov 1994 08:48:37 GMT".parse::<HttpDate>().unwrap(), NOV_07);
assert_eq!( assert_eq!(
"Sun, 07 Nov 1994 08:48:37 GMT" "Sunday, 07-Nov-94 08:48:37 GMT".parse::<HttpDate>().unwrap(),
.parse::<HttpDate>()
.unwrap(),
NOV_07
);
assert_eq!(
"Sunday, 07-Nov-94 08:48:37 GMT"
.parse::<HttpDate>()
.unwrap(),
NOV_07
);
assert_eq!(
"Sun Nov 7 08:48:37 1994"
.parse::<HttpDate>()
.unwrap(),
NOV_07 NOV_07
); );
assert_eq!("Sun Nov 7 08:48:37 1994".parse::<HttpDate>().unwrap(), NOV_07);
assert!("this-is-no-date".parse::<HttpDate>().is_err()); assert!("this-is-no-date".parse::<HttpDate>().is_err());
} }
} }

View File

@ -47,7 +47,10 @@ impl<T> QualityItem<T> {
/// The item can be of any type. /// The item can be of any type.
/// The quality should be a value in the range [0, 1]. /// The quality should be a value in the range [0, 1].
pub fn new(item: T, quality: Quality) -> QualityItem<T> { pub fn new(item: T, quality: Quality) -> QualityItem<T> {
QualityItem { item, quality } QualityItem {
item,
quality,
}
} }
} }
@ -63,11 +66,7 @@ impl<T: fmt::Display> fmt::Display for QualityItem<T> {
match self.quality.0 { match self.quality.0 {
1000 => Ok(()), 1000 => Ok(()),
0 => f.write_str("; q=0"), 0 => f.write_str("; q=0"),
x => write!( x => write!(f, "; q=0.{}", format!("{:03}", x).trim_right_matches('0')),
f,
"; q=0.{}",
format!("{:03}", x).trim_right_matches('0')
),
} }
} }
} }
@ -120,10 +119,7 @@ fn from_f32(f: f32) -> Quality {
// this function is only used internally. A check that `f` is within range // this function is only used internally. A check that `f` is within range
// should be done before calling this method. Just in case, this // should be done before calling this method. Just in case, this
// debug_assert should catch if we were forgetful // debug_assert should catch if we were forgetful
debug_assert!( debug_assert!(f >= 0f32 && f <= 1f32, "q value must be between 0.0 and 1.0");
f >= 0f32 && f <= 1f32,
"q value must be between 0.0 and 1.0"
);
Quality((f * 1000f32) as u16) Quality((f * 1000f32) as u16)
} }
@ -156,10 +152,7 @@ mod internal {
impl IntoQuality for f32 { impl IntoQuality for f32 {
fn into_quality(self) -> Quality { fn into_quality(self) -> Quality {
assert!( assert!(self >= 0f32 && self <= 1f32, "float must be between 0.0 and 1.0");
self >= 0f32 && self <= 1f32,
"float must be between 0.0 and 1.0"
);
super::from_f32(self) super::from_f32(self)
} }
} }
@ -295,10 +288,6 @@ mod tests {
#[test] #[test]
fn test_fuzzing_bugs() { fn test_fuzzing_bugs() {
assert!("99999;".parse::<QualityItem<String>>().is_err()); assert!("99999;".parse::<QualityItem<String>>().is_err());
assert!( assert!("\x0d;;;=\u{d6aa}==".parse::<QualityItem<String>>().is_err())
"\x0d;;;=\u{d6aa}=="
.parse::<QualityItem<String>>()
.is_err()
)
} }
} }

View File

@ -190,16 +190,8 @@ mod tests {
// trailing slashes // trailing slashes
let params = vec![ let params = vec![
("/resource1", "", StatusCode::OK), ("/resource1", "", StatusCode::OK),
( ("/resource1/", "/resource1", StatusCode::MOVED_PERMANENTLY),
"/resource1/", ("/resource2", "/resource2/", StatusCode::MOVED_PERMANENTLY),
"/resource1",
StatusCode::MOVED_PERMANENTLY,
),
(
"/resource2",
"/resource2/",
StatusCode::MOVED_PERMANENTLY,
),
("/resource2/", "", StatusCode::OK), ("/resource2/", "", StatusCode::OK),
("/resource1?p1=1&p2=2", "", StatusCode::OK), ("/resource1?p1=1&p2=2", "", StatusCode::OK),
( (
@ -222,11 +214,7 @@ mod tests {
if !target.is_empty() { if !target.is_empty() {
assert_eq!( assert_eq!(
target, target,
r.headers() r.headers().get(header::LOCATION).unwrap().to_str().unwrap()
.get(header::LOCATION)
.unwrap()
.to_str()
.unwrap()
); );
} }
} }
@ -238,11 +226,7 @@ mod tests {
.resource("/resource1", |r| r.method(Method::GET).f(index)) .resource("/resource1", |r| r.method(Method::GET).f(index))
.resource("/resource2/", |r| r.method(Method::GET).f(index)) .resource("/resource2/", |r| r.method(Method::GET).f(index))
.default_resource(|r| { .default_resource(|r| {
r.h(NormalizePath::new( r.h(NormalizePath::new(false, true, StatusCode::MOVED_PERMANENTLY))
false,
true,
StatusCode::MOVED_PERMANENTLY,
))
}) })
.finish(); .finish();
@ -276,46 +260,14 @@ mod tests {
// trailing slashes // trailing slashes
let params = vec![ let params = vec![
("/resource1/a/b", "", StatusCode::OK), ("/resource1/a/b", "", StatusCode::OK),
( ("/resource1/", "/resource1", StatusCode::MOVED_PERMANENTLY),
"/resource1/", ("/resource1//", "/resource1", StatusCode::MOVED_PERMANENTLY),
"/resource1", ("//resource1//a//b", "/resource1/a/b", StatusCode::MOVED_PERMANENTLY),
StatusCode::MOVED_PERMANENTLY, ("//resource1//a//b/", "/resource1/a/b", StatusCode::MOVED_PERMANENTLY),
), ("//resource1//a//b//", "/resource1/a/b", StatusCode::MOVED_PERMANENTLY),
( ("///resource1//a//b", "/resource1/a/b", StatusCode::MOVED_PERMANENTLY),
"/resource1//", ("/////resource1/a///b", "/resource1/a/b", StatusCode::MOVED_PERMANENTLY),
"/resource1", ("/////resource1/a//b/", "/resource1/a/b", StatusCode::MOVED_PERMANENTLY),
StatusCode::MOVED_PERMANENTLY,
),
(
"//resource1//a//b",
"/resource1/a/b",
StatusCode::MOVED_PERMANENTLY,
),
(
"//resource1//a//b/",
"/resource1/a/b",
StatusCode::MOVED_PERMANENTLY,
),
(
"//resource1//a//b//",
"/resource1/a/b",
StatusCode::MOVED_PERMANENTLY,
),
(
"///resource1//a//b",
"/resource1/a/b",
StatusCode::MOVED_PERMANENTLY,
),
(
"/////resource1/a///b",
"/resource1/a/b",
StatusCode::MOVED_PERMANENTLY,
),
(
"/////resource1/a//b/",
"/resource1/a/b",
StatusCode::MOVED_PERMANENTLY,
),
("/resource1/a/b?p=1", "", StatusCode::OK), ("/resource1/a/b?p=1", "", StatusCode::OK),
( (
"//resource1//a//b?p=1", "//resource1//a//b?p=1",
@ -356,11 +308,7 @@ mod tests {
if !target.is_empty() { if !target.is_empty() {
assert_eq!( assert_eq!(
target, target,
r.headers() r.headers().get(header::LOCATION).unwrap().to_str().unwrap()
.get(header::LOCATION)
.unwrap()
.to_str()
.unwrap()
); );
} }
} }
@ -379,88 +327,24 @@ mod tests {
// trailing slashes // trailing slashes
let params = vec![ let params = vec![
("/resource1/a/b", "", StatusCode::OK), ("/resource1/a/b", "", StatusCode::OK),
( ("/resource1/a/b/", "/resource1/a/b", StatusCode::MOVED_PERMANENTLY),
"/resource1/a/b/", ("//resource2//a//b", "/resource2/a/b/", StatusCode::MOVED_PERMANENTLY),
"/resource1/a/b", ("//resource2//a//b/", "/resource2/a/b/", StatusCode::MOVED_PERMANENTLY),
StatusCode::MOVED_PERMANENTLY, ("//resource2//a//b//", "/resource2/a/b/", StatusCode::MOVED_PERMANENTLY),
), ("///resource1//a//b", "/resource1/a/b", StatusCode::MOVED_PERMANENTLY),
( ("///resource1//a//b/", "/resource1/a/b", StatusCode::MOVED_PERMANENTLY),
"//resource2//a//b", ("/////resource1/a///b", "/resource1/a/b", StatusCode::MOVED_PERMANENTLY),
"/resource2/a/b/", ("/////resource1/a///b/", "/resource1/a/b", StatusCode::MOVED_PERMANENTLY),
StatusCode::MOVED_PERMANENTLY, ("/resource2/a/b", "/resource2/a/b/", StatusCode::MOVED_PERMANENTLY),
),
(
"//resource2//a//b/",
"/resource2/a/b/",
StatusCode::MOVED_PERMANENTLY,
),
(
"//resource2//a//b//",
"/resource2/a/b/",
StatusCode::MOVED_PERMANENTLY,
),
(
"///resource1//a//b",
"/resource1/a/b",
StatusCode::MOVED_PERMANENTLY,
),
(
"///resource1//a//b/",
"/resource1/a/b",
StatusCode::MOVED_PERMANENTLY,
),
(
"/////resource1/a///b",
"/resource1/a/b",
StatusCode::MOVED_PERMANENTLY,
),
(
"/////resource1/a///b/",
"/resource1/a/b",
StatusCode::MOVED_PERMANENTLY,
),
(
"/resource2/a/b",
"/resource2/a/b/",
StatusCode::MOVED_PERMANENTLY,
),
("/resource2/a/b/", "", StatusCode::OK), ("/resource2/a/b/", "", StatusCode::OK),
( ("//resource2//a//b", "/resource2/a/b/", StatusCode::MOVED_PERMANENTLY),
"//resource2//a//b", ("//resource2//a//b/", "/resource2/a/b/", StatusCode::MOVED_PERMANENTLY),
"/resource2/a/b/", ("///resource2//a//b", "/resource2/a/b/", StatusCode::MOVED_PERMANENTLY),
StatusCode::MOVED_PERMANENTLY, ("///resource2//a//b/", "/resource2/a/b/", StatusCode::MOVED_PERMANENTLY),
), ("/////resource2/a///b", "/resource2/a/b/", StatusCode::MOVED_PERMANENTLY),
( ("/////resource2/a///b/", "/resource2/a/b/", StatusCode::MOVED_PERMANENTLY),
"//resource2//a//b/",
"/resource2/a/b/",
StatusCode::MOVED_PERMANENTLY,
),
(
"///resource2//a//b",
"/resource2/a/b/",
StatusCode::MOVED_PERMANENTLY,
),
(
"///resource2//a//b/",
"/resource2/a/b/",
StatusCode::MOVED_PERMANENTLY,
),
(
"/////resource2/a///b",
"/resource2/a/b/",
StatusCode::MOVED_PERMANENTLY,
),
(
"/////resource2/a///b/",
"/resource2/a/b/",
StatusCode::MOVED_PERMANENTLY,
),
("/resource1/a/b?p=1", "", StatusCode::OK), ("/resource1/a/b?p=1", "", StatusCode::OK),
( ("/resource1/a/b/?p=1", "/resource1/a/b?p=1", StatusCode::MOVED_PERMANENTLY),
"/resource1/a/b/?p=1",
"/resource1/a/b?p=1",
StatusCode::MOVED_PERMANENTLY,
),
( (
"//resource2//a//b?p=1", "//resource2//a//b?p=1",
"/resource2/a/b/?p=1", "/resource2/a/b/?p=1",
@ -496,11 +380,7 @@ mod tests {
"/resource1/a/b?p=1", "/resource1/a/b?p=1",
StatusCode::MOVED_PERMANENTLY, StatusCode::MOVED_PERMANENTLY,
), ),
( ("/resource2/a/b?p=1", "/resource2/a/b/?p=1", StatusCode::MOVED_PERMANENTLY),
"/resource2/a/b?p=1",
"/resource2/a/b/?p=1",
StatusCode::MOVED_PERMANENTLY,
),
( (
"//resource2//a//b?p=1", "//resource2//a//b?p=1",
"/resource2/a/b/?p=1", "/resource2/a/b/?p=1",
@ -540,11 +420,7 @@ mod tests {
if !target.is_empty() { if !target.is_empty() {
assert_eq!( assert_eq!(
target, target,
r.headers() r.headers().get(header::LOCATION).unwrap().to_str().unwrap()
.get(header::LOCATION)
.unwrap()
.to_str()
.unwrap()
); );
} }
} }

View File

@ -14,32 +14,37 @@ pub const HttpOk: StaticResponse = StaticResponse(StatusCode::OK);
pub const HttpCreated: StaticResponse = StaticResponse(StatusCode::CREATED); pub const HttpCreated: StaticResponse = StaticResponse(StatusCode::CREATED);
#[deprecated(since = "0.5.0", note = "please use `HttpResponse::Accepted()` instead")] #[deprecated(since = "0.5.0", note = "please use `HttpResponse::Accepted()` instead")]
pub const HttpAccepted: StaticResponse = StaticResponse(StatusCode::ACCEPTED); pub const HttpAccepted: StaticResponse = StaticResponse(StatusCode::ACCEPTED);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::pNonAuthoritativeInformation()` instead")] since = "0.5.0",
note = "please use `HttpResponse::pNonAuthoritativeInformation()` instead"
)]
pub const HttpNonAuthoritativeInformation: StaticResponse = pub const HttpNonAuthoritativeInformation: StaticResponse =
StaticResponse(StatusCode::NON_AUTHORITATIVE_INFORMATION); StaticResponse(StatusCode::NON_AUTHORITATIVE_INFORMATION);
#[deprecated(since = "0.5.0", note = "please use `HttpResponse::NoContent()` instead")] #[deprecated(since = "0.5.0", note = "please use `HttpResponse::NoContent()` instead")]
pub const HttpNoContent: StaticResponse = StaticResponse(StatusCode::NO_CONTENT); pub const HttpNoContent: StaticResponse = StaticResponse(StatusCode::NO_CONTENT);
#[deprecated(since = "0.5.0", #[deprecated(since = "0.5.0", note = "please use `HttpResponse::ResetContent()` instead")]
note = "please use `HttpResponse::ResetContent()` instead")]
pub const HttpResetContent: StaticResponse = StaticResponse(StatusCode::RESET_CONTENT); pub const HttpResetContent: StaticResponse = StaticResponse(StatusCode::RESET_CONTENT);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::PartialContent()` instead")] since = "0.5.0", note = "please use `HttpResponse::PartialContent()` instead"
)]
pub const HttpPartialContent: StaticResponse = pub const HttpPartialContent: StaticResponse =
StaticResponse(StatusCode::PARTIAL_CONTENT); StaticResponse(StatusCode::PARTIAL_CONTENT);
#[deprecated(since = "0.5.0", note = "please use `HttpResponse::MultiStatus()` instead")] #[deprecated(since = "0.5.0", note = "please use `HttpResponse::MultiStatus()` instead")]
pub const HttpMultiStatus: StaticResponse = StaticResponse(StatusCode::MULTI_STATUS); pub const HttpMultiStatus: StaticResponse = StaticResponse(StatusCode::MULTI_STATUS);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::AlreadyReported()` instead")] since = "0.5.0", note = "please use `HttpResponse::AlreadyReported()` instead"
)]
pub const HttpAlreadyReported: StaticResponse = pub const HttpAlreadyReported: StaticResponse =
StaticResponse(StatusCode::ALREADY_REPORTED); StaticResponse(StatusCode::ALREADY_REPORTED);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::MultipleChoices()` instead")] since = "0.5.0", note = "please use `HttpResponse::MultipleChoices()` instead"
)]
pub const HttpMultipleChoices: StaticResponse = pub const HttpMultipleChoices: StaticResponse =
StaticResponse(StatusCode::MULTIPLE_CHOICES); StaticResponse(StatusCode::MULTIPLE_CHOICES);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::MovedPermanently()` instead")] since = "0.5.0", note = "please use `HttpResponse::MovedPermanently()` instead"
)]
pub const HttpMovedPermanently: StaticResponse = pub const HttpMovedPermanently: StaticResponse =
StaticResponse(StatusCode::MOVED_PERMANENTLY); StaticResponse(StatusCode::MOVED_PERMANENTLY);
#[deprecated(since = "0.5.0", note = "please use `HttpResponse::Found()` instead")] #[deprecated(since = "0.5.0", note = "please use `HttpResponse::Found()` instead")]
@ -50,106 +55,125 @@ pub const HttpSeeOther: StaticResponse = StaticResponse(StatusCode::SEE_OTHER);
pub const HttpNotModified: StaticResponse = StaticResponse(StatusCode::NOT_MODIFIED); pub const HttpNotModified: StaticResponse = StaticResponse(StatusCode::NOT_MODIFIED);
#[deprecated(since = "0.5.0", note = "please use `HttpResponse::UseProxy()` instead")] #[deprecated(since = "0.5.0", note = "please use `HttpResponse::UseProxy()` instead")]
pub const HttpUseProxy: StaticResponse = StaticResponse(StatusCode::USE_PROXY); pub const HttpUseProxy: StaticResponse = StaticResponse(StatusCode::USE_PROXY);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::TemporaryRedirect()` instead")] since = "0.5.0", note = "please use `HttpResponse::TemporaryRedirect()` instead"
)]
pub const HttpTemporaryRedirect: StaticResponse = pub const HttpTemporaryRedirect: StaticResponse =
StaticResponse(StatusCode::TEMPORARY_REDIRECT); StaticResponse(StatusCode::TEMPORARY_REDIRECT);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::PermanentRedirect()` instead")] since = "0.5.0", note = "please use `HttpResponse::PermanentRedirect()` instead"
)]
pub const HttpPermanentRedirect: StaticResponse = pub const HttpPermanentRedirect: StaticResponse =
StaticResponse(StatusCode::PERMANENT_REDIRECT); StaticResponse(StatusCode::PERMANENT_REDIRECT);
#[deprecated(since = "0.5.0", note = "please use `HttpResponse::BadRequest()` instead")] #[deprecated(since = "0.5.0", note = "please use `HttpResponse::BadRequest()` instead")]
pub const HttpBadRequest: StaticResponse = StaticResponse(StatusCode::BAD_REQUEST); pub const HttpBadRequest: StaticResponse = StaticResponse(StatusCode::BAD_REQUEST);
#[deprecated(since = "0.5.0", #[deprecated(since = "0.5.0", note = "please use `HttpResponse::Unauthorized()` instead")]
note = "please use `HttpResponse::Unauthorized()` instead")]
pub const HttpUnauthorized: StaticResponse = StaticResponse(StatusCode::UNAUTHORIZED); pub const HttpUnauthorized: StaticResponse = StaticResponse(StatusCode::UNAUTHORIZED);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::PaymentRequired()` instead")] since = "0.5.0", note = "please use `HttpResponse::PaymentRequired()` instead"
)]
pub const HttpPaymentRequired: StaticResponse = pub const HttpPaymentRequired: StaticResponse =
StaticResponse(StatusCode::PAYMENT_REQUIRED); StaticResponse(StatusCode::PAYMENT_REQUIRED);
#[deprecated(since = "0.5.0", note = "please use `HttpResponse::Forbidden()` instead")] #[deprecated(since = "0.5.0", note = "please use `HttpResponse::Forbidden()` instead")]
pub const HttpForbidden: StaticResponse = StaticResponse(StatusCode::FORBIDDEN); pub const HttpForbidden: StaticResponse = StaticResponse(StatusCode::FORBIDDEN);
#[deprecated(since = "0.5.0", note = "please use `HttpResponse::NotFound()` instead")] #[deprecated(since = "0.5.0", note = "please use `HttpResponse::NotFound()` instead")]
pub const HttpNotFound: StaticResponse = StaticResponse(StatusCode::NOT_FOUND); pub const HttpNotFound: StaticResponse = StaticResponse(StatusCode::NOT_FOUND);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::MethodNotAllowed()` instead")] since = "0.5.0", note = "please use `HttpResponse::MethodNotAllowed()` instead"
)]
pub const HttpMethodNotAllowed: StaticResponse = pub const HttpMethodNotAllowed: StaticResponse =
StaticResponse(StatusCode::METHOD_NOT_ALLOWED); StaticResponse(StatusCode::METHOD_NOT_ALLOWED);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::NotAcceptable()` instead")] since = "0.5.0", note = "please use `HttpResponse::NotAcceptable()` instead"
)]
pub const HttpNotAcceptable: StaticResponse = StaticResponse(StatusCode::NOT_ACCEPTABLE); pub const HttpNotAcceptable: StaticResponse = StaticResponse(StatusCode::NOT_ACCEPTABLE);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::ProxyAuthenticationRequired()` instead")] since = "0.5.0",
note = "please use `HttpResponse::ProxyAuthenticationRequired()` instead"
)]
pub const HttpProxyAuthenticationRequired: StaticResponse = pub const HttpProxyAuthenticationRequired: StaticResponse =
StaticResponse(StatusCode::PROXY_AUTHENTICATION_REQUIRED); StaticResponse(StatusCode::PROXY_AUTHENTICATION_REQUIRED);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::RequestTimeout()` instead")] since = "0.5.0", note = "please use `HttpResponse::RequestTimeout()` instead"
)]
pub const HttpRequestTimeout: StaticResponse = pub const HttpRequestTimeout: StaticResponse =
StaticResponse(StatusCode::REQUEST_TIMEOUT); StaticResponse(StatusCode::REQUEST_TIMEOUT);
#[deprecated(since = "0.5.0", note = "please use `HttpResponse::Conflict()` instead")] #[deprecated(since = "0.5.0", note = "please use `HttpResponse::Conflict()` instead")]
pub const HttpConflict: StaticResponse = StaticResponse(StatusCode::CONFLICT); pub const HttpConflict: StaticResponse = StaticResponse(StatusCode::CONFLICT);
#[deprecated(since = "0.5.0", note = "please use `HttpResponse::Gone()` instead")] #[deprecated(since = "0.5.0", note = "please use `HttpResponse::Gone()` instead")]
pub const HttpGone: StaticResponse = StaticResponse(StatusCode::GONE); pub const HttpGone: StaticResponse = StaticResponse(StatusCode::GONE);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::LengthRequired()` instead")] since = "0.5.0", note = "please use `HttpResponse::LengthRequired()` instead"
)]
pub const HttpLengthRequired: StaticResponse = pub const HttpLengthRequired: StaticResponse =
StaticResponse(StatusCode::LENGTH_REQUIRED); StaticResponse(StatusCode::LENGTH_REQUIRED);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::PreconditionFailed()` instead")] since = "0.5.0", note = "please use `HttpResponse::PreconditionFailed()` instead"
)]
pub const HttpPreconditionFailed: StaticResponse = pub const HttpPreconditionFailed: StaticResponse =
StaticResponse(StatusCode::PRECONDITION_FAILED); StaticResponse(StatusCode::PRECONDITION_FAILED);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::PayloadTooLarge()` instead")] since = "0.5.0", note = "please use `HttpResponse::PayloadTooLarge()` instead"
)]
pub const HttpPayloadTooLarge: StaticResponse = pub const HttpPayloadTooLarge: StaticResponse =
StaticResponse(StatusCode::PAYLOAD_TOO_LARGE); StaticResponse(StatusCode::PAYLOAD_TOO_LARGE);
#[deprecated(since = "0.5.0", note = "please use `HttpResponse::UriTooLong()` instead")] #[deprecated(since = "0.5.0", note = "please use `HttpResponse::UriTooLong()` instead")]
pub const HttpUriTooLong: StaticResponse = StaticResponse(StatusCode::URI_TOO_LONG); pub const HttpUriTooLong: StaticResponse = StaticResponse(StatusCode::URI_TOO_LONG);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::UnsupportedMediaType()` instead")] since = "0.5.0", note = "please use `HttpResponse::UnsupportedMediaType()` instead"
)]
pub const HttpUnsupportedMediaType: StaticResponse = pub const HttpUnsupportedMediaType: StaticResponse =
StaticResponse(StatusCode::UNSUPPORTED_MEDIA_TYPE); StaticResponse(StatusCode::UNSUPPORTED_MEDIA_TYPE);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::RangeNotSatisfiable()` instead")] since = "0.5.0", note = "please use `HttpResponse::RangeNotSatisfiable()` instead"
)]
pub const HttpRangeNotSatisfiable: StaticResponse = pub const HttpRangeNotSatisfiable: StaticResponse =
StaticResponse(StatusCode::RANGE_NOT_SATISFIABLE); StaticResponse(StatusCode::RANGE_NOT_SATISFIABLE);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::ExpectationFailed()` instead")] since = "0.5.0", note = "please use `HttpResponse::ExpectationFailed()` instead"
)]
pub const HttpExpectationFailed: StaticResponse = pub const HttpExpectationFailed: StaticResponse =
StaticResponse(StatusCode::EXPECTATION_FAILED); StaticResponse(StatusCode::EXPECTATION_FAILED);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::InternalServerError()` instead")] since = "0.5.0", note = "please use `HttpResponse::InternalServerError()` instead"
)]
pub const HttpInternalServerError: StaticResponse = pub const HttpInternalServerError: StaticResponse =
StaticResponse(StatusCode::INTERNAL_SERVER_ERROR); StaticResponse(StatusCode::INTERNAL_SERVER_ERROR);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::NotImplemented()` instead")] since = "0.5.0", note = "please use `HttpResponse::NotImplemented()` instead"
)]
pub const HttpNotImplemented: StaticResponse = pub const HttpNotImplemented: StaticResponse =
StaticResponse(StatusCode::NOT_IMPLEMENTED); StaticResponse(StatusCode::NOT_IMPLEMENTED);
#[deprecated(since = "0.5.0", note = "please use `HttpResponse::BadGateway()` instead")] #[deprecated(since = "0.5.0", note = "please use `HttpResponse::BadGateway()` instead")]
pub const HttpBadGateway: StaticResponse = StaticResponse(StatusCode::BAD_GATEWAY); pub const HttpBadGateway: StaticResponse = StaticResponse(StatusCode::BAD_GATEWAY);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::ServiceUnavailable()` instead")] since = "0.5.0", note = "please use `HttpResponse::ServiceUnavailable()` instead"
)]
pub const HttpServiceUnavailable: StaticResponse = pub const HttpServiceUnavailable: StaticResponse =
StaticResponse(StatusCode::SERVICE_UNAVAILABLE); StaticResponse(StatusCode::SERVICE_UNAVAILABLE);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::GatewayTimeout()` instead")] since = "0.5.0", note = "please use `HttpResponse::GatewayTimeout()` instead"
)]
pub const HttpGatewayTimeout: StaticResponse = pub const HttpGatewayTimeout: StaticResponse =
StaticResponse(StatusCode::GATEWAY_TIMEOUT); StaticResponse(StatusCode::GATEWAY_TIMEOUT);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::VersionNotSupported()` instead")] since = "0.5.0", note = "please use `HttpResponse::VersionNotSupported()` instead"
)]
pub const HttpVersionNotSupported: StaticResponse = pub const HttpVersionNotSupported: StaticResponse =
StaticResponse(StatusCode::HTTP_VERSION_NOT_SUPPORTED); StaticResponse(StatusCode::HTTP_VERSION_NOT_SUPPORTED);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::VariantAlsoNegotiates()` instead")] since = "0.5.0", note = "please use `HttpResponse::VariantAlsoNegotiates()` instead"
)]
pub const HttpVariantAlsoNegotiates: StaticResponse = pub const HttpVariantAlsoNegotiates: StaticResponse =
StaticResponse(StatusCode::VARIANT_ALSO_NEGOTIATES); StaticResponse(StatusCode::VARIANT_ALSO_NEGOTIATES);
#[deprecated(since = "0.5.0", #[deprecated(
note = "please use `HttpResponse::InsufficientStorage()` instead")] since = "0.5.0", note = "please use `HttpResponse::InsufficientStorage()` instead"
)]
pub const HttpInsufficientStorage: StaticResponse = pub const HttpInsufficientStorage: StaticResponse =
StaticResponse(StatusCode::INSUFFICIENT_STORAGE); StaticResponse(StatusCode::INSUFFICIENT_STORAGE);
#[deprecated(since = "0.5.0", #[deprecated(since = "0.5.0", note = "please use `HttpResponse::LoopDetected()` instead")]
note = "please use `HttpResponse::LoopDetected()` instead")]
pub const HttpLoopDetected: StaticResponse = StaticResponse(StatusCode::LOOP_DETECTED); pub const HttpLoopDetected: StaticResponse = StaticResponse(StatusCode::LOOP_DETECTED);
#[deprecated(since = "0.5.0", note = "please use `HttpResponse` instead")] #[deprecated(since = "0.5.0", note = "please use `HttpResponse` instead")]
@ -221,10 +245,7 @@ impl HttpResponse {
STATIC_RESP!(Ok, StatusCode::OK); STATIC_RESP!(Ok, StatusCode::OK);
STATIC_RESP!(Created, StatusCode::CREATED); STATIC_RESP!(Created, StatusCode::CREATED);
STATIC_RESP!(Accepted, StatusCode::ACCEPTED); STATIC_RESP!(Accepted, StatusCode::ACCEPTED);
STATIC_RESP!( STATIC_RESP!(NonAuthoritativeInformation, StatusCode::NON_AUTHORITATIVE_INFORMATION);
NonAuthoritativeInformation,
StatusCode::NON_AUTHORITATIVE_INFORMATION
);
STATIC_RESP!(NoContent, StatusCode::NO_CONTENT); STATIC_RESP!(NoContent, StatusCode::NO_CONTENT);
STATIC_RESP!(ResetContent, StatusCode::RESET_CONTENT); STATIC_RESP!(ResetContent, StatusCode::RESET_CONTENT);
@ -249,10 +270,7 @@ impl HttpResponse {
STATIC_RESP!(Forbidden, StatusCode::FORBIDDEN); STATIC_RESP!(Forbidden, StatusCode::FORBIDDEN);
STATIC_RESP!(MethodNotAllowed, StatusCode::METHOD_NOT_ALLOWED); STATIC_RESP!(MethodNotAllowed, StatusCode::METHOD_NOT_ALLOWED);
STATIC_RESP!(NotAcceptable, StatusCode::NOT_ACCEPTABLE); STATIC_RESP!(NotAcceptable, StatusCode::NOT_ACCEPTABLE);
STATIC_RESP!( STATIC_RESP!(ProxyAuthenticationRequired, StatusCode::PROXY_AUTHENTICATION_REQUIRED);
ProxyAuthenticationRequired,
StatusCode::PROXY_AUTHENTICATION_REQUIRED
);
STATIC_RESP!(RequestTimeout, StatusCode::REQUEST_TIMEOUT); STATIC_RESP!(RequestTimeout, StatusCode::REQUEST_TIMEOUT);
STATIC_RESP!(Conflict, StatusCode::CONFLICT); STATIC_RESP!(Conflict, StatusCode::CONFLICT);
STATIC_RESP!(Gone, StatusCode::GONE); STATIC_RESP!(Gone, StatusCode::GONE);
@ -260,10 +278,7 @@ impl HttpResponse {
STATIC_RESP!(PreconditionFailed, StatusCode::PRECONDITION_FAILED); STATIC_RESP!(PreconditionFailed, StatusCode::PRECONDITION_FAILED);
STATIC_RESP!(PayloadTooLarge, StatusCode::PAYLOAD_TOO_LARGE); STATIC_RESP!(PayloadTooLarge, StatusCode::PAYLOAD_TOO_LARGE);
STATIC_RESP!(UriTooLong, StatusCode::URI_TOO_LONG); STATIC_RESP!(UriTooLong, StatusCode::URI_TOO_LONG);
STATIC_RESP!( STATIC_RESP!(UnsupportedMediaType, StatusCode::UNSUPPORTED_MEDIA_TYPE);
UnsupportedMediaType,
StatusCode::UNSUPPORTED_MEDIA_TYPE
);
STATIC_RESP!(RangeNotSatisfiable, StatusCode::RANGE_NOT_SATISFIABLE); STATIC_RESP!(RangeNotSatisfiable, StatusCode::RANGE_NOT_SATISFIABLE);
STATIC_RESP!(ExpectationFailed, StatusCode::EXPECTATION_FAILED); STATIC_RESP!(ExpectationFailed, StatusCode::EXPECTATION_FAILED);
@ -272,14 +287,8 @@ impl HttpResponse {
STATIC_RESP!(BadGateway, StatusCode::BAD_GATEWAY); STATIC_RESP!(BadGateway, StatusCode::BAD_GATEWAY);
STATIC_RESP!(ServiceUnavailable, StatusCode::SERVICE_UNAVAILABLE); STATIC_RESP!(ServiceUnavailable, StatusCode::SERVICE_UNAVAILABLE);
STATIC_RESP!(GatewayTimeout, StatusCode::GATEWAY_TIMEOUT); STATIC_RESP!(GatewayTimeout, StatusCode::GATEWAY_TIMEOUT);
STATIC_RESP!( STATIC_RESP!(VersionNotSupported, StatusCode::HTTP_VERSION_NOT_SUPPORTED);
VersionNotSupported, STATIC_RESP!(VariantAlsoNegotiates, StatusCode::VARIANT_ALSO_NEGOTIATES);
StatusCode::HTTP_VERSION_NOT_SUPPORTED
);
STATIC_RESP!(
VariantAlsoNegotiates,
StatusCode::VARIANT_ALSO_NEGOTIATES
);
STATIC_RESP!(InsufficientStorage, StatusCode::INSUFFICIENT_STORAGE); STATIC_RESP!(InsufficientStorage, StatusCode::INSUFFICIENT_STORAGE);
STATIC_RESP!(LoopDetected, StatusCode::LOOP_DETECTED); STATIC_RESP!(LoopDetected, StatusCode::LOOP_DETECTED);
} }

View File

@ -1,8 +1,8 @@
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use encoding::EncodingRef;
use encoding::all::UTF_8; use encoding::all::UTF_8;
use encoding::label::encoding_from_whatwg_label; use encoding::label::encoding_from_whatwg_label;
use encoding::types::{DecoderTrap, Encoding}; use encoding::types::{DecoderTrap, Encoding};
use encoding::EncodingRef;
use futures::{Future, Poll, Stream}; use futures::{Future, Poll, Stream};
use http::{header, HeaderMap}; use http::{header, HeaderMap};
use http_range::HttpRange; use http_range::HttpRange;
@ -96,10 +96,8 @@ pub trait HttpMessage {
/// `size` is full size of response (file). /// `size` is full size of response (file).
fn range(&self, size: u64) -> Result<Vec<HttpRange>, HttpRangeError> { fn range(&self, size: u64) -> Result<Vec<HttpRange>, HttpRangeError> {
if let Some(range) = self.headers().get(header::RANGE) { if let Some(range) = self.headers().get(header::RANGE) {
HttpRange::parse( HttpRange::parse(unsafe { str::from_utf8_unchecked(range.as_bytes()) }, size)
unsafe { str::from_utf8_unchecked(range.as_bytes()) }, .map_err(|e| e.into())
size,
).map_err(|e| e.into())
} else { } else {
Ok(Vec::new()) Ok(Vec::new())
} }
@ -325,10 +323,7 @@ where
)); ));
} }
self.fut self.fut.as_mut().expect("UrlEncoded could not be used second time").poll()
.as_mut()
.expect("UrlEncoded could not be used second time")
.poll()
} }
} }
@ -385,8 +380,7 @@ where
if req.content_type().to_lowercase() != "application/x-www-form-urlencoded" { if req.content_type().to_lowercase() != "application/x-www-form-urlencoded" {
return Err(UrlencodedError::ContentType); return Err(UrlencodedError::ContentType);
} }
let encoding = req.encoding() let encoding = req.encoding().map_err(|_| UrlencodedError::ContentType)?;
.map_err(|_| UrlencodedError::ContentType)?;
// future // future
let limit = self.limit; let limit = self.limit;
@ -415,18 +409,15 @@ where
self.fut = Some(Box::new(fut)); self.fut = Some(Box::new(fut));
} }
self.fut self.fut.as_mut().expect("UrlEncoded could not be used second time").poll()
.as_mut()
.expect("UrlEncoded could not be used second time")
.poll()
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use encoding::Encoding;
use encoding::all::ISO_8859_2; use encoding::all::ISO_8859_2;
use encoding::Encoding;
use futures::Async; use futures::Async;
use http::{Method, Uri, Version}; use http::{Method, Uri, Version};
use httprequest::HttpRequest; use httprequest::HttpRequest;
@ -488,19 +479,13 @@ mod tests {
#[test] #[test]
fn test_encoding_error() { fn test_encoding_error() {
let req = TestRequest::with_header("content-type", "applicatjson").finish(); let req = TestRequest::with_header("content-type", "applicatjson").finish();
assert_eq!( assert_eq!(Some(ContentTypeError::ParseError), req.encoding().err());
Some(ContentTypeError::ParseError),
req.encoding().err()
);
let req = TestRequest::with_header( let req = TestRequest::with_header(
"content-type", "content-type",
"application/json; charset=kkkttktk", "application/json; charset=kkkttktk",
).finish(); ).finish();
assert_eq!( assert_eq!(Some(ContentTypeError::UnknownEncoding), req.encoding().err());
Some(ContentTypeError::UnknownEncoding),
req.encoding().err()
);
} }
#[test] #[test]
@ -621,8 +606,7 @@ mod tests {
"application/x-www-form-urlencoded", "application/x-www-form-urlencoded",
).header(header::CONTENT_LENGTH, "11") ).header(header::CONTENT_LENGTH, "11")
.finish(); .finish();
req.payload_mut() req.payload_mut().unread_data(Bytes::from_static(b"hello=world"));
.unread_data(Bytes::from_static(b"hello=world"));
let result = req.urlencoded::<Info>().poll().ok().unwrap(); let result = req.urlencoded::<Info>().poll().ok().unwrap();
assert_eq!( assert_eq!(
@ -637,8 +621,7 @@ mod tests {
"application/x-www-form-urlencoded; charset=utf-8", "application/x-www-form-urlencoded; charset=utf-8",
).header(header::CONTENT_LENGTH, "11") ).header(header::CONTENT_LENGTH, "11")
.finish(); .finish();
req.payload_mut() req.payload_mut().unread_data(Bytes::from_static(b"hello=world"));
.unread_data(Bytes::from_static(b"hello=world"));
let result = req.urlencoded().poll().ok().unwrap(); let result = req.urlencoded().poll().ok().unwrap();
assert_eq!( assert_eq!(
@ -664,16 +647,14 @@ mod tests {
} }
let mut req = HttpRequest::default(); let mut req = HttpRequest::default();
req.payload_mut() req.payload_mut().unread_data(Bytes::from_static(b"test"));
.unread_data(Bytes::from_static(b"test"));
match req.body().poll().ok().unwrap() { match req.body().poll().ok().unwrap() {
Async::Ready(bytes) => assert_eq!(bytes, Bytes::from_static(b"test")), Async::Ready(bytes) => assert_eq!(bytes, Bytes::from_static(b"test")),
_ => unreachable!("error"), _ => unreachable!("error"),
} }
let mut req = HttpRequest::default(); let mut req = HttpRequest::default();
req.payload_mut() req.payload_mut().unread_data(Bytes::from_static(b"11111111111111"));
.unread_data(Bytes::from_static(b"11111111111111"));
match req.body().limit(5).poll().err().unwrap() { match req.body().limit(5).poll().err().unwrap() {
PayloadError::Overflow => (), PayloadError::Overflow => (),
_ => unreachable!("error"), _ => unreachable!("error"),

View File

@ -1,4 +1,5 @@
//! HTTP Request message related code. //! HTTP Request message related code.
#![cfg_attr(feature = "cargo-clippy", allow(transmute_ptr_to_ptr))]
use bytes::Bytes; use bytes::Bytes;
use cookie::Cookie; use cookie::Cookie;
use failure; use failure;
@ -314,7 +315,7 @@ impl<S> HttpRequest<S> {
/// } /// }
/// ``` /// ```
pub fn url_for<U, I>( pub fn url_for<U, I>(
&self, name: &str, elements: U &self, name: &str, elements: U,
) -> Result<Url, UrlGenerationError> ) -> Result<Url, UrlGenerationError>
where where
U: IntoIterator<Item = I>, U: IntoIterator<Item = I>,
@ -326,12 +327,7 @@ impl<S> HttpRequest<S> {
let path = self.router().unwrap().resource_path(name, elements)?; let path = self.router().unwrap().resource_path(name, elements)?;
if path.starts_with('/') { if path.starts_with('/') {
let conn = self.connection_info(); let conn = self.connection_info();
Ok(Url::parse(&format!( Ok(Url::parse(&format!("{}://{}{}", conn.scheme(), conn.host(), path))?)
"{}://{}{}",
conn.scheme(),
conn.host(),
path
))?)
} else { } else {
Ok(Url::parse(&path)?) Ok(Url::parse(&path)?)
} }
@ -681,12 +677,8 @@ mod tests {
let mut resource = ResourceHandler::<()>::default(); let mut resource = ResourceHandler::<()>::default();
resource.name("index"); resource.name("index");
let routes = vec![ let routes =
( vec![(Resource::new("index", "/user/{name}.{ext}"), Some(resource))];
Resource::new("index", "/user/{name}.{ext}"),
Some(resource),
),
];
let (router, _) = Router::new("/", ServerSettings::default(), routes); let (router, _) = Router::new("/", ServerSettings::default(), routes);
assert!(router.has_route("/user/test.html")); assert!(router.has_route("/user/test.html"));
assert!(!router.has_route("/test/unknown")); assert!(!router.has_route("/test/unknown"));
@ -715,12 +707,8 @@ mod tests {
let mut resource = ResourceHandler::<()>::default(); let mut resource = ResourceHandler::<()>::default();
resource.name("index"); resource.name("index");
let routes = vec![ let routes =
( vec![(Resource::new("index", "/user/{name}.{ext}"), Some(resource))];
Resource::new("index", "/user/{name}.{ext}"),
Some(resource),
),
];
let (router, _) = Router::new("/prefix/", ServerSettings::default(), routes); let (router, _) = Router::new("/prefix/", ServerSettings::default(), routes);
assert!(router.has_route("/user/test.html")); assert!(router.has_route("/user/test.html"));
assert!(!router.has_route("/prefix/user/test.html")); assert!(!router.has_route("/prefix/user/test.html"));
@ -739,20 +727,15 @@ mod tests {
let mut resource = ResourceHandler::<()>::default(); let mut resource = ResourceHandler::<()>::default();
resource.name("index"); resource.name("index");
let routes = vec![ let routes = vec![(
( Resource::external("youtube", "https://youtube.com/watch/{video_id}"),
Resource::external("youtube", "https://youtube.com/watch/{video_id}"), None,
None, )];
),
];
let (router, _) = Router::new::<()>("", ServerSettings::default(), routes); let (router, _) = Router::new::<()>("", ServerSettings::default(), routes);
assert!(!router.has_route("https://youtube.com/watch/unknown")); assert!(!router.has_route("https://youtube.com/watch/unknown"));
let req = req.with_state(Rc::new(()), router); let req = req.with_state(Rc::new(()), router);
let url = req.url_for("youtube", &["oHg5SJYRHA0"]); let url = req.url_for("youtube", &["oHg5SJYRHA0"]);
assert_eq!( assert_eq!(url.ok().unwrap().as_str(), "https://youtube.com/watch/oHg5SJYRHA0");
url.ok().unwrap().as_str(),
"https://youtube.com/watch/oHg5SJYRHA0"
);
} }
} }

View File

@ -150,10 +150,7 @@ impl HttpResponse {
if let Some(reason) = self.get_ref().reason { if let Some(reason) = self.get_ref().reason {
reason reason
} else { } else {
self.get_ref() self.get_ref().status.canonical_reason().unwrap_or("<unknown status code>")
.status
.canonical_reason()
.unwrap_or("<unknown status code>")
} }
} }
@ -466,10 +463,7 @@ impl HttpResponseBuilder {
jar.add(cookie.into_owned()); jar.add(cookie.into_owned());
self.cookies = Some(jar) self.cookies = Some(jar)
} else { } else {
self.cookies self.cookies.as_mut().unwrap().add(cookie.into_owned());
.as_mut()
.unwrap()
.add(cookie.into_owned());
} }
self self
} }
@ -534,9 +528,7 @@ impl HttpResponseBuilder {
if let Some(e) = self.err.take() { if let Some(e) = self.err.take() {
return Error::from(e).into(); return Error::from(e).into();
} }
let mut response = self.response let mut response = self.response.take().expect("cannot reuse response builder");
.take()
.expect("cannot reuse response builder");
if let Some(ref jar) = self.cookies { if let Some(ref jar) = self.cookies {
for cookie in jar.delta() { for cookie in jar.delta() {
match HeaderValue::from_str(&cookie.to_string()) { match HeaderValue::from_str(&cookie.to_string()) {
@ -558,9 +550,7 @@ impl HttpResponseBuilder {
S: Stream<Item = Bytes, Error = E> + 'static, S: Stream<Item = Bytes, Error = E> + 'static,
E: Into<Error>, E: Into<Error>,
{ {
self.body(Body::Streaming(Box::new( self.body(Body::Streaming(Box::new(stream.map_err(|e| e.into()))))
stream.map_err(|e| e.into()),
)))
} }
/// Set a json body and generate `HttpResponse` /// Set a json body and generate `HttpResponse`
@ -607,7 +597,7 @@ impl HttpResponseBuilder {
#[inline] #[inline]
#[cfg_attr(feature = "cargo-clippy", allow(borrowed_box))] #[cfg_attr(feature = "cargo-clippy", allow(borrowed_box))]
fn parts<'a>( fn parts<'a>(
parts: &'a mut Option<Box<InnerHttpResponse>>, err: &Option<HttpError> parts: &'a mut Option<Box<InnerHttpResponse>>, err: &Option<HttpError>,
) -> Option<&'a mut Box<InnerHttpResponse>> { ) -> Option<&'a mut Box<InnerHttpResponse>> {
if err.is_some() { if err.is_some() {
return None; return None;
@ -643,9 +633,7 @@ impl Responder for HttpResponseBuilder {
impl From<&'static str> for HttpResponse { impl From<&'static str> for HttpResponse {
fn from(val: &'static str) -> Self { fn from(val: &'static str) -> Self {
HttpResponse::Ok() HttpResponse::Ok().content_type("text/plain; charset=utf-8").body(val)
.content_type("text/plain; charset=utf-8")
.body(val)
} }
} }
@ -662,9 +650,7 @@ impl Responder for &'static str {
impl From<&'static [u8]> for HttpResponse { impl From<&'static [u8]> for HttpResponse {
fn from(val: &'static [u8]) -> Self { fn from(val: &'static [u8]) -> Self {
HttpResponse::Ok() HttpResponse::Ok().content_type("application/octet-stream").body(val)
.content_type("application/octet-stream")
.body(val)
} }
} }
@ -681,9 +667,7 @@ impl Responder for &'static [u8] {
impl From<String> for HttpResponse { impl From<String> for HttpResponse {
fn from(val: String) -> Self { fn from(val: String) -> Self {
HttpResponse::Ok() HttpResponse::Ok().content_type("text/plain; charset=utf-8").body(val)
.content_type("text/plain; charset=utf-8")
.body(val)
} }
} }
@ -719,9 +703,7 @@ impl<'a> Responder for &'a String {
impl From<Bytes> for HttpResponse { impl From<Bytes> for HttpResponse {
fn from(val: Bytes) -> Self { fn from(val: Bytes) -> Self {
HttpResponse::Ok() HttpResponse::Ok().content_type("application/octet-stream").body(val)
.content_type("application/octet-stream")
.body(val)
} }
} }
@ -738,9 +720,7 @@ impl Responder for Bytes {
impl From<BytesMut> for HttpResponse { impl From<BytesMut> for HttpResponse {
fn from(val: BytesMut) -> Self { fn from(val: BytesMut) -> Self {
HttpResponse::Ok() HttpResponse::Ok().content_type("application/octet-stream").body(val)
.content_type("application/octet-stream")
.body(val)
} }
} }
@ -772,9 +752,7 @@ impl<'a> From<&'a ClientResponse> for HttpResponseBuilder {
impl<'a, S> From<&'a HttpRequest<S>> for HttpResponseBuilder { impl<'a, S> From<&'a HttpRequest<S>> for HttpResponseBuilder {
fn from(req: &'a HttpRequest<S>) -> HttpResponseBuilder { fn from(req: &'a HttpRequest<S>) -> HttpResponseBuilder {
if let Some(router) = req.router() { if let Some(router) = req.router() {
router router.server_settings().get_response_builder(StatusCode::OK)
.server_settings()
.get_response_builder(StatusCode::OK)
} else { } else {
HttpResponse::Ok() HttpResponse::Ok()
} }
@ -822,14 +800,12 @@ thread_local!(static POOL: Rc<UnsafeCell<HttpResponsePool>> = HttpResponsePool::
impl HttpResponsePool { impl HttpResponsePool {
pub fn pool() -> Rc<UnsafeCell<HttpResponsePool>> { pub fn pool() -> Rc<UnsafeCell<HttpResponsePool>> {
Rc::new(UnsafeCell::new(HttpResponsePool( Rc::new(UnsafeCell::new(HttpResponsePool(VecDeque::with_capacity(128))))
VecDeque::with_capacity(128),
)))
} }
#[inline] #[inline]
pub fn get_builder( pub fn get_builder(
pool: &Rc<UnsafeCell<HttpResponsePool>>, status: StatusCode pool: &Rc<UnsafeCell<HttpResponsePool>>, status: StatusCode,
) -> HttpResponseBuilder { ) -> HttpResponseBuilder {
let p = unsafe { &mut *pool.as_ref().get() }; let p = unsafe { &mut *pool.as_ref().get() };
if let Some(mut msg) = p.0.pop_front() { if let Some(mut msg) = p.0.pop_front() {
@ -853,7 +829,7 @@ impl HttpResponsePool {
#[inline] #[inline]
pub fn get_response( pub fn get_response(
pool: &Rc<UnsafeCell<HttpResponsePool>>, status: StatusCode, body: Body pool: &Rc<UnsafeCell<HttpResponsePool>>, status: StatusCode, body: Body,
) -> HttpResponse { ) -> HttpResponse {
let p = unsafe { &mut *pool.as_ref().get() }; let p = unsafe { &mut *pool.as_ref().get() };
if let Some(mut msg) = p.0.pop_front() { if let Some(mut msg) = p.0.pop_front() {
@ -879,7 +855,7 @@ impl HttpResponsePool {
#[inline(always)] #[inline(always)]
#[cfg_attr(feature = "cargo-clippy", allow(boxed_local, inline_always))] #[cfg_attr(feature = "cargo-clippy", allow(boxed_local, inline_always))]
fn release( fn release(
pool: &Rc<UnsafeCell<HttpResponsePool>>, mut inner: Box<InnerHttpResponse> pool: &Rc<UnsafeCell<HttpResponsePool>>, mut inner: Box<InnerHttpResponse>,
) { ) {
let pool = unsafe { &mut *pool.as_ref().get() }; let pool = unsafe { &mut *pool.as_ref().get() };
if pool.0.len() < 128 { if pool.0.len() < 128 {
@ -975,9 +951,7 @@ mod tests {
#[test] #[test]
fn test_force_close() { fn test_force_close() {
let resp = HttpResponse::build(StatusCode::OK) let resp = HttpResponse::build(StatusCode::OK).force_close().finish();
.force_close()
.finish();
assert!(!resp.keep_alive().unwrap()) assert!(!resp.keep_alive().unwrap())
} }
@ -986,10 +960,7 @@ mod tests {
let resp = HttpResponse::build(StatusCode::OK) let resp = HttpResponse::build(StatusCode::OK)
.content_type("text/plain") .content_type("text/plain")
.body(Body::Empty); .body(Body::Empty);
assert_eq!( assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "text/plain")
resp.headers().get(CONTENT_TYPE).unwrap(),
"text/plain"
)
} }
#[test] #[test]
@ -1073,10 +1044,7 @@ mod tests {
HeaderValue::from_static("application/octet-stream") HeaderValue::from_static("application/octet-stream")
); );
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!( assert_eq!(resp.body().binary().unwrap(), &Binary::from(b"test".as_ref()));
resp.body().binary().unwrap(),
&Binary::from(b"test".as_ref())
);
let resp: HttpResponse = b"test".as_ref().respond_to(req.clone()).ok().unwrap(); let resp: HttpResponse = b"test".as_ref().respond_to(req.clone()).ok().unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
@ -1085,10 +1053,7 @@ mod tests {
HeaderValue::from_static("application/octet-stream") HeaderValue::from_static("application/octet-stream")
); );
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!( assert_eq!(resp.body().binary().unwrap(), &Binary::from(b"test".as_ref()));
resp.body().binary().unwrap(),
&Binary::from(b"test".as_ref())
);
let resp: HttpResponse = "test".to_owned().into(); let resp: HttpResponse = "test".to_owned().into();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
@ -1097,26 +1062,16 @@ mod tests {
HeaderValue::from_static("text/plain; charset=utf-8") HeaderValue::from_static("text/plain; charset=utf-8")
); );
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!( assert_eq!(resp.body().binary().unwrap(), &Binary::from("test".to_owned()));
resp.body().binary().unwrap(),
&Binary::from("test".to_owned())
);
let resp: HttpResponse = "test" let resp: HttpResponse = "test".to_owned().respond_to(req.clone()).ok().unwrap();
.to_owned()
.respond_to(req.clone())
.ok()
.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!( assert_eq!(
resp.headers().get(CONTENT_TYPE).unwrap(), resp.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("text/plain; charset=utf-8") HeaderValue::from_static("text/plain; charset=utf-8")
); );
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!( assert_eq!(resp.body().binary().unwrap(), &Binary::from("test".to_owned()));
resp.body().binary().unwrap(),
&Binary::from("test".to_owned())
);
let resp: HttpResponse = (&"test".to_owned()).into(); let resp: HttpResponse = (&"test".to_owned()).into();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
@ -1125,25 +1080,17 @@ mod tests {
HeaderValue::from_static("text/plain; charset=utf-8") HeaderValue::from_static("text/plain; charset=utf-8")
); );
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!( assert_eq!(resp.body().binary().unwrap(), &Binary::from(&"test".to_owned()));
resp.body().binary().unwrap(),
&Binary::from(&"test".to_owned())
);
let resp: HttpResponse = (&"test".to_owned()) let resp: HttpResponse =
.respond_to(req.clone()) (&"test".to_owned()).respond_to(req.clone()).ok().unwrap();
.ok()
.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!( assert_eq!(
resp.headers().get(CONTENT_TYPE).unwrap(), resp.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("text/plain; charset=utf-8") HeaderValue::from_static("text/plain; charset=utf-8")
); );
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!( assert_eq!(resp.body().binary().unwrap(), &Binary::from(&"test".to_owned()));
resp.body().binary().unwrap(),
&Binary::from(&"test".to_owned())
);
let b = Bytes::from_static(b"test"); let b = Bytes::from_static(b"test");
let resp: HttpResponse = b.into(); let resp: HttpResponse = b.into();
@ -1179,10 +1126,7 @@ mod tests {
HeaderValue::from_static("application/octet-stream") HeaderValue::from_static("application/octet-stream")
); );
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!( assert_eq!(resp.body().binary().unwrap(), &Binary::from(BytesMut::from("test")));
resp.body().binary().unwrap(),
&Binary::from(BytesMut::from("test"))
);
let b = BytesMut::from("test"); let b = BytesMut::from("test");
let resp: HttpResponse = b.respond_to(req.clone()).ok().unwrap(); let resp: HttpResponse = b.respond_to(req.clone()).ok().unwrap();
@ -1192,10 +1136,7 @@ mod tests {
HeaderValue::from_static("application/octet-stream") HeaderValue::from_static("application/octet-stream")
); );
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!( assert_eq!(resp.body().binary().unwrap(), &Binary::from(BytesMut::from("test")));
resp.body().binary().unwrap(),
&Binary::from(BytesMut::from("test"))
);
} }
#[test] #[test]

View File

@ -53,8 +53,8 @@ impl<'a> ConnectionInfo<'a> {
// scheme // scheme
if scheme.is_none() { if scheme.is_none() {
if let Some(h) = req.headers() if let Some(h) =
.get(HeaderName::from_str(X_FORWARDED_PROTO).unwrap()) req.headers().get(HeaderName::from_str(X_FORWARDED_PROTO).unwrap())
{ {
if let Ok(h) = h.to_str() { if let Ok(h) = h.to_str() {
scheme = h.split(',').next().map(|v| v.trim()); scheme = h.split(',').next().map(|v| v.trim());
@ -74,8 +74,8 @@ impl<'a> ConnectionInfo<'a> {
// host // host
if host.is_none() { if host.is_none() {
if let Some(h) = req.headers() if let Some(h) =
.get(HeaderName::from_str(X_FORWARDED_HOST).unwrap()) req.headers().get(HeaderName::from_str(X_FORWARDED_HOST).unwrap())
{ {
if let Ok(h) = h.to_str() { if let Ok(h) = h.to_str() {
host = h.split(',').next().map(|v| v.trim()); host = h.split(',').next().map(|v| v.trim());
@ -98,8 +98,8 @@ impl<'a> ConnectionInfo<'a> {
// remote addr // remote addr
if remote.is_none() { if remote.is_none() {
if let Some(h) = req.headers() if let Some(h) =
.get(HeaderName::from_str(X_FORWARDED_FOR).unwrap()) req.headers().get(HeaderName::from_str(X_FORWARDED_FOR).unwrap())
{ {
if let Ok(h) = h.to_str() { if let Ok(h) = h.to_str() {
remote = h.split(',').next().map(|v| v.trim()); remote = h.split(',').next().map(|v| v.trim());
@ -189,10 +189,8 @@ mod tests {
assert_eq!(info.remote(), Some("192.0.2.60")); assert_eq!(info.remote(), Some("192.0.2.60"));
let mut req = HttpRequest::default(); let mut req = HttpRequest::default();
req.headers_mut().insert( req.headers_mut()
header::HOST, .insert(header::HOST, HeaderValue::from_static("rust-lang.org"));
HeaderValue::from_static("rust-lang.org"),
);
let info = ConnectionInfo::new(&req); let info = ConnectionInfo::new(&req);
assert_eq!(info.scheme(), "http"); assert_eq!(info.scheme(), "http");

View File

@ -6,8 +6,8 @@ use std::ops::{Deref, DerefMut};
use std::rc::Rc; use std::rc::Rc;
use mime; use mime;
use serde::Serialize;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json; use serde_json;
use error::{Error, JsonPayloadError, PayloadError}; use error::{Error, JsonPayloadError, PayloadError};
@ -308,10 +308,7 @@ where
self.fut = Some(Box::new(fut)); self.fut = Some(Box::new(fut));
} }
self.fut self.fut.as_mut().expect("JsonBody could not be used second time").poll()
.as_mut()
.expect("JsonBody could not be used second time")
.poll()
} }
} }
@ -362,10 +359,7 @@ mod tests {
fn test_json_body() { fn test_json_body() {
let req = HttpRequest::default(); let req = HttpRequest::default();
let mut json = req.json::<MyObject>(); let mut json = req.json::<MyObject>();
assert_eq!( assert_eq!(json.poll().err().unwrap(), JsonPayloadError::ContentType);
json.poll().err().unwrap(),
JsonPayloadError::ContentType
);
let mut req = HttpRequest::default(); let mut req = HttpRequest::default();
req.headers_mut().insert( req.headers_mut().insert(
@ -373,20 +367,15 @@ mod tests {
header::HeaderValue::from_static("application/text"), header::HeaderValue::from_static("application/text"),
); );
let mut json = req.json::<MyObject>(); let mut json = req.json::<MyObject>();
assert_eq!( assert_eq!(json.poll().err().unwrap(), JsonPayloadError::ContentType);
json.poll().err().unwrap(),
JsonPayloadError::ContentType
);
let mut req = HttpRequest::default(); let mut req = HttpRequest::default();
req.headers_mut().insert( req.headers_mut().insert(
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"), header::HeaderValue::from_static("application/json"),
); );
req.headers_mut().insert( req.headers_mut()
header::CONTENT_LENGTH, .insert(header::CONTENT_LENGTH, header::HeaderValue::from_static("10000"));
header::HeaderValue::from_static("10000"),
);
let mut json = req.json::<MyObject>().limit(100); let mut json = req.json::<MyObject>().limit(100);
assert_eq!(json.poll().err().unwrap(), JsonPayloadError::Overflow); assert_eq!(json.poll().err().unwrap(), JsonPayloadError::Overflow);
@ -395,12 +384,9 @@ mod tests {
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"), header::HeaderValue::from_static("application/json"),
); );
req.headers_mut().insert( req.headers_mut()
header::CONTENT_LENGTH, .insert(header::CONTENT_LENGTH, header::HeaderValue::from_static("16"));
header::HeaderValue::from_static("16"), req.payload_mut().unread_data(Bytes::from_static(b"{\"name\": \"test\"}"));
);
req.payload_mut()
.unread_data(Bytes::from_static(b"{\"name\": \"test\"}"));
let mut json = req.json::<MyObject>(); let mut json = req.json::<MyObject>();
assert_eq!( assert_eq!(
json.poll().ok().unwrap(), json.poll().ok().unwrap(),
@ -417,12 +403,7 @@ mod tests {
let mut handler = With::new(|data: Json<MyObject>| data, cfg); let mut handler = With::new(|data: Json<MyObject>| data, cfg);
let req = HttpRequest::default(); let req = HttpRequest::default();
let err = handler let err = handler.handle(req).as_response().unwrap().error().is_some();
.handle(req)
.as_response()
.unwrap()
.error()
.is_some();
assert!(err); assert!(err);
let mut req = HttpRequest::default(); let mut req = HttpRequest::default();
@ -430,18 +411,10 @@ mod tests {
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"), header::HeaderValue::from_static("application/json"),
); );
req.headers_mut().insert( req.headers_mut()
header::CONTENT_LENGTH, .insert(header::CONTENT_LENGTH, header::HeaderValue::from_static("16"));
header::HeaderValue::from_static("16"), req.payload_mut().unread_data(Bytes::from_static(b"{\"name\": \"test\"}"));
); let ok = handler.handle(req).as_response().unwrap().error().is_none();
req.payload_mut()
.unread_data(Bytes::from_static(b"{\"name\": \"test\"}"));
let ok = handler
.handle(req)
.as_response()
.unwrap()
.error()
.is_none();
assert!(ok) assert!(ok)
} }
} }

View File

@ -64,26 +64,36 @@ use resource::ResourceHandler;
#[derive(Debug, Fail)] #[derive(Debug, Fail)]
pub enum CorsError { pub enum CorsError {
/// The HTTP request header `Origin` is required but was not provided /// The HTTP request header `Origin` is required but was not provided
#[fail(display = "The HTTP request header `Origin` is required but was not provided")] #[fail(
display = "The HTTP request header `Origin` is required but was not provided"
)]
MissingOrigin, MissingOrigin,
/// The HTTP request header `Origin` could not be parsed correctly. /// The HTTP request header `Origin` could not be parsed correctly.
#[fail(display = "The HTTP request header `Origin` could not be parsed correctly.")] #[fail(display = "The HTTP request header `Origin` could not be parsed correctly.")]
BadOrigin, BadOrigin,
/// The request header `Access-Control-Request-Method` is required but is /// The request header `Access-Control-Request-Method` is required but is
/// missing /// missing
#[fail(display = "The request header `Access-Control-Request-Method` is required but is missing")] #[fail(
display = "The request header `Access-Control-Request-Method` is required but is missing"
)]
MissingRequestMethod, MissingRequestMethod,
/// The request header `Access-Control-Request-Method` has an invalid value /// The request header `Access-Control-Request-Method` has an invalid value
#[fail(display = "The request header `Access-Control-Request-Method` has an invalid value")] #[fail(
display = "The request header `Access-Control-Request-Method` has an invalid value"
)]
BadRequestMethod, BadRequestMethod,
/// The request header `Access-Control-Request-Headers` has an invalid /// The request header `Access-Control-Request-Headers` has an invalid
/// value /// value
#[fail(display = "The request header `Access-Control-Request-Headers` has an invalid value")] #[fail(
display = "The request header `Access-Control-Request-Headers` has an invalid value"
)]
BadRequestHeaders, BadRequestHeaders,
/// The request header `Access-Control-Request-Headers` is required but is /// The request header `Access-Control-Request-Headers` is required but is
/// missing. /// missing.
#[fail(display = "The request header `Access-Control-Request-Headers` is required but is #[fail(
missing")] display = "The request header `Access-Control-Request-Headers` is required but is
missing"
)]
MissingRequestHeaders, MissingRequestHeaders,
/// Origin is not allowed to make this request /// Origin is not allowed to make this request
#[fail(display = "Origin is not allowed to make this request")] #[fail(display = "Origin is not allowed to make this request")]
@ -265,9 +275,7 @@ impl Cors {
/// `ResourceHandler::middleware()` method, but in that case *Cors* /// `ResourceHandler::middleware()` method, but in that case *Cors*
/// middleware wont be able to handle *OPTIONS* requests. /// middleware wont be able to handle *OPTIONS* requests.
pub fn register<S: 'static>(self, resource: &mut ResourceHandler<S>) { pub fn register<S: 'static>(self, resource: &mut ResourceHandler<S>) {
resource resource.method(Method::OPTIONS).h(|_| HttpResponse::Ok());
.method(Method::OPTIONS)
.h(|_| HttpResponse::Ok());
resource.middleware(self); resource.middleware(self);
} }
@ -292,11 +300,9 @@ impl Cors {
} }
fn validate_allowed_method<S>( fn validate_allowed_method<S>(
&self, req: &mut HttpRequest<S> &self, req: &mut HttpRequest<S>,
) -> Result<(), CorsError> { ) -> Result<(), CorsError> {
if let Some(hdr) = req.headers() if let Some(hdr) = req.headers().get(header::ACCESS_CONTROL_REQUEST_METHOD) {
.get(header::ACCESS_CONTROL_REQUEST_METHOD)
{
if let Ok(meth) = hdr.to_str() { if let Ok(meth) = hdr.to_str() {
if let Ok(method) = Method::try_from(meth) { if let Ok(method) = Method::try_from(meth) {
return self.inner return self.inner
@ -313,13 +319,13 @@ impl Cors {
} }
fn validate_allowed_headers<S>( fn validate_allowed_headers<S>(
&self, req: &mut HttpRequest<S> &self, req: &mut HttpRequest<S>,
) -> Result<(), CorsError> { ) -> Result<(), CorsError> {
match self.inner.headers { match self.inner.headers {
AllOrSome::All => Ok(()), AllOrSome::All => Ok(()),
AllOrSome::Some(ref allowed_headers) => { AllOrSome::Some(ref allowed_headers) => {
if let Some(hdr) = req.headers() if let Some(hdr) =
.get(header::ACCESS_CONTROL_REQUEST_HEADERS) req.headers().get(header::ACCESS_CONTROL_REQUEST_HEADERS)
{ {
if let Ok(headers) = hdr.to_str() { if let Ok(headers) = hdr.to_str() {
let mut hdrs = HashSet::new(); let mut hdrs = HashSet::new();
@ -361,8 +367,8 @@ impl<S> Middleware<S> for Cors {
.as_str()[1..], .as_str()[1..],
).unwrap(), ).unwrap(),
) )
} else if let Some(hdr) = req.headers() } else if let Some(hdr) =
.get(header::ACCESS_CONTROL_REQUEST_HEADERS) req.headers().get(header::ACCESS_CONTROL_REQUEST_HEADERS)
{ {
Some(hdr.clone()) Some(hdr.clone())
} else { } else {
@ -419,7 +425,7 @@ impl<S> Middleware<S> for Cors {
} }
fn response( fn response(
&self, req: &mut HttpRequest<S>, mut resp: HttpResponse &self, req: &mut HttpRequest<S>, mut resp: HttpResponse,
) -> Result<Response> { ) -> Result<Response> {
match self.inner.origins { match self.inner.origins {
AllOrSome::All => { AllOrSome::All => {
@ -506,7 +512,7 @@ pub struct CorsBuilder<S = ()> {
} }
fn cors<'a>( fn cors<'a>(
parts: &'a mut Option<Inner>, err: &Option<http::Error> parts: &'a mut Option<Inner>, err: &Option<http::Error>,
) -> Option<&'a mut Inner> { ) -> Option<&'a mut Inner> {
if err.is_some() { if err.is_some() {
return None; return None;
@ -813,17 +819,13 @@ impl<S: 'static> CorsBuilder<S> {
} }
if let AllOrSome::Some(ref origins) = cors.origins { if let AllOrSome::Some(ref origins) = cors.origins {
let s = origins let s = origins.iter().fold(String::new(), |s, v| s + &v.to_string());
.iter()
.fold(String::new(), |s, v| s + &format!("{}", v));
cors.origins_str = Some(HeaderValue::try_from(s.as_str()).unwrap()); cors.origins_str = Some(HeaderValue::try_from(s.as_str()).unwrap());
} }
if !self.expose_hdrs.is_empty() { if !self.expose_hdrs.is_empty() {
cors.expose_hdrs = Some( cors.expose_hdrs = Some(
self.expose_hdrs self.expose_hdrs.iter().fold(String::new(), |s, v| s + v.as_str())[1..]
.iter()
.fold(String::new(), |s, v| s + v.as_str())[1..]
.to_owned(), .to_owned(),
); );
} }
@ -901,27 +903,19 @@ mod tests {
#[test] #[test]
#[should_panic(expected = "Credentials are allowed, but the Origin is set to")] #[should_panic(expected = "Credentials are allowed, but the Origin is set to")]
fn cors_validates_illegal_allow_credentials() { fn cors_validates_illegal_allow_credentials() {
Cors::build() Cors::build().supports_credentials().send_wildcard().finish();
.supports_credentials()
.send_wildcard()
.finish();
} }
#[test] #[test]
#[should_panic(expected = "No resources are registered")] #[should_panic(expected = "No resources are registered")]
fn no_resource() { fn no_resource() {
Cors::build() Cors::build().supports_credentials().send_wildcard().register();
.supports_credentials()
.send_wildcard()
.register();
} }
#[test] #[test]
#[should_panic(expected = "Cors::for_app(app)")] #[should_panic(expected = "Cors::for_app(app)")]
fn no_resource2() { fn no_resource2() {
Cors::build() Cors::build().resource("/test", |r| r.f(|_| HttpResponse::Ok())).register();
.resource("/test", |r| r.f(|_| HttpResponse::Ok()))
.register();
} }
#[test] #[test]
@ -958,27 +952,18 @@ mod tests {
let mut req = TestRequest::with_header("Origin", "https://www.example.com") let mut req = TestRequest::with_header("Origin", "https://www.example.com")
.header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST")
.header( .header(header::ACCESS_CONTROL_REQUEST_HEADERS, "AUTHORIZATION,ACCEPT")
header::ACCESS_CONTROL_REQUEST_HEADERS,
"AUTHORIZATION,ACCEPT",
)
.method(Method::OPTIONS) .method(Method::OPTIONS)
.finish(); .finish();
let resp = cors.start(&mut req).unwrap().response(); let resp = cors.start(&mut req).unwrap().response();
assert_eq!( assert_eq!(
&b"*"[..], &b"*"[..],
resp.headers() resp.headers().get(header::ACCESS_CONTROL_ALLOW_ORIGIN).unwrap().as_bytes()
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
.unwrap()
.as_bytes()
); );
assert_eq!( assert_eq!(
&b"3600"[..], &b"3600"[..],
resp.headers() resp.headers().get(header::ACCESS_CONTROL_MAX_AGE).unwrap().as_bytes()
.get(header::ACCESS_CONTROL_MAX_AGE)
.unwrap()
.as_bytes()
); );
//assert_eq!( //assert_eq!(
// &b"authorization,accept,content-type"[..], // &b"authorization,accept,content-type"[..],
@ -995,9 +980,7 @@ mod tests {
#[test] #[test]
#[should_panic(expected = "MissingOrigin")] #[should_panic(expected = "MissingOrigin")]
fn test_validate_missing_origin() { fn test_validate_missing_origin() {
let cors = Cors::build() let cors = Cors::build().allowed_origin("https://www.example.com").finish();
.allowed_origin("https://www.example.com")
.finish();
let mut req = HttpRequest::default(); let mut req = HttpRequest::default();
cors.start(&mut req).unwrap(); cors.start(&mut req).unwrap();
@ -1006,9 +989,7 @@ mod tests {
#[test] #[test]
#[should_panic(expected = "OriginNotAllowed")] #[should_panic(expected = "OriginNotAllowed")]
fn test_validate_not_allowed_origin() { fn test_validate_not_allowed_origin() {
let cors = Cors::build() let cors = Cors::build().allowed_origin("https://www.example.com").finish();
.allowed_origin("https://www.example.com")
.finish();
let mut req = TestRequest::with_header("Origin", "https://www.unknown.com") let mut req = TestRequest::with_header("Origin", "https://www.unknown.com")
.method(Method::GET) .method(Method::GET)
@ -1018,9 +999,7 @@ mod tests {
#[test] #[test]
fn test_validate_origin() { fn test_validate_origin() {
let cors = Cors::build() let cors = Cors::build().allowed_origin("https://www.example.com").finish();
.allowed_origin("https://www.example.com")
.finish();
let mut req = TestRequest::with_header("Origin", "https://www.example.com") let mut req = TestRequest::with_header("Origin", "https://www.example.com")
.method(Method::GET) .method(Method::GET)
@ -1036,11 +1015,7 @@ mod tests {
let mut req = TestRequest::default().method(Method::GET).finish(); let mut req = TestRequest::default().method(Method::GET).finish();
let resp: HttpResponse = HttpResponse::Ok().into(); let resp: HttpResponse = HttpResponse::Ok().into();
let resp = cors.response(&mut req, resp).unwrap().response(); let resp = cors.response(&mut req, resp).unwrap().response();
assert!( assert!(resp.headers().get(header::ACCESS_CONTROL_ALLOW_ORIGIN).is_none());
resp.headers()
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
.is_none()
);
let mut req = TestRequest::with_header("Origin", "https://www.example.com") let mut req = TestRequest::with_header("Origin", "https://www.example.com")
.method(Method::OPTIONS) .method(Method::OPTIONS)
@ -1048,10 +1023,7 @@ mod tests {
let resp = cors.response(&mut req, resp).unwrap().response(); let resp = cors.response(&mut req, resp).unwrap().response();
assert_eq!( assert_eq!(
&b"https://www.example.com"[..], &b"https://www.example.com"[..],
resp.headers() resp.headers().get(header::ACCESS_CONTROL_ALLOW_ORIGIN).unwrap().as_bytes()
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
.unwrap()
.as_bytes()
); );
} }
@ -1074,19 +1046,12 @@ mod tests {
let resp = cors.response(&mut req, resp).unwrap().response(); let resp = cors.response(&mut req, resp).unwrap().response();
assert_eq!( assert_eq!(
&b"*"[..], &b"*"[..],
resp.headers() resp.headers().get(header::ACCESS_CONTROL_ALLOW_ORIGIN).unwrap().as_bytes()
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
.unwrap()
.as_bytes()
);
assert_eq!(
&b"Origin"[..],
resp.headers().get(header::VARY).unwrap().as_bytes()
); );
assert_eq!(&b"Origin"[..], resp.headers().get(header::VARY).unwrap().as_bytes());
let resp: HttpResponse = HttpResponse::Ok() let resp: HttpResponse =
.header(header::VARY, "Accept") HttpResponse::Ok().header(header::VARY, "Accept").finish();
.finish();
let resp = cors.response(&mut req, resp).unwrap().response(); let resp = cors.response(&mut req, resp).unwrap().response();
assert_eq!( assert_eq!(
&b"Accept, Origin"[..], &b"Accept, Origin"[..],
@ -1101,10 +1066,7 @@ mod tests {
let resp = cors.response(&mut req, resp).unwrap().response(); let resp = cors.response(&mut req, resp).unwrap().response();
assert_eq!( assert_eq!(
&b"https://www.example.com"[..], &b"https://www.example.com"[..],
resp.headers() resp.headers().get(header::ACCESS_CONTROL_ALLOW_ORIGIN).unwrap().as_bytes()
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
.unwrap()
.as_bytes()
); );
} }

View File

@ -89,10 +89,7 @@ fn origin(headers: &HeaderMap) -> Option<Result<Cow<str>, CsrfError>> {
headers headers
.get(header::ORIGIN) .get(header::ORIGIN)
.map(|origin| { .map(|origin| {
origin origin.to_str().map_err(|_| CsrfError::BadOrigin).map(|o| o.into())
.to_str()
.map_err(|_| CsrfError::BadOrigin)
.map(|o| o.into())
}) })
.or_else(|| { .or_else(|| {
headers.get(header::REFERER).map(|referer| { headers.get(header::REFERER).map(|referer| {
@ -261,9 +258,8 @@ mod tests {
fn test_upgrade() { fn test_upgrade() {
let strict_csrf = CsrfFilter::new().allowed_origin("https://www.example.com"); let strict_csrf = CsrfFilter::new().allowed_origin("https://www.example.com");
let lax_csrf = CsrfFilter::new() let lax_csrf =
.allowed_origin("https://www.example.com") CsrfFilter::new().allowed_origin("https://www.example.com").allow_upgrade();
.allow_upgrade();
let mut req = TestRequest::with_header("Origin", "https://cswsh.com") let mut req = TestRequest::with_header("Origin", "https://cswsh.com")
.header("Connection", "Upgrade") .header("Connection", "Upgrade")

View File

@ -76,7 +76,7 @@ impl DefaultHeaders {
impl<S> Middleware<S> for DefaultHeaders { impl<S> Middleware<S> for DefaultHeaders {
fn response( fn response(
&self, _: &mut HttpRequest<S>, mut resp: HttpResponse &self, _: &mut HttpRequest<S>, mut resp: HttpResponse,
) -> Result<Response> { ) -> Result<Response> {
for (key, value) in self.headers.iter() { for (key, value) in self.headers.iter() {
if !resp.headers().contains_key(key) { if !resp.headers().contains_key(key) {
@ -112,9 +112,7 @@ mod tests {
}; };
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
let resp = HttpResponse::Ok() let resp = HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish();
.header(CONTENT_TYPE, "0002")
.finish();
let resp = match mw.response(&mut req, resp) { let resp = match mw.response(&mut req, resp) {
Ok(Response::Done(resp)) => resp, Ok(Response::Done(resp)) => resp,
_ => panic!(), _ => panic!(),

View File

@ -69,7 +69,7 @@ impl<S> ErrorHandlers<S> {
impl<S: 'static> Middleware<S> for ErrorHandlers<S> { impl<S: 'static> Middleware<S> for ErrorHandlers<S> {
fn response( fn response(
&self, req: &mut HttpRequest<S>, resp: HttpResponse &self, req: &mut HttpRequest<S>, resp: HttpResponse,
) -> Result<Response> { ) -> Result<Response> {
if let Some(handler) = self.handlers.get(&resp.status()) { if let Some(handler) = self.handlers.get(&resp.status()) {
handler(req, resp) handler(req, resp)
@ -82,8 +82,8 @@ impl<S: 'static> Middleware<S> for ErrorHandlers<S> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use http::StatusCode;
use http::header::CONTENT_TYPE; use http::header::CONTENT_TYPE;
use http::StatusCode;
fn render_500<S>(_: &mut HttpRequest<S>, resp: HttpResponse) -> Result<Response> { fn render_500<S>(_: &mut HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
let mut builder = resp.into_builder(); let mut builder = resp.into_builder();

View File

@ -49,8 +49,8 @@
use std::rc::Rc; use std::rc::Rc;
use cookie::{Cookie, CookieJar, Key}; use cookie::{Cookie, CookieJar, Key};
use futures::future::{err as FutErr, ok as FutOk, FutureResult};
use futures::Future; use futures::Future;
use futures::future::{FutureResult, err as FutErr, ok as FutOk};
use time::Duration; use time::Duration;
use error::{Error, Result}; use error::{Error, Result};
@ -164,7 +164,9 @@ pub struct IdentityService<T> {
impl<T> IdentityService<T> { impl<T> IdentityService<T> {
/// Create new identity service with specified backend. /// Create new identity service with specified backend.
pub fn new(backend: T) -> Self { pub fn new(backend: T) -> Self {
IdentityService { backend } IdentityService {
backend,
}
} }
} }
@ -179,20 +181,18 @@ impl<S: 'static, T: IdentityPolicy<S>> Middleware<S> for IdentityService<T> {
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> { fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
let mut req = req.clone(); let mut req = req.clone();
let fut = self.backend let fut = self.backend.from_request(&mut req).then(move |res| match res {
.from_request(&mut req) Ok(id) => {
.then(move |res| match res { req.extensions().insert(IdentityBox(Box::new(id)));
Ok(id) => { FutOk(None)
req.extensions().insert(IdentityBox(Box::new(id))); }
FutOk(None) Err(err) => FutErr(err),
} });
Err(err) => FutErr(err),
});
Ok(Started::Future(Box::new(fut))) Ok(Started::Future(Box::new(fut)))
} }
fn response( fn response(
&self, req: &mut HttpRequest<S>, resp: HttpResponse &self, req: &mut HttpRequest<S>, resp: HttpResponse,
) -> Result<Response> { ) -> Result<Response> {
if let Some(mut id) = req.extensions().remove::<IdentityBox>() { if let Some(mut id) = req.extensions().remove::<IdentityBox>() {
id.0.write(resp) id.0.write(resp)

View File

@ -254,10 +254,9 @@ impl FormatText {
"-".fmt(fmt) "-".fmt(fmt)
} }
} }
FormatText::RequestTime => entry_time FormatText::RequestTime => {
.strftime("[%d/%b/%Y:%H:%M:%S %z]") entry_time.strftime("[%d/%b/%Y:%H:%M:%S %z]").unwrap().fmt(fmt)
.unwrap() }
.fmt(fmt),
FormatText::RequestHeader(ref name) => { FormatText::RequestHeader(ref name) => {
let s = if let Some(val) = req.headers().get(name) { let s = if let Some(val) = req.headers().get(name) {
if let Ok(s) = val.to_str() { if let Ok(s) = val.to_str() {
@ -314,10 +313,8 @@ mod tests {
let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test"); let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test");
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert( headers
header::USER_AGENT, .insert(header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"));
header::HeaderValue::from_static("ACTIX-WEB"),
);
let mut req = HttpRequest::new( let mut req = HttpRequest::new(
Method::GET, Method::GET,
Uri::from_str("/").unwrap(), Uri::from_str("/").unwrap(),
@ -354,10 +351,8 @@ mod tests {
let format = Format::default(); let format = Format::default();
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert( headers
header::USER_AGENT, .insert(header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"));
header::HeaderValue::from_static("ACTIX-WEB"),
);
let req = HttpRequest::new( let req = HttpRequest::new(
Method::GET, Method::GET,
Uri::from_str("/").unwrap(), Uri::from_str("/").unwrap(),
@ -365,9 +360,7 @@ mod tests {
headers, headers,
None, None,
); );
let resp = HttpResponse::build(StatusCode::OK) let resp = HttpResponse::build(StatusCode::OK).force_close().finish();
.force_close()
.finish();
let entry_time = time::now(); let entry_time = time::now();
let render = |fmt: &mut Formatter| { let render = |fmt: &mut Formatter| {
@ -388,9 +381,7 @@ mod tests {
HeaderMap::new(), HeaderMap::new(),
None, None,
); );
let resp = HttpResponse::build(StatusCode::OK) let resp = HttpResponse::build(StatusCode::OK).force_close().finish();
.force_close()
.finish();
let entry_time = time::now(); let entry_time = time::now();
let render = |fmt: &mut Formatter| { let render = |fmt: &mut Formatter| {

View File

@ -21,8 +21,9 @@ pub use self::logger::Logger;
#[cfg(feature = "session")] #[cfg(feature = "session")]
#[doc(hidden)] #[doc(hidden)]
#[deprecated(since = "0.5.4", #[deprecated(
note = "please use `actix_web::middleware::session` instead")] since = "0.5.4", note = "please use `actix_web::middleware::session` instead"
)]
pub use self::session::{CookieSessionBackend, CookieSessionError, RequestSession, pub use self::session::{CookieSessionBackend, CookieSessionError, RequestSession,
Session, SessionBackend, SessionImpl, SessionStorage}; Session, SessionBackend, SessionImpl, SessionStorage};
@ -65,7 +66,7 @@ pub trait Middleware<S>: 'static {
/// Method is called when handler returns response, /// Method is called when handler returns response,
/// but before sending http message to peer. /// but before sending http message to peer.
fn response( fn response(
&self, req: &mut HttpRequest<S>, resp: HttpResponse &self, req: &mut HttpRequest<S>, resp: HttpResponse,
) -> Result<Response> { ) -> Result<Response> {
Ok(Response::Done(resp)) Ok(Response::Done(resp))
} }

View File

@ -69,8 +69,8 @@ use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use cookie::{Cookie, CookieJar, Key}; use cookie::{Cookie, CookieJar, Key};
use futures::future::{err as FutErr, ok as FutOk, FutureResult};
use futures::Future; use futures::Future;
use futures::future::{FutureResult, err as FutErr, ok as FutOk};
use http::header::{self, HeaderValue}; use http::header::{self, HeaderValue};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json; use serde_json;
@ -202,21 +202,18 @@ impl<S: 'static, T: SessionBackend<S>> Middleware<S> for SessionStorage<T, S> {
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> { fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
let mut req = req.clone(); let mut req = req.clone();
let fut = self.0 let fut = self.0.from_request(&mut req).then(move |res| match res {
.from_request(&mut req) Ok(sess) => {
.then(move |res| match res { req.extensions().insert(Arc::new(SessionImplBox(Box::new(sess))));
Ok(sess) => { FutOk(None)
req.extensions() }
.insert(Arc::new(SessionImplBox(Box::new(sess)))); Err(err) => FutErr(err),
FutOk(None) });
}
Err(err) => FutErr(err),
});
Ok(Started::Future(Box::new(fut))) Ok(Started::Future(Box::new(fut)))
} }
fn response( fn response(
&self, req: &mut HttpRequest<S>, resp: HttpResponse &self, req: &mut HttpRequest<S>, resp: HttpResponse,
) -> Result<Response> { ) -> Result<Response> {
if let Some(s_box) = req.extensions().remove::<Arc<SessionImplBox>>() { if let Some(s_box) = req.extensions().remove::<Arc<SessionImplBox>>() {
s_box.0.write(resp) s_box.0.write(resp)
@ -349,7 +346,7 @@ impl CookieSessionInner {
} }
fn set_cookie( fn set_cookie(
&self, resp: &mut HttpResponse, state: &HashMap<String, String> &self, resp: &mut HttpResponse, state: &HashMap<String, String>,
) -> Result<()> { ) -> Result<()> {
let value = let value =
serde_json::to_string(&state).map_err(CookieSessionError::Serialize)?; serde_json::to_string(&state).map_err(CookieSessionError::Serialize)?;

View File

@ -7,8 +7,8 @@ use std::{cmp, fmt};
use bytes::Bytes; use bytes::Bytes;
use futures::task::{current as current_task, Task}; use futures::task::{current as current_task, Task};
use futures::{Async, Poll, Stream}; use futures::{Async, Poll, Stream};
use http::HttpTryFrom;
use http::header::{self, HeaderMap, HeaderName, HeaderValue}; use http::header::{self, HeaderMap, HeaderName, HeaderValue};
use http::HttpTryFrom;
use httparse; use httparse;
use mime; use mime;
@ -122,11 +122,7 @@ where
if let Some(err) = self.error.take() { if let Some(err) = self.error.take() {
Err(err) Err(err)
} else if self.safety.current() { } else if self.safety.current() {
self.inner self.inner.as_mut().unwrap().borrow_mut().poll(&self.safety)
.as_mut()
.unwrap()
.borrow_mut()
.poll(&self.safety)
} else { } else {
Ok(Async::NotReady) Ok(Async::NotReady)
} }
@ -168,7 +164,7 @@ where
} }
fn read_boundary( fn read_boundary(
payload: &mut PayloadHelper<S>, boundary: &str payload: &mut PayloadHelper<S>, boundary: &str,
) -> Poll<bool, MultipartError> { ) -> Poll<bool, MultipartError> {
// TODO: need to read epilogue // TODO: need to read epilogue
match payload.readline()? { match payload.readline()? {
@ -192,7 +188,7 @@ where
} }
fn skip_until_boundary( fn skip_until_boundary(
payload: &mut PayloadHelper<S>, boundary: &str payload: &mut PayloadHelper<S>, boundary: &str,
) -> Poll<bool, MultipartError> { ) -> Poll<bool, MultipartError> {
let mut eof = false; let mut eof = false;
loop { loop {
@ -230,7 +226,7 @@ where
} }
fn poll( fn poll(
&mut self, safety: &Safety &mut self, safety: &Safety,
) -> Poll<Option<MultipartItem<S>>, MultipartError> { ) -> Poll<Option<MultipartItem<S>>, MultipartError> {
if self.state == InnerState::Eof { if self.state == InnerState::Eof {
Ok(Async::Ready(None)) Ok(Async::Ready(None))
@ -450,7 +446,7 @@ where
S: Stream<Item = Bytes, Error = PayloadError>, S: Stream<Item = Bytes, Error = PayloadError>,
{ {
fn new( fn new(
payload: PayloadRef<S>, boundary: String, headers: &HeaderMap payload: PayloadRef<S>, boundary: String, headers: &HeaderMap,
) -> Result<InnerField<S>, PayloadError> { ) -> Result<InnerField<S>, PayloadError> {
let len = if let Some(len) = headers.get(header::CONTENT_LENGTH) { let len = if let Some(len) = headers.get(header::CONTENT_LENGTH) {
if let Ok(s) = len.to_str() { if let Ok(s) = len.to_str() {
@ -477,7 +473,7 @@ where
/// Reads body part content chunk of the specified size. /// Reads body part content chunk of the specified size.
/// The body part must has `Content-Length` header with proper value. /// The body part must has `Content-Length` header with proper value.
fn read_len( fn read_len(
payload: &mut PayloadHelper<S>, size: &mut u64 payload: &mut PayloadHelper<S>, size: &mut u64,
) -> Poll<Option<Bytes>, MultipartError> { ) -> Poll<Option<Bytes>, MultipartError> {
if *size == 0 { if *size == 0 {
Ok(Async::Ready(None)) Ok(Async::Ready(None))
@ -502,7 +498,7 @@ where
/// Reads content chunk of body part with unknown length. /// Reads content chunk of body part with unknown length.
/// The `Content-Length` header for body part is not necessary. /// The `Content-Length` header for body part is not necessary.
fn read_stream( fn read_stream(
payload: &mut PayloadHelper<S>, boundary: &str payload: &mut PayloadHelper<S>, boundary: &str,
) -> Poll<Option<Bytes>, MultipartError> { ) -> Poll<Option<Bytes>, MultipartError> {
match payload.read_until(b"\r")? { match payload.read_until(b"\r")? {
Async::NotReady => Ok(Async::NotReady), Async::NotReady => Ok(Async::NotReady),
@ -675,10 +671,7 @@ mod tests {
} }
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert( headers.insert(header::CONTENT_TYPE, header::HeaderValue::from_static("test"));
header::CONTENT_TYPE,
header::HeaderValue::from_static("test"),
);
match Multipart::boundary(&headers) { match Multipart::boundary(&headers) {
Err(MultipartError::ParseContentType) => (), Err(MultipartError::ParseContentType) => (),

View File

@ -94,8 +94,7 @@ impl<'a, 'b, 'c: 'a> Index<&'b str> for &'c Params<'a> {
type Output = str; type Output = str;
fn index(&self, name: &'b str) -> &str { fn index(&self, name: &'b str) -> &str {
self.get(name) self.get(name).expect("Value for parameter is not available")
.expect("Value for parameter is not available")
} }
} }
@ -202,18 +201,9 @@ mod tests {
PathBuf::from_param("/test/*tt"), PathBuf::from_param("/test/*tt"),
Err(UriSegmentError::BadStart('*')) Err(UriSegmentError::BadStart('*'))
); );
assert_eq!( assert_eq!(PathBuf::from_param("/test/tt:"), Err(UriSegmentError::BadEnd(':')));
PathBuf::from_param("/test/tt:"), assert_eq!(PathBuf::from_param("/test/tt<"), Err(UriSegmentError::BadEnd('<')));
Err(UriSegmentError::BadEnd(':')) assert_eq!(PathBuf::from_param("/test/tt>"), Err(UriSegmentError::BadEnd('>')));
);
assert_eq!(
PathBuf::from_param("/test/tt<"),
Err(UriSegmentError::BadEnd('<'))
);
assert_eq!(
PathBuf::from_param("/test/tt>"),
Err(UriSegmentError::BadEnd('>'))
);
assert_eq!( assert_eq!(
PathBuf::from_param("/seg1/seg2/"), PathBuf::from_param("/seg1/seg2/"),
Ok(PathBuf::from_iter(vec!["seg1", "seg2"])) Ok(PathBuf::from_iter(vec!["seg1", "seg2"]))

View File

@ -47,7 +47,9 @@ impl Payload {
PayloadSender { PayloadSender {
inner: Rc::downgrade(&shared), inner: Rc::downgrade(&shared),
}, },
Payload { inner: shared }, Payload {
inner: shared,
},
) )
} }
@ -534,10 +536,7 @@ mod tests {
assert_eq!(format!("{}", err.cause().unwrap()), "ParseError"); assert_eq!(format!("{}", err.cause().unwrap()), "ParseError");
let err = PayloadError::Incomplete; let err = PayloadError::Incomplete;
assert_eq!( assert_eq!(format!("{}", err), "A payload reached EOF, but is not complete.");
format!("{}", err),
"A payload reached EOF, but is not complete."
);
} }
#[test] #[test]
@ -671,10 +670,7 @@ mod tests {
let (mut sender, payload) = Payload::new(false); let (mut sender, payload) = Payload::new(false);
let mut payload = PayloadHelper::new(payload); let mut payload = PayloadHelper::new(payload);
assert_eq!( assert_eq!(Async::NotReady, payload.read_until(b"ne").ok().unwrap());
Async::NotReady,
payload.read_until(b"ne").ok().unwrap()
);
sender.feed_data(Bytes::from("line1")); sender.feed_data(Bytes::from("line1"));
sender.feed_data(Bytes::from("line2")); sender.feed_data(Bytes::from("line2"));

View File

@ -67,7 +67,7 @@ impl<S: 'static, H: PipelineHandler<S>> PipelineState<S, H> {
} }
struct PipelineInfo<S> { struct PipelineInfo<S> {
req: HttpRequest<S>, req: UnsafeCell<HttpRequest<S>>,
count: u16, count: u16,
mws: Rc<Vec<Box<Middleware<S>>>>, mws: Rc<Vec<Box<Middleware<S>>>>,
context: Option<Box<ActorHttpContext>>, context: Option<Box<ActorHttpContext>>,
@ -79,7 +79,7 @@ struct PipelineInfo<S> {
impl<S> PipelineInfo<S> { impl<S> PipelineInfo<S> {
fn new(req: HttpRequest<S>) -> PipelineInfo<S> { fn new(req: HttpRequest<S>) -> PipelineInfo<S> {
PipelineInfo { PipelineInfo {
req, req: UnsafeCell::new(req),
count: 0, count: 0,
mws: Rc::new(Vec::new()), mws: Rc::new(Vec::new()),
error: None, error: None,
@ -89,11 +89,17 @@ impl<S> PipelineInfo<S> {
} }
} }
#[inline]
fn req(&self) -> &HttpRequest<S> {
unsafe { &*self.req.get() }
}
#[inline]
#[cfg_attr(feature = "cargo-clippy", allow(mut_from_ref))] #[cfg_attr(feature = "cargo-clippy", allow(mut_from_ref))]
fn req_mut(&self) -> &mut HttpRequest<S> { fn req_mut(&self) -> &mut HttpRequest<S> {
#[allow(mutable_transmutes)] #[allow(mutable_transmutes)]
unsafe { unsafe {
mem::transmute(&self.req) &mut *self.req.get()
} }
} }
@ -116,8 +122,8 @@ impl<S: 'static, H: PipelineHandler<S>> Pipeline<S, H> {
handler: Rc<UnsafeCell<H>>, htype: HandlerType, handler: Rc<UnsafeCell<H>>, htype: HandlerType,
) -> Pipeline<S, H> { ) -> Pipeline<S, H> {
let mut info = PipelineInfo { let mut info = PipelineInfo {
req,
mws, mws,
req: UnsafeCell::new(req),
count: 0, count: 0,
error: None, error: None,
context: None, context: None,
@ -159,7 +165,7 @@ impl<S: 'static, H: PipelineHandler<S>> HttpHandlerTask for Pipeline<S, H> {
} }
fn poll_io(&mut self, io: &mut Writer) -> Poll<bool, Error> { fn poll_io(&mut self, io: &mut Writer) -> Poll<bool, Error> {
let info: &mut PipelineInfo<_> = unsafe { mem::transmute(&mut self.0) }; let info: &mut PipelineInfo<_> = unsafe { &mut *(&mut self.0 as *mut _) };
loop { loop {
if self.1.is_response() { if self.1.is_response() {
@ -197,7 +203,7 @@ impl<S: 'static, H: PipelineHandler<S>> HttpHandlerTask for Pipeline<S, H> {
} }
fn poll(&mut self) -> Poll<(), Error> { fn poll(&mut self) -> Poll<(), Error> {
let info: &mut PipelineInfo<_> = unsafe { mem::transmute(&mut self.0) }; let info: &mut PipelineInfo<_> = unsafe { &mut *(&mut self.0 as *mut _) };
loop { loop {
match self.1 { match self.1 {
@ -228,17 +234,17 @@ struct StartMiddlewares<S, H> {
impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> { impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
fn init( fn init(
info: &mut PipelineInfo<S>, hnd: Rc<UnsafeCell<H>>, htype: HandlerType info: &mut PipelineInfo<S>, hnd: Rc<UnsafeCell<H>>, htype: HandlerType,
) -> PipelineState<S, H> { ) -> PipelineState<S, H> {
// execute middlewares, we need this stage because middlewares could be // execute middlewares, we need this stage because middlewares could be
// non-async and we can move to next state immediately // non-async and we can move to next state immediately
let len = info.mws.len() as u16; let len = info.mws.len() as u16;
loop { loop {
if info.count == len { if info.count == len {
let reply = unsafe { &mut *hnd.get() }.handle(info.req.clone(), htype); let reply = unsafe { &mut *hnd.get() }.handle(info.req().clone(), htype);
return WaitingResponse::init(info, reply); return WaitingResponse::init(info, reply);
} else { } else {
match info.mws[info.count as usize].start(&mut info.req) { match info.mws[info.count as usize].start(info.req_mut()) {
Ok(Started::Done) => info.count += 1, Ok(Started::Done) => info.count += 1,
Ok(Started::Response(resp)) => { Ok(Started::Response(resp)) => {
return RunMiddlewares::init(info, resp) return RunMiddlewares::init(info, resp)
@ -278,7 +284,7 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
} }
if info.count == len { if info.count == len {
let reply = unsafe { &mut *self.hnd.get() } let reply = unsafe { &mut *self.hnd.get() }
.handle(info.req.clone(), self.htype); .handle(info.req().clone(), self.htype);
return Some(WaitingResponse::init(info, reply)); return Some(WaitingResponse::init(info, reply));
} else { } else {
loop { loop {
@ -462,7 +468,7 @@ impl<S: 'static, H> ProcessResponse<S, H> {
} }
fn poll_io( fn poll_io(
mut self, io: &mut Writer, info: &mut PipelineInfo<S> mut self, io: &mut Writer, info: &mut PipelineInfo<S>,
) -> Result<PipelineState<S, H>, PipelineState<S, H>> { ) -> Result<PipelineState<S, H>, PipelineState<S, H>> {
loop { loop {
if self.drain.is_none() && self.running != RunningState::Paused { if self.drain.is_none() && self.running != RunningState::Paused {
@ -482,8 +488,7 @@ impl<S: 'static, H> ProcessResponse<S, H> {
Err(err) => { Err(err) => {
info.error = Some(err.into()); info.error = Some(err.into());
return Ok(FinishingMiddlewares::init( return Ok(FinishingMiddlewares::init(
info, info, self.resp,
self.resp,
)); ));
} }
}; };
@ -525,8 +530,7 @@ impl<S: 'static, H> ProcessResponse<S, H> {
if let Err(err) = io.write_eof() { if let Err(err) = io.write_eof() {
info.error = Some(err.into()); info.error = Some(err.into());
return Ok(FinishingMiddlewares::init( return Ok(FinishingMiddlewares::init(
info, info, self.resp,
self.resp,
)); ));
} }
break; break;
@ -537,8 +541,7 @@ impl<S: 'static, H> ProcessResponse<S, H> {
Err(err) => { Err(err) => {
info.error = Some(err.into()); info.error = Some(err.into());
return Ok(FinishingMiddlewares::init( return Ok(FinishingMiddlewares::init(
info, info, self.resp,
self.resp,
)); ));
} }
Ok(result) => result, Ok(result) => result,
@ -572,8 +575,7 @@ impl<S: 'static, H> ProcessResponse<S, H> {
info.error = Some(err.into()); info.error = Some(err.into());
return Ok( return Ok(
FinishingMiddlewares::init( FinishingMiddlewares::init(
info, info, self.resp,
self.resp,
), ),
); );
} }
@ -585,8 +587,7 @@ impl<S: 'static, H> ProcessResponse<S, H> {
info.error = Some(err.into()); info.error = Some(err.into());
return Ok( return Ok(
FinishingMiddlewares::init( FinishingMiddlewares::init(
info, info, self.resp,
self.resp,
), ),
); );
} }
@ -611,8 +612,7 @@ impl<S: 'static, H> ProcessResponse<S, H> {
Err(err) => { Err(err) => {
info.error = Some(err); info.error = Some(err);
return Ok(FinishingMiddlewares::init( return Ok(FinishingMiddlewares::init(
info, info, self.resp,
self.resp,
)); ));
} }
} }
@ -796,18 +796,15 @@ mod tests {
.unwrap() .unwrap()
.run(lazy(|| { .run(lazy(|| {
let mut info = PipelineInfo::new(HttpRequest::default()); let mut info = PipelineInfo::new(HttpRequest::default());
Completed::<(), Inner<()>>::init(&mut info) Completed::<(), Inner<()>>::init(&mut info).is_none().unwrap();
.is_none()
.unwrap();
let req = HttpRequest::default(); let req = HttpRequest::default();
let mut ctx = HttpContext::new(req.clone(), MyActor); let mut ctx = HttpContext::new(req.clone(), MyActor);
let addr: Addr<Unsync, _> = ctx.address(); let addr: Addr<Unsync, _> = ctx.address();
let mut info = PipelineInfo::new(req); let mut info = PipelineInfo::new(req);
info.context = Some(Box::new(ctx)); info.context = Some(Box::new(ctx));
let mut state = Completed::<(), Inner<()>>::init(&mut info) let mut state =
.completed() Completed::<(), Inner<()>>::init(&mut info).completed().unwrap();
.unwrap();
assert!(state.poll(&mut info).is_none()); assert!(state.poll(&mut info).is_none());
let pp = Pipeline(info, PipelineState::Completed(state)); let pp = Pipeline(info, PipelineState::Completed(state));

View File

@ -171,7 +171,7 @@ pub fn Method<S: 'static>(method: http::Method) -> MethodPredicate<S> {
/// Return predicate that matches if request contains specified header and /// Return predicate that matches if request contains specified header and
/// value. /// value.
pub fn Header<S: 'static>( pub fn Header<S: 'static>(
name: &'static str, value: &'static str name: &'static str, value: &'static str,
) -> HeaderPredicate<S> { ) -> HeaderPredicate<S> {
HeaderPredicate( HeaderPredicate(
header::HeaderName::try_from(name).unwrap(), header::HeaderName::try_from(name).unwrap(),
@ -181,11 +181,7 @@ pub fn Header<S: 'static>(
} }
#[doc(hidden)] #[doc(hidden)]
pub struct HeaderPredicate<S>( pub struct HeaderPredicate<S>(header::HeaderName, header::HeaderValue, PhantomData<S>);
header::HeaderName,
header::HeaderValue,
PhantomData<S>,
);
impl<S: 'static> Predicate<S> for HeaderPredicate<S> { impl<S: 'static> Predicate<S> for HeaderPredicate<S> {
fn check(&self, req: &mut HttpRequest<S>) -> bool { fn check(&self, req: &mut HttpRequest<S>) -> bool {

View File

@ -132,10 +132,7 @@ impl<S: 'static> ResourceHandler<S> {
/// ``` /// ```
pub fn method(&mut self, method: Method) -> &mut Route<S> { pub fn method(&mut self, method: Method) -> &mut Route<S> {
self.routes.push(Route::default()); self.routes.push(Route::default());
self.routes self.routes.last_mut().unwrap().filter(pred::Method(method))
.last_mut()
.unwrap()
.filter(pred::Method(method))
} }
/// Register a new route and add handler object. /// Register a new route and add handler object.
@ -188,13 +185,11 @@ impl<S: 'static> ResourceHandler<S> {
/// This is similar to `App's` middlewares, but /// This is similar to `App's` middlewares, but
/// middlewares get invoked on resource level. /// middlewares get invoked on resource level.
pub fn middleware<M: Middleware<S>>(&mut self, mw: M) { pub fn middleware<M: Middleware<S>>(&mut self, mw: M) {
Rc::get_mut(&mut self.middlewares) Rc::get_mut(&mut self.middlewares).unwrap().push(Box::new(mw));
.unwrap()
.push(Box::new(mw));
} }
pub(crate) fn handle( pub(crate) fn handle(
&mut self, mut req: HttpRequest<S>, default: Option<&mut ResourceHandler<S>> &mut self, mut req: HttpRequest<S>, default: Option<&mut ResourceHandler<S>>,
) -> Reply { ) -> Reply {
for route in &mut self.routes { for route in &mut self.routes {
if route.check(&mut req) { if route.check(&mut req) {

View File

@ -50,7 +50,7 @@ impl<S: 'static> Route<S> {
#[inline] #[inline]
pub(crate) fn compose( pub(crate) fn compose(
&mut self, req: HttpRequest<S>, mws: Rc<Vec<Box<Middleware<S>>>> &mut self, req: HttpRequest<S>, mws: Rc<Vec<Box<Middleware<S>>>>,
) -> Reply { ) -> Reply {
Reply::async(Compose::new(req, mws, self.handler.clone())) Reply::async(Compose::new(req, mws, self.handler.clone()))
} }
@ -170,7 +170,7 @@ impl<S: 'static> Route<S> {
/// } /// }
/// ``` /// ```
pub fn with2<T1, T2, F, R>( pub fn with2<T1, T2, F, R>(
&mut self, handler: F &mut self, handler: F,
) -> (ExtractorConfig<S, T1>, ExtractorConfig<S, T2>) ) -> (ExtractorConfig<S, T1>, ExtractorConfig<S, T2>)
where where
F: Fn(T1, T2) -> R + 'static, F: Fn(T1, T2) -> R + 'static,
@ -180,22 +180,14 @@ impl<S: 'static> Route<S> {
{ {
let cfg1 = ExtractorConfig::default(); let cfg1 = ExtractorConfig::default();
let cfg2 = ExtractorConfig::default(); let cfg2 = ExtractorConfig::default();
self.h(With2::new( self.h(With2::new(handler, Clone::clone(&cfg1), Clone::clone(&cfg2)));
handler,
Clone::clone(&cfg1),
Clone::clone(&cfg2),
));
(cfg1, cfg2) (cfg1, cfg2)
} }
/// Set handler function, use request extractor for all paramters. /// Set handler function, use request extractor for all paramters.
pub fn with3<T1, T2, T3, F, R>( pub fn with3<T1, T2, T3, F, R>(
&mut self, handler: F &mut self, handler: F,
) -> ( ) -> (ExtractorConfig<S, T1>, ExtractorConfig<S, T2>, ExtractorConfig<S, T3>)
ExtractorConfig<S, T1>,
ExtractorConfig<S, T2>,
ExtractorConfig<S, T3>,
)
where where
F: Fn(T1, T2, T3) -> R + 'static, F: Fn(T1, T2, T3) -> R + 'static,
R: Responder + 'static, R: Responder + 'static,
@ -288,7 +280,7 @@ impl<S: 'static> ComposeState<S> {
impl<S: 'static> Compose<S> { impl<S: 'static> Compose<S> {
fn new( fn new(
req: HttpRequest<S>, mws: Rc<Vec<Box<Middleware<S>>>>, handler: InnerHandler<S> req: HttpRequest<S>, mws: Rc<Vec<Box<Middleware<S>>>>, handler: InnerHandler<S>,
) -> Self { ) -> Self {
let mut info = ComposeInfo { let mut info = ComposeInfo {
count: 0, count: 0,
@ -298,7 +290,10 @@ impl<S: 'static> Compose<S> {
}; };
let state = StartMiddlewares::init(&mut info); let state = StartMiddlewares::init(&mut info);
Compose { state, info } Compose {
state,
info,
}
} }
} }

View File

@ -80,7 +80,11 @@ impl Router {
return None; return None;
} }
let path: &str = unsafe { mem::transmute(&req.path()[self.0.prefix_len..]) }; let path: &str = unsafe { mem::transmute(&req.path()[self.0.prefix_len..]) };
let route_path = if path.is_empty() { "/" } else { path }; let route_path = if path.is_empty() {
"/"
} else {
path
};
for (idx, pattern) in self.0.patterns.iter().enumerate() { for (idx, pattern) in self.0.patterns.iter().enumerate() {
if pattern.match_with_params(route_path, req.match_info_mut()) { if pattern.match_with_params(route_path, req.match_info_mut()) {
@ -98,7 +102,11 @@ impl Router {
/// following path would be recognizable `/test/name` but `has_route()` call /// following path would be recognizable `/test/name` but `has_route()` call
/// would return `false`. /// would return `false`.
pub fn has_route(&self, path: &str) -> bool { pub fn has_route(&self, path: &str) -> bool {
let path = if path.is_empty() { "/" } else { path }; let path = if path.is_empty() {
"/"
} else {
path
};
for pattern in &self.0.patterns { for pattern in &self.0.patterns {
if pattern.is_match(path) { if pattern.is_match(path) {
@ -113,7 +121,7 @@ impl Router {
/// Check [`HttpRequest::url_for()`](../struct.HttpRequest.html#method. /// Check [`HttpRequest::url_for()`](../struct.HttpRequest.html#method.
/// url_for) for detailed information. /// url_for) for detailed information.
pub fn resource_path<U, I>( pub fn resource_path<U, I>(
&self, name: &str, elements: U &self, name: &str, elements: U,
) -> Result<String, UrlGenerationError> ) -> Result<String, UrlGenerationError>
where where
U: IntoIterator<Item = I>, U: IntoIterator<Item = I>,
@ -245,7 +253,7 @@ impl Resource {
} }
pub fn match_with_params<'a>( pub fn match_with_params<'a>(
&'a self, path: &'a str, params: &'a mut Params<'a> &'a self, path: &'a str, params: &'a mut Params<'a>,
) -> bool { ) -> bool {
match self.tp { match self.tp {
PatternType::Static(ref s) => s == path, PatternType::Static(ref s) => s == path,
@ -270,7 +278,7 @@ impl Resource {
/// Build reousrce path. /// Build reousrce path.
pub fn resource_path<U, I>( pub fn resource_path<U, I>(
&self, router: &Router, elements: U &self, router: &Router, elements: U,
) -> Result<String, UrlGenerationError> ) -> Result<String, UrlGenerationError>
where where
U: IntoIterator<Item = I>, U: IntoIterator<Item = I>,
@ -383,34 +391,19 @@ mod tests {
#[test] #[test]
fn test_recognizer() { fn test_recognizer() {
let routes = vec![ let routes = vec![
( (Resource::new("", "/name"), Some(ResourceHandler::default())),
Resource::new("", "/name"), (Resource::new("", "/name/{val}"), Some(ResourceHandler::default())),
Some(ResourceHandler::default()),
),
(
Resource::new("", "/name/{val}"),
Some(ResourceHandler::default()),
),
( (
Resource::new("", "/name/{val}/index.html"), Resource::new("", "/name/{val}/index.html"),
Some(ResourceHandler::default()), Some(ResourceHandler::default()),
), ),
( (Resource::new("", "/file/{file}.{ext}"), Some(ResourceHandler::default())),
Resource::new("", "/file/{file}.{ext}"),
Some(ResourceHandler::default()),
),
( (
Resource::new("", "/v{val}/{val2}/index.html"), Resource::new("", "/v{val}/{val2}/index.html"),
Some(ResourceHandler::default()), Some(ResourceHandler::default()),
), ),
( (Resource::new("", "/v/{tail:.*}"), Some(ResourceHandler::default())),
Resource::new("", "/v/{tail:.*}"), (Resource::new("", "{test}/index.html"), Some(ResourceHandler::default())),
Some(ResourceHandler::default()),
),
(
Resource::new("", "{test}/index.html"),
Some(ResourceHandler::default()),
),
]; ];
let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes); let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes);
@ -439,10 +432,7 @@ mod tests {
let mut req = TestRequest::with_uri("/v/blah-blah/index.html").finish(); let mut req = TestRequest::with_uri("/v/blah-blah/index.html").finish();
assert_eq!(rec.recognize(&mut req), Some(5)); assert_eq!(rec.recognize(&mut req), Some(5));
assert_eq!( assert_eq!(req.match_info().get("tail").unwrap(), "blah-blah/index.html");
req.match_info().get("tail").unwrap(),
"blah-blah/index.html"
);
let mut req = TestRequest::with_uri("/bbb/index.html").finish(); let mut req = TestRequest::with_uri("/bbb/index.html").finish();
assert_eq!(rec.recognize(&mut req), Some(6)); assert_eq!(rec.recognize(&mut req), Some(6));
@ -452,14 +442,8 @@ mod tests {
#[test] #[test]
fn test_recognizer_2() { fn test_recognizer_2() {
let routes = vec![ let routes = vec![
( (Resource::new("", "/index.json"), Some(ResourceHandler::default())),
Resource::new("", "/index.json"), (Resource::new("", "/{source}.json"), Some(ResourceHandler::default())),
Some(ResourceHandler::default()),
),
(
Resource::new("", "/{source}.json"),
Some(ResourceHandler::default()),
),
]; ];
let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes); let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes);
@ -473,14 +457,8 @@ mod tests {
#[test] #[test]
fn test_recognizer_with_prefix() { fn test_recognizer_with_prefix() {
let routes = vec![ let routes = vec![
( (Resource::new("", "/name"), Some(ResourceHandler::default())),
Resource::new("", "/name"), (Resource::new("", "/name/{val}"), Some(ResourceHandler::default())),
Some(ResourceHandler::default()),
),
(
Resource::new("", "/name/{val}"),
Some(ResourceHandler::default()),
),
]; ];
let (rec, _) = Router::new::<()>("/test", ServerSettings::default(), routes); let (rec, _) = Router::new::<()>("/test", ServerSettings::default(), routes);
@ -497,14 +475,8 @@ mod tests {
// same patterns // same patterns
let routes = vec![ let routes = vec![
( (Resource::new("", "/name"), Some(ResourceHandler::default())),
Resource::new("", "/name"), (Resource::new("", "/name/{val}"), Some(ResourceHandler::default())),
Some(ResourceHandler::default()),
),
(
Resource::new("", "/name/{val}"),
Some(ResourceHandler::default()),
),
]; ];
let (rec, _) = Router::new::<()>("/test2", ServerSettings::default(), routes); let (rec, _) = Router::new::<()>("/test2", ServerSettings::default(), routes);
@ -573,14 +545,8 @@ mod tests {
#[test] #[test]
fn test_request_resource() { fn test_request_resource() {
let routes = vec![ let routes = vec![
( (Resource::new("r1", "/index.json"), Some(ResourceHandler::default())),
Resource::new("r1", "/index.json"), (Resource::new("r2", "/test.json"), Some(ResourceHandler::default())),
Some(ResourceHandler::default()),
),
(
Resource::new("r2", "/test.json"),
Some(ResourceHandler::default()),
),
]; ];
let (router, _) = Router::new::<()>("", ServerSettings::default(), routes); let (router, _) = Router::new::<()>("", ServerSettings::default(), routes);

View File

@ -7,7 +7,7 @@ use futures::{Async, Future, Poll};
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use super::settings::WorkerSettings; use super::settings::WorkerSettings;
use super::{utils, HttpHandler, IoStream, h1, h2}; use super::{h1, h2, utils, HttpHandler, IoStream};
const HTTP2_PREFACE: [u8; 14] = *b"PRI * HTTP/2.0"; const HTTP2_PREFACE: [u8; 14] = *b"PRI * HTTP/2.0";
@ -93,12 +93,12 @@ where
let el = self as *mut _; let el = self as *mut _;
self.node = Some(Node::new(el)); self.node = Some(Node::new(el));
let _ = match self.proto { let _ = match self.proto {
Some(HttpProtocol::H1(ref mut h1)) => self.node Some(HttpProtocol::H1(ref mut h1)) => {
.as_ref() self.node.as_ref().map(|n| h1.settings().head().insert(n))
.map(|n| h1.settings().head().insert(n)), }
Some(HttpProtocol::H2(ref mut h2)) => self.node Some(HttpProtocol::H2(ref mut h2)) => {
.as_ref() self.node.as_ref().map(|n| h2.settings().head().insert(n))
.map(|n| h2.settings().head().insert(n)), }
Some(HttpProtocol::Unknown(ref mut settings, _, _, _)) => { Some(HttpProtocol::Unknown(ref mut settings, _, _, _)) => {
self.node.as_ref().map(|n| settings.head().insert(n)) self.node.as_ref().map(|n| settings.head().insert(n))
} }
@ -112,7 +112,9 @@ where
match result { match result {
Ok(Async::Ready(())) | Err(_) => { Ok(Async::Ready(())) | Err(_) => {
h1.settings().remove_channel(); h1.settings().remove_channel();
self.node.as_mut().map(|n| n.remove()); if let Some(n) = self.node.as_mut() {
n.remove()
};
} }
_ => (), _ => (),
} }
@ -123,7 +125,9 @@ where
match result { match result {
Ok(Async::Ready(())) | Err(_) => { Ok(Async::Ready(())) | Err(_) => {
h2.settings().remove_channel(); h2.settings().remove_channel();
self.node.as_mut().map(|n| n.remove()); if let Some(n) = self.node.as_mut() {
n.remove()
};
} }
_ => (), _ => (),
} }
@ -139,7 +143,9 @@ where
Ok(Async::Ready(0)) | Err(_) => { Ok(Async::Ready(0)) | Err(_) => {
debug!("Ignored premature client disconnection"); debug!("Ignored premature client disconnection");
settings.remove_channel(); settings.remove_channel();
self.node.as_mut().map(|n| n.remove()); if let Some(n) = self.node.as_mut() {
n.remove()
};
return Err(()); return Err(());
} }
_ => (), _ => (),
@ -162,12 +168,8 @@ where
if let Some(HttpProtocol::Unknown(settings, addr, io, buf)) = self.proto.take() { if let Some(HttpProtocol::Unknown(settings, addr, io, buf)) = self.proto.take() {
match kind { match kind {
ProtocolKind::Http1 => { ProtocolKind::Http1 => {
self.proto = Some(HttpProtocol::H1(h1::Http1::new( self.proto =
settings, Some(HttpProtocol::H1(h1::Http1::new(settings, io, addr, buf)));
io,
addr,
buf,
)));
return self.poll(); return self.poll();
} }
ProtocolKind::Http2 => { ProtocolKind::Http2 => {
@ -204,7 +206,8 @@ impl<T> Node<T> {
#[allow(mutable_transmutes)] #[allow(mutable_transmutes)]
unsafe { unsafe {
if let Some(ref next2) = self.next { if let Some(ref next2) = self.next {
let n: &mut Node<()> = mem::transmute(next2.as_ref().unwrap()); let n: &mut Node<()> =
&mut *(next2.as_ref().unwrap() as *const _ as *mut _);
n.prev = Some(next as *const _ as *mut _); n.prev = Some(next as *const _ as *mut _);
} }
let slf: &mut Node<T> = mem::transmute(self); let slf: &mut Node<T> = mem::transmute(self);
@ -275,7 +278,9 @@ where
T: AsyncRead + AsyncWrite + 'static, T: AsyncRead + AsyncWrite + 'static,
{ {
pub fn new(io: T) -> Self { pub fn new(io: T) -> Self {
WrapperStream { io } WrapperStream {
io,
}
} }
} }

View File

@ -763,8 +763,7 @@ impl TransferEncoding {
return Ok(*remaining == 0); return Ok(*remaining == 0);
} }
let len = cmp::min(*remaining, msg.len() as u64); let len = cmp::min(*remaining, msg.len() as u64);
self.buffer self.buffer.extend(msg.take().split_to(len as usize).into());
.extend(msg.take().split_to(len as usize).into());
*remaining -= len as u64; *remaining -= len as u64;
Ok(*remaining == 0) Ok(*remaining == 0)
@ -856,10 +855,8 @@ impl AcceptEncoding {
/// Parse a raw Accept-Encoding header value into an ordered list. /// Parse a raw Accept-Encoding header value into an ordered list.
pub fn parse(raw: &str) -> ContentEncoding { pub fn parse(raw: &str) -> ContentEncoding {
let mut encodings: Vec<_> = raw.replace(' ', "") let mut encodings: Vec<_> =
.split(',') raw.replace(' ', "").split(',').map(|l| AcceptEncoding::new(l)).collect();
.map(|l| AcceptEncoding::new(l))
.collect();
encodings.sort(); encodings.sort();
for enc in encodings { for enc in encodings {
@ -879,9 +876,7 @@ mod tests {
fn test_chunked_te() { fn test_chunked_te() {
let bytes = SharedBytes::default(); let bytes = SharedBytes::default();
let mut enc = TransferEncoding::chunked(bytes.clone()); let mut enc = TransferEncoding::chunked(bytes.clone());
assert!(!enc.encode(Binary::from(b"test".as_ref())) assert!(!enc.encode(Binary::from(b"test".as_ref())).ok().unwrap());
.ok()
.unwrap());
assert!(enc.encode(Binary::from(b"".as_ref())).ok().unwrap()); assert!(enc.encode(Binary::from(b"".as_ref())).ok().unwrap());
assert_eq!( assert_eq!(
bytes.get_mut().take().freeze(), bytes.get_mut().take().freeze(),

File diff suppressed because it is too large Load Diff

482
src/server/h1decoder.rs Normal file
View File

@ -0,0 +1,482 @@
use std::{io, mem};
use bytes::{Bytes, BytesMut};
use futures::{Async, Poll};
use httparse;
use super::helpers::SharedHttpInnerMessage;
use super::settings::WorkerSettings;
use error::ParseError;
use http::header::{HeaderName, HeaderValue};
use http::{header, HttpTryFrom, Method, Uri, Version};
use uri::Url;
const MAX_BUFFER_SIZE: usize = 131_072;
const MAX_HEADERS: usize = 96;
pub(crate) struct H1Decoder {
decoder: Option<EncodingDecoder>,
}
pub(crate) enum Message {
Message {
msg: SharedHttpInnerMessage,
payload: bool,
},
Chunk(Bytes),
Eof,
}
#[derive(Debug)]
pub(crate) enum DecoderError {
Io(io::Error),
Error(ParseError),
}
impl From<io::Error> for DecoderError {
fn from(err: io::Error) -> DecoderError {
DecoderError::Io(err)
}
}
impl H1Decoder {
pub fn new() -> H1Decoder {
H1Decoder {
decoder: None,
}
}
pub fn decode<H>(
&mut self, src: &mut BytesMut, settings: &WorkerSettings<H>,
) -> Result<Option<Message>, DecoderError> {
// read payload
if self.decoder.is_some() {
match self.decoder.as_mut().unwrap().decode(src)? {
Async::Ready(Some(bytes)) => return Ok(Some(Message::Chunk(bytes))),
Async::Ready(None) => {
self.decoder.take();
return Ok(Some(Message::Eof));
}
Async::NotReady => return Ok(None),
}
}
match self.parse_message(src, settings).map_err(DecoderError::Error)? {
Async::Ready((msg, decoder)) => {
if let Some(decoder) = decoder {
self.decoder = Some(decoder);
Ok(Some(Message::Message {
msg,
payload: true,
}))
} else {
Ok(Some(Message::Message {
msg,
payload: false,
}))
}
}
Async::NotReady => {
if src.len() >= MAX_BUFFER_SIZE {
error!("MAX_BUFFER_SIZE unprocessed data reached, closing");
Err(DecoderError::Error(ParseError::TooLarge))
} else {
Ok(None)
}
}
}
}
fn parse_message<H>(
&self, buf: &mut BytesMut, settings: &WorkerSettings<H>,
) -> Poll<(SharedHttpInnerMessage, Option<EncodingDecoder>), ParseError> {
// Parse http message
let mut has_upgrade = false;
let mut chunked = false;
let mut content_length = None;
let msg = {
let bytes_ptr = buf.as_ref().as_ptr() as usize;
let mut headers: [httparse::Header; MAX_HEADERS] =
unsafe { mem::uninitialized() };
let (len, method, path, version, headers_len) = {
let b = unsafe {
let b: &[u8] = buf;
&*(b as *const [u8])
};
let mut req = httparse::Request::new(&mut headers);
match req.parse(b)? {
httparse::Status::Complete(len) => {
let method = Method::from_bytes(req.method.unwrap().as_bytes())
.map_err(|_| ParseError::Method)?;
let path = Url::new(Uri::try_from(req.path.unwrap())?);
let version = if req.version.unwrap() == 1 {
Version::HTTP_11
} else {
Version::HTTP_10
};
(len, method, path, version, req.headers.len())
}
httparse::Status::Partial => return Ok(Async::NotReady),
}
};
let slice = buf.split_to(len).freeze();
// convert headers
let msg = settings.get_http_message();
{
let msg_mut = msg.get_mut();
msg_mut.keep_alive = version != Version::HTTP_10;
for header in headers[..headers_len].iter() {
if let Ok(name) = HeaderName::from_bytes(header.name.as_bytes()) {
has_upgrade = has_upgrade || name == header::UPGRADE;
let v_start = header.value.as_ptr() as usize - bytes_ptr;
let v_end = v_start + header.value.len();
let value = unsafe {
HeaderValue::from_shared_unchecked(
slice.slice(v_start, v_end),
)
};
match name {
header::CONTENT_LENGTH => {
if let Ok(s) = value.to_str() {
if let Ok(len) = s.parse::<u64>() {
content_length = Some(len)
} else {
debug!("illegal Content-Length: {:?}", len);
return Err(ParseError::Header);
}
} else {
debug!("illegal Content-Length: {:?}", len);
return Err(ParseError::Header);
}
}
// transfer-encoding
header::TRANSFER_ENCODING => {
if let Ok(s) = value.to_str() {
chunked = s.to_lowercase().contains("chunked");
} else {
return Err(ParseError::Header);
}
}
// connection keep-alive state
header::CONNECTION => {
msg_mut.keep_alive = if let Ok(conn) = value.to_str() {
if version == Version::HTTP_10
&& conn.contains("keep-alive")
{
true
} else {
version == Version::HTTP_11
&& !(conn.contains("close")
|| conn.contains("upgrade"))
}
} else {
false
};
}
_ => (),
}
msg_mut.headers.append(name, value);
} else {
return Err(ParseError::Header);
}
}
msg_mut.url = path;
msg_mut.method = method;
msg_mut.version = version;
}
msg
};
// https://tools.ietf.org/html/rfc7230#section-3.3.3
let decoder = if chunked {
// Chunked encoding
Some(EncodingDecoder::chunked())
} else if let Some(len) = content_length {
// Content-Length
Some(EncodingDecoder::length(len))
} else if has_upgrade || msg.get_ref().method == Method::CONNECT {
// upgrade(websocket) or connect
Some(EncodingDecoder::eof())
} else {
None
};
Ok(Async::Ready((msg, decoder)))
}
}
/// Decoders to handle different Transfer-Encodings.
///
/// If a message body does not include a Transfer-Encoding, it *should*
/// include a Content-Length header.
#[derive(Debug, Clone, PartialEq)]
pub struct EncodingDecoder {
kind: Kind,
}
impl EncodingDecoder {
pub fn length(x: u64) -> EncodingDecoder {
EncodingDecoder {
kind: Kind::Length(x),
}
}
pub fn chunked() -> EncodingDecoder {
EncodingDecoder {
kind: Kind::Chunked(ChunkedState::Size, 0),
}
}
pub fn eof() -> EncodingDecoder {
EncodingDecoder {
kind: Kind::Eof(false),
}
}
}
#[derive(Debug, Clone, PartialEq)]
enum Kind {
/// A Reader used when a Content-Length header is passed with a positive
/// integer.
Length(u64),
/// A Reader used when Transfer-Encoding is `chunked`.
Chunked(ChunkedState, u64),
/// A Reader used for responses that don't indicate a length or chunked.
///
/// Note: This should only used for `Response`s. It is illegal for a
/// `Request` to be made with both `Content-Length` and
/// `Transfer-Encoding: chunked` missing, as explained from the spec:
///
/// > If a Transfer-Encoding header field is present in a response and
/// > the chunked transfer coding is not the final encoding, the
/// > message body length is determined by reading the connection until
/// > it is closed by the server. If a Transfer-Encoding header field
/// > is present in a request and the chunked transfer coding is not
/// > the final encoding, the message body length cannot be determined
/// > reliably; the server MUST respond with the 400 (Bad Request)
/// > status code and then close the connection.
Eof(bool),
}
#[derive(Debug, PartialEq, Clone)]
enum ChunkedState {
Size,
SizeLws,
Extension,
SizeLf,
Body,
BodyCr,
BodyLf,
EndCr,
EndLf,
End,
}
impl EncodingDecoder {
pub fn decode(&mut self, body: &mut BytesMut) -> Poll<Option<Bytes>, io::Error> {
match self.kind {
Kind::Length(ref mut remaining) => {
if *remaining == 0 {
Ok(Async::Ready(None))
} else {
if body.is_empty() {
return Ok(Async::NotReady);
}
let len = body.len() as u64;
let buf;
if *remaining > len {
buf = body.take().freeze();
*remaining -= len;
} else {
buf = body.split_to(*remaining as usize).freeze();
*remaining = 0;
}
trace!("Length read: {}", buf.len());
Ok(Async::Ready(Some(buf)))
}
}
Kind::Chunked(ref mut state, ref mut size) => {
loop {
let mut buf = None;
// advances the chunked state
*state = try_ready!(state.step(body, size, &mut buf));
if *state == ChunkedState::End {
trace!("End of chunked stream");
return Ok(Async::Ready(None));
}
if let Some(buf) = buf {
return Ok(Async::Ready(Some(buf)));
}
if body.is_empty() {
return Ok(Async::NotReady);
}
}
}
Kind::Eof(ref mut is_eof) => {
if *is_eof {
Ok(Async::Ready(None))
} else if !body.is_empty() {
Ok(Async::Ready(Some(body.take().freeze())))
} else {
Ok(Async::NotReady)
}
}
}
}
}
macro_rules! byte (
($rdr:ident) => ({
if $rdr.len() > 0 {
let b = $rdr[0];
$rdr.split_to(1);
b
} else {
return Ok(Async::NotReady)
}
})
);
impl ChunkedState {
fn step(
&self, body: &mut BytesMut, size: &mut u64, buf: &mut Option<Bytes>,
) -> Poll<ChunkedState, io::Error> {
use self::ChunkedState::*;
match *self {
Size => ChunkedState::read_size(body, size),
SizeLws => ChunkedState::read_size_lws(body),
Extension => ChunkedState::read_extension(body),
SizeLf => ChunkedState::read_size_lf(body, size),
Body => ChunkedState::read_body(body, size, buf),
BodyCr => ChunkedState::read_body_cr(body),
BodyLf => ChunkedState::read_body_lf(body),
EndCr => ChunkedState::read_end_cr(body),
EndLf => ChunkedState::read_end_lf(body),
End => Ok(Async::Ready(ChunkedState::End)),
}
}
fn read_size(rdr: &mut BytesMut, size: &mut u64) -> Poll<ChunkedState, io::Error> {
let radix = 16;
match byte!(rdr) {
b @ b'0'...b'9' => {
*size *= radix;
*size += u64::from(b - b'0');
}
b @ b'a'...b'f' => {
*size *= radix;
*size += u64::from(b + 10 - b'a');
}
b @ b'A'...b'F' => {
*size *= radix;
*size += u64::from(b + 10 - b'A');
}
b'\t' | b' ' => return Ok(Async::Ready(ChunkedState::SizeLws)),
b';' => return Ok(Async::Ready(ChunkedState::Extension)),
b'\r' => return Ok(Async::Ready(ChunkedState::SizeLf)),
_ => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid chunk size line: Invalid Size",
));
}
}
Ok(Async::Ready(ChunkedState::Size))
}
fn read_size_lws(rdr: &mut BytesMut) -> Poll<ChunkedState, io::Error> {
trace!("read_size_lws");
match byte!(rdr) {
// LWS can follow the chunk size, but no more digits can come
b'\t' | b' ' => Ok(Async::Ready(ChunkedState::SizeLws)),
b';' => Ok(Async::Ready(ChunkedState::Extension)),
b'\r' => Ok(Async::Ready(ChunkedState::SizeLf)),
_ => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid chunk size linear white space",
)),
}
}
fn read_extension(rdr: &mut BytesMut) -> Poll<ChunkedState, io::Error> {
match byte!(rdr) {
b'\r' => Ok(Async::Ready(ChunkedState::SizeLf)),
_ => Ok(Async::Ready(ChunkedState::Extension)), // no supported extensions
}
}
fn read_size_lf(
rdr: &mut BytesMut, size: &mut u64,
) -> Poll<ChunkedState, io::Error> {
match byte!(rdr) {
b'\n' if *size > 0 => Ok(Async::Ready(ChunkedState::Body)),
b'\n' if *size == 0 => Ok(Async::Ready(ChunkedState::EndCr)),
_ => {
Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk size LF"))
}
}
}
fn read_body(
rdr: &mut BytesMut, rem: &mut u64, buf: &mut Option<Bytes>,
) -> Poll<ChunkedState, io::Error> {
trace!("Chunked read, remaining={:?}", rem);
let len = rdr.len() as u64;
if len == 0 {
Ok(Async::Ready(ChunkedState::Body))
} else {
let slice;
if *rem > len {
slice = rdr.take().freeze();
*rem -= len;
} else {
slice = rdr.split_to(*rem as usize).freeze();
*rem = 0;
}
*buf = Some(slice);
if *rem > 0 {
Ok(Async::Ready(ChunkedState::Body))
} else {
Ok(Async::Ready(ChunkedState::BodyCr))
}
}
}
fn read_body_cr(rdr: &mut BytesMut) -> Poll<ChunkedState, io::Error> {
match byte!(rdr) {
b'\r' => Ok(Async::Ready(ChunkedState::BodyLf)),
_ => {
Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk body CR"))
}
}
}
fn read_body_lf(rdr: &mut BytesMut) -> Poll<ChunkedState, io::Error> {
match byte!(rdr) {
b'\n' => Ok(Async::Ready(ChunkedState::Size)),
_ => {
Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk body LF"))
}
}
}
fn read_end_cr(rdr: &mut BytesMut) -> Poll<ChunkedState, io::Error> {
match byte!(rdr) {
b'\r' => Ok(Async::Ready(ChunkedState::EndLf)),
_ => {
Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk end CR"))
}
}
}
fn read_end_lf(rdr: &mut BytesMut) -> Poll<ChunkedState, io::Error> {
match byte!(rdr) {
b'\n' => Ok(Async::Ready(ChunkedState::End)),
_ => {
Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk end LF"))
}
}
}
}

View File

@ -2,8 +2,8 @@
use bytes::BufMut; use bytes::BufMut;
use futures::{Async, Poll}; use futures::{Async, Poll};
use std::io;
use std::rc::Rc; use std::rc::Rc;
use std::{io, mem};
use tokio_io::AsyncWrite; use tokio_io::AsyncWrite;
use super::encoding::ContentEncoder; use super::encoding::ContentEncoder;
@ -13,10 +13,10 @@ use super::shared::SharedBytes;
use super::{Writer, WriterState, MAX_WRITE_BUFFER_SIZE}; use super::{Writer, WriterState, MAX_WRITE_BUFFER_SIZE};
use body::{Binary, Body}; use body::{Binary, Body};
use header::ContentEncoding; use header::ContentEncoding;
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE};
use http::{Method, Version};
use httprequest::HttpInnerMessage; use httprequest::HttpInnerMessage;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use http::{Method, Version};
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE};
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
@ -42,7 +42,7 @@ pub(crate) struct H1Writer<T: AsyncWrite, H: 'static> {
impl<T: AsyncWrite, H: 'static> H1Writer<T, H> { impl<T: AsyncWrite, H: 'static> H1Writer<T, H> {
pub fn new( pub fn new(
stream: T, buf: SharedBytes, settings: Rc<WorkerSettings<H>> stream: T, buf: SharedBytes, settings: Rc<WorkerSettings<H>>,
) -> H1Writer<T, H> { ) -> H1Writer<T, H> {
H1Writer { H1Writer {
flags: Flags::empty(), flags: Flags::empty(),
@ -117,8 +117,7 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
let version = msg.version().unwrap_or_else(|| req.version); let version = msg.version().unwrap_or_else(|| req.version);
if msg.upgrade() { if msg.upgrade() {
self.flags.insert(Flags::UPGRADE); self.flags.insert(Flags::UPGRADE);
msg.headers_mut() msg.headers_mut().insert(CONNECTION, HeaderValue::from_static("upgrade"));
.insert(CONNECTION, HeaderValue::from_static("upgrade"));
} }
// keep-alive // keep-alive
else if self.flags.contains(Flags::KEEPALIVE) { else if self.flags.contains(Flags::KEEPALIVE) {
@ -127,8 +126,7 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
.insert(CONNECTION, HeaderValue::from_static("keep-alive")); .insert(CONNECTION, HeaderValue::from_static("keep-alive"));
} }
} else if version >= Version::HTTP_11 { } else if version >= Version::HTTP_11 {
msg.headers_mut() msg.headers_mut().insert(CONNECTION, HeaderValue::from_static("close"));
.insert(CONNECTION, HeaderValue::from_static("close"));
} }
let body = msg.replace_body(Body::Empty); let body = msg.replace_body(Body::Empty);
@ -169,7 +167,7 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
let mut pos = 0; let mut pos = 0;
let mut has_date = false; let mut has_date = false;
let mut remaining = buffer.remaining_mut(); let mut remaining = buffer.remaining_mut();
let mut buf: &mut [u8] = unsafe { mem::transmute(buffer.bytes_mut()) }; let mut buf = unsafe { &mut *(buffer.bytes_mut() as *mut [u8]) };
for (key, value) in msg.headers() { for (key, value) in msg.headers() {
if is_bin && key == CONTENT_LENGTH { if is_bin && key == CONTENT_LENGTH {
is_bin = false; is_bin = false;
@ -184,7 +182,7 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
pos = 0; pos = 0;
buffer.reserve(len); buffer.reserve(len);
remaining = buffer.remaining_mut(); remaining = buffer.remaining_mut();
buf = unsafe { mem::transmute(buffer.bytes_mut()) }; buf = unsafe { &mut *(buffer.bytes_mut() as *mut _) };
} }
buf[pos..pos + k.len()].copy_from_slice(k); buf[pos..pos + k.len()].copy_from_slice(k);
@ -272,7 +270,8 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
#[inline] #[inline]
fn poll_completed(&mut self, shutdown: bool) -> Poll<(), io::Error> { fn poll_completed(&mut self, shutdown: bool) -> Poll<(), io::Error> {
if !self.buffer.is_empty() { if !self.buffer.is_empty() {
let buf: &[u8] = unsafe { mem::transmute(self.buffer.as_ref()) }; let buf: &[u8] =
unsafe { &mut *(self.buffer.as_ref() as *const _ as *mut _) };
let written = self.write_data(buf)?; let written = self.write_data(buf)?;
let _ = self.buffer.split_to(written); let _ = self.buffer.split_to(written);
if self.buffer.len() > self.buffer_capacity { if self.buffer.len() > self.buffer_capacity {

View File

@ -61,7 +61,7 @@ where
H: HttpHandler + 'static, H: HttpHandler + 'static,
{ {
pub fn new( pub fn new(
settings: Rc<WorkerSettings<H>>, io: T, addr: Option<SocketAddr>, buf: Bytes settings: Rc<WorkerSettings<H>>, io: T, addr: Option<SocketAddr>, buf: Bytes,
) -> Self { ) -> Self {
Http2 { Http2 {
flags: Flags::empty(), flags: Flags::empty(),

View File

@ -45,7 +45,7 @@ pub(crate) struct H2Writer<H: 'static> {
impl<H: 'static> H2Writer<H> { impl<H: 'static> H2Writer<H> {
pub fn new( pub fn new(
respond: SendResponse<Bytes>, buf: SharedBytes, settings: Rc<WorkerSettings<H>> respond: SendResponse<Bytes>, buf: SharedBytes, settings: Rc<WorkerSettings<H>>,
) -> H2Writer<H> { ) -> H2Writer<H> {
H2Writer { H2Writer {
respond, respond,
@ -107,8 +107,7 @@ impl<H: 'static> Writer for H2Writer<H> {
); );
} }
Body::Empty => { Body::Empty => {
msg.headers_mut() msg.headers_mut().insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
.insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
} }
_ => (), _ => (),
} }
@ -120,9 +119,7 @@ impl<H: 'static> Writer for H2Writer<H> {
resp.headers_mut().insert(key, value.clone()); resp.headers_mut().insert(key, value.clone());
} }
match self.respond match self.respond.send_response(resp, self.flags.contains(Flags::EOF)) {
.send_response(resp, self.flags.contains(Flags::EOF))
{
Ok(stream) => self.stream = Some(stream), Ok(stream) => self.stream = Some(stream),
Err(_) => return Err(io::Error::new(io::ErrorKind::Other, "err")), Err(_) => return Err(io::Error::new(io::ErrorKind::Other, "err")),
} }

View File

@ -69,7 +69,7 @@ impl SharedHttpInnerMessage {
} }
pub fn new( pub fn new(
msg: Rc<HttpInnerMessage>, pool: Rc<SharedMessagePool> msg: Rc<HttpInnerMessage>, pool: Rc<SharedMessagePool>,
) -> SharedHttpInnerMessage { ) -> SharedHttpInnerMessage {
SharedHttpInnerMessage(Some(msg), Some(pool)) SharedHttpInnerMessage(Some(msg), Some(pool))
} }
@ -79,7 +79,7 @@ impl SharedHttpInnerMessage {
#[cfg_attr(feature = "cargo-clippy", allow(mut_from_ref, inline_always))] #[cfg_attr(feature = "cargo-clippy", allow(mut_from_ref, inline_always))]
pub fn get_mut(&self) -> &mut HttpInnerMessage { pub fn get_mut(&self) -> &mut HttpInnerMessage {
let r: &HttpInnerMessage = self.0.as_ref().unwrap().as_ref(); let r: &HttpInnerMessage = self.0.as_ref().unwrap().as_ref();
unsafe { mem::transmute(r) } unsafe { &mut *(r as *const _ as *mut _) }
} }
#[inline(always)] #[inline(always)]
@ -96,9 +96,8 @@ const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\
8081828384858687888990919293949596979899"; 8081828384858687888990919293949596979899";
pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesMut) { pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesMut) {
let mut buf: [u8; 13] = [ let mut buf: [u8; 13] =
b'H', b'T', b'T', b'P', b'/', b'1', b'.', b'1', b' ', b' ', b' ', b' ', b' ' [b'H', b'T', b'T', b'P', b'/', b'1', b'.', b'1', b' ', b' ', b' ', b' ', b' '];
];
match version { match version {
Version::HTTP_2 => buf[5] = b'2', Version::HTTP_2 => buf[5] = b'2',
Version::HTTP_10 => buf[7] = b'0', Version::HTTP_10 => buf[7] = b'0',
@ -251,63 +250,33 @@ mod tests {
let mut bytes = BytesMut::new(); let mut bytes = BytesMut::new();
bytes.reserve(50); bytes.reserve(50);
write_content_length(0, &mut bytes); write_content_length(0, &mut bytes);
assert_eq!( assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 0\r\n"[..]);
bytes.take().freeze(),
b"\r\ncontent-length: 0\r\n"[..]
);
bytes.reserve(50); bytes.reserve(50);
write_content_length(9, &mut bytes); write_content_length(9, &mut bytes);
assert_eq!( assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 9\r\n"[..]);
bytes.take().freeze(),
b"\r\ncontent-length: 9\r\n"[..]
);
bytes.reserve(50); bytes.reserve(50);
write_content_length(10, &mut bytes); write_content_length(10, &mut bytes);
assert_eq!( assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 10\r\n"[..]);
bytes.take().freeze(),
b"\r\ncontent-length: 10\r\n"[..]
);
bytes.reserve(50); bytes.reserve(50);
write_content_length(99, &mut bytes); write_content_length(99, &mut bytes);
assert_eq!( assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 99\r\n"[..]);
bytes.take().freeze(),
b"\r\ncontent-length: 99\r\n"[..]
);
bytes.reserve(50); bytes.reserve(50);
write_content_length(100, &mut bytes); write_content_length(100, &mut bytes);
assert_eq!( assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 100\r\n"[..]);
bytes.take().freeze(),
b"\r\ncontent-length: 100\r\n"[..]
);
bytes.reserve(50); bytes.reserve(50);
write_content_length(101, &mut bytes); write_content_length(101, &mut bytes);
assert_eq!( assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 101\r\n"[..]);
bytes.take().freeze(),
b"\r\ncontent-length: 101\r\n"[..]
);
bytes.reserve(50); bytes.reserve(50);
write_content_length(998, &mut bytes); write_content_length(998, &mut bytes);
assert_eq!( assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 998\r\n"[..]);
bytes.take().freeze(),
b"\r\ncontent-length: 998\r\n"[..]
);
bytes.reserve(50); bytes.reserve(50);
write_content_length(1000, &mut bytes); write_content_length(1000, &mut bytes);
assert_eq!( assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 1000\r\n"[..]);
bytes.take().freeze(),
b"\r\ncontent-length: 1000\r\n"[..]
);
bytes.reserve(50); bytes.reserve(50);
write_content_length(1001, &mut bytes); write_content_length(1001, &mut bytes);
assert_eq!( assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 1001\r\n"[..]);
bytes.take().freeze(),
b"\r\ncontent-length: 1001\r\n"[..]
);
bytes.reserve(50); bytes.reserve(50);
write_content_length(5909, &mut bytes); write_content_length(5909, &mut bytes);
assert_eq!( assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 5909\r\n"[..]);
bytes.take().freeze(),
b"\r\ncontent-length: 5909\r\n"[..]
);
} }
} }

View File

@ -10,6 +10,7 @@ use tokio_io::{AsyncRead, AsyncWrite};
mod channel; mod channel;
pub(crate) mod encoding; pub(crate) mod encoding;
pub(crate) mod h1; pub(crate) mod h1;
pub(crate) mod h1decoder;
mod h1writer; mod h1writer;
mod h2; mod h2;
mod h2writer; mod h2writer;

View File

@ -8,10 +8,10 @@ use std::sync::Arc;
use std::{fmt, mem, net}; use std::{fmt, mem, net};
use time; use time;
use super::KeepAlive;
use super::channel::Node; use super::channel::Node;
use super::helpers; use super::helpers;
use super::shared::{SharedBytes, SharedBytesPool}; use super::shared::{SharedBytes, SharedBytesPool};
use super::KeepAlive;
use body::Body; use body::Body;
use httpresponse::{HttpResponse, HttpResponseBuilder, HttpResponsePool}; use httpresponse::{HttpResponse, HttpResponseBuilder, HttpResponsePool};
@ -72,7 +72,7 @@ impl Default for ServerSettings {
impl ServerSettings { impl ServerSettings {
/// Crate server settings instance /// Crate server settings instance
pub(crate) fn new( pub(crate) fn new(
addr: Option<net::SocketAddr>, host: &Option<String>, secure: bool addr: Option<net::SocketAddr>, host: &Option<String>, secure: bool,
) -> ServerSettings { ) -> ServerSettings {
let host = if let Some(ref host) = *host { let host = if let Some(ref host) = *host {
host.clone() host.clone()
@ -119,7 +119,7 @@ impl ServerSettings {
#[inline] #[inline]
pub(crate) fn get_response_builder( pub(crate) fn get_response_builder(
&self, status: StatusCode &self, status: StatusCode,
) -> HttpResponseBuilder { ) -> HttpResponseBuilder {
HttpResponsePool::get_builder(&self.responses, status) HttpResponsePool::get_builder(&self.responses, status)
} }
@ -255,10 +255,7 @@ mod tests {
#[test] #[test]
fn test_date_len() { fn test_date_len() {
assert_eq!( assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len());
DATE_VALUE_LENGTH,
"Sun, 06 Nov 1994 08:49:37 GMT".len()
);
} }
#[test] #[test]

View File

@ -1,8 +1,8 @@
use bytes::{BufMut, BytesMut}; use bytes::{BufMut, BytesMut};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::io;
use std::rc::Rc; use std::rc::Rc;
use std::{io, mem};
use body::Binary; use body::Binary;
@ -61,7 +61,7 @@ impl SharedBytes {
#[cfg_attr(feature = "cargo-clippy", allow(mut_from_ref, inline_always))] #[cfg_attr(feature = "cargo-clippy", allow(mut_from_ref, inline_always))]
pub(crate) fn get_mut(&self) -> &mut BytesMut { pub(crate) fn get_mut(&self) -> &mut BytesMut {
let r: &BytesMut = self.0.as_ref().unwrap().as_ref(); let r: &BytesMut = self.0.as_ref().unwrap().as_ref();
unsafe { mem::transmute(r) } unsafe { &mut *(r as *const _ as *mut _) }
} }
#[inline] #[inline]

View File

@ -219,10 +219,7 @@ where
if let Some(e) = err.take() { if let Some(e) = err.take() {
Err(e) Err(e)
} else { } else {
Err(io::Error::new( Err(io::Error::new(io::ErrorKind::Other, "Can not bind to address."))
io::ErrorKind::Other,
"Can not bind to address.",
))
} }
} else { } else {
Ok(self) Ok(self)
@ -230,7 +227,7 @@ where
} }
fn start_workers( fn start_workers(
&mut self, settings: &ServerSettings, handler: &StreamHandlerType &mut self, settings: &ServerSettings, handler: &StreamHandlerType,
) -> Vec<(usize, mpsc::UnboundedSender<Conn<net::TcpStream>>)> { ) -> Vec<(usize, mpsc::UnboundedSender<Conn<net::TcpStream>>)> {
// start workers // start workers
let mut workers = Vec::new(); let mut workers = Vec::new();
@ -332,9 +329,9 @@ impl<H: IntoHttpHandler> HttpServer<H> {
ctx.add_stream(rx); ctx.add_stream(rx);
self self
}); });
signals.map(|signals| { if let Some(signals) = signals {
signals.do_send(signal::Subscribe(addr.clone().recipient())) signals.do_send(signal::Subscribe(addr.clone().recipient()))
}); }
addr addr
} }
} }
@ -378,10 +375,7 @@ impl<H: IntoHttpHandler> HttpServer<H> {
/// Start listening for incoming tls connections. /// Start listening for incoming tls connections.
pub fn start_tls(mut self, acceptor: TlsAcceptor) -> io::Result<Addr<Syn, Self>> { pub fn start_tls(mut self, acceptor: TlsAcceptor) -> io::Result<Addr<Syn, Self>> {
if self.sockets.is_empty() { if self.sockets.is_empty() {
Err(io::Error::new( Err(io::Error::new(io::ErrorKind::Other, "No socket addresses are bound"))
io::ErrorKind::Other,
"No socket addresses are bound",
))
} else { } else {
let (tx, rx) = mpsc::unbounded(); let (tx, rx) = mpsc::unbounded();
let addrs: Vec<(net::SocketAddr, net::TcpListener)> = let addrs: Vec<(net::SocketAddr, net::TcpListener)> =
@ -427,13 +421,10 @@ impl<H: IntoHttpHandler> HttpServer<H> {
/// ///
/// This method sets alpn protocols to "h2" and "http/1.1" /// This method sets alpn protocols to "h2" and "http/1.1"
pub fn start_ssl( pub fn start_ssl(
mut self, mut builder: SslAcceptorBuilder mut self, mut builder: SslAcceptorBuilder,
) -> io::Result<Addr<Syn, Self>> { ) -> io::Result<Addr<Syn, Self>> {
if self.sockets.is_empty() { if self.sockets.is_empty() {
Err(io::Error::new( Err(io::Error::new(io::ErrorKind::Other, "No socket addresses are bound"))
io::ErrorKind::Other,
"No socket addresses are bound",
))
} else { } else {
// alpn support // alpn support
if !self.no_http2 { if !self.no_http2 {
@ -545,8 +536,9 @@ impl<H: IntoHttpHandler> HttpServer<H> {
})); }));
self self
}); });
signals if let Some(signals) = signals {
.map(|signals| signals.do_send(signal::Subscribe(addr.clone().recipient()))); signals.do_send(signal::Subscribe(addr.clone().recipient()))
}
addr addr
} }
} }
@ -562,17 +554,35 @@ impl<H: IntoHttpHandler> Handler<signal::Signal> for HttpServer<H> {
signal::SignalType::Int => { signal::SignalType::Int => {
info!("SIGINT received, exiting"); info!("SIGINT received, exiting");
self.exit = true; self.exit = true;
Handler::<StopServer>::handle(self, StopServer { graceful: false }, ctx); Handler::<StopServer>::handle(
self,
StopServer {
graceful: false,
},
ctx,
);
} }
signal::SignalType::Term => { signal::SignalType::Term => {
info!("SIGTERM received, stopping"); info!("SIGTERM received, stopping");
self.exit = true; self.exit = true;
Handler::<StopServer>::handle(self, StopServer { graceful: true }, ctx); Handler::<StopServer>::handle(
self,
StopServer {
graceful: true,
},
ctx,
);
} }
signal::SignalType::Quit => { signal::SignalType::Quit => {
info!("SIGQUIT received, exiting"); info!("SIGQUIT received, exiting");
self.exit = true; self.exit = true;
Handler::<StopServer>::handle(self, StopServer { graceful: false }, ctx); Handler::<StopServer>::handle(
self,
StopServer {
graceful: false,
},
ctx,
);
} }
_ => (), _ => (),
} }
@ -696,7 +706,9 @@ impl<H: IntoHttpHandler> Handler<StopServer> for HttpServer<H> {
let tx2 = tx.clone(); let tx2 = tx.clone();
worker worker
.1 .1
.send(StopWorker { graceful: dur }) .send(StopWorker {
graceful: dur,
})
.into_actor(self) .into_actor(self)
.then(move |_, slf, ctx| { .then(move |_, slf, ctx| {
slf.workers.pop(); slf.workers.pop();
@ -746,9 +758,8 @@ fn start_accept_thread(
// start accept thread // start accept thread
#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))] #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
let _ = thread::Builder::new() let _ = thread::Builder::new().name(format!("Accept on {}", addr)).spawn(
.name(format!("Accept on {}", addr)) move || {
.spawn(move || {
const SRV: mio::Token = mio::Token(0); const SRV: mio::Token = mio::Token(0);
const CMD: mio::Token = mio::Token(1); const CMD: mio::Token = mio::Token(1);
@ -773,12 +784,9 @@ fn start_accept_thread(
} }
// Start listening for incoming commands // Start listening for incoming commands
if let Err(err) = poll.register( if let Err(err) =
&reg, poll.register(&reg, CMD, mio::Ready::readable(), mio::PollOpt::edge())
CMD, {
mio::Ready::readable(),
mio::PollOpt::edge(),
) {
panic!("Can not register Registration: {}", err); panic!("Can not register Registration: {}", err);
} }
@ -909,13 +917,14 @@ fn start_accept_thread(
} }
} }
} }
}); },
);
(readiness, tx) (readiness, tx)
} }
fn create_tcp_listener( fn create_tcp_listener(
addr: net::SocketAddr, backlog: i32 addr: net::SocketAddr, backlog: i32,
) -> io::Result<net::TcpListener> { ) -> io::Result<net::TcpListener> {
let builder = match addr { let builder = match addr {
net::SocketAddr::V4(_) => TcpBuilder::new_v4()?, net::SocketAddr::V4(_) => TcpBuilder::new_v4()?,

View File

@ -8,7 +8,7 @@ const LW_BUFFER_SIZE: usize = 4096;
const HW_BUFFER_SIZE: usize = 32_768; const HW_BUFFER_SIZE: usize = 32_768;
pub fn read_from_io<T: IoStream>( pub fn read_from_io<T: IoStream>(
io: &mut T, buf: &mut BytesMut io: &mut T, buf: &mut BytesMut,
) -> Poll<usize, io::Error> { ) -> Poll<usize, io::Error> {
unsafe { unsafe {
if buf.remaining_mut() < LW_BUFFER_SIZE { if buf.remaining_mut() < LW_BUFFER_SIZE {

View File

@ -1,5 +1,5 @@
use futures::Future;
use futures::unsync::oneshot; use futures::unsync::oneshot;
use futures::Future;
use net2::TcpStreamExt; use net2::TcpStreamExt;
use std::rc::Rc; use std::rc::Rc;
use std::{net, time}; use std::{net, time};
@ -59,7 +59,7 @@ where
impl<H: HttpHandler + 'static> Worker<H> { impl<H: HttpHandler + 'static> Worker<H> {
pub(crate) fn new( pub(crate) fn new(
h: Vec<H>, handler: StreamHandlerType, keep_alive: KeepAlive h: Vec<H>, handler: StreamHandlerType, keep_alive: KeepAlive,
) -> Worker<H> { ) -> Worker<H> {
let tcp_ka = if let KeepAlive::Tcp(val) = keep_alive { let tcp_ka = if let KeepAlive::Tcp(val) = keep_alive {
Some(time::Duration::new(val as u64, 0)) Some(time::Duration::new(val as u64, 0))
@ -77,13 +77,11 @@ impl<H: HttpHandler + 'static> Worker<H> {
fn update_time(&self, ctx: &mut Context<Self>) { fn update_time(&self, ctx: &mut Context<Self>) {
self.settings.update_date(); self.settings.update_date();
ctx.run_later(time::Duration::new(1, 0), |slf, ctx| { ctx.run_later(time::Duration::new(1, 0), |slf, ctx| slf.update_time(ctx));
slf.update_time(ctx)
});
} }
fn shutdown_timeout( fn shutdown_timeout(
&self, ctx: &mut Context<Self>, tx: oneshot::Sender<bool>, dur: time::Duration &self, ctx: &mut Context<Self>, tx: oneshot::Sender<bool>, dur: time::Duration,
) { ) {
// sleep for 1 second and then check again // sleep for 1 second and then check again
ctx.run_later(time::Duration::new(1, 0), move |slf, ctx| { ctx.run_later(time::Duration::new(1, 0), move |slf, ctx| {
@ -124,8 +122,7 @@ where
if self.tcp_ka.is_some() && msg.io.set_keepalive(self.tcp_ka).is_err() { if self.tcp_ka.is_some() && msg.io.set_keepalive(self.tcp_ka).is_err() {
error!("Can not set socket keep-alive option"); error!("Can not set socket keep-alive option");
} }
self.handler self.handler.handle(Rc::clone(&self.settings), &self.hnd, msg);
.handle(Rc::clone(&self.settings), &self.hnd, msg);
} }
} }
@ -165,7 +162,7 @@ pub(crate) enum StreamHandlerType {
impl StreamHandlerType { impl StreamHandlerType {
fn handle<H: HttpHandler>( fn handle<H: HttpHandler>(
&mut self, h: Rc<WorkerSettings<H>>, hnd: &Handle, msg: Conn<net::TcpStream> &mut self, h: Rc<WorkerSettings<H>>, hnd: &Handle, msg: Conn<net::TcpStream>,
) { ) {
match *self { match *self {
StreamHandlerType::Normal => { StreamHandlerType::Normal => {
@ -177,60 +174,57 @@ impl StreamHandlerType {
} }
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
StreamHandlerType::Tls(ref acceptor) => { StreamHandlerType::Tls(ref acceptor) => {
let Conn { io, peer, http2 } = msg; let Conn {
io,
peer,
http2,
} = msg;
let _ = io.set_nodelay(true); let _ = io.set_nodelay(true);
let io = TcpStream::from_stream(io, hnd) let io = TcpStream::from_stream(io, hnd)
.expect("failed to associate TCP stream"); .expect("failed to associate TCP stream");
hnd.spawn( hnd.spawn(TlsAcceptorExt::accept_async(acceptor, io).then(move |res| {
TlsAcceptorExt::accept_async(acceptor, io).then(move |res| { match res {
match res { Ok(io) => {
Ok(io) => Arbiter::handle().spawn(HttpChannel::new( Arbiter::handle().spawn(HttpChannel::new(h, io, peer, http2))
h, }
io, Err(err) => {
peer, trace!("Error during handling tls connection: {}", err)
http2, }
)), };
Err(err) => { future::result(Ok(()))
trace!("Error during handling tls connection: {}", err) }));
}
};
future::result(Ok(()))
}),
);
} }
#[cfg(feature = "alpn")] #[cfg(feature = "alpn")]
StreamHandlerType::Alpn(ref acceptor) => { StreamHandlerType::Alpn(ref acceptor) => {
let Conn { io, peer, .. } = msg; let Conn {
io,
peer,
..
} = msg;
let _ = io.set_nodelay(true); let _ = io.set_nodelay(true);
let io = TcpStream::from_stream(io, hnd) let io = TcpStream::from_stream(io, hnd)
.expect("failed to associate TCP stream"); .expect("failed to associate TCP stream");
hnd.spawn( hnd.spawn(SslAcceptorExt::accept_async(acceptor, io).then(move |res| {
SslAcceptorExt::accept_async(acceptor, io).then(move |res| { match res {
match res { Ok(io) => {
Ok(io) => { let http2 = if let Some(p) =
let http2 = if let Some(p) = io.get_ref().ssl().selected_alpn_protocol()
io.get_ref().ssl().selected_alpn_protocol() {
{ p.len() == 2 && &p == b"h2"
p.len() == 2 && &p == b"h2" } else {
} else { false
false };
}; Arbiter::handle()
Arbiter::handle().spawn(HttpChannel::new( .spawn(HttpChannel::new(h, io, peer, http2));
h, }
io, Err(err) => {
peer, trace!("Error during handling tls connection: {}", err)
http2, }
)); };
} future::result(Ok(()))
Err(err) => { }));
trace!("Error during handling tls connection: {}", err)
}
};
future::result(Ok(()))
}),
);
} }
} }
} }

View File

@ -170,14 +170,22 @@ impl TestServer {
if uri.starts_with('/') { if uri.starts_with('/') {
format!( format!(
"{}://{}{}", "{}://{}{}",
if self.ssl { "https" } else { "http" }, if self.ssl {
"https"
} else {
"http"
},
self.addr, self.addr,
uri uri
) )
} else { } else {
format!( format!(
"{}://{}/{}", "{}://{}/{}",
if self.ssl { "https" } else { "http" }, if self.ssl {
"https"
} else {
"http"
},
self.addr, self.addr,
uri uri
) )
@ -202,7 +210,7 @@ impl TestServer {
/// Connect to websocket server /// Connect to websocket server
pub fn ws( pub fn ws(
&mut self &mut self,
) -> Result<(ws::ClientReader, ws::ClientWriter), ws::ClientError> { ) -> Result<(ws::ClientReader, ws::ClientWriter), ws::ClientError> {
let url = self.url("/"); let url = self.url("/");
self.system.run_until_complete( self.system.run_until_complete(
@ -350,17 +358,14 @@ pub struct TestApp<S = ()> {
impl<S: 'static> TestApp<S> { impl<S: 'static> TestApp<S> {
fn new(state: S) -> TestApp<S> { fn new(state: S) -> TestApp<S> {
let app = App::with_state(state); let app = App::with_state(state);
TestApp { app: Some(app) } TestApp {
app: Some(app),
}
} }
/// Register handler for "/" /// Register handler for "/"
pub fn handler<H: Handler<S>>(&mut self, handler: H) { pub fn handler<H: Handler<S>>(&mut self, handler: H) {
self.app = Some( self.app = Some(self.app.take().unwrap().resource("/", |r| r.h(handler)));
self.app
.take()
.unwrap()
.resource("/", |r| r.h(handler)),
);
} }
/// Register middleware /// Register middleware
@ -594,7 +599,7 @@ impl<S> TestRequest<S> {
/// ///
/// This method panics is handler returns actor or async result. /// This method panics is handler returns actor or async result.
pub fn run<H: Handler<S>>( pub fn run<H: Handler<S>>(
self, mut h: H self, mut h: H,
) -> Result<HttpResponse, <<H as Handler<S>>::Result as Responder>::Error> { ) -> Result<HttpResponse, <<H as Handler<S>>::Result as Responder>::Error> {
let req = self.finish(); let req = self.finish();
let resp = h.handle(req.clone()); let resp = h.handle(req.clone());

View File

@ -44,7 +44,10 @@ impl Url {
pub fn new(uri: Uri) -> Url { pub fn new(uri: Uri) -> Url {
let path = DEFAULT_QUOTER.requote(uri.path().as_bytes()); let path = DEFAULT_QUOTER.requote(uri.path().as_bytes());
Url { uri, path } Url {
uri,
path,
}
} }
pub fn uri(&self) -> &Uri { pub fn uri(&self) -> &Uri {

View File

@ -187,7 +187,7 @@ where
S: 'static, S: 'static,
{ {
pub fn new( pub fn new(
f: F, cfg1: ExtractorConfig<S, T1>, cfg2: ExtractorConfig<S, T2> f: F, cfg1: ExtractorConfig<S, T1>, cfg2: ExtractorConfig<S, T2>,
) -> Self { ) -> Self {
With2 { With2 {
hnd: Rc::new(UnsafeCell::new(f)), hnd: Rc::new(UnsafeCell::new(f)),
@ -307,10 +307,8 @@ where
Async::Ready(item) => { Async::Ready(item) => {
self.item = Some(item); self.item = Some(item);
self.fut1.take(); self.fut1.take();
self.fut2 = Some(Box::new(T2::from_request( self.fut2 =
&self.req, Some(Box::new(T2::from_request(&self.req, self.cfg2.as_ref())));
self.cfg2.as_ref(),
)));
} }
Async::NotReady => return Ok(Async::NotReady), Async::NotReady => return Ok(Async::NotReady),
} }
@ -510,10 +508,8 @@ where
Async::Ready(item) => { Async::Ready(item) => {
self.item1 = Some(item); self.item1 = Some(item);
self.fut1.take(); self.fut1.take();
self.fut2 = Some(Box::new(T2::from_request( self.fut2 =
&self.req, Some(Box::new(T2::from_request(&self.req, self.cfg2.as_ref())));
self.cfg2.as_ref(),
)));
} }
Async::NotReady => return Ok(Async::NotReady), Async::NotReady => return Ok(Async::NotReady),
} }
@ -524,10 +520,8 @@ where
Async::Ready(item) => { Async::Ready(item) => {
self.item2 = Some(item); self.item2 = Some(item);
self.fut2.take(); self.fut2.take();
self.fut3 = Some(Box::new(T3::from_request( self.fut3 =
&self.req, Some(Box::new(T3::from_request(&self.req, self.cfg3.as_ref())));
self.cfg3.as_ref(),
)));
} }
Async::NotReady => return Ok(Async::NotReady), Async::NotReady => return Ok(Async::NotReady),
} }
@ -539,15 +533,13 @@ where
}; };
let hnd: &mut F = unsafe { &mut *self.hnd.get() }; let hnd: &mut F = unsafe { &mut *self.hnd.get() };
let item = match (*hnd)( let item =
self.item1.take().unwrap(), match (*hnd)(self.item1.take().unwrap(), self.item2.take().unwrap(), item)
self.item2.take().unwrap(), .respond_to(self.req.drop_state())
item, {
).respond_to(self.req.drop_state()) Ok(item) => item.into(),
{ Err(err) => return Err(err.into()),
Ok(item) => item.into(), };
Err(err) => return Err(err.into()),
};
match item.into() { match item.into() {
ReplyItem::Message(resp) => return Ok(Async::Ready(resp)), ReplyItem::Message(resp) => return Ok(Async::Ready(resp)),

View File

@ -121,7 +121,7 @@ impl Client {
/// Create new websocket connection with custom `ClientConnector` /// Create new websocket connection with custom `ClientConnector`
pub fn with_connector<S: AsRef<str>>( pub fn with_connector<S: AsRef<str>>(
uri: S, conn: Addr<Unsync, ClientConnector> uri: S, conn: Addr<Unsync, ClientConnector>,
) -> Client { ) -> Client {
let mut cl = Client { let mut cl = Client {
request: ClientRequest::build(), request: ClientRequest::build(),
@ -142,9 +142,8 @@ impl Client {
U: IntoIterator<Item = V> + 'static, U: IntoIterator<Item = V> + 'static,
V: AsRef<str>, V: AsRef<str>,
{ {
let mut protos = protos let mut protos =
.into_iter() protos.into_iter().fold(String::new(), |acc, s| acc + s.as_ref() + ",");
.fold(String::new(), |acc, s| acc + s.as_ref() + ",");
protos.pop(); protos.pop();
self.protocols = Some(protos); self.protocols = Some(protos);
self self
@ -218,8 +217,7 @@ impl Client {
self.request.upgrade(); self.request.upgrade();
self.request.set_header(header::UPGRADE, "websocket"); self.request.set_header(header::UPGRADE, "websocket");
self.request.set_header(header::CONNECTION, "upgrade"); self.request.set_header(header::CONNECTION, "upgrade");
self.request self.request.set_header(header::SEC_WEBSOCKET_VERSION, "13");
.set_header(header::SEC_WEBSOCKET_VERSION, "13");
self.request.with_connector(self.conn.clone()); self.request.with_connector(self.conn.clone());
if let Some(protocols) = self.protocols.take() { if let Some(protocols) = self.protocols.take() {
@ -394,10 +392,7 @@ impl Future for ClientHandshake {
encoded, encoded,
key key
); );
return Err(ClientError::InvalidChallengeResponse( return Err(ClientError::InvalidChallengeResponse(encoded, key.clone()));
encoded,
key.clone(),
));
} }
} else { } else {
trace!("Missing SEC-WEBSOCKET-ACCEPT header"); trace!("Missing SEC-WEBSOCKET-ACCEPT header");
@ -416,7 +411,9 @@ impl Future for ClientHandshake {
inner: Rc::clone(&inner), inner: Rc::clone(&inner),
max_size: self.max_size, max_size: self.max_size,
}, },
ClientWriter { inner }, ClientWriter {
inner,
},
))) )))
} }
} }
@ -536,23 +533,13 @@ impl ClientWriter {
/// Send ping frame /// Send ping frame
#[inline] #[inline]
pub fn ping(&mut self, message: &str) { pub fn ping(&mut self, message: &str) {
self.write(Frame::message( self.write(Frame::message(Vec::from(message), OpCode::Ping, true, true));
Vec::from(message),
OpCode::Ping,
true,
true,
));
} }
/// Send pong frame /// Send pong frame
#[inline] #[inline]
pub fn pong(&mut self, message: &str) { pub fn pong(&mut self, message: &str) {
self.write(Frame::message( self.write(Frame::message(Vec::from(message), OpCode::Pong, true, true));
Vec::from(message),
OpCode::Pong,
true,
true,
));
} }
/// Send close frame /// Send close frame

View File

@ -156,23 +156,13 @@ where
/// Send ping frame /// Send ping frame
#[inline] #[inline]
pub fn ping(&mut self, message: &str) { pub fn ping(&mut self, message: &str) {
self.write(Frame::message( self.write(Frame::message(Vec::from(message), OpCode::Ping, true, false));
Vec::from(message),
OpCode::Ping,
true,
false,
));
} }
/// Send pong frame /// Send pong frame
#[inline] #[inline]
pub fn pong(&mut self, message: &str) { pub fn pong(&mut self, message: &str) {
self.write(Frame::message( self.write(Frame::message(Vec::from(message), OpCode::Pong, true, false));
Vec::from(message),
OpCode::Pong,
true,
false,
));
} }
/// Send close frame /// Send close frame

View File

@ -8,9 +8,9 @@ use body::Binary;
use error::PayloadError; use error::PayloadError;
use payload::PayloadHelper; use payload::PayloadHelper;
use ws::ProtocolError;
use ws::mask::apply_mask; use ws::mask::apply_mask;
use ws::proto::{CloseCode, CloseReason, OpCode}; use ws::proto::{CloseCode, CloseReason, OpCode};
use ws::ProtocolError;
/// A struct representing a `WebSocket` frame. /// A struct representing a `WebSocket` frame.
#[derive(Debug)] #[derive(Debug)]
@ -36,7 +36,7 @@ impl Frame {
NetworkEndian::write_u16(&mut code_bytes, reason.code.into()); NetworkEndian::write_u16(&mut code_bytes, reason.code.into());
let mut payload = Vec::from(&code_bytes[..]); let mut payload = Vec::from(&code_bytes[..]);
if let Some(description) = reason.description{ if let Some(description) = reason.description {
payload.extend(description.as_bytes()); payload.extend(description.as_bytes());
} }
payload payload
@ -48,7 +48,7 @@ impl Frame {
#[cfg_attr(feature = "cargo-clippy", allow(type_complexity))] #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
fn read_copy_md<S>( fn read_copy_md<S>(
pl: &mut PayloadHelper<S>, server: bool, max_size: usize pl: &mut PayloadHelper<S>, server: bool, max_size: usize,
) -> Poll<Option<(usize, bool, OpCode, usize, Option<u32>)>, ProtocolError> ) -> Poll<Option<(usize, bool, OpCode, usize, Option<u32>)>, ProtocolError>
where where
S: Stream<Item = Bytes, Error = PayloadError>, S: Stream<Item = Bytes, Error = PayloadError>,
@ -122,17 +122,11 @@ impl Frame {
None None
}; };
Ok(Async::Ready(Some(( Ok(Async::Ready(Some((idx, finished, opcode, length, mask))))
idx,
finished,
opcode,
length,
mask,
))))
} }
fn read_chunk_md( fn read_chunk_md(
chunk: &[u8], server: bool, max_size: usize chunk: &[u8], server: bool, max_size: usize,
) -> Poll<(usize, bool, OpCode, usize, Option<u32>), ProtocolError> { ) -> Poll<(usize, bool, OpCode, usize, Option<u32>), ProtocolError> {
let chunk_len = chunk.len(); let chunk_len = chunk.len();
@ -203,7 +197,7 @@ impl Frame {
/// Parse the input stream into a frame. /// Parse the input stream into a frame.
pub fn parse<S>( pub fn parse<S>(
pl: &mut PayloadHelper<S>, server: bool, max_size: usize pl: &mut PayloadHelper<S>, server: bool, max_size: usize,
) -> Poll<Option<Frame>, ProtocolError> ) -> Poll<Option<Frame>, ProtocolError>
where where
S: Stream<Item = Bytes, Error = PayloadError>, S: Stream<Item = Bytes, Error = PayloadError>,
@ -288,7 +282,10 @@ impl Frame {
} else { } else {
None None
}; };
Some(CloseReason { code, description }) Some(CloseReason {
code,
description,
})
} else { } else {
None None
} }
@ -296,7 +293,7 @@ impl Frame {
/// Generate binary representation /// Generate binary representation
pub fn message<B: Into<Binary>>( pub fn message<B: Into<Binary>>(
data: B, code: OpCode, finished: bool, genmask: bool data: B, code: OpCode, finished: bool, genmask: bool,
) -> Binary { ) -> Binary {
let payload = data.into(); let payload = data.into();
let one: u8 = if finished { let one: u8 = if finished {

View File

@ -28,7 +28,11 @@ fn apply_mask_fast32(buf: &mut [u8], mask_u32: u32) {
// Possible first unaligned block. // Possible first unaligned block.
let head = min(len, (8 - (ptr as usize & 0x7)) & 0x3); let head = min(len, (8 - (ptr as usize & 0x7)) & 0x3);
let mask_u32 = if head > 0 { let mask_u32 = if head > 0 {
let n = if head > 4 { head - 4 } else { head }; let n = if head > 4 {
head - 4
} else {
head
};
let mask_u32 = if n > 0 { let mask_u32 = if n > 0 {
unsafe { unsafe {

View File

@ -133,24 +133,24 @@ pub enum HandshakeError {
impl ResponseError for HandshakeError { impl ResponseError for HandshakeError {
fn error_response(&self) -> HttpResponse { fn error_response(&self) -> HttpResponse {
match *self { match *self {
HandshakeError::GetMethodRequired => HttpResponse::MethodNotAllowed() HandshakeError::GetMethodRequired => {
.header(header::ALLOW, "GET") HttpResponse::MethodNotAllowed().header(header::ALLOW, "GET").finish()
.finish(), }
HandshakeError::NoWebsocketUpgrade => HttpResponse::BadRequest() HandshakeError::NoWebsocketUpgrade => HttpResponse::BadRequest()
.reason("No WebSocket UPGRADE header found") .reason("No WebSocket UPGRADE header found")
.finish(), .finish(),
HandshakeError::NoConnectionUpgrade => HttpResponse::BadRequest() HandshakeError::NoConnectionUpgrade => {
.reason("No CONNECTION upgrade") HttpResponse::BadRequest().reason("No CONNECTION upgrade").finish()
.finish(), }
HandshakeError::NoVersionHeader => HttpResponse::BadRequest() HandshakeError::NoVersionHeader => HttpResponse::BadRequest()
.reason("Websocket version header is required") .reason("Websocket version header is required")
.finish(), .finish(),
HandshakeError::UnsupportedVersion => HttpResponse::BadRequest() HandshakeError::UnsupportedVersion => {
.reason("Unsupported version") HttpResponse::BadRequest().reason("Unsupported version").finish()
.finish(), }
HandshakeError::BadWebsocketKey => HttpResponse::BadRequest() HandshakeError::BadWebsocketKey => {
.reason("Handshake error") HttpResponse::BadRequest().reason("Handshake error").finish()
.finish(), }
} }
} }
} }
@ -189,7 +189,7 @@ where
// /// the returned response headers contain the first protocol in this list // /// the returned response headers contain the first protocol in this list
// /// which the server also knows. // /// which the server also knows.
pub fn handshake<S>( pub fn handshake<S>(
req: &HttpRequest<S> req: &HttpRequest<S>,
) -> Result<HttpResponseBuilder, HandshakeError> { ) -> Result<HttpResponseBuilder, HandshakeError> {
// WebSocket accepts only GET // WebSocket accepts only GET
if *req.method() != Method::GET { if *req.method() != Method::GET {
@ -216,9 +216,7 @@ pub fn handshake<S>(
} }
// check supported version // check supported version
if !req.headers() if !req.headers().contains_key(header::SEC_WEBSOCKET_VERSION) {
.contains_key(header::SEC_WEBSOCKET_VERSION)
{
return Err(HandshakeError::NoVersionHeader); return Err(HandshakeError::NoVersionHeader);
} }
let supported_ver = { let supported_ver = {
@ -355,10 +353,7 @@ mod tests {
HeaderMap::new(), HeaderMap::new(),
None, None,
); );
assert_eq!( assert_eq!(HandshakeError::GetMethodRequired, handshake(&req).err().unwrap());
HandshakeError::GetMethodRequired,
handshake(&req).err().unwrap()
);
let req = HttpRequest::new( let req = HttpRequest::new(
Method::GET, Method::GET,
@ -367,16 +362,10 @@ mod tests {
HeaderMap::new(), HeaderMap::new(),
None, None,
); );
assert_eq!( assert_eq!(HandshakeError::NoWebsocketUpgrade, handshake(&req).err().unwrap());
HandshakeError::NoWebsocketUpgrade,
handshake(&req).err().unwrap()
);
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert( headers.insert(header::UPGRADE, header::HeaderValue::from_static("test"));
header::UPGRADE,
header::HeaderValue::from_static("test"),
);
let req = HttpRequest::new( let req = HttpRequest::new(
Method::GET, Method::GET,
Uri::from_str("/").unwrap(), Uri::from_str("/").unwrap(),
@ -384,16 +373,10 @@ mod tests {
headers, headers,
None, None,
); );
assert_eq!( assert_eq!(HandshakeError::NoWebsocketUpgrade, handshake(&req).err().unwrap());
HandshakeError::NoWebsocketUpgrade,
handshake(&req).err().unwrap()
);
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert( headers.insert(header::UPGRADE, header::HeaderValue::from_static("websocket"));
header::UPGRADE,
header::HeaderValue::from_static("websocket"),
);
let req = HttpRequest::new( let req = HttpRequest::new(
Method::GET, Method::GET,
Uri::from_str("/").unwrap(), Uri::from_str("/").unwrap(),
@ -401,20 +384,11 @@ mod tests {
headers, headers,
None, None,
); );
assert_eq!( assert_eq!(HandshakeError::NoConnectionUpgrade, handshake(&req).err().unwrap());
HandshakeError::NoConnectionUpgrade,
handshake(&req).err().unwrap()
);
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert( headers.insert(header::UPGRADE, header::HeaderValue::from_static("websocket"));
header::UPGRADE, headers.insert(header::CONNECTION, header::HeaderValue::from_static("upgrade"));
header::HeaderValue::from_static("websocket"),
);
headers.insert(
header::CONNECTION,
header::HeaderValue::from_static("upgrade"),
);
let req = HttpRequest::new( let req = HttpRequest::new(
Method::GET, Method::GET,
Uri::from_str("/").unwrap(), Uri::from_str("/").unwrap(),
@ -422,20 +396,11 @@ mod tests {
headers, headers,
None, None,
); );
assert_eq!( assert_eq!(HandshakeError::NoVersionHeader, handshake(&req).err().unwrap());
HandshakeError::NoVersionHeader,
handshake(&req).err().unwrap()
);
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert( headers.insert(header::UPGRADE, header::HeaderValue::from_static("websocket"));
header::UPGRADE, headers.insert(header::CONNECTION, header::HeaderValue::from_static("upgrade"));
header::HeaderValue::from_static("websocket"),
);
headers.insert(
header::CONNECTION,
header::HeaderValue::from_static("upgrade"),
);
headers.insert( headers.insert(
header::SEC_WEBSOCKET_VERSION, header::SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("5"), header::HeaderValue::from_static("5"),
@ -447,20 +412,11 @@ mod tests {
headers, headers,
None, None,
); );
assert_eq!( assert_eq!(HandshakeError::UnsupportedVersion, handshake(&req).err().unwrap());
HandshakeError::UnsupportedVersion,
handshake(&req).err().unwrap()
);
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert( headers.insert(header::UPGRADE, header::HeaderValue::from_static("websocket"));
header::UPGRADE, headers.insert(header::CONNECTION, header::HeaderValue::from_static("upgrade"));
header::HeaderValue::from_static("websocket"),
);
headers.insert(
header::CONNECTION,
header::HeaderValue::from_static("upgrade"),
);
headers.insert( headers.insert(
header::SEC_WEBSOCKET_VERSION, header::SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("13"), header::HeaderValue::from_static("13"),
@ -472,28 +428,17 @@ mod tests {
headers, headers,
None, None,
); );
assert_eq!( assert_eq!(HandshakeError::BadWebsocketKey, handshake(&req).err().unwrap());
HandshakeError::BadWebsocketKey,
handshake(&req).err().unwrap()
);
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert( headers.insert(header::UPGRADE, header::HeaderValue::from_static("websocket"));
header::UPGRADE, headers.insert(header::CONNECTION, header::HeaderValue::from_static("upgrade"));
header::HeaderValue::from_static("websocket"),
);
headers.insert(
header::CONNECTION,
header::HeaderValue::from_static("upgrade"),
);
headers.insert( headers.insert(
header::SEC_WEBSOCKET_VERSION, header::SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("13"), header::HeaderValue::from_static("13"),
); );
headers.insert( headers
header::SEC_WEBSOCKET_KEY, .insert(header::SEC_WEBSOCKET_KEY, header::HeaderValue::from_static("13"));
header::HeaderValue::from_static("13"),
);
let req = HttpRequest::new( let req = HttpRequest::new(
Method::GET, Method::GET,
Uri::from_str("/").unwrap(), Uri::from_str("/").unwrap(),

View File

@ -194,11 +194,11 @@ impl From<CloseCode> for CloseReason {
} }
} }
impl <T: Into<String>> From<(CloseCode, T)> for CloseReason { impl<T: Into<String>> From<(CloseCode, T)> for CloseReason {
fn from(info: (CloseCode, T)) -> Self { fn from(info: (CloseCode, T)) -> Self {
CloseReason{ CloseReason {
code: info.0, code: info.0,
description: Some(info.1.into()) description: Some(info.1.into()),
} }
} }
} }

1
tests/test.binary Normal file
View File

@ -0,0 +1 @@
ÂTÇÉVù2þvI ª\ÇRË™ˆæeÞ<04>vDØ:è—½¬RVÖYpíÿ;ÍÏGñùp!2÷CŒ. <0C>û®õpA !ûߦÙx j+Uc÷±©X”c%Û;ï"yì­AI

BIN
tests/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

View File

@ -9,8 +9,8 @@ use std::io::Read;
use bytes::Bytes; use bytes::Bytes;
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use futures::Future;
use futures::stream::once; use futures::stream::once;
use futures::Future;
use rand::Rng; use rand::Rng;
use actix_web::*; use actix_web::*;
@ -72,10 +72,7 @@ fn test_with_query_parameter() {
}) })
}); });
let request = srv.get() let request = srv.get().uri(srv.url("/?qp=5").as_str()).finish().unwrap();
.uri(srv.url("/?qp=5").as_str())
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -124,10 +121,8 @@ fn test_client_gzip_encoding() {
}); });
// client request // client request
let request = srv.post() let request =
.content_encoding(http::ContentEncoding::Gzip) srv.post().content_encoding(http::ContentEncoding::Gzip).body(STR).unwrap();
.body(STR)
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -167,10 +162,7 @@ fn test_client_gzip_encoding_large() {
#[test] #[test]
fn test_client_gzip_encoding_large_random() { fn test_client_gzip_encoding_large_random() {
let data = rand::thread_rng() let data = rand::thread_rng().gen_ascii_chars().take(100_000).collect::<String>();
.gen_ascii_chars()
.take(100_000)
.collect::<String>();
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: HttpRequest| {
@ -228,10 +220,7 @@ fn test_client_brotli_encoding() {
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
#[test] #[test]
fn test_client_brotli_encoding_large_random() { fn test_client_brotli_encoding_large_random() {
let data = rand::thread_rng() let data = rand::thread_rng().gen_ascii_chars().take(70_000).collect::<String>();
.gen_ascii_chars()
.take(70_000)
.collect::<String>();
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: HttpRequest| {
@ -275,10 +264,8 @@ fn test_client_deflate_encoding() {
}); });
// client request // client request
let request = srv.post() let request =
.content_encoding(http::ContentEncoding::Deflate) srv.post().content_encoding(http::ContentEncoding::Deflate).body(STR).unwrap();
.body(STR)
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -290,10 +277,7 @@ fn test_client_deflate_encoding() {
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
#[test] #[test]
fn test_client_deflate_encoding_large_random() { fn test_client_deflate_encoding_large_random() {
let data = rand::thread_rng() let data = rand::thread_rng().gen_ascii_chars().take(70_000).collect::<String>();
.gen_ascii_chars()
.take(70_000)
.collect::<String>();
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: HttpRequest| {
@ -338,9 +322,7 @@ fn test_client_streaming_explicit() {
let body = once(Ok(Bytes::from_static(STR.as_ref()))); let body = once(Ok(Bytes::from_static(STR.as_ref())));
let request = srv.get() let request = srv.get().body(Body::Streaming(Box::new(body))).unwrap();
.body(Body::Streaming(Box::new(body)))
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -413,11 +395,8 @@ fn test_client_cookie_handling() {
}) })
}); });
let request = srv.get() let request =
.cookie(cookie1.clone()) srv.get().cookie(cookie1.clone()).cookie(cookie2.clone()).finish().unwrap();
.cookie(cookie2.clone())
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
let c1 = response.cookie("cookie1").expect("Missing cookie1"); let c1 = response.cookie("cookie1").expect("Missing cookie1");

View File

@ -26,10 +26,7 @@ fn test_path_extractor() {
}); });
// client request // client request
let request = srv.get() let request = srv.get().uri(srv.url("/test/index.html")).finish().unwrap();
.uri(srv.url("/test/index.html"))
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -47,10 +44,7 @@ fn test_query_extractor() {
}); });
// client request // client request
let request = srv.get() let request = srv.get().uri(srv.url("/index.html?username=test")).finish().unwrap();
.uri(srv.url("/index.html?username=test"))
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -59,10 +53,7 @@ fn test_query_extractor() {
assert_eq!(bytes, Bytes::from_static(b"Welcome test!")); assert_eq!(bytes, Bytes::from_static(b"Welcome test!"));
// client request // client request
let request = srv.get() let request = srv.get().uri(srv.url("/index.html")).finish().unwrap();
.uri(srv.url("/index.html"))
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::BAD_REQUEST); assert_eq!(response.status(), StatusCode::BAD_REQUEST);
} }
@ -78,10 +69,8 @@ fn test_path_and_query_extractor() {
}); });
// client request // client request
let request = srv.get() let request =
.uri(srv.url("/test1/index.html?username=test2")) srv.get().uri(srv.url("/test1/index.html?username=test2")).finish().unwrap();
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -90,10 +79,7 @@ fn test_path_and_query_extractor() {
assert_eq!(bytes, Bytes::from_static(b"Welcome test1 - test2!")); assert_eq!(bytes, Bytes::from_static(b"Welcome test1 - test2!"));
// client request // client request
let request = srv.get() let request = srv.get().uri(srv.url("/test1/index.html")).finish().unwrap();
.uri(srv.url("/test1/index.html"))
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::BAD_REQUEST); assert_eq!(response.status(), StatusCode::BAD_REQUEST);
} }
@ -102,18 +88,15 @@ fn test_path_and_query_extractor() {
fn test_path_and_query_extractor2() { fn test_path_and_query_extractor2() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.resource("/{username}/index.html", |r| { app.resource("/{username}/index.html", |r| {
r.route() r.route().with3(|_: HttpRequest, p: Path<PParam>, q: Query<PParam>| {
.with3(|_: HttpRequest, p: Path<PParam>, q: Query<PParam>| { format!("Welcome {} - {}!", p.username, q.username)
format!("Welcome {} - {}!", p.username, q.username) })
})
}); });
}); });
// client request // client request
let request = srv.get() let request =
.uri(srv.url("/test1/index.html?username=test2")) srv.get().uri(srv.url("/test1/index.html?username=test2")).finish().unwrap();
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -122,10 +105,7 @@ fn test_path_and_query_extractor2() {
assert_eq!(bytes, Bytes::from_static(b"Welcome test1 - test2!")); assert_eq!(bytes, Bytes::from_static(b"Welcome test1 - test2!"));
// client request // client request
let request = srv.get() let request = srv.get().uri(srv.url("/test1/index.html")).finish().unwrap();
.uri(srv.url("/test1/index.html"))
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::BAD_REQUEST); assert_eq!(response.status(), StatusCode::BAD_REQUEST);
} }
@ -137,10 +117,7 @@ fn test_non_ascii_route() {
}); });
// client request // client request
let request = srv.get() let request = srv.get().uri(srv.url("/中文/index.html")).finish().unwrap();
.uri(srv.url("/中文/index.html"))
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -158,17 +135,12 @@ fn test_unsafe_path_route() {
}); });
// client request // client request
let request = srv.get() let request =
.uri(srv.url("/test/http%3A%2F%2Fexample.com")) srv.get().uri(srv.url("/test/http%3A%2F%2Fexample.com")).finish().unwrap();
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.execute(response.body()).unwrap(); let bytes = srv.execute(response.body()).unwrap();
assert_eq!( assert_eq!(bytes, Bytes::from_static(b"success: http:%2F%2Fexample.com"));
bytes,
Bytes::from_static(b"success: http:%2F%2Fexample.com")
);
} }

View File

@ -14,9 +14,9 @@ extern crate brotli2;
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
use brotli2::write::{BrotliDecoder, BrotliEncoder}; use brotli2::write::{BrotliDecoder, BrotliEncoder};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use flate2::Compression;
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use flate2::write::{DeflateDecoder, DeflateEncoder, GzEncoder}; use flate2::write::{DeflateDecoder, DeflateEncoder, GzEncoder};
use flate2::Compression;
use futures::stream::once; use futures::stream::once;
use futures::{future, Future, Stream}; use futures::{future, Future, Stream};
use h2::client as h2client; use h2::client as h2client;
@ -62,11 +62,9 @@ fn test_start() {
thread::spawn(move || { thread::spawn(move || {
let sys = System::new("test"); let sys = System::new("test");
let srv = server::new(|| { let srv = server::new(|| {
vec![ vec![App::new().resource("/", |r| {
App::new().resource("/", |r| { r.method(http::Method::GET).f(|_| HttpResponse::Ok())
r.method(http::Method::GET).f(|_| HttpResponse::Ok()) })]
}),
]
}); });
let srv = srv.bind("127.0.0.1:0").unwrap(); let srv = srv.bind("127.0.0.1:0").unwrap();
@ -113,11 +111,9 @@ fn test_shutdown() {
thread::spawn(move || { thread::spawn(move || {
let sys = System::new("test"); let sys = System::new("test");
let srv = server::new(|| { let srv = server::new(|| {
vec![ vec![App::new().resource("/", |r| {
App::new().resource("/", |r| { r.method(http::Method::GET).f(|_| HttpResponse::Ok())
r.method(http::Method::GET).f(|_| HttpResponse::Ok()) })]
}),
]
}); });
let srv = srv.bind("127.0.0.1:0").unwrap(); let srv = srv.bind("127.0.0.1:0").unwrap();
@ -135,7 +131,9 @@ fn test_shutdown() {
.finish() .finish()
.unwrap(); .unwrap();
let response = sys.run_until_complete(req.send()).unwrap(); let response = sys.run_until_complete(req.send()).unwrap();
srv_addr.do_send(server::StopServer { graceful: true }); srv_addr.do_send(server::StopServer {
graceful: true,
});
assert!(response.status().is_success()); assert!(response.status().is_success());
} }
@ -208,9 +206,7 @@ fn test_body() {
fn test_body_gzip() { fn test_body_gzip() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|_| { app.handler(|_| {
HttpResponse::Ok() HttpResponse::Ok().content_encoding(http::ContentEncoding::Gzip).body(STR)
.content_encoding(http::ContentEncoding::Gzip)
.body(STR)
}) })
}); });
@ -258,10 +254,7 @@ fn test_body_gzip_large() {
#[test] #[test]
fn test_body_gzip_large_random() { fn test_body_gzip_large_random() {
let data = rand::thread_rng() let data = rand::thread_rng().gen_ascii_chars().take(70_000).collect::<String>();
.gen_ascii_chars()
.take(70_000)
.collect::<String>();
let srv_data = Arc::new(data.clone()); let srv_data = Arc::new(data.clone());
let mut srv = test::TestServer::new(move |app| { let mut srv = test::TestServer::new(move |app| {
@ -342,11 +335,7 @@ fn test_body_br_streaming() {
#[test] #[test]
fn test_head_empty() { fn test_head_empty() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|_| { app.handler(|_| HttpResponse::Ok().content_length(STR.len() as u64).finish())
HttpResponse::Ok()
.content_length(STR.len() as u64)
.finish()
})
}); });
let request = srv.head().finish().unwrap(); let request = srv.head().finish().unwrap();
@ -354,10 +343,7 @@ fn test_head_empty() {
assert!(response.status().is_success()); assert!(response.status().is_success());
{ {
let len = response let len = response.headers().get(http::header::CONTENT_LENGTH).unwrap();
.headers()
.get(http::header::CONTENT_LENGTH)
.unwrap();
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
} }
@ -382,10 +368,7 @@ fn test_head_binary() {
assert!(response.status().is_success()); assert!(response.status().is_success());
{ {
let len = response let len = response.headers().get(http::header::CONTENT_LENGTH).unwrap();
.headers()
.get(http::header::CONTENT_LENGTH)
.unwrap();
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
} }
@ -409,10 +392,7 @@ fn test_head_binary2() {
assert!(response.status().is_success()); assert!(response.status().is_success());
{ {
let len = response let len = response.headers().get(http::header::CONTENT_LENGTH).unwrap();
.headers()
.get(http::header::CONTENT_LENGTH)
.unwrap();
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
} }
} }
@ -468,9 +448,7 @@ fn test_body_chunked_explicit() {
fn test_body_deflate() { fn test_body_deflate() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|_| { app.handler(|_| {
HttpResponse::Ok() HttpResponse::Ok().content_encoding(http::ContentEncoding::Deflate).body(STR)
.content_encoding(http::ContentEncoding::Deflate)
.body(STR)
}) })
}); });
@ -494,9 +472,7 @@ fn test_body_deflate() {
fn test_body_brotli() { fn test_body_brotli() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|_| { app.handler(|_| {
HttpResponse::Ok() HttpResponse::Ok().content_encoding(http::ContentEncoding::Br).body(STR)
.content_encoding(http::ContentEncoding::Br)
.body(STR)
}) })
}); });
@ -580,10 +556,7 @@ fn test_gzip_encoding_large() {
#[test] #[test]
fn test_reading_gzip_encoding_large_random() { fn test_reading_gzip_encoding_large_random() {
let data = rand::thread_rng() let data = rand::thread_rng().gen_ascii_chars().take(60_000).collect::<String>();
.gen_ascii_chars()
.take(60_000)
.collect::<String>();
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: HttpRequest| {
@ -634,10 +607,8 @@ fn test_reading_deflate_encoding() {
let enc = e.finish().unwrap(); let enc = e.finish().unwrap();
// client request // client request
let request = srv.post() let request =
.header(http::header::CONTENT_ENCODING, "deflate") srv.post().header(http::header::CONTENT_ENCODING, "deflate").body(enc).unwrap();
.body(enc)
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -666,10 +637,8 @@ fn test_reading_deflate_encoding_large() {
let enc = e.finish().unwrap(); let enc = e.finish().unwrap();
// client request // client request
let request = srv.post() let request =
.header(http::header::CONTENT_ENCODING, "deflate") srv.post().header(http::header::CONTENT_ENCODING, "deflate").body(enc).unwrap();
.body(enc)
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -680,10 +649,7 @@ fn test_reading_deflate_encoding_large() {
#[test] #[test]
fn test_reading_deflate_encoding_large_random() { fn test_reading_deflate_encoding_large_random() {
let data = rand::thread_rng() let data = rand::thread_rng().gen_ascii_chars().take(160_000).collect::<String>();
.gen_ascii_chars()
.take(160_000)
.collect::<String>();
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: HttpRequest| {
@ -702,10 +668,8 @@ fn test_reading_deflate_encoding_large_random() {
let enc = e.finish().unwrap(); let enc = e.finish().unwrap();
// client request // client request
let request = srv.post() let request =
.header(http::header::CONTENT_ENCODING, "deflate") srv.post().header(http::header::CONTENT_ENCODING, "deflate").body(enc).unwrap();
.body(enc)
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -735,10 +699,8 @@ fn test_brotli_encoding() {
let enc = e.finish().unwrap(); let enc = e.finish().unwrap();
// client request // client request
let request = srv.post() let request =
.header(http::header::CONTENT_ENCODING, "br") srv.post().header(http::header::CONTENT_ENCODING, "br").body(enc).unwrap();
.body(enc)
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -768,10 +730,8 @@ fn test_brotli_encoding_large() {
let enc = e.finish().unwrap(); let enc = e.finish().unwrap();
// client request // client request
let request = srv.post() let request =
.header(http::header::CONTENT_ENCODING, "br") srv.post().header(http::header::CONTENT_ENCODING, "br").body(enc).unwrap();
.body(enc)
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
@ -789,30 +749,29 @@ fn test_h2() {
let handle = core.handle(); let handle = core.handle();
let tcp = TcpStream::connect(&addr, &handle); let tcp = TcpStream::connect(&addr, &handle);
let tcp = tcp.then(|res| h2client::handshake(res.unwrap())) let tcp = tcp.then(|res| h2client::handshake(res.unwrap())).then(move |res| {
.then(move |res| { let (mut client, h2) = res.unwrap();
let (mut client, h2) = res.unwrap();
let request = Request::builder() let request = Request::builder()
.uri(format!("https://{}/", addr).as_str()) .uri(format!("https://{}/", addr).as_str())
.body(()) .body(())
.unwrap(); .unwrap();
let (response, _) = client.send_request(request, false).unwrap(); let (response, _) = client.send_request(request, false).unwrap();
// Spawn a task to run the conn... // Spawn a task to run the conn...
handle.spawn(h2.map_err(|e| println!("GOT ERR={:?}", e))); handle.spawn(h2.map_err(|e| println!("GOT ERR={:?}", e)));
response.and_then(|response| { response.and_then(|response| {
assert_eq!(response.status(), http::StatusCode::OK); assert_eq!(response.status(), http::StatusCode::OK);
let (_, body) = response.into_parts(); let (_, body) = response.into_parts();
body.fold(BytesMut::new(), |mut b, c| -> Result<_, h2::Error> { body.fold(BytesMut::new(), |mut b, c| -> Result<_, h2::Error> {
b.extend(c); b.extend(c);
Ok(b) Ok(b)
})
}) })
}); })
});
let _res = core.run(tcp); let _res = core.run(tcp);
// assert_eq!(res.unwrap(), Bytes::from_static(STR.as_ref())); // assert_eq!(res.unwrap(), Bytes::from_static(STR.as_ref()));
} }
@ -836,28 +795,20 @@ struct MiddlewareTest {
impl<S> middleware::Middleware<S> for MiddlewareTest { impl<S> middleware::Middleware<S> for MiddlewareTest {
fn start(&self, _: &mut HttpRequest<S>) -> Result<middleware::Started> { fn start(&self, _: &mut HttpRequest<S>) -> Result<middleware::Started> {
self.start.store( self.start.store(self.start.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
self.start.load(Ordering::Relaxed) + 1,
Ordering::Relaxed,
);
Ok(middleware::Started::Done) Ok(middleware::Started::Done)
} }
fn response( fn response(
&self, _: &mut HttpRequest<S>, resp: HttpResponse &self, _: &mut HttpRequest<S>, resp: HttpResponse,
) -> Result<middleware::Response> { ) -> Result<middleware::Response> {
self.response.store( self.response
self.response.load(Ordering::Relaxed) + 1, .store(self.response.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
Ordering::Relaxed,
);
Ok(middleware::Response::Done(resp)) Ok(middleware::Response::Done(resp))
} }
fn finish(&self, _: &mut HttpRequest<S>, _: &HttpResponse) -> middleware::Finished { fn finish(&self, _: &mut HttpRequest<S>, _: &HttpResponse) -> middleware::Finished {
self.finish.store( self.finish.store(self.finish.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
self.finish.load(Ordering::Relaxed) + 1,
Ordering::Relaxed,
);
middleware::Finished::Done middleware::Finished::Done
} }
} }

View File

@ -44,12 +44,7 @@ fn test_simple() {
writer.binary(b"text".as_ref()); writer.binary(b"text".as_ref());
let (item, reader) = srv.execute(reader.into_future()).unwrap(); let (item, reader) = srv.execute(reader.into_future()).unwrap();
assert_eq!( assert_eq!(item, Some(ws::Message::Binary(Bytes::from_static(b"text").into())));
item,
Some(ws::Message::Binary(
Bytes::from_static(b"text").into()
))
);
writer.ping("ping"); writer.ping("ping");
let (item, reader) = srv.execute(reader.into_future()).unwrap(); let (item, reader) = srv.execute(reader.into_future()).unwrap();
@ -75,7 +70,8 @@ fn test_close_description() {
let mut srv = test::TestServer::new(|app| app.handler(|req| ws::start(req, Ws))); let mut srv = test::TestServer::new(|app| app.handler(|req| ws::start(req, Ws)));
let (reader, mut writer) = srv.ws().unwrap(); let (reader, mut writer) = srv.ws().unwrap();
let close_reason:ws::CloseReason = (ws::CloseCode::Normal, "close description").into(); let close_reason: ws::CloseReason =
(ws::CloseCode::Normal, "close description").into();
writer.close(Some(close_reason.clone())); writer.close(Some(close_reason.clone()));
let (item, _) = srv.execute(reader.into_future()).unwrap(); let (item, _) = srv.execute(reader.into_future()).unwrap();
assert_eq!(item, Some(ws::Message::Close(Some(close_reason)))); assert_eq!(item, Some(ws::Message::Close(Some(close_reason))));
@ -83,10 +79,7 @@ fn test_close_description() {
#[test] #[test]
fn test_large_text() { fn test_large_text() {
let data = rand::thread_rng() let data = rand::thread_rng().gen_ascii_chars().take(65_536).collect::<String>();
.gen_ascii_chars()
.take(65_536)
.collect::<String>();
let mut srv = test::TestServer::new(|app| app.handler(|req| ws::start(req, Ws))); let mut srv = test::TestServer::new(|app| app.handler(|req| ws::start(req, Ws)));
let (mut reader, mut writer) = srv.ws().unwrap(); let (mut reader, mut writer) = srv.ws().unwrap();
@ -101,10 +94,7 @@ fn test_large_text() {
#[test] #[test]
fn test_large_bin() { fn test_large_bin() {
let data = rand::thread_rng() let data = rand::thread_rng().gen_ascii_chars().take(65_536).collect::<String>();
.gen_ascii_chars()
.take(65_536)
.collect::<String>();
let mut srv = test::TestServer::new(|app| app.handler(|req| ws::start(req, Ws))); let mut srv = test::TestServer::new(|app| app.handler(|req| ws::start(req, Ws)));
let (mut reader, mut writer) = srv.ws().unwrap(); let (mut reader, mut writer) = srv.ws().unwrap();
@ -113,10 +103,7 @@ fn test_large_bin() {
writer.binary(data.clone()); writer.binary(data.clone());
let (item, r) = srv.execute(reader.into_future()).unwrap(); let (item, r) = srv.execute(reader.into_future()).unwrap();
reader = r; reader = r;
assert_eq!( assert_eq!(item, Some(ws::Message::Binary(Binary::from(data.clone()))));
item,
Some(ws::Message::Binary(Binary::from(data.clone())))
);
} }
} }
@ -220,26 +207,20 @@ fn test_ws_server_ssl() {
// load ssl keys // load ssl keys
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder builder.set_private_key_file("tests/key.pem", SslFiletype::PEM).unwrap();
.set_private_key_file("tests/key.pem", SslFiletype::PEM) builder.set_certificate_chain_file("tests/cert.pem").unwrap();
.unwrap();
builder
.set_certificate_chain_file("tests/cert.pem")
.unwrap();
let mut srv = test::TestServer::build() let mut srv = test::TestServer::build().ssl(builder.build()).start(|app| {
.ssl(builder.build()) app.handler(|req| {
.start(|app| { ws::start(
app.handler(|req| { req,
ws::start( Ws2 {
req, count: 0,
Ws2 { bin: false,
count: 0, },
bin: false, )
}, })
) });
})
});
let (mut reader, _writer) = srv.ws().unwrap(); let (mut reader, _writer) = srv.ws().unwrap();
let data = Some(ws::Message::Text("0".repeat(65_536))); let data = Some(ws::Message::Text("0".repeat(65_536)));

View File

@ -14,8 +14,8 @@ extern crate url;
use futures::Future; use futures::Future;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use actix::prelude::*; use actix::prelude::*;
@ -61,12 +61,8 @@ fn main() {
let sample_rate = parse_u64_default(matches.value_of("sample-rate"), 1) as usize; let sample_rate = parse_u64_default(matches.value_of("sample-rate"), 1) as usize;
let perf_counters = Arc::new(PerfCounters::new()); let perf_counters = Arc::new(PerfCounters::new());
let payload = Arc::new( let payload =
thread_rng() Arc::new(thread_rng().gen_ascii_chars().take(payload_size).collect::<String>());
.gen_ascii_chars()
.take(payload_size)
.collect::<String>(),
);
let sys = actix::System::new("ws-client"); let sys = actix::System::new("ws-client");
@ -82,43 +78,40 @@ fn main() {
let perf = perf_counters.clone(); let perf = perf_counters.clone();
let addr = Arbiter::new(format!("test {}", t)); let addr = Arbiter::new(format!("test {}", t));
addr.do_send(actix::msgs::Execute::new( addr.do_send(actix::msgs::Execute::new(move || -> Result<(), ()> {
move || -> Result<(), ()> { for _ in 0..concurrency {
for _ in 0..concurrency { let pl2 = pl.clone();
let pl2 = pl.clone(); let perf2 = perf.clone();
let perf2 = perf.clone(); let ws2 = ws.clone();
let ws2 = ws.clone();
Arbiter::handle().spawn( Arbiter::handle().spawn(
ws::Client::new(&ws) ws::Client::new(&ws)
.write_buffer_capacity(0) .write_buffer_capacity(0)
.connect() .connect()
.map_err(|e| { .map_err(|e| {
println!("Error: {}", e); println!("Error: {}", e);
//Arbiter::system().do_send(actix::msgs::SystemExit(0)); //Arbiter::system().do_send(actix::msgs::SystemExit(0));
() ()
}) })
.map(move |(reader, writer)| { .map(move |(reader, writer)| {
let addr: Addr<Syn, _> = let addr: Addr<Syn, _> = ChatClient::create(move |ctx| {
ChatClient::create(move |ctx| { ChatClient::add_stream(reader, ctx);
ChatClient::add_stream(reader, ctx); ChatClient {
ChatClient { url: ws2,
url: ws2, conn: writer,
conn: writer, payload: pl2,
payload: pl2, bin: bin,
bin: bin, ts: time::precise_time_ns(),
ts: time::precise_time_ns(), perf_counters: perf2,
perf_counters: perf2, sent: 0,
sent: 0, max_payload_size: max_payload_size,
max_payload_size: max_payload_size, }
} });
}); }),
}), );
); }
} Ok(())
Ok(()) }));
},
));
} }
let res = sys.run(); let res = sys.run();
@ -126,10 +119,7 @@ fn main() {
fn parse_u64_default(input: Option<&str>, default: u64) -> u64 { fn parse_u64_default(input: Option<&str>, default: u64) -> u64 {
input input
.map(|v| { .map(|v| v.parse().expect(&format!("not a valid number: {}", v)))
v.parse()
.expect(&format!("not a valid number: {}", v))
})
.unwrap_or(default) .unwrap_or(default)
} }
@ -149,15 +139,13 @@ impl Actor for Perf {
impl Perf { impl Perf {
fn sample_rate(&self, ctx: &mut Context<Self>) { fn sample_rate(&self, ctx: &mut Context<Self>) {
ctx.run_later( ctx.run_later(Duration::new(self.sample_rate_secs as u64, 0), |act, ctx| {
Duration::new(self.sample_rate_secs as u64, 0), let req_count = act.counters.pull_request_count();
|act, ctx| { if req_count != 0 {
let req_count = act.counters.pull_request_count(); let conns = act.counters.pull_connections_count();
if req_count != 0 { let latency = act.counters.pull_latency_ns();
let conns = act.counters.pull_connections_count(); let latency_max = act.counters.pull_latency_max_ns();
let latency = act.counters.pull_latency_ns(); println!(
let latency_max = act.counters.pull_latency_max_ns();
println!(
"rate: {}, conns: {}, throughput: {:?} kb, latency: {}, latency max: {}", "rate: {}, conns: {}, throughput: {:?} kb, latency: {}, latency max: {}",
req_count / act.sample_rate_secs, req_count / act.sample_rate_secs,
conns / act.sample_rate_secs, conns / act.sample_rate_secs,
@ -166,11 +154,10 @@ impl Perf {
time::Duration::nanoseconds((latency / req_count as u64) as i64), time::Duration::nanoseconds((latency / req_count as u64) as i64),
time::Duration::nanoseconds(latency_max as i64) time::Duration::nanoseconds(latency_max as i64)
); );
} }
act.sample_rate(ctx); act.sample_rate(ctx);
}, });
);
} }
} }
@ -314,8 +301,7 @@ impl PerfCounters {
loop { loop {
let current = self.lat_max.load(Ordering::SeqCst); let current = self.lat_max.load(Ordering::SeqCst);
if current >= nanos if current >= nanos
|| self.lat_max || self.lat_max.compare_and_swap(current, nanos, Ordering::SeqCst)
.compare_and_swap(current, nanos, Ordering::SeqCst)
== current == current
{ {
break; break;