1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-12-01 02:44:37 +01:00

cleanups and tests

This commit is contained in:
Nikolay Kim 2018-10-04 21:14:18 -07:00
parent 4ca711909b
commit 829dbae609
35 changed files with 1063 additions and 811 deletions

View File

@ -96,6 +96,7 @@ bytes = "0.4"
byteorder = "1.2" byteorder = "1.2"
futures = "0.1" futures = "0.1"
tokio-codec = "0.1" tokio-codec = "0.1"
tokio = "0.1"
tokio-io = "0.1" tokio-io = "0.1"
tokio-tcp = "0.1" tokio-tcp = "0.1"
tokio-timer = "0.2" tokio-timer = "0.2"
@ -123,7 +124,6 @@ tokio-uds = { version="0.2", optional = true }
actix-web = "0.7" actix-web = "0.7"
env_logger = "0.5" env_logger = "0.5"
serde_derive = "1.0" serde_derive = "1.0"
tokio = "0.1"
[build-dependencies] [build-dependencies]
version_check = "0.1" version_check = "0.1"

View File

@ -4,7 +4,6 @@ use std::sync::Arc;
use std::{fmt, mem}; use std::{fmt, mem};
use error::Error; use error::Error;
use httpresponse::HttpResponse;
/// Type represent streaming body /// Type represent streaming body
pub type BodyStream = Box<Stream<Item = Bytes, Error = Error>>; pub type BodyStream = Box<Stream<Item = Bytes, Error = Error>>;

View File

@ -1,20 +1,15 @@
use std::cell::{RefCell, RefMut, UnsafeCell}; use std::cell::UnsafeCell;
use std::collections::VecDeque;
use std::fmt::Write; use std::fmt::Write;
use std::rc::Rc; use std::rc::Rc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use std::{env, fmt, net}; use std::{fmt, net};
use bytes::BytesMut; use bytes::BytesMut;
use futures::{future, Future}; use futures::{future, Future};
use http::StatusCode;
use time; use time;
use tokio_current_thread::spawn; use tokio_current_thread::spawn;
use tokio_timer::{sleep, Delay}; use tokio_timer::{sleep, Delay};
use body::Body;
use httpresponse::{HttpResponse, HttpResponseBuilder, HttpResponsePool};
use request::{Request, RequestPool};
use server::KeepAlive; use server::KeepAlive;
// "Sun, 06 Nov 1994 08:49:37 GMT".len() // "Sun, 06 Nov 1994 08:49:37 GMT".len()
@ -336,13 +331,7 @@ mod tests {
let mut rt = current_thread::Runtime::new().unwrap(); let mut rt = current_thread::Runtime::new().unwrap();
let _ = rt.block_on(future::lazy(|| { let _ = rt.block_on(future::lazy(|| {
let settings = ServiceConfig::<()>::new( let settings = ServiceConfig::new(KeepAlive::Os, 0, 0);
(),
KeepAlive::Os,
0,
0,
ServerSettings::default(),
);
let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
settings.set_date(&mut buf1, true); settings.set_date(&mut buf1, true);
let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);

View File

@ -28,7 +28,7 @@ use httpresponse::{HttpResponse, HttpResponseParts};
/// for actix web operations /// for actix web operations
/// ///
/// This typedef is generally used to avoid writing out /// This typedef is generally used to avoid writing out
/// `actix_web::error::Error` directly and is otherwise a direct mapping to /// `actix_http::error::Error` directly and is otherwise a direct mapping to
/// `Result`. /// `Result`.
pub type Result<T, E = Error> = result::Result<T, E>; pub type Result<T, E = Error> = result::Result<T, E>;
@ -589,13 +589,12 @@ impl From<UrlParseError> for UrlGenerationError {
/// default. /// default.
/// ///
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_http;
/// # use actix_web::*; /// # use std::io;
/// use actix_web::fs::NamedFile; /// # use actix_http::*;
/// ///
/// fn index(req: HttpRequest) -> Result<fs::NamedFile> { /// fn index(req: Request) -> Result<&'static str> {
/// let f = NamedFile::open("test.txt").map_err(error::ErrorBadRequest)?; /// Err(error::ErrorBadRequest(io::Error::new(io::ErrorKind::Other, "error")))
/// Ok(f)
/// } /// }
/// # fn main() {} /// # fn main() {}
/// ``` /// ```
@ -837,14 +836,6 @@ mod tests {
use std::error::Error as StdError; use std::error::Error as StdError;
use std::io; use std::io;
#[test]
#[cfg(actix_nightly)]
fn test_nightly() {
let resp: HttpResponse =
IoError::new(io::ErrorKind::Other, "test").error_response();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test] #[test]
fn test_into_response() { fn test_into_response() {
let resp: HttpResponse = ParseError::Incomplete.error_response(); let resp: HttpResponse = ParseError::Incomplete.error_response();
@ -853,9 +844,6 @@ mod tests {
let resp: HttpResponse = CookieParseError::EmptyName.error_response(); let resp: HttpResponse = CookieParseError::EmptyName.error_response();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let resp: HttpResponse = MultipartError::Boundary.error_response();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let err: HttpError = StatusCode::from_u16(10000).err().unwrap().into(); let err: HttpError = StatusCode::from_u16(10000).err().unwrap().into();
let resp: HttpResponse = err.error_response(); let resp: HttpResponse = err.error_response();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
@ -899,14 +887,6 @@ mod tests {
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
} }
#[test]
fn test_expect_error() {
let resp: HttpResponse = ExpectError::Encoding.error_response();
assert_eq!(resp.status(), StatusCode::EXPECTATION_FAILED);
let resp: HttpResponse = ExpectError::UnknownExpect.error_response();
assert_eq!(resp.status(), StatusCode::EXPECTATION_FAILED);
}
macro_rules! from { macro_rules! from {
($from:expr => $error:pat) => { ($from:expr => $error:pat) => {
match ParseError::from($from) { match ParseError::from($from) {
@ -963,10 +943,8 @@ mod tests {
#[test] #[test]
fn test_internal_error() { fn test_internal_error() {
let err = InternalError::from_response( let err =
ExpectError::Encoding, InternalError::from_response(ParseError::Method, HttpResponse::Ok().into());
HttpResponse::Ok().into(),
);
let resp: HttpResponse = err.error_response(); let resp: HttpResponse = err.error_response();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
} }

View File

@ -15,8 +15,11 @@ use httpresponse::HttpResponse;
use request::RequestPool; use request::RequestPool;
use server::output::{ResponseInfo, ResponseLength}; use server::output::{ResponseInfo, ResponseLength};
/// Http response
pub enum OutMessage { pub enum OutMessage {
/// Http response message
Response(HttpResponse), Response(HttpResponse),
/// Payload chunk
Payload(Bytes), Payload(Bytes),
} }
@ -35,7 +38,7 @@ impl Codec {
/// Create HTTP/1 codec with request's pool /// Create HTTP/1 codec with request's pool
pub(crate) fn with_pool(pool: &'static RequestPool) -> Self { pub(crate) fn with_pool(pool: &'static RequestPool) -> Self {
Codec { Codec {
decoder: H1Decoder::new(pool), decoder: H1Decoder::with_pool(pool),
encoder: H1Writer::new(), encoder: H1Writer::new(),
} }
} }

View File

@ -18,16 +18,26 @@ pub(crate) struct H1Decoder {
pool: &'static RequestPool, pool: &'static RequestPool,
} }
/// Incoming http/1 request
#[derive(Debug)] #[derive(Debug)]
pub enum InMessage { pub enum InMessage {
/// Request
Message(Request), Message(Request),
/// Request with payload
MessageWithPayload(Request), MessageWithPayload(Request),
/// Payload chunk
Chunk(Bytes), Chunk(Bytes),
/// End of payload
Eof, Eof,
} }
impl H1Decoder { impl H1Decoder {
pub fn new(pool: &'static RequestPool) -> H1Decoder { #[cfg(test)]
pub fn new() -> H1Decoder {
H1Decoder::with_pool(RequestPool::pool())
}
pub fn with_pool(pool: &'static RequestPool) -> H1Decoder {
H1Decoder { H1Decoder {
pool, pool,
decoder: None, decoder: None,
@ -497,3 +507,657 @@ impl ChunkedState {
} }
} }
} }
#[cfg(test)]
mod tests {
use std::net::Shutdown;
use std::{cmp, io, time};
use actix::System;
use bytes::{Buf, Bytes, BytesMut};
use futures::{future, future::ok};
use http::{Method, Version};
use tokio_io::{AsyncRead, AsyncWrite};
use super::*;
use error::ParseError;
use h1::{Dispatcher, InMessage};
use httpmessage::HttpMessage;
use request::Request;
use server::KeepAlive;
impl InMessage {
fn message(self) -> Request {
match self {
InMessage::Message(msg) => msg,
InMessage::MessageWithPayload(msg) => msg,
_ => panic!("error"),
}
}
fn is_payload(&self) -> bool {
match *self {
InMessage::MessageWithPayload(_) => true,
_ => panic!("error"),
}
}
fn chunk(self) -> Bytes {
match self {
InMessage::Chunk(chunk) => chunk,
_ => panic!("error"),
}
}
fn eof(&self) -> bool {
match *self {
InMessage::Eof => true,
_ => false,
}
}
}
macro_rules! parse_ready {
($e:expr) => {{
match H1Decoder::new().decode($e) {
Ok(Some(msg)) => msg.message(),
Ok(_) => unreachable!("Eof during parsing http request"),
Err(err) => unreachable!("Error during parsing http request: {:?}", err),
}
}};
}
macro_rules! expect_parse_err {
($e:expr) => {{
match H1Decoder::new().decode($e) {
Err(err) => match err {
ParseError::Io(_) => unreachable!("Parse error expected"),
_ => (),
},
_ => unreachable!("Error expected"),
}
}};
}
struct Buffer {
buf: Bytes,
err: Option<io::Error>,
}
impl Buffer {
fn new(data: &'static str) -> Buffer {
Buffer {
buf: Bytes::from(data),
err: None,
}
}
}
impl AsyncRead for Buffer {}
impl io::Read for Buffer {
fn read(&mut self, dst: &mut [u8]) -> Result<usize, io::Error> {
if self.buf.is_empty() {
if self.err.is_some() {
Err(self.err.take().unwrap())
} else {
Err(io::Error::new(io::ErrorKind::WouldBlock, ""))
}
} else {
let size = cmp::min(self.buf.len(), dst.len());
let b = self.buf.split_to(size);
dst[..size].copy_from_slice(&b);
Ok(size)
}
}
}
impl io::Write for Buffer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl AsyncWrite for Buffer {
fn shutdown(&mut self) -> Poll<(), io::Error> {
Ok(Async::Ready(()))
}
fn write_buf<B: Buf>(&mut self, _: &mut B) -> Poll<usize, io::Error> {
Ok(Async::NotReady)
}
}
// #[test]
// fn test_req_parse_err() {
// let mut sys = System::new("test");
// let _ = sys.block_on(future::lazy(|| {
// let buf = Buffer::new("GET /test HTTP/1\r\n\r\n");
// let readbuf = BytesMut::new();
// let mut h1 = Dispatcher::new(buf, |req| ok(HttpResponse::Ok().finish()));
// assert!(h1.poll_io().is_ok());
// assert!(h1.poll_io().is_ok());
// assert!(h1.flags.contains(Flags::READ_DISCONNECTED));
// assert_eq!(h1.tasks.len(), 1);
// future::ok::<_, ()>(())
// }));
// }
#[test]
fn test_parse() {
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n\r\n");
let mut reader = H1Decoder::new();
match reader.decode(&mut buf) {
Ok(Some(msg)) => {
let req = msg.message();
assert_eq!(req.version(), Version::HTTP_11);
assert_eq!(*req.method(), Method::GET);
assert_eq!(req.path(), "/test");
}
Ok(_) | Err(_) => unreachable!("Error during parsing http request"),
}
}
#[test]
fn test_parse_partial() {
let mut buf = BytesMut::from("PUT /test HTTP/1");
let mut reader = H1Decoder::new();
match reader.decode(&mut buf) {
Ok(None) => (),
_ => unreachable!("Error"),
}
buf.extend(b".1\r\n\r\n");
match reader.decode(&mut buf) {
Ok(Some(msg)) => {
let mut req = msg.message();
assert_eq!(req.version(), Version::HTTP_11);
assert_eq!(*req.method(), Method::PUT);
assert_eq!(req.path(), "/test");
}
Ok(_) | Err(_) => unreachable!("Error during parsing http request"),
}
}
#[test]
fn test_parse_post() {
let mut buf = BytesMut::from("POST /test2 HTTP/1.0\r\n\r\n");
let mut reader = H1Decoder::new();
match reader.decode(&mut buf) {
Ok(Some(msg)) => {
let mut req = msg.message();
assert_eq!(req.version(), Version::HTTP_10);
assert_eq!(*req.method(), Method::POST);
assert_eq!(req.path(), "/test2");
}
Ok(_) | Err(_) => unreachable!("Error during parsing http request"),
}
}
#[test]
fn test_parse_body() {
let mut buf =
BytesMut::from("GET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody");
let mut reader = H1Decoder::new();
match reader.decode(&mut buf) {
Ok(Some(msg)) => {
let mut req = msg.message();
assert_eq!(req.version(), Version::HTTP_11);
assert_eq!(*req.method(), Method::GET);
assert_eq!(req.path(), "/test");
assert_eq!(
reader.decode(&mut buf).unwrap().unwrap().chunk().as_ref(),
b"body"
);
}
Ok(_) | Err(_) => unreachable!("Error during parsing http request"),
}
}
#[test]
fn test_parse_body_crlf() {
let mut buf =
BytesMut::from("\r\nGET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody");
let mut reader = H1Decoder::new();
match reader.decode(&mut buf) {
Ok(Some(msg)) => {
let mut req = msg.message();
assert_eq!(req.version(), Version::HTTP_11);
assert_eq!(*req.method(), Method::GET);
assert_eq!(req.path(), "/test");
assert_eq!(
reader.decode(&mut buf).unwrap().unwrap().chunk().as_ref(),
b"body"
);
}
Ok(_) | Err(_) => unreachable!("Error during parsing http request"),
}
}
#[test]
fn test_parse_partial_eof() {
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n");
let mut reader = H1Decoder::new();
assert!(reader.decode(&mut buf).unwrap().is_none());
buf.extend(b"\r\n");
match reader.decode(&mut buf) {
Ok(Some(msg)) => {
let req = msg.message();
assert_eq!(req.version(), Version::HTTP_11);
assert_eq!(*req.method(), Method::GET);
assert_eq!(req.path(), "/test");
}
Ok(_) | Err(_) => unreachable!("Error during parsing http request"),
}
}
#[test]
fn test_headers_split_field() {
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n");
let mut reader = H1Decoder::new();
assert!{ reader.decode(&mut buf).unwrap().is_none() }
buf.extend(b"t");
assert!{ reader.decode(&mut buf).unwrap().is_none() }
buf.extend(b"es");
assert!{ reader.decode(&mut buf).unwrap().is_none() }
buf.extend(b"t: value\r\n\r\n");
match reader.decode(&mut buf) {
Ok(Some(msg)) => {
let req = msg.message();
assert_eq!(req.version(), Version::HTTP_11);
assert_eq!(*req.method(), Method::GET);
assert_eq!(req.path(), "/test");
assert_eq!(req.headers().get("test").unwrap().as_bytes(), b"value");
}
Ok(_) | Err(_) => unreachable!("Error during parsing http request"),
}
}
#[test]
fn test_headers_multi_value() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
Set-Cookie: c1=cookie1\r\n\
Set-Cookie: c2=cookie2\r\n\r\n",
);
let mut reader = H1Decoder::new();
let msg = reader.decode(&mut buf).unwrap().unwrap();
let req = msg.message();
let val: Vec<_> = req
.headers()
.get_all("Set-Cookie")
.iter()
.map(|v| v.to_str().unwrap().to_owned())
.collect();
assert_eq!(val[0], "c1=cookie1");
assert_eq!(val[1], "c2=cookie2");
}
#[test]
fn test_conn_default_1_0() {
let mut buf = BytesMut::from("GET /test HTTP/1.0\r\n\r\n");
let req = parse_ready!(&mut buf);
assert!(!req.keep_alive());
}
#[test]
fn test_conn_default_1_1() {
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n\r\n");
let req = parse_ready!(&mut buf);
assert!(req.keep_alive());
}
#[test]
fn test_conn_close() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
connection: close\r\n\r\n",
);
let req = parse_ready!(&mut buf);
assert!(!req.keep_alive());
}
#[test]
fn test_conn_close_1_0() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.0\r\n\
connection: close\r\n\r\n",
);
let req = parse_ready!(&mut buf);
assert!(!req.keep_alive());
}
#[test]
fn test_conn_keep_alive_1_0() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.0\r\n\
connection: keep-alive\r\n\r\n",
);
let req = parse_ready!(&mut buf);
assert!(req.keep_alive());
}
#[test]
fn test_conn_keep_alive_1_1() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
connection: keep-alive\r\n\r\n",
);
let req = parse_ready!(&mut buf);
assert!(req.keep_alive());
}
#[test]
fn test_conn_other_1_0() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.0\r\n\
connection: other\r\n\r\n",
);
let req = parse_ready!(&mut buf);
assert!(!req.keep_alive());
}
#[test]
fn test_conn_other_1_1() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
connection: other\r\n\r\n",
);
let req = parse_ready!(&mut buf);
assert!(req.keep_alive());
}
#[test]
fn test_conn_upgrade() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
upgrade: websockets\r\n\
connection: upgrade\r\n\r\n",
);
let req = parse_ready!(&mut buf);
assert!(req.upgrade());
}
#[test]
fn test_conn_upgrade_connect_method() {
let mut buf = BytesMut::from(
"CONNECT /test HTTP/1.1\r\n\
content-type: text/plain\r\n\r\n",
);
let req = parse_ready!(&mut buf);
assert!(req.upgrade());
}
#[test]
fn test_request_chunked() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
transfer-encoding: chunked\r\n\r\n",
);
let req = parse_ready!(&mut buf);
if let Ok(val) = req.chunked() {
assert!(val);
} else {
unreachable!("Error");
}
// type in chunked
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
transfer-encoding: chnked\r\n\r\n",
);
let req = parse_ready!(&mut buf);
if let Ok(val) = req.chunked() {
assert!(!val);
} else {
unreachable!("Error");
}
}
#[test]
fn test_headers_content_length_err_1() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
content-length: line\r\n\r\n",
);
expect_parse_err!(&mut buf)
}
#[test]
fn test_headers_content_length_err_2() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
content-length: -1\r\n\r\n",
);
expect_parse_err!(&mut buf);
}
#[test]
fn test_invalid_header() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
test line\r\n\r\n",
);
expect_parse_err!(&mut buf);
}
#[test]
fn test_invalid_name() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
test[]: line\r\n\r\n",
);
expect_parse_err!(&mut buf);
}
#[test]
fn test_http_request_bad_status_line() {
let mut buf = BytesMut::from("getpath \r\n\r\n");
expect_parse_err!(&mut buf);
}
#[test]
fn test_http_request_upgrade() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
connection: upgrade\r\n\
upgrade: websocket\r\n\r\n\
some raw data",
);
let mut reader = H1Decoder::new();
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert!(msg.is_payload());
let req = msg.message();
assert!(!req.keep_alive());
assert!(req.upgrade());
assert_eq!(
reader.decode(&mut buf).unwrap().unwrap().chunk().as_ref(),
b"some raw data"
);
}
#[test]
fn test_http_request_parser_utf8() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
x-test: тест\r\n\r\n",
);
let req = parse_ready!(&mut buf);
assert_eq!(
req.headers().get("x-test").unwrap().as_bytes(),
"тест".as_bytes()
);
}
#[test]
fn test_http_request_parser_two_slashes() {
let mut buf = BytesMut::from("GET //path HTTP/1.1\r\n\r\n");
let req = parse_ready!(&mut buf);
assert_eq!(req.path(), "//path");
}
#[test]
fn test_http_request_parser_bad_method() {
let mut buf = BytesMut::from("!12%()+=~$ /get HTTP/1.1\r\n\r\n");
expect_parse_err!(&mut buf);
}
#[test]
fn test_http_request_parser_bad_version() {
let mut buf = BytesMut::from("GET //get HT/11\r\n\r\n");
expect_parse_err!(&mut buf);
}
#[test]
fn test_http_request_chunked_payload() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
transfer-encoding: chunked\r\n\r\n",
);
let mut reader = H1Decoder::new();
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert!(msg.is_payload());
let req = msg.message();
assert!(req.chunked().unwrap());
buf.extend(b"4\r\ndata\r\n4\r\nline\r\n0\r\n\r\n");
assert_eq!(
reader.decode(&mut buf).unwrap().unwrap().chunk().as_ref(),
b"data"
);
assert_eq!(
reader.decode(&mut buf).unwrap().unwrap().chunk().as_ref(),
b"line"
);
assert!(reader.decode(&mut buf).unwrap().unwrap().eof());
}
#[test]
fn test_http_request_chunked_payload_and_next_message() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
transfer-encoding: chunked\r\n\r\n",
);
let mut reader = H1Decoder::new();
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert!(msg.is_payload());
let req = msg.message();
assert!(req.chunked().unwrap());
buf.extend(
b"4\r\ndata\r\n4\r\nline\r\n0\r\n\r\n\
POST /test2 HTTP/1.1\r\n\
transfer-encoding: chunked\r\n\r\n"
.iter(),
);
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert_eq!(msg.chunk().as_ref(), b"data");
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert_eq!(msg.chunk().as_ref(), b"line");
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert!(msg.eof());
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert!(msg.is_payload());
let req2 = msg.message();
assert!(req2.chunked().unwrap());
assert_eq!(*req2.method(), Method::POST);
assert!(req2.chunked().unwrap());
}
#[test]
fn test_http_request_chunked_payload_chunks() {
let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\
transfer-encoding: chunked\r\n\r\n",
);
let mut reader = H1Decoder::new();
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert!(msg.is_payload());
let req = msg.message();
assert!(req.chunked().unwrap());
buf.extend(b"4\r\n1111\r\n");
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert_eq!(msg.chunk().as_ref(), b"1111");
buf.extend(b"4\r\ndata\r");
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert_eq!(msg.chunk().as_ref(), b"data");
buf.extend(b"\n4");
assert!(reader.decode(&mut buf).unwrap().is_none());
buf.extend(b"\r");
assert!(reader.decode(&mut buf).unwrap().is_none());
buf.extend(b"\n");
assert!(reader.decode(&mut buf).unwrap().is_none());
buf.extend(b"li");
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert_eq!(msg.chunk().as_ref(), b"li");
//trailers
//buf.feed_data("test: test\r\n");
//not_ready!(reader.parse(&mut buf, &mut readbuf));
buf.extend(b"ne\r\n0\r\n");
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert_eq!(msg.chunk().as_ref(), b"ne");
assert!(reader.decode(&mut buf).unwrap().is_none());
buf.extend(b"\r\n");
assert!(reader.decode(&mut buf).unwrap().unwrap().eof());
}
#[test]
fn test_parse_chunked_payload_chunk_extension() {
let mut buf = BytesMut::from(
&"GET /test HTTP/1.1\r\n\
transfer-encoding: chunked\r\n\r\n"[..],
);
let mut reader = H1Decoder::new();
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert!(msg.is_payload());
assert!(msg.message().chunked().unwrap());
buf.extend(b"4;test\r\ndata\r\n4\r\nline\r\n0\r\n\r\n"); // test: test\r\n\r\n")
let chunk = reader.decode(&mut buf).unwrap().unwrap().chunk();
assert_eq!(chunk, Bytes::from_static(b"data"));
let chunk = reader.decode(&mut buf).unwrap().unwrap().chunk();
assert_eq!(chunk, Bytes::from_static(b"line"));
let msg = reader.decode(&mut buf).unwrap().unwrap();
assert!(msg.eof());
}
}

View File

@ -1,7 +1,6 @@
// #![allow(unused_imports, unused_variables, dead_code)] // #![allow(unused_imports, unused_variables, dead_code)]
use std::collections::VecDeque; use std::collections::VecDeque;
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use std::net::SocketAddr;
// use std::time::{Duration, Instant}; // use std::time::{Duration, Instant};
use actix_net::service::Service; use actix_net::service::Service;
@ -16,10 +15,10 @@ use error::{ParseError, PayloadError};
use payload::{Payload, PayloadStatus, PayloadWriter}; use payload::{Payload, PayloadStatus, PayloadWriter};
use body::Body; use body::Body;
use config::ServiceConfig;
use error::DispatchError; use error::DispatchError;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use request::Request;
use request::{Request, RequestPool};
use server::input::PayloadType; use server::input::PayloadType;
use super::codec::{Codec, InMessage, OutMessage}; use super::codec::{Codec, InMessage, OutMessage};
@ -52,6 +51,8 @@ where
state: State<S>, state: State<S>,
payload: Option<PayloadType>, payload: Option<PayloadType>,
messages: VecDeque<Request>, messages: VecDeque<Request>,
config: ServiceConfig,
} }
enum State<S: Service> { enum State<S: Service> {
@ -79,7 +80,7 @@ where
S::Error: Debug + Display, S::Error: Debug + Display,
{ {
/// Create http/1 dispatcher. /// Create http/1 dispatcher.
pub fn new(stream: T, service: S) -> Self { pub fn new(stream: T, config: ServiceConfig, service: S) -> Self {
let flags = Flags::FLUSHED; let flags = Flags::FLUSHED;
let framed = Framed::new(stream, Codec::new()); let framed = Framed::new(stream, Codec::new());
@ -91,6 +92,7 @@ where
service, service,
flags, flags,
framed, framed,
config,
} }
} }
@ -108,7 +110,7 @@ where
} }
// if checked is set to true, delay disconnect until all tasks have finished. // if checked is set to true, delay disconnect until all tasks have finished.
fn client_disconnected(&mut self, checked: bool) { fn client_disconnected(&mut self, _checked: bool) {
self.flags.insert(Flags::READ_DISCONNECTED); self.flags.insert(Flags::READ_DISCONNECTED);
if let Some(mut payload) = self.payload.take() { if let Some(mut payload) = self.payload.take() {
payload.set_error(PayloadError::Incomplete); payload.set_error(PayloadError::Incomplete);
@ -187,7 +189,7 @@ where
None None
}; };
}, },
State::Payload(ref mut body) => unimplemented!(), State::Payload(ref mut _body) => unimplemented!(),
State::Response(ref mut fut) => { State::Response(ref mut fut) => {
match fut.poll() { match fut.poll() {
Ok(Async::Ready(res)) => { Ok(Async::Ready(res)) => {

View File

@ -4,6 +4,6 @@ mod decoder;
mod dispatcher; mod dispatcher;
mod service; mod service;
pub use self::codec::Codec; pub use self::codec::{Codec, InMessage, OutMessage};
pub use self::dispatcher::Dispatcher; pub use self::dispatcher::Dispatcher;
pub use self::service::{H1Service, H1ServiceHandler}; pub use self::service::{H1Service, H1ServiceHandler};

View File

@ -1,9 +1,7 @@
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::time::Duration;
use actix_net::service::{IntoNewService, NewService, Service}; use actix_net::service::{IntoNewService, NewService, Service};
use futures::future::{ok, FutureResult};
use futures::{Async, Future, Poll}; use futures::{Async, Future, Poll};
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
@ -120,6 +118,6 @@ where
} }
fn call(&mut self, req: Self::Request) -> Self::Future { fn call(&mut self, req: Self::Request) -> Self::Future {
Dispatcher::new(req, self.srv.clone()) Dispatcher::new(req, self.cfg.clone(), self.srv.clone())
} }
} }

View File

@ -30,10 +30,10 @@ header! {
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_http;
/// extern crate mime; /// extern crate mime;
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{Accept, qitem}; /// use actix_http::http::header::{Accept, qitem};
/// ///
/// # fn main() { /// # fn main() {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
@ -47,10 +47,10 @@ header! {
/// ``` /// ```
/// ///
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_http;
/// extern crate mime; /// extern crate mime;
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{Accept, qitem}; /// use actix_http::http::header::{Accept, qitem};
/// ///
/// # fn main() { /// # fn main() {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
@ -64,10 +64,10 @@ header! {
/// ``` /// ```
/// ///
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_http;
/// extern crate mime; /// extern crate mime;
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{Accept, QualityItem, q, qitem}; /// use actix_http::http::header::{Accept, QualityItem, q, qitem};
/// ///
/// # fn main() { /// # fn main() {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();

View File

@ -22,9 +22,9 @@ header! {
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_http;
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{AcceptCharset, Charset, qitem}; /// use actix_http::http::header::{AcceptCharset, Charset, qitem};
/// ///
/// # fn main() { /// # fn main() {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
@ -34,9 +34,9 @@ header! {
/// # } /// # }
/// ``` /// ```
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_http;
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{AcceptCharset, Charset, q, QualityItem}; /// use actix_http::http::header::{AcceptCharset, Charset, q, QualityItem};
/// ///
/// # fn main() { /// # fn main() {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
@ -49,9 +49,9 @@ header! {
/// # } /// # }
/// ``` /// ```
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_http;
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{AcceptCharset, Charset, qitem}; /// use actix_http::http::header::{AcceptCharset, Charset, qitem};
/// ///
/// # fn main() { /// # fn main() {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();

View File

@ -23,10 +23,10 @@ header! {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_http;
/// # extern crate language_tags; /// # extern crate language_tags;
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{AcceptLanguage, LanguageTag, qitem}; /// use actix_http::http::header::{AcceptLanguage, LanguageTag, qitem};
/// ///
/// # fn main() { /// # fn main() {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
@ -42,10 +42,10 @@ header! {
/// ``` /// ```
/// ///
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_http;
/// # #[macro_use] extern crate language_tags; /// # #[macro_use] extern crate language_tags;
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{AcceptLanguage, QualityItem, q, qitem}; /// use actix_http::http::header::{AcceptLanguage, QualityItem, q, qitem};
/// # /// #
/// # fn main() { /// # fn main() {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();

View File

@ -1,5 +1,5 @@
use http::Method;
use http::header; use http::header;
use http::Method;
header! { header! {
/// `Allow` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.4.1) /// `Allow` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.4.1)
@ -23,11 +23,10 @@ header! {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # extern crate http; /// # extern crate actix_http;
/// # extern crate actix_web; /// use actix_http::HttpResponse;
/// use actix_web::HttpResponse; /// use actix_http::http::header::Allow;
/// use actix_web::http::header::Allow; /// use actix_http::http::Method;
/// use http::Method;
/// ///
/// # fn main() { /// # fn main() {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
@ -38,11 +37,9 @@ header! {
/// ``` /// ```
/// ///
/// ```rust /// ```rust
/// # extern crate http; /// # extern crate actix_http;
/// # extern crate actix_web; /// use actix_http::HttpResponse;
/// use actix_web::HttpResponse; /// use actix_http::http::{Method, header::Allow};
/// use actix_web::http::header::Allow;
/// use http::Method;
/// ///
/// # fn main() { /// # fn main() {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();

View File

@ -1,5 +1,5 @@
use header::{Header, IntoHeaderValue, Writer};
use header::{fmt_comma_delimited, from_comma_delimited}; use header::{fmt_comma_delimited, from_comma_delimited};
use header::{Header, IntoHeaderValue, Writer};
use http::header; use http::header;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::str::FromStr; use std::str::FromStr;
@ -26,16 +26,16 @@ use std::str::FromStr;
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{CacheControl, CacheDirective}; /// use actix_http::http::header::{CacheControl, CacheDirective};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.set(CacheControl(vec![CacheDirective::MaxAge(86400u32)])); /// builder.set(CacheControl(vec![CacheDirective::MaxAge(86400u32)]));
/// ``` /// ```
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{CacheControl, CacheDirective}; /// use actix_http::http::header::{CacheControl, CacheDirective};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.set(CacheControl(vec![ /// builder.set(CacheControl(vec![

View File

@ -64,7 +64,7 @@ impl<'a> From<&'a str> for DispositionType {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_web::http::header::DispositionParam; /// use actix_http::http::header::DispositionParam;
/// ///
/// let param = DispositionParam::Filename(String::from("sample.txt")); /// let param = DispositionParam::Filename(String::from("sample.txt"));
/// assert!(param.is_filename()); /// assert!(param.is_filename());
@ -226,7 +226,7 @@ impl DispositionParam {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// use actix_web::http::header::{ /// use actix_http::http::header::{
/// Charset, ContentDisposition, DispositionParam, DispositionType, /// Charset, ContentDisposition, DispositionParam, DispositionType,
/// ExtendedValue, /// ExtendedValue,
/// }; /// };
@ -327,7 +327,8 @@ impl ContentDisposition {
left = &left[end.ok_or(::error::ParseError::Header)? + 1..]; left = &left[end.ok_or(::error::ParseError::Header)? + 1..];
left = split_once(left, ';').1.trim_left(); left = split_once(left, ';').1.trim_left();
// In fact, it should not be Err if the above code is correct. // In fact, it should not be Err if the above code is correct.
String::from_utf8(quoted_string).map_err(|_| ::error::ParseError::Header)? String::from_utf8(quoted_string)
.map_err(|_| ::error::ParseError::Header)?
} else { } else {
// token: won't contains semicolon according to RFC 2616 Section 2.2 // token: won't contains semicolon according to RFC 2616 Section 2.2
let (token, new_left) = split_once_and_trim(left, ';'); let (token, new_left) = split_once_and_trim(left, ';');

View File

@ -24,10 +24,10 @@ header! {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_http;
/// # #[macro_use] extern crate language_tags; /// # #[macro_use] extern crate language_tags;
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// # use actix_web::http::header::{ContentLanguage, qitem}; /// # use actix_http::http::header::{ContentLanguage, qitem};
/// # /// #
/// # fn main() { /// # fn main() {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
@ -40,10 +40,10 @@ header! {
/// ``` /// ```
/// ///
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_http;
/// # #[macro_use] extern crate language_tags; /// # #[macro_use] extern crate language_tags;
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// # use actix_web::http::header::{ContentLanguage, qitem}; /// # use actix_http::http::header::{ContentLanguage, qitem};
/// # /// #
/// # fn main() { /// # fn main() {
/// ///

View File

@ -31,8 +31,8 @@ header! {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::ContentType; /// use actix_http::http::header::ContentType;
/// ///
/// # fn main() { /// # fn main() {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
@ -44,10 +44,10 @@ header! {
/// ///
/// ```rust /// ```rust
/// # extern crate mime; /// # extern crate mime;
/// # extern crate actix_web; /// # extern crate actix_http;
/// use mime::TEXT_HTML; /// use mime::TEXT_HTML;
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::ContentType; /// use actix_http::http::header::ContentType;
/// ///
/// # fn main() { /// # fn main() {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();

View File

@ -20,8 +20,8 @@ header! {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::Date; /// use actix_http::http::header::Date;
/// use std::time::SystemTime; /// use std::time::SystemTime;
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();

View File

@ -28,16 +28,16 @@ header! {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{ETag, EntityTag}; /// use actix_http::http::header::{ETag, EntityTag};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.set(ETag(EntityTag::new(false, "xyzzy".to_owned()))); /// builder.set(ETag(EntityTag::new(false, "xyzzy".to_owned())));
/// ``` /// ```
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{ETag, EntityTag}; /// use actix_http::http::header::{ETag, EntityTag};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.set(ETag(EntityTag::new(true, "xyzzy".to_owned()))); /// builder.set(ETag(EntityTag::new(true, "xyzzy".to_owned())));

View File

@ -22,8 +22,8 @@ header! {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::Expires; /// use actix_http::http::header::Expires;
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();

View File

@ -30,16 +30,16 @@ header! {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::IfMatch; /// use actix_http::http::header::IfMatch;
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.set(IfMatch::Any); /// builder.set(IfMatch::Any);
/// ``` /// ```
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{IfMatch, EntityTag}; /// use actix_http::http::header::{IfMatch, EntityTag};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.set( /// builder.set(

View File

@ -22,8 +22,8 @@ header! {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::IfModifiedSince; /// use actix_http::http::header::IfModifiedSince;
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();

View File

@ -32,16 +32,16 @@ header! {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::IfNoneMatch; /// use actix_http::http::header::IfNoneMatch;
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.set(IfNoneMatch::Any); /// builder.set(IfNoneMatch::Any);
/// ``` /// ```
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{IfNoneMatch, EntityTag}; /// use actix_http::http::header::{IfNoneMatch, EntityTag};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.set( /// builder.set(

View File

@ -1,7 +1,9 @@
use error::ParseError; use error::ParseError;
use header::from_one_raw_str; use header::from_one_raw_str;
use header::{EntityTag, Header, HeaderName, HeaderValue, HttpDate, IntoHeaderValue, use header::{
InvalidHeaderValueBytes, Writer}; EntityTag, Header, HeaderName, HeaderValue, HttpDate, IntoHeaderValue,
InvalidHeaderValueBytes, Writer,
};
use http::header; use http::header;
use httpmessage::HttpMessage; use httpmessage::HttpMessage;
use std::fmt::{self, Display, Write}; use std::fmt::{self, Display, Write};
@ -35,8 +37,8 @@ use std::fmt::{self, Display, Write};
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::{EntityTag, IfRange}; /// use actix_http::http::header::{EntityTag, IfRange};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.set(IfRange::EntityTag(EntityTag::new( /// builder.set(IfRange::EntityTag(EntityTag::new(
@ -46,8 +48,8 @@ use std::fmt::{self, Display, Write};
/// ``` /// ```
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::IfRange; /// use actix_http::http::header::IfRange;
/// use std::time::{Duration, SystemTime}; /// use std::time::{Duration, SystemTime};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();

View File

@ -23,8 +23,8 @@ header! {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::IfUnmodifiedSince; /// use actix_http::http::header::IfUnmodifiedSince;
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();

View File

@ -22,8 +22,8 @@ header! {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use actix_web::HttpResponse; /// use actix_http::HttpResponse;
/// use actix_web::http::header::LastModified; /// use actix_http::http::header::LastModified;
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();

View File

@ -106,7 +106,7 @@ pub trait HttpMessage: Sized {
/// ///
/// ## Server example /// ## Server example
/// ///
/// ```rust /// ```rust,ignore
/// # extern crate bytes; /// # extern crate bytes;
/// # extern crate actix_web; /// # extern crate actix_web;
/// # extern crate futures; /// # extern crate futures;
@ -143,7 +143,7 @@ pub trait HttpMessage: Sized {
/// ///
/// ## Server example /// ## Server example
/// ///
/// ```rust /// ```rust,ignore
/// # extern crate actix_web; /// # extern crate actix_web;
/// # extern crate futures; /// # extern crate futures;
/// # use futures::Future; /// # use futures::Future;
@ -176,7 +176,7 @@ pub trait HttpMessage: Sized {
/// ///
/// ## Server example /// ## Server example
/// ///
/// ```rust /// ```rust,ignore
/// # extern crate actix_web; /// # extern crate actix_web;
/// # extern crate futures; /// # extern crate futures;
/// # #[macro_use] extern crate serde_derive; /// # #[macro_use] extern crate serde_derive;

View File

@ -15,7 +15,7 @@ use serde_json;
use body::Body; use body::Body;
use error::Error; use error::Error;
use header::{ContentEncoding, Header, IntoHeaderValue}; use header::{ContentEncoding, Header, IntoHeaderValue};
use httpmessage::HttpMessage; // use httpmessage::HttpMessage;
// use httprequest::HttpRequest; // use httprequest::HttpRequest;
/// max write buffer size 64k /// max write buffer size 64k
@ -366,7 +366,7 @@ impl HttpResponseBuilder {
/// Set a header. /// Set a header.
/// ///
/// ```rust /// ```rust,ignore
/// # extern crate actix_web; /// # extern crate actix_web;
/// use actix_web::{http, HttpRequest, HttpResponse, Result}; /// use actix_web::{http, HttpRequest, HttpResponse, Result};
/// ///
@ -394,7 +394,7 @@ impl HttpResponseBuilder {
/// Set a header. /// Set a header.
/// ///
/// ```rust /// ```rust,ignore
/// # extern crate actix_web; /// # extern crate actix_web;
/// use actix_web::{http, HttpRequest, HttpResponse}; /// use actix_web::{http, HttpRequest, HttpResponse};
/// ///
@ -516,7 +516,7 @@ impl HttpResponseBuilder {
/// Set a cookie /// Set a cookie
/// ///
/// ```rust /// ```rust,ignore
/// # extern crate actix_web; /// # extern crate actix_web;
/// use actix_web::{http, HttpRequest, HttpResponse, Result}; /// use actix_web::{http, HttpRequest, HttpResponse, Result};
/// ///
@ -546,7 +546,7 @@ impl HttpResponseBuilder {
/// Remove cookie /// Remove cookie
/// ///
/// ```rust /// ```rust,ignore
/// # extern crate actix_web; /// # extern crate actix_web;
/// use actix_web::{http, HttpRequest, HttpResponse, Result}; /// use actix_web::{http, HttpRequest, HttpResponse, Result};
/// ///
@ -956,38 +956,38 @@ mod tests {
assert!(dbg.contains("HttpResponse")); assert!(dbg.contains("HttpResponse"));
} }
#[test] // #[test]
fn test_response_cookies() { // fn test_response_cookies() {
let req = TestRequest::default() // let req = TestRequest::default()
.header(COOKIE, "cookie1=value1") // .header(COOKIE, "cookie1=value1")
.header(COOKIE, "cookie2=value2") // .header(COOKIE, "cookie2=value2")
.finish(); // .finish();
let cookies = req.cookies().unwrap(); // let cookies = req.cookies().unwrap();
let resp = HttpResponse::Ok() // let resp = HttpResponse::Ok()
.cookie( // .cookie(
http::Cookie::build("name", "value") // http::Cookie::build("name", "value")
.domain("www.rust-lang.org") // .domain("www.rust-lang.org")
.path("/test") // .path("/test")
.http_only(true) // .http_only(true)
.max_age(Duration::days(1)) // .max_age(Duration::days(1))
.finish(), // .finish(),
).del_cookie(&cookies[0]) // ).del_cookie(&cookies[0])
.finish(); // .finish();
let mut val: Vec<_> = resp // let mut val: Vec<_> = resp
.headers() // .headers()
.get_all("Set-Cookie") // .get_all("Set-Cookie")
.iter() // .iter()
.map(|v| v.to_str().unwrap().to_owned()) // .map(|v| v.to_str().unwrap().to_owned())
.collect(); // .collect();
val.sort(); // val.sort();
assert!(val[0].starts_with("cookie1=; Max-Age=0;")); // assert!(val[0].starts_with("cookie1=; Max-Age=0;"));
assert_eq!( // assert_eq!(
val[1], // val[1],
"name=value; HttpOnly; Path=/test; Domain=www.rust-lang.org; Max-Age=86400" // "name=value; HttpOnly; Path=/test; Domain=www.rust-lang.org; Max-Age=86400"
); // );
} // }
#[test] #[test]
fn test_update_response_cookies() { fn test_update_response_cookies() {
@ -1131,15 +1131,6 @@ mod tests {
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), &Binary::from("test")); assert_eq!(resp.body().bin_ref(), &Binary::from("test"));
let resp: HttpResponse = "test".respond_to(&req).ok().unwrap();
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("text/plain; charset=utf-8")
);
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), &Binary::from("test"));
let resp: HttpResponse = b"test".as_ref().into(); let resp: HttpResponse = b"test".as_ref().into();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!( assert_eq!(
@ -1149,15 +1140,6 @@ mod tests {
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), &Binary::from(b"test".as_ref())); assert_eq!(resp.body().bin_ref(), &Binary::from(b"test".as_ref()));
let resp: HttpResponse = b"test".as_ref().respond_to(&req).ok().unwrap();
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("application/octet-stream")
);
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), &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);
assert_eq!( assert_eq!(
@ -1167,15 +1149,6 @@ mod tests {
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), &Binary::from("test".to_owned())); assert_eq!(resp.body().bin_ref(), &Binary::from("test".to_owned()));
let resp: HttpResponse = "test".to_owned().respond_to(&req).ok().unwrap();
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("text/plain; charset=utf-8")
);
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), &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);
assert_eq!( assert_eq!(
@ -1185,15 +1158,6 @@ mod tests {
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), &Binary::from(&"test".to_owned())); assert_eq!(resp.body().bin_ref(), &Binary::from(&"test".to_owned()));
let resp: HttpResponse = (&"test".to_owned()).respond_to(&req).ok().unwrap();
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("text/plain; charset=utf-8")
);
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), &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();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
@ -1208,19 +1172,6 @@ mod tests {
); );
let b = Bytes::from_static(b"test"); let b = Bytes::from_static(b"test");
let resp: HttpResponse = b.respond_to(&req).ok().unwrap();
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("application/octet-stream")
);
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.body().bin_ref(),
&Binary::from(Bytes::from_static(b"test"))
);
let b = BytesMut::from("test");
let resp: HttpResponse = b.into(); let resp: HttpResponse = b.into();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!( assert_eq!(
@ -1231,7 +1182,7 @@ mod tests {
assert_eq!(resp.body().bin_ref(), &Binary::from(BytesMut::from("test"))); assert_eq!(resp.body().bin_ref(), &Binary::from(BytesMut::from("test")));
let b = BytesMut::from("test"); let b = BytesMut::from("test");
let resp: HttpResponse = b.respond_to(&req).ok().unwrap(); let resp: HttpResponse = b.into();
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(),

View File

@ -3,18 +3,13 @@ use futures::{Future, Poll, Stream};
use http::header::CONTENT_LENGTH; use http::header::CONTENT_LENGTH;
use std::fmt; use std::fmt;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use mime; use mime;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json; use serde_json;
use error::{Error, JsonPayloadError}; use error::JsonPayloadError;
use http::StatusCode;
use httpmessage::HttpMessage; use httpmessage::HttpMessage;
// use httprequest::HttpRequest;
use httpresponse::HttpResponse;
/// Json helper /// Json helper
/// ///
@ -30,7 +25,7 @@ use httpresponse::HttpResponse;
/// ///
/// ## Example /// ## Example
/// ///
/// ```rust /// ```rust,ignore
/// # extern crate actix_web; /// # extern crate actix_web;
/// #[macro_use] extern crate serde_derive; /// #[macro_use] extern crate serde_derive;
/// use actix_web::{App, Json, Result, http}; /// use actix_web::{App, Json, Result, http};
@ -57,7 +52,7 @@ use httpresponse::HttpResponse;
/// to serialize into *JSON*. The type `T` must implement the `Serialize` /// to serialize into *JSON*. The type `T` must implement the `Serialize`
/// trait from *serde*. /// trait from *serde*.
/// ///
/// ```rust /// ```rust,ignore
/// # extern crate actix_web; /// # extern crate actix_web;
/// # #[macro_use] extern crate serde_derive; /// # #[macro_use] extern crate serde_derive;
/// # use actix_web::*; /// # use actix_web::*;
@ -124,7 +119,7 @@ where
/// ///
/// # Server example /// # Server example
/// ///
/// ```rust /// ```rust,ignore
/// # extern crate actix_web; /// # extern crate actix_web;
/// # extern crate futures; /// # extern crate futures;
/// # #[macro_use] extern crate serde_derive; /// # #[macro_use] extern crate serde_derive;
@ -243,9 +238,7 @@ mod tests {
use futures::Async; use futures::Async;
use http::header; use http::header;
use handler::Handler;
use test::TestRequest; use test::TestRequest;
use with::With;
impl PartialEq for JsonPayloadError { impl PartialEq for JsonPayloadError {
fn eq(&self, other: &JsonPayloadError) -> bool { fn eq(&self, other: &JsonPayloadError) -> bool {
@ -268,18 +261,6 @@ mod tests {
name: String, name: String,
} }
#[test]
fn test_json() {
let json = Json(MyObject {
name: "test".to_owned(),
});
let resp = json.respond_to(&TestRequest::default().finish()).unwrap();
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
"application/json"
);
}
#[test] #[test]
fn test_json_body() { fn test_json_body() {
let req = TestRequest::default().finish(); let req = TestRequest::default().finish();
@ -323,24 +304,4 @@ mod tests {
}) })
); );
} }
#[test]
fn test_with_json() {
let mut cfg = JsonConfig::default();
cfg.limit(4096);
let handler = With::new(|data: Json<MyObject>| data, cfg);
let req = TestRequest::default().finish();
assert!(handler.handle(&req).as_err().is_some());
let req = TestRequest::with_header(
header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"),
).header(
header::CONTENT_LENGTH,
header::HeaderValue::from_static("16"),
).set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.finish();
assert!(handler.handle(&req).as_err().is_none())
}
} }

View File

@ -1,7 +1,7 @@
//! Actix web is a small, pragmatic, and extremely fast web framework //! Actix web is a small, pragmatic, and extremely fast web framework
//! for Rust. //! for Rust.
//! //!
//! ```rust //! ```rust,ignore
//! use actix_web::{server, App, Path, Responder}; //! use actix_web::{server, App, Path, Responder};
//! # use std::thread; //! # use std::thread;
//! //!
@ -78,10 +78,11 @@
//! `gzip`, `deflate` compression. //! `gzip`, `deflate` compression.
//! //!
#![cfg_attr(actix_nightly, feature(tool_lints))] #![cfg_attr(actix_nightly, feature(tool_lints))]
#![warn(missing_docs)] // #![warn(missing_docs)]
#![allow(unused_imports, unused_variables, dead_code)] // #![allow(unused_imports, unused_variables, dead_code)]
extern crate actix; extern crate actix;
extern crate actix_net;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate base64; extern crate base64;
@ -98,7 +99,12 @@ extern crate failure;
extern crate lazy_static; extern crate lazy_static;
#[macro_use] #[macro_use]
extern crate futures; extern crate futures;
#[cfg(feature = "brotli")]
extern crate brotli2;
extern crate cookie; extern crate cookie;
extern crate encoding;
#[cfg(feature = "flate2")]
extern crate flate2;
extern crate http as modhttp; extern crate http as modhttp;
extern crate httparse; extern crate httparse;
extern crate language_tags; extern crate language_tags;
@ -106,6 +112,8 @@ extern crate mime;
extern crate mime_guess; extern crate mime_guess;
extern crate net2; extern crate net2;
extern crate rand; extern crate rand;
extern crate serde;
extern crate serde_urlencoded;
extern crate tokio_codec; extern crate tokio_codec;
extern crate tokio_current_thread; extern crate tokio_current_thread;
extern crate tokio_io; extern crate tokio_io;
@ -116,19 +124,10 @@ extern crate tokio_timer;
extern crate tokio_uds; extern crate tokio_uds;
extern crate url; extern crate url;
#[macro_use] #[macro_use]
extern crate serde;
#[cfg(feature = "brotli")]
extern crate brotli2;
extern crate encoding;
#[cfg(feature = "flate2")]
extern crate flate2;
extern crate serde_urlencoded;
#[macro_use]
extern crate percent_encoding; extern crate percent_encoding;
extern crate serde_json; extern crate serde_json;
extern crate smallvec; extern crate smallvec;
extern crate tokio;
extern crate actix_net;
#[cfg(test)] #[cfg(test)]
#[macro_use] #[macro_use]
@ -150,7 +149,7 @@ pub mod error;
pub mod h1; pub mod h1;
pub(crate) mod helpers; pub(crate) mod helpers;
pub mod server; pub mod server;
//pub mod test; pub mod test;
//pub mod ws; //pub mod ws;
pub use body::{Binary, Body}; pub use body::{Binary, Body};
pub use error::{Error, ResponseError, Result}; pub use error::{Error, ResponseError, Result};
@ -170,7 +169,7 @@ pub mod dev {
//! //!
//! ``` //! ```
//! # #![allow(unused_imports)] //! # #![allow(unused_imports)]
//! use actix_web::dev::*; //! use actix_http::dev::*;
//! ``` //! ```
pub use body::BodyStream; pub use body::BodyStream;

View File

@ -1,7 +1,6 @@
use std::cell::{Cell, Ref, RefCell, RefMut}; use std::cell::{Cell, Ref, RefCell, RefMut};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::fmt; use std::fmt;
use std::net::SocketAddr;
use std::rc::Rc; use std::rc::Rc;
use http::{header, HeaderMap, Method, Uri, Version}; use http::{header, HeaderMap, Method, Uri, Version};
@ -31,7 +30,6 @@ pub(crate) struct InnerRequest {
pub(crate) headers: HeaderMap, pub(crate) headers: HeaderMap,
pub(crate) extensions: RefCell<Extensions>, pub(crate) extensions: RefCell<Extensions>,
pub(crate) payload: RefCell<Option<Payload>>, pub(crate) payload: RefCell<Option<Payload>>,
pub(crate) stream_extensions: Option<Rc<Extensions>>,
pool: &'static RequestPool, pool: &'static RequestPool,
} }
@ -81,7 +79,6 @@ impl Request {
flags: Cell::new(MessageFlags::empty()), flags: Cell::new(MessageFlags::empty()),
payload: RefCell::new(None), payload: RefCell::new(None),
extensions: RefCell::new(Extensions::new()), extensions: RefCell::new(Extensions::new()),
stream_extensions: None,
}), }),
} }
} }
@ -170,15 +167,13 @@ impl Request {
inner: self.inner.clone(), inner: self.inner.clone(),
} }
} }
}
pub(crate) fn release(self) { impl Drop for Request {
let mut inner = self.inner; fn drop(&mut self) {
if let Some(r) = Rc::get_mut(&mut inner) { if Rc::strong_count(&self.inner) == 1 {
r.reset(); self.inner.pool.release(self.inner.clone());
} else {
return;
} }
inner.pool.release(inner);
} }
} }
@ -221,11 +216,13 @@ impl RequestPool {
/// Get Request object /// Get Request object
#[inline] #[inline]
pub fn get(pool: &'static RequestPool) -> Request { pub fn get(pool: &'static RequestPool) -> Request {
if let Some(msg) = pool.0.borrow_mut().pop_front() { if let Some(mut msg) = pool.0.borrow_mut().pop_front() {
Request { inner: msg } if let Some(r) = Rc::get_mut(&mut msg) {
} else { r.reset();
Request::with_pool(pool)
} }
return Request { inner: msg };
}
Request::with_pool(pool)
} }
#[inline] #[inline]

View File

@ -106,12 +106,9 @@
//! let _ = sys.run(); //! let _ = sys.run();
//!} //!}
//! ``` //! ```
use std::net::{Shutdown, SocketAddr}; use std::net::SocketAddr;
use std::rc::Rc;
use std::{io, time}; use std::{io, time};
use bytes::{BufMut, BytesMut};
use futures::{Async, Poll};
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use tokio_tcp::TcpStream; use tokio_tcp::TcpStream;
@ -123,16 +120,8 @@ pub(crate) mod output;
#[doc(hidden)] #[doc(hidden)]
pub use super::helpers::write_content_length; pub use super::helpers::write_content_length;
use body::Binary; // /// max buffer size 64k
use extensions::Extensions; // pub(crate) const MAX_WRITE_BUFFER_SIZE: usize = 65_536;
use header::ContentEncoding;
use httpresponse::HttpResponse;
/// max buffer size 64k
pub(crate) const MAX_WRITE_BUFFER_SIZE: usize = 65_536;
const LW_BUFFER_SIZE: usize = 4096;
const HW_BUFFER_SIZE: usize = 32_768;
#[derive(Debug, PartialEq, Clone, Copy)] #[derive(Debug, PartialEq, Clone, Copy)]
/// Server keep-alive setting /// Server keep-alive setting

View File

@ -1,3 +1,4 @@
#![allow(unused_imports, unused_variables, dead_code)]
use std::fmt::Write as FmtWrite; use std::fmt::Write as FmtWrite;
use std::io::Write; use std::io::Write;
use std::str::FromStr; use std::str::FromStr;

View File

@ -1,10 +1,8 @@
//! Various helpers for Actix applications to use during testing. //! Various helpers for Actix applications to use during testing.
use std::rc::Rc; use std::net;
use std::str::FromStr; use std::str::FromStr;
use std::sync::mpsc;
use std::{net, thread};
use actix_inner::{Actor, Addr, System}; use actix::System;
use cookie::Cookie; use cookie::Cookie;
use futures::Future; use futures::Future;
@ -13,28 +11,12 @@ use http::{HeaderMap, HttpTryFrom, Method, Uri, Version};
use net2::TcpBuilder; use net2::TcpBuilder;
use tokio::runtime::current_thread::Runtime; use tokio::runtime::current_thread::Runtime;
#[cfg(any(feature = "alpn", feature = "ssl"))]
use openssl::ssl::SslAcceptorBuilder;
#[cfg(feature = "rust-tls")]
use rustls::ServerConfig;
use application::{App, HttpApplication};
use body::Binary; use body::Binary;
use client::{ClientConnector, ClientRequest, ClientRequestBuilder};
use error::Error;
use handler::{AsyncResult, AsyncResultItem, Handler, Responder};
use header::{Header, IntoHeaderValue}; use header::{Header, IntoHeaderValue};
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use middleware::Middleware;
use param::Params;
use payload::Payload; use payload::Payload;
use resource::Resource; use request::Request;
use router::Router;
use server::message::{Request, RequestPool};
use server::{HttpServer, IntoHttpHandler, ServerSettings};
use uri::Url as InnerUrl; use uri::Url as InnerUrl;
use ws; // use ws;
/// The `TestServer` type. /// The `TestServer` type.
/// ///
@ -63,9 +45,8 @@ use ws;
/// ``` /// ```
pub struct TestServer { pub struct TestServer {
addr: net::SocketAddr, addr: net::SocketAddr,
ssl: bool,
conn: Addr<ClientConnector>,
rt: Runtime, rt: Runtime,
ssl: bool,
} }
impl TestServer { impl TestServer {
@ -73,92 +54,11 @@ impl TestServer {
/// ///
/// This method accepts configuration method. You can add /// This method accepts configuration method. You can add
/// middlewares or set handlers for test application. /// middlewares or set handlers for test application.
pub fn new<F>(config: F) -> Self pub fn new<F>(_config: F) -> Self
where where
F: Clone + Send + 'static + Fn(&mut TestApp<()>), F: Fn() + Clone + Send + 'static,
{ {
TestServerBuilder::new(|| ()).start(config) unimplemented!()
}
/// Create test server builder
pub fn build() -> TestServerBuilder<(), impl Fn() -> () + Clone + Send + 'static> {
TestServerBuilder::new(|| ())
}
/// Create test server builder with specific state factory
///
/// This method can be used for constructing application state.
/// Also it can be used for external dependency initialization,
/// like creating sync actors for diesel integration.
pub fn build_with_state<S, F>(state: F) -> TestServerBuilder<S, F>
where
F: Fn() -> S + Clone + Send + 'static,
S: 'static,
{
TestServerBuilder::new(state)
}
/// Start new test server with application factory
pub fn with_factory<F, H>(factory: F) -> Self
where
F: Fn() -> H + Send + Clone + 'static,
H: IntoHttpHandler + 'static,
{
let (tx, rx) = mpsc::channel();
// run server in separate thread
thread::spawn(move || {
let sys = System::new("actix-test-server");
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
let local_addr = tcp.local_addr().unwrap();
let _ = HttpServer::new(factory)
.disable_signals()
.listen(tcp)
.keep_alive(5)
.start();
tx.send((System::current(), local_addr, TestServer::get_conn()))
.unwrap();
sys.run();
});
let (system, addr, conn) = rx.recv().unwrap();
System::set_current(system);
TestServer {
addr,
conn,
ssl: false,
rt: Runtime::new().unwrap(),
}
}
fn get_conn() -> Addr<ClientConnector> {
#[cfg(any(feature = "alpn", feature = "ssl"))]
{
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
builder.set_verify(SslVerifyMode::NONE);
ClientConnector::with_connector(builder.build()).start()
}
#[cfg(all(
feature = "rust-tls",
not(any(feature = "alpn", feature = "ssl"))
))]
{
use rustls::ClientConfig;
use std::fs::File;
use std::io::BufReader;
let mut config = ClientConfig::new();
let pem_file = &mut BufReader::new(File::open("tests/cert.pem").unwrap());
config.root_store.add_pem_file(pem_file).unwrap();
ClientConnector::with_connector(config).start()
}
#[cfg(not(any(feature = "alpn", feature = "ssl", feature = "rust-tls")))]
{
ClientConnector::default().start()
}
} }
/// Get firat available unused address /// Get firat available unused address
@ -208,45 +108,45 @@ impl TestServer {
self.rt.block_on(fut) self.rt.block_on(fut)
} }
/// Connect to websocket server at a given path // /// Connect to websocket server at a given path
pub fn ws_at( // pub fn ws_at(
&mut self, path: &str, // &mut self, path: &str,
) -> Result<(ws::ClientReader, ws::ClientWriter), ws::ClientError> { // ) -> Result<(ws::ClientReader, ws::ClientWriter), ws::ClientError> {
let url = self.url(path); // let url = self.url(path);
self.rt // self.rt
.block_on(ws::Client::with_connector(url, self.conn.clone()).connect()) // .block_on(ws::Client::with_connector(url, self.conn.clone()).connect())
} // }
/// Connect to a websocket server // /// Connect to a 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> {
self.ws_at("/") // self.ws_at("/")
} // }
/// Create `GET` request // /// Create `GET` request
pub fn get(&self) -> ClientRequestBuilder { // pub fn get(&self) -> ClientRequestBuilder {
ClientRequest::get(self.url("/").as_str()) // ClientRequest::get(self.url("/").as_str())
} // }
/// Create `POST` request // /// Create `POST` request
pub fn post(&self) -> ClientRequestBuilder { // pub fn post(&self) -> ClientRequestBuilder {
ClientRequest::post(self.url("/").as_str()) // ClientRequest::post(self.url("/").as_str())
} // }
/// Create `HEAD` request // /// Create `HEAD` request
pub fn head(&self) -> ClientRequestBuilder { // pub fn head(&self) -> ClientRequestBuilder {
ClientRequest::head(self.url("/").as_str()) // ClientRequest::head(self.url("/").as_str())
} // }
/// Connect to test http server // /// Connect to test http server
pub fn client(&self, meth: Method, path: &str) -> ClientRequestBuilder { // pub fn client(&self, meth: Method, path: &str) -> ClientRequestBuilder {
ClientRequest::build() // ClientRequest::build()
.method(meth) // .method(meth)
.uri(self.url(path).as_str()) // .uri(self.url(path).as_str())
.with_connector(self.conn.clone()) // .with_connector(self.conn.clone())
.take() // .take()
} // }
} }
impl Drop for TestServer { impl Drop for TestServer {
@ -255,183 +155,98 @@ impl Drop for TestServer {
} }
} }
/// An `TestServer` builder // /// An `TestServer` builder
/// // ///
/// This type can be used to construct an instance of `TestServer` through a // /// This type can be used to construct an instance of `TestServer` through a
/// builder-like pattern. // /// builder-like pattern.
pub struct TestServerBuilder<S, F> // pub struct TestServerBuilder<S, F>
where // where
F: Fn() -> S + Send + Clone + 'static, // F: Fn() -> S + Send + Clone + 'static,
{ // {
state: F, // state: F,
#[cfg(any(feature = "alpn", feature = "ssl"))] // }
ssl: Option<SslAcceptorBuilder>,
#[cfg(feature = "rust-tls")]
rust_ssl: Option<ServerConfig>,
}
impl<S: 'static, F> TestServerBuilder<S, F> // impl<S: 'static, F> TestServerBuilder<S, F>
where // where
F: Fn() -> S + Send + Clone + 'static, // F: Fn() -> S + Send + Clone + 'static,
{ // {
/// Create a new test server // /// Create a new test server
pub fn new(state: F) -> TestServerBuilder<S, F> { // pub fn new(state: F) -> TestServerBuilder<S, F> {
TestServerBuilder { // TestServerBuilder { state }
state, // }
#[cfg(any(feature = "alpn", feature = "ssl"))]
ssl: None,
#[cfg(feature = "rust-tls")]
rust_ssl: None,
}
}
#[cfg(any(feature = "alpn", feature = "ssl"))] // #[allow(unused_mut)]
/// Create ssl server // /// Configure test application and run test server
pub fn ssl(mut self, ssl: SslAcceptorBuilder) -> Self { // pub fn start<C>(mut self, config: C) -> TestServer
self.ssl = Some(ssl); // where
self // C: Fn(&mut TestApp<S>) + Clone + Send + 'static,
} // {
// let (tx, rx) = mpsc::channel();
#[cfg(feature = "rust-tls")] // let mut has_ssl = false;
/// Create rust tls server
pub fn rustls(mut self, ssl: ServerConfig) -> Self {
self.rust_ssl = Some(ssl);
self
}
#[allow(unused_mut)] // #[cfg(any(feature = "alpn", feature = "ssl"))]
/// Configure test application and run test server // {
pub fn start<C>(mut self, config: C) -> TestServer // has_ssl = has_ssl || self.ssl.is_some();
where // }
C: Fn(&mut TestApp<S>) + Clone + Send + 'static,
{
let (tx, rx) = mpsc::channel();
let mut has_ssl = false; // #[cfg(feature = "rust-tls")]
// {
// has_ssl = has_ssl || self.rust_ssl.is_some();
// }
#[cfg(any(feature = "alpn", feature = "ssl"))] // // run server in separate thread
{ // thread::spawn(move || {
has_ssl = has_ssl || self.ssl.is_some(); // let addr = TestServer::unused_addr();
}
#[cfg(feature = "rust-tls")] // let sys = System::new("actix-test-server");
{ // let state = self.state;
has_ssl = has_ssl || self.rust_ssl.is_some(); // let mut srv = HttpServer::new(move || {
} // let mut app = TestApp::new(state());
// config(&mut app);
// app
// }).workers(1)
// .keep_alive(5)
// .disable_signals();
// run server in separate thread // tx.send((System::current(), addr, TestServer::get_conn()))
thread::spawn(move || { // .unwrap();
let addr = TestServer::unused_addr();
let sys = System::new("actix-test-server"); // #[cfg(any(feature = "alpn", feature = "ssl"))]
let state = self.state; // {
let mut srv = HttpServer::new(move || { // let ssl = self.ssl.take();
let mut app = TestApp::new(state()); // if let Some(ssl) = ssl {
config(&mut app); // let tcp = net::TcpListener::bind(addr).unwrap();
app // srv = srv.listen_ssl(tcp, ssl).unwrap();
}).workers(1) // }
.keep_alive(5) // }
.disable_signals(); // #[cfg(feature = "rust-tls")]
// {
// let ssl = self.rust_ssl.take();
// if let Some(ssl) = ssl {
// let tcp = net::TcpListener::bind(addr).unwrap();
// srv = srv.listen_rustls(tcp, ssl);
// }
// }
// if !has_ssl {
// let tcp = net::TcpListener::bind(addr).unwrap();
// srv = srv.listen(tcp);
// }
// srv.start();
tx.send((System::current(), addr, TestServer::get_conn())) // sys.run();
.unwrap(); // });
#[cfg(any(feature = "alpn", feature = "ssl"))] // let (system, addr, conn) = rx.recv().unwrap();
{ // System::set_current(system);
let ssl = self.ssl.take(); // TestServer {
if let Some(ssl) = ssl { // addr,
let tcp = net::TcpListener::bind(addr).unwrap(); // conn,
srv = srv.listen_ssl(tcp, ssl).unwrap(); // ssl: has_ssl,
} // rt: Runtime::new().unwrap(),
} // }
#[cfg(feature = "rust-tls")] // }
{ // }
let ssl = self.rust_ssl.take();
if let Some(ssl) = ssl {
let tcp = net::TcpListener::bind(addr).unwrap();
srv = srv.listen_rustls(tcp, ssl);
}
}
if !has_ssl {
let tcp = net::TcpListener::bind(addr).unwrap();
srv = srv.listen(tcp);
}
srv.start();
sys.run();
});
let (system, addr, conn) = rx.recv().unwrap();
System::set_current(system);
TestServer {
addr,
conn,
ssl: has_ssl,
rt: Runtime::new().unwrap(),
}
}
}
/// Test application helper for testing request handlers.
pub struct TestApp<S = ()> {
app: Option<App<S>>,
}
impl<S: 'static> TestApp<S> {
fn new(state: S) -> TestApp<S> {
let app = App::with_state(state);
TestApp { app: Some(app) }
}
/// Register handler for "/"
pub fn handler<F, R>(&mut self, handler: F)
where
F: Fn(&HttpRequest<S>) -> R + 'static,
R: Responder + 'static,
{
self.app = Some(self.app.take().unwrap().resource("/", |r| r.f(handler)));
}
/// Register middleware
pub fn middleware<T>(&mut self, mw: T) -> &mut TestApp<S>
where
T: Middleware<S> + 'static,
{
self.app = Some(self.app.take().unwrap().middleware(mw));
self
}
/// Register resource. This method is similar
/// to `App::resource()` method.
pub fn resource<F, R>(&mut self, path: &str, f: F) -> &mut TestApp<S>
where
F: FnOnce(&mut Resource<S>) -> R + 'static,
{
self.app = Some(self.app.take().unwrap().resource(path, f));
self
}
}
impl<S: 'static> IntoHttpHandler for TestApp<S> {
type Handler = HttpApplication<S>;
fn into_handler(mut self) -> HttpApplication<S> {
self.app.take().unwrap().into_handler()
}
}
#[doc(hidden)]
impl<S: 'static> Iterator for TestApp<S> {
type Item = HttpApplication<S>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(mut app) = self.app.take() {
Some(app.finish())
} else {
None
}
}
}
/// Test `HttpRequest` builder /// Test `HttpRequest` builder
/// ///
@ -460,70 +275,49 @@ impl<S: 'static> Iterator for TestApp<S> {
/// assert_eq!(resp.status(), StatusCode::BAD_REQUEST); /// assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
/// } /// }
/// ``` /// ```
pub struct TestRequest<S> { pub struct TestRequest {
state: S,
version: Version, version: Version,
method: Method, method: Method,
uri: Uri, uri: Uri,
headers: HeaderMap, headers: HeaderMap,
params: Params, _cookies: Option<Vec<Cookie<'static>>>,
cookies: Option<Vec<Cookie<'static>>>,
payload: Option<Payload>, payload: Option<Payload>,
prefix: u16, prefix: u16,
} }
impl Default for TestRequest<()> { impl Default for TestRequest {
fn default() -> TestRequest<()> { fn default() -> TestRequest {
TestRequest { TestRequest {
state: (),
method: Method::GET, method: Method::GET,
uri: Uri::from_str("/").unwrap(), uri: Uri::from_str("/").unwrap(),
version: Version::HTTP_11, version: Version::HTTP_11,
headers: HeaderMap::new(), headers: HeaderMap::new(),
params: Params::new(), _cookies: None,
cookies: None,
payload: None, payload: None,
prefix: 0, prefix: 0,
} }
} }
} }
impl TestRequest<()> { impl TestRequest {
/// Create TestRequest and set request uri /// Create TestRequest and set request uri
pub fn with_uri(path: &str) -> TestRequest<()> { pub fn with_uri(path: &str) -> TestRequest {
TestRequest::default().uri(path) TestRequest::default().uri(path)
} }
/// Create TestRequest and set header /// Create TestRequest and set header
pub fn with_hdr<H: Header>(hdr: H) -> TestRequest<()> { pub fn with_hdr<H: Header>(hdr: H) -> TestRequest {
TestRequest::default().set(hdr) TestRequest::default().set(hdr)
} }
/// Create TestRequest and set header /// Create TestRequest and set header
pub fn with_header<K, V>(key: K, value: V) -> TestRequest<()> pub fn with_header<K, V>(key: K, value: V) -> TestRequest
where where
HeaderName: HttpTryFrom<K>, HeaderName: HttpTryFrom<K>,
V: IntoHeaderValue, V: IntoHeaderValue,
{ {
TestRequest::default().header(key, value) TestRequest::default().header(key, value)
} }
}
impl<S: 'static> TestRequest<S> {
/// Start HttpRequest build process with application state
pub fn with_state(state: S) -> TestRequest<S> {
TestRequest {
state,
method: Method::GET,
uri: Uri::from_str("/").unwrap(),
version: Version::HTTP_11,
headers: HeaderMap::new(),
params: Params::new(),
cookies: None,
payload: None,
prefix: 0,
}
}
/// Set HTTP version of this request /// Set HTTP version of this request
pub fn version(mut self, ver: Version) -> Self { pub fn version(mut self, ver: Version) -> Self {
@ -567,12 +361,6 @@ impl<S: 'static> TestRequest<S> {
panic!("Can not create header"); panic!("Can not create header");
} }
/// Set request path pattern parameter
pub fn param(mut self, name: &'static str, value: &'static str) -> Self {
self.params.add_static(name, value);
self
}
/// Set request payload /// Set request payload
pub fn set_payload<B: Into<Binary>>(mut self, data: B) -> Self { pub fn set_payload<B: Into<Binary>>(mut self, data: B) -> Self {
let mut data = data.into(); let mut data = data.into();
@ -588,23 +376,19 @@ impl<S: 'static> TestRequest<S> {
self self
} }
/// Complete request creation and generate `HttpRequest` instance /// Complete request creation and generate `Request` instance
pub fn finish(self) -> HttpRequest<S> { pub fn finish(self) -> Request {
let TestRequest { let TestRequest {
state,
method, method,
uri, uri,
version, version,
headers, headers,
mut params, _cookies: _,
cookies,
payload, payload,
prefix, prefix: _,
} = self; } = self;
let router = Router::<()>::default();
let pool = RequestPool::pool(ServerSettings::default()); let mut req = Request::new();
let mut req = RequestPool::get(pool);
{ {
let inner = req.inner_mut(); let inner = req.inner_mut();
inner.method = method; inner.method = method;
@ -613,156 +397,94 @@ impl<S: 'static> TestRequest<S> {
inner.headers = headers; inner.headers = headers;
*inner.payload.borrow_mut() = payload; *inner.payload.borrow_mut() = payload;
} }
params.set_url(req.url().clone()); // req.set_cookies(cookies);
let mut info = router.route_info_params(0, params);
info.set_prefix(prefix);
let mut req = HttpRequest::new(req, Rc::new(state), info);
req.set_cookies(cookies);
req req
} }
#[cfg(test)] // /// This method generates `HttpRequest` instance and runs handler
/// Complete request creation and generate `HttpRequest` instance // /// with generated request.
pub(crate) fn finish_with_router(self, router: Router<S>) -> HttpRequest<S> { // pub fn run<H: Handler<S>>(self, h: &H) -> Result<HttpResponse, Error> {
let TestRequest { // let req = self.finish();
state, // let resp = h.handle(&req);
method,
uri,
version,
headers,
mut params,
cookies,
payload,
prefix,
} = self;
let pool = RequestPool::pool(ServerSettings::default()); // match resp.respond_to(&req) {
let mut req = RequestPool::get(pool); // Ok(resp) => match resp.into().into() {
{ // AsyncResultItem::Ok(resp) => Ok(resp),
let inner = req.inner_mut(); // AsyncResultItem::Err(err) => Err(err),
inner.method = method; // AsyncResultItem::Future(fut) => {
inner.url = InnerUrl::new(uri); // let mut sys = System::new("test");
inner.version = version; // sys.block_on(fut)
inner.headers = headers; // }
*inner.payload.borrow_mut() = payload; // },
} // Err(err) => Err(err.into()),
params.set_url(req.url().clone()); // }
let mut info = router.route_info_params(0, params); // }
info.set_prefix(prefix);
let mut req = HttpRequest::new(req, Rc::new(state), info);
req.set_cookies(cookies);
req
}
/// Complete request creation and generate server `Request` instance // /// This method generates `HttpRequest` instance and runs handler
pub fn request(self) -> Request { // /// with generated request.
let TestRequest { // ///
method, // /// This method panics is handler returns actor.
uri, // pub fn run_async<H, R, F, E>(self, h: H) -> Result<HttpResponse, E>
version, // where
headers, // H: Fn(HttpRequest<S>) -> F + 'static,
payload, // F: Future<Item = R, Error = E> + 'static,
.. // R: Responder<Error = E> + 'static,
} = self; // E: Into<Error> + 'static,
// {
// let req = self.finish();
// let fut = h(req.clone());
let pool = RequestPool::pool(ServerSettings::default()); // let mut sys = System::new("test");
let mut req = RequestPool::get(pool); // match sys.block_on(fut) {
{ // Ok(r) => match r.respond_to(&req) {
let inner = req.inner_mut(); // Ok(reply) => match reply.into().into() {
inner.method = method; // AsyncResultItem::Ok(resp) => Ok(resp),
inner.url = InnerUrl::new(uri); // _ => panic!("Nested async replies are not supported"),
inner.version = version; // },
inner.headers = headers; // Err(e) => Err(e),
*inner.payload.borrow_mut() = payload; // },
} // Err(err) => Err(err),
req // }
} // }
/// This method generates `HttpRequest` instance and runs handler // /// This method generates `HttpRequest` instance and executes handler
/// with generated request. // pub fn run_async_result<F, R, I, E>(self, f: F) -> Result<I, E>
pub fn run<H: Handler<S>>(self, h: &H) -> Result<HttpResponse, Error> { // where
let req = self.finish(); // F: FnOnce(&HttpRequest<S>) -> R,
let resp = h.handle(&req); // R: Into<AsyncResult<I, E>>,
// {
// let req = self.finish();
// let res = f(&req);
match resp.respond_to(&req) { // match res.into().into() {
Ok(resp) => match resp.into().into() { // AsyncResultItem::Ok(resp) => Ok(resp),
AsyncResultItem::Ok(resp) => Ok(resp), // AsyncResultItem::Err(err) => Err(err),
AsyncResultItem::Err(err) => Err(err), // AsyncResultItem::Future(fut) => {
AsyncResultItem::Future(fut) => { // let mut sys = System::new("test");
let mut sys = System::new("test"); // sys.block_on(fut)
sys.block_on(fut) // }
} // }
}, // }
Err(err) => Err(err.into()),
}
}
/// This method generates `HttpRequest` instance and runs handler // /// This method generates `HttpRequest` instance and executes handler
/// with generated request. // pub fn execute<F, R>(self, f: F) -> Result<HttpResponse, Error>
/// // where
/// This method panics is handler returns actor. // F: FnOnce(&HttpRequest<S>) -> R,
pub fn run_async<H, R, F, E>(self, h: H) -> Result<HttpResponse, E> // R: Responder + 'static,
where // {
H: Fn(HttpRequest<S>) -> F + 'static, // let req = self.finish();
F: Future<Item = R, Error = E> + 'static, // let resp = f(&req);
R: Responder<Error = E> + 'static,
E: Into<Error> + 'static,
{
let req = self.finish();
let fut = h(req.clone());
let mut sys = System::new("test"); // match resp.respond_to(&req) {
match sys.block_on(fut) { // Ok(resp) => match resp.into().into() {
Ok(r) => match r.respond_to(&req) { // AsyncResultItem::Ok(resp) => Ok(resp),
Ok(reply) => match reply.into().into() { // AsyncResultItem::Err(err) => Err(err),
AsyncResultItem::Ok(resp) => Ok(resp), // AsyncResultItem::Future(fut) => {
_ => panic!("Nested async replies are not supported"), // let mut sys = System::new("test");
}, // sys.block_on(fut)
Err(e) => Err(e), // }
}, // },
Err(err) => Err(err), // Err(err) => Err(err.into()),
} // }
} // }
/// This method generates `HttpRequest` instance and executes handler
pub fn run_async_result<F, R, I, E>(self, f: F) -> Result<I, E>
where
F: FnOnce(&HttpRequest<S>) -> R,
R: Into<AsyncResult<I, E>>,
{
let req = self.finish();
let res = f(&req);
match res.into().into() {
AsyncResultItem::Ok(resp) => Ok(resp),
AsyncResultItem::Err(err) => Err(err),
AsyncResultItem::Future(fut) => {
let mut sys = System::new("test");
sys.block_on(fut)
}
}
}
/// This method generates `HttpRequest` instance and executes handler
pub fn execute<F, R>(self, f: F) -> Result<HttpResponse, Error>
where
F: FnOnce(&HttpRequest<S>) -> R,
R: Responder + 'static,
{
let req = self.finish();
let resp = f(&req);
match resp.respond_to(&req) {
Ok(resp) => match resp.into().into() {
AsyncResultItem::Ok(resp) => Ok(resp),
AsyncResultItem::Err(err) => Err(err),
AsyncResultItem::Future(fut) => {
let mut sys = System::new("test");
sys.block_on(fut)
}
},
Err(err) => Err(err.into()),
}
}
} }

View File

@ -8,7 +8,6 @@ use std::thread;
use actix::System; use actix::System;
use actix_net::server::Server; use actix_net::server::Server;
use actix_net::service::{IntoNewService, IntoService};
use actix_web::{client, test}; use actix_web::{client, test};
use futures::future; use futures::future;