Merge branch 'master' into master

This commit is contained in:
Nikolay Kim 2019-04-03 15:11:27 -07:00 committed by GitHub
commit ca9775f6e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 1233 additions and 1062 deletions

View File

@ -11,9 +11,9 @@ matrix:
- rust: 1.31.0
- rust: stable
- rust: beta
- rust: nightly-2019-03-02
- rust: nightly-2019-04-02
allow_failures:
- rust: nightly-2019-03-02
- rust: nightly-2019-04-02
env:
global:
@ -26,7 +26,7 @@ before_install:
- sudo apt-get install -y openssl libssl-dev libelf-dev libdw-dev cmake gcc binutils-dev libiberty-dev
before_cache: |
if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-03-02" ]]; then
if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-04-02" ]]; then
RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install cargo-tarpaulin
fi
@ -50,7 +50,7 @@ after_success:
echo "Uploaded documentation"
fi
- |
if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-03-02" ]]; then
if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-04-02" ]]; then
taskset -c 0 cargo tarpaulin --out Xml --all --all-features
bash <(curl -s https://codecov.io/bash)
echo "Uploaded code coverage"

View File

@ -1,13 +1,27 @@
# Changes
## [1.0.0-alpha.4] - 2019-04-xx
### Added
* `App::configure()` allow to offload app configuration to different methods
* Added `URLPath` option for logger
### Changed
* Move multipart support to actix-multipart crate
## [1.0.0-alpha.3] - 2019-04-02
### Changed
* Renamed `TestRequest::to_service()` to `TestRequest::to_srv_request()`
* Renamed `TestRequest::to_response()` to `TestRequest::to_srv_response()`
* Added `URLPath` option for logger
* Removed `Deref` impls
### Removed

View File

@ -30,8 +30,10 @@ members = [
"actix-http",
"actix-files",
"actix-session",
"actix-multipart",
"actix-web-actors",
"actix-web-codegen",
"test-server",
]
[package.metadata.docs.rs]
@ -71,18 +73,17 @@ actix-utils = "0.3.4"
actix-router = "0.1.0"
actix-rt = "0.2.2"
actix-web-codegen = "0.1.0-alpha.1"
actix-http = { version = "0.1.0-alpha.2", features=["fail"] }
actix-http = { version = "0.1.0-alpha.3", features=["fail"] }
actix-server = "0.4.2"
actix-server-config = "0.1.0"
actix-threadpool = "0.1.0"
awc = { version = "0.1.0-alpha.2", optional = true }
awc = { version = "0.1.0-alpha.3", optional = true }
bytes = "0.4"
derive_more = "0.14"
encoding = "0.2"
futures = "0.1"
hashbrown = "0.1.8"
httparse = "1.3"
log = "0.4"
mime = "0.3"
net2 = "0.2.33"
@ -100,8 +101,8 @@ openssl = { version="0.10", optional = true }
rustls = { version = "^0.15", optional = true }
[dev-dependencies]
actix-http = { version = "0.1.0-alpha.2", features=["ssl", "brotli", "flate2-zlib"] }
actix-http-test = { version = "0.1.0-alpha.2", features=["ssl"] }
actix-http = { version = "0.1.0-alpha.3", features=["ssl", "brotli", "flate2-zlib"] }
actix-http-test = { version = "0.1.0-alpha.3", features=["ssl"] }
rand = "0.6"
env_logger = "0.6"
serde_derive = "1.0"
@ -116,4 +117,11 @@ codegen-units = 1
[patch.crates-io]
actix = { git = "https://github.com/actix/actix.git" }
actix-web = { path = "." }
actix-http = { path = "actix-http" }
actix-http-test = { path = "test-server" }
actix-web-codegen = { path = "actix-web-codegen" }
actix-web-actors = { path = "actix-web-actors" }
actix-session = { path = "actix-session" }
actix-files = { path = "actix-files" }
awc = { path = "awc" }

View File

@ -1,6 +1,6 @@
# Changes
## [0.1.0-alpha.2] - 2019-04-xx
## [0.1.0-alpha.2] - 2019-04-02
* Add default handler support

View File

@ -18,7 +18,7 @@ name = "actix_files"
path = "src/lib.rs"
[dependencies]
actix-web = "1.0.0-alpha.2"
actix-web = "1.0.0-alpha.3"
actix-service = "0.3.4"
bitflags = "1"
bytes = "0.4"
@ -31,4 +31,4 @@ percent-encoding = "1.0"
v_htmlescape = "0.4"
[dev-dependencies]
actix-web = { version = "1.0.0-alpha.2", features=["ssl"] }
actix-web = { version = "1.0.0-alpha.3", features=["ssl"] }

View File

@ -552,7 +552,7 @@ impl<P> FromRequest<P> for PathBufWrp {
type Future = Result<Self, Self::Error>;
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
PathBufWrp::get_pathbuf(req.match_info().path())
PathBufWrp::get_pathbuf(req.request().match_info().path())
}
}
@ -1049,7 +1049,7 @@ mod tests {
.new_service(&()),
)
.unwrap();
let req = TestRequest::with_uri("/missing").to_service();
let req = TestRequest::with_uri("/missing").to_srv_request();
let mut resp = test::call_success(&mut st, req);
assert_eq!(resp.status(), StatusCode::OK);

View File

@ -1,6 +1,20 @@
# Changes
## [0.1.0-alpha.3] - 2019-04-xx
## [0.1.0-alpha.4] - 2019-04-xx
### Changed
* Export IntoHeaderValue
### Deleted
* Removed PayloadBuffer
## [0.1.0-alpha.3] - 2019-04-02
### Added
* Warn when an unsealed private cookie isn't valid UTF-8
### Fixed
@ -8,6 +22,8 @@
* Preallocate read buffer for h1 codec
* Detect socket disconnection during protocol selection
## [0.1.0-alpha.2] - 2019-03-29

View File

@ -100,7 +100,7 @@ openssl = { version="0.10", optional = true }
actix-rt = "0.2.2"
actix-server = { version = "0.4.1", features=["ssl"] }
actix-connect = { version = "0.1.0", features=["ssl"] }
actix-http-test = { version = "0.1.0-alpha.2", features=["ssl"] }
actix-http-test = { version = "0.1.0-alpha.3", features=["ssl"] }
env_logger = "0.6"
serde_derive = "1.0"
openssl = { version="0.10" }

View File

@ -10,7 +10,7 @@ use http::header::{
use http::{Method, Version};
use super::decoder::{PayloadDecoder, PayloadItem, PayloadType};
use super::{decoder, encoder};
use super::{decoder, encoder, reserve_readbuf};
use super::{Message, MessageType};
use crate::body::BodySize;
use crate::config::ServiceConfig;
@ -150,6 +150,7 @@ impl Decoder for ClientCodec {
} else {
self.inner.payload = None;
}
reserve_readbuf(src);
Ok(Some(req))
} else {
Ok(None)
@ -168,7 +169,10 @@ impl Decoder for ClientPayloadCodec {
);
Ok(match self.inner.payload.as_mut().unwrap().decode(src)? {
Some(PayloadItem::Chunk(chunk)) => Some(Some(chunk)),
Some(PayloadItem::Chunk(chunk)) => {
reserve_readbuf(src);
Some(Some(chunk))
}
Some(PayloadItem::Eof) => {
self.inner.payload.take();
Some(None)

View File

@ -9,7 +9,7 @@ use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCOD
use http::{Method, StatusCode, Version};
use super::decoder::{PayloadDecoder, PayloadItem, PayloadType};
use super::{decoder, encoder};
use super::{decoder, encoder, reserve_readbuf};
use super::{Message, MessageType};
use crate::body::BodySize;
use crate::config::ServiceConfig;
@ -19,9 +19,6 @@ use crate::message::{ConnectionType, Head, ResponseHead};
use crate::request::Request;
use crate::response::Response;
const LW: usize = 2 * 1024;
const HW: usize = 32 * 1024;
bitflags! {
struct Flags: u8 {
const HEAD = 0b0000_0001;
@ -108,14 +105,12 @@ impl Decoder for Codec {
type Error = ParseError;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
let cap = src.capacity();
if cap < LW {
src.reserve(HW - cap);
}
if self.payload.is_some() {
Ok(match self.payload.as_mut().unwrap().decode(src)? {
Some(PayloadItem::Chunk(chunk)) => Some(Message::Chunk(Some(chunk))),
Some(PayloadItem::Chunk(chunk)) => {
reserve_readbuf(src);
Some(Message::Chunk(Some(chunk)))
}
Some(PayloadItem::Eof) => {
self.payload.take();
Some(Message::Chunk(None))
@ -140,6 +135,7 @@ impl Decoder for Codec {
self.flags.insert(Flags::STREAM);
}
}
reserve_readbuf(src);
Ok(Some(Message::Item(req)))
} else {
Ok(None)

View File

@ -1,5 +1,5 @@
//! HTTP/1 implementation
use bytes::Bytes;
use bytes::{Bytes, BytesMut};
mod client;
mod codec;
@ -12,7 +12,7 @@ mod service;
pub use self::client::{ClientCodec, ClientPayloadCodec};
pub use self::codec::Codec;
pub use self::dispatcher::Dispatcher;
pub use self::payload::{Payload, PayloadBuffer};
pub use self::payload::{Payload, PayloadWriter};
pub use self::service::{H1Service, H1ServiceHandler, OneRequest};
#[derive(Debug)]
@ -38,6 +38,16 @@ pub enum MessageType {
Stream,
}
const LW: usize = 2 * 1024;
const HW: usize = 32 * 1024;
pub(crate) fn reserve_readbuf(src: &mut BytesMut) {
let cap = src.capacity();
if cap < LW {
src.reserve(HW - cap);
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -1,10 +1,9 @@
//! Payload stream
use std::cell::RefCell;
use std::cmp;
use std::collections::VecDeque;
use std::rc::{Rc, Weak};
use bytes::{Bytes, BytesMut};
use bytes::Bytes;
use futures::task::current as current_task;
use futures::task::Task;
use futures::{Async, Poll, Stream};
@ -15,7 +14,7 @@ use crate::error::PayloadError;
pub(crate) const MAX_BUFFER_SIZE: usize = 32_768;
#[derive(Debug, PartialEq)]
pub(crate) enum PayloadStatus {
pub enum PayloadStatus {
Read,
Pause,
Dropped,
@ -107,7 +106,7 @@ impl Clone for Payload {
}
/// Payload writer interface.
pub(crate) trait PayloadWriter {
pub trait PayloadWriter {
/// Set stream error.
fn set_error(&mut self, err: PayloadError);
@ -258,407 +257,12 @@ impl Inner {
}
}
/// Payload buffer
pub struct PayloadBuffer<S> {
len: usize,
items: VecDeque<Bytes>,
stream: S,
}
impl<S> PayloadBuffer<S>
where
S: Stream<Item = Bytes, Error = PayloadError>,
{
/// Create new `PayloadBuffer` instance
pub fn new(stream: S) -> Self {
PayloadBuffer {
len: 0,
items: VecDeque::new(),
stream,
}
}
/// Get mutable reference to an inner stream.
pub fn get_mut(&mut self) -> &mut S {
&mut self.stream
}
#[inline]
fn poll_stream(&mut self) -> Poll<bool, PayloadError> {
self.stream.poll().map(|res| match res {
Async::Ready(Some(data)) => {
self.len += data.len();
self.items.push_back(data);
Async::Ready(true)
}
Async::Ready(None) => Async::Ready(false),
Async::NotReady => Async::NotReady,
})
}
/// Read first available chunk of bytes
#[inline]
pub fn readany(&mut self) -> Poll<Option<Bytes>, PayloadError> {
if let Some(data) = self.items.pop_front() {
self.len -= data.len();
Ok(Async::Ready(Some(data)))
} else {
match self.poll_stream()? {
Async::Ready(true) => self.readany(),
Async::Ready(false) => Ok(Async::Ready(None)),
Async::NotReady => Ok(Async::NotReady),
}
}
}
/// Check if buffer contains enough bytes
#[inline]
pub fn can_read(&mut self, size: usize) -> Poll<Option<bool>, PayloadError> {
if size <= self.len {
Ok(Async::Ready(Some(true)))
} else {
match self.poll_stream()? {
Async::Ready(true) => self.can_read(size),
Async::Ready(false) => Ok(Async::Ready(None)),
Async::NotReady => Ok(Async::NotReady),
}
}
}
/// Return reference to the first chunk of data
#[inline]
pub fn get_chunk(&mut self) -> Poll<Option<&[u8]>, PayloadError> {
if self.items.is_empty() {
match self.poll_stream()? {
Async::Ready(true) => (),
Async::Ready(false) => return Ok(Async::Ready(None)),
Async::NotReady => return Ok(Async::NotReady),
}
}
match self.items.front().map(|c| c.as_ref()) {
Some(chunk) => Ok(Async::Ready(Some(chunk))),
None => Ok(Async::NotReady),
}
}
/// Read exact number of bytes
#[inline]
pub fn read_exact(&mut self, size: usize) -> Poll<Option<Bytes>, PayloadError> {
if size <= self.len {
self.len -= size;
let mut chunk = self.items.pop_front().unwrap();
if size < chunk.len() {
let buf = chunk.split_to(size);
self.items.push_front(chunk);
Ok(Async::Ready(Some(buf)))
} else if size == chunk.len() {
Ok(Async::Ready(Some(chunk)))
} else {
let mut buf = BytesMut::with_capacity(size);
buf.extend_from_slice(&chunk);
while buf.len() < size {
let mut chunk = self.items.pop_front().unwrap();
let rem = cmp::min(size - buf.len(), chunk.len());
buf.extend_from_slice(&chunk.split_to(rem));
if !chunk.is_empty() {
self.items.push_front(chunk);
}
}
Ok(Async::Ready(Some(buf.freeze())))
}
} else {
match self.poll_stream()? {
Async::Ready(true) => self.read_exact(size),
Async::Ready(false) => Ok(Async::Ready(None)),
Async::NotReady => Ok(Async::NotReady),
}
}
}
/// Remove specified amount if bytes from buffer
#[inline]
pub fn drop_bytes(&mut self, size: usize) {
if size <= self.len {
self.len -= size;
let mut len = 0;
while len < size {
let mut chunk = self.items.pop_front().unwrap();
let rem = cmp::min(size - len, chunk.len());
len += rem;
if rem < chunk.len() {
chunk.split_to(rem);
self.items.push_front(chunk);
}
}
}
}
/// Copy buffered data
pub fn copy(&mut self, size: usize) -> Poll<Option<BytesMut>, PayloadError> {
if size <= self.len {
let mut buf = BytesMut::with_capacity(size);
for chunk in &self.items {
if buf.len() < size {
let rem = cmp::min(size - buf.len(), chunk.len());
buf.extend_from_slice(&chunk[..rem]);
}
if buf.len() == size {
return Ok(Async::Ready(Some(buf)));
}
}
}
match self.poll_stream()? {
Async::Ready(true) => self.copy(size),
Async::Ready(false) => Ok(Async::Ready(None)),
Async::NotReady => Ok(Async::NotReady),
}
}
/// Read until specified ending
pub fn read_until(&mut self, line: &[u8]) -> Poll<Option<Bytes>, PayloadError> {
let mut idx = 0;
let mut num = 0;
let mut offset = 0;
let mut found = false;
let mut length = 0;
for no in 0..self.items.len() {
{
let chunk = &self.items[no];
for (pos, ch) in chunk.iter().enumerate() {
if *ch == line[idx] {
idx += 1;
if idx == line.len() {
num = no;
offset = pos + 1;
length += pos + 1;
found = true;
break;
}
} else {
idx = 0
}
}
if !found {
length += chunk.len()
}
}
if found {
let mut buf = BytesMut::with_capacity(length);
if num > 0 {
for _ in 0..num {
buf.extend_from_slice(&self.items.pop_front().unwrap());
}
}
if offset > 0 {
let mut chunk = self.items.pop_front().unwrap();
buf.extend_from_slice(&chunk.split_to(offset));
if !chunk.is_empty() {
self.items.push_front(chunk)
}
}
self.len -= length;
return Ok(Async::Ready(Some(buf.freeze())));
}
}
match self.poll_stream()? {
Async::Ready(true) => self.read_until(line),
Async::Ready(false) => Ok(Async::Ready(None)),
Async::NotReady => Ok(Async::NotReady),
}
}
/// Read bytes until new line delimiter
pub fn readline(&mut self) -> Poll<Option<Bytes>, PayloadError> {
self.read_until(b"\n")
}
/// Put unprocessed data back to the buffer
pub fn unprocessed(&mut self, data: Bytes) {
self.len += data.len();
self.items.push_front(data);
}
/// Get remaining data from the buffer
pub fn remaining(&mut self) -> Bytes {
self.items
.iter_mut()
.fold(BytesMut::new(), |mut b, c| {
b.extend_from_slice(c);
b
})
.freeze()
}
}
#[cfg(test)]
mod tests {
use super::*;
use actix_rt::Runtime;
use futures::future::{lazy, result};
#[test]
fn test_basic() {
Runtime::new()
.unwrap()
.block_on(lazy(|| {
let (_, payload) = Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(payload.len, 0);
assert_eq!(Async::NotReady, payload.readany().ok().unwrap());
let res: Result<(), ()> = Ok(());
result(res)
}))
.unwrap();
}
#[test]
fn test_eof() {
Runtime::new()
.unwrap()
.block_on(lazy(|| {
let (mut sender, payload) = Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(Async::NotReady, payload.readany().ok().unwrap());
sender.feed_data(Bytes::from("data"));
sender.feed_eof();
assert_eq!(
Async::Ready(Some(Bytes::from("data"))),
payload.readany().ok().unwrap()
);
assert_eq!(payload.len, 0);
assert_eq!(Async::Ready(None), payload.readany().ok().unwrap());
let res: Result<(), ()> = Ok(());
result(res)
}))
.unwrap();
}
#[test]
fn test_err() {
Runtime::new()
.unwrap()
.block_on(lazy(|| {
let (mut sender, payload) = Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(Async::NotReady, payload.readany().ok().unwrap());
sender.set_error(PayloadError::Incomplete(None));
payload.readany().err().unwrap();
let res: Result<(), ()> = Ok(());
result(res)
}))
.unwrap();
}
#[test]
fn test_readany() {
Runtime::new()
.unwrap()
.block_on(lazy(|| {
let (mut sender, payload) = Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
sender.feed_data(Bytes::from("line1"));
sender.feed_data(Bytes::from("line2"));
assert_eq!(
Async::Ready(Some(Bytes::from("line1"))),
payload.readany().ok().unwrap()
);
assert_eq!(payload.len, 0);
assert_eq!(
Async::Ready(Some(Bytes::from("line2"))),
payload.readany().ok().unwrap()
);
assert_eq!(payload.len, 0);
let res: Result<(), ()> = Ok(());
result(res)
}))
.unwrap();
}
#[test]
fn test_readexactly() {
Runtime::new()
.unwrap()
.block_on(lazy(|| {
let (mut sender, payload) = Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(Async::NotReady, payload.read_exact(2).ok().unwrap());
sender.feed_data(Bytes::from("line1"));
sender.feed_data(Bytes::from("line2"));
assert_eq!(
Async::Ready(Some(Bytes::from_static(b"li"))),
payload.read_exact(2).ok().unwrap()
);
assert_eq!(payload.len, 3);
assert_eq!(
Async::Ready(Some(Bytes::from_static(b"ne1l"))),
payload.read_exact(4).ok().unwrap()
);
assert_eq!(payload.len, 4);
sender.set_error(PayloadError::Incomplete(None));
payload.read_exact(10).err().unwrap();
let res: Result<(), ()> = Ok(());
result(res)
}))
.unwrap();
}
#[test]
fn test_readuntil() {
Runtime::new()
.unwrap()
.block_on(lazy(|| {
let (mut sender, payload) = Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(Async::NotReady, payload.read_until(b"ne").ok().unwrap());
sender.feed_data(Bytes::from("line1"));
sender.feed_data(Bytes::from("line2"));
assert_eq!(
Async::Ready(Some(Bytes::from("line"))),
payload.read_until(b"ne").ok().unwrap()
);
assert_eq!(payload.len, 1);
assert_eq!(
Async::Ready(Some(Bytes::from("1line2"))),
payload.read_until(b"2").ok().unwrap()
);
assert_eq!(payload.len, 0);
sender.set_error(PayloadError::Incomplete(None));
payload.read_until(b"b").err().unwrap();
let res: Result<(), ()> = Ok(());
result(res)
}))
.unwrap();
}
#[test]
fn test_unread_data() {
Runtime::new()

View File

@ -20,7 +20,6 @@ pub use self::common::*;
#[doc(hidden)]
pub use self::shared::*;
#[doc(hidden)]
/// A trait for any object that will represent a header field and value.
pub trait Header
where
@ -33,7 +32,6 @@ where
fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError>;
}
#[doc(hidden)]
/// A trait for any object that can be Converted to a `HeaderValue`
pub trait IntoHeaderValue: Sized {
/// The type returned in the event of a conversion error.
@ -97,6 +95,26 @@ impl IntoHeaderValue for String {
}
}
impl IntoHeaderValue for usize {
type Error = InvalidHeaderValueBytes;
#[inline]
fn try_into(self) -> Result<HeaderValue, Self::Error> {
let s = format!("{}", self);
HeaderValue::from_shared(Bytes::from(s))
}
}
impl IntoHeaderValue for u64 {
type Error = InvalidHeaderValueBytes;
#[inline]
fn try_into(self) -> Result<HeaderValue, Self::Error> {
let s = format!("{}", self);
HeaderValue::from_shared(Bytes::from(s))
}
}
impl IntoHeaderValue for Mime {
type Error = InvalidHeaderValueBytes;

View File

@ -247,7 +247,10 @@ where
loop {
unsafe {
let b = item.1.bytes_mut();
let n = { try_ready!(item.0.poll_read(b)) };
let n = try_ready!(item.0.poll_read(b));
if n == 0 {
return Ok(Async::Ready(()));
}
item.1.advance_mut(n);
if item.1.len() >= HTTP2_PREFACE.len() {
break;

View File

@ -35,10 +35,10 @@ fn test_h1_v2() {
.finish(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
.map(|_| ())
});
let response = srv.block_on(srv.get().send()).unwrap();
let response = srv.block_on(srv.get("/").send()).unwrap();
assert!(response.status().is_success());
let request = srv.get().header("x-test", "111").send();
let request = srv.get("/").header("x-test", "111").send();
let response = srv.block_on(request).unwrap();
assert!(response.status().is_success());
@ -46,7 +46,7 @@ fn test_h1_v2() {
let bytes = srv.load_body(response).unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
let response = srv.block_on(srv.post().send()).unwrap();
let response = srv.block_on(srv.post("/").send()).unwrap();
assert!(response.status().is_success());
// read response
@ -61,7 +61,7 @@ fn test_connection_close() {
.finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
.map(|_| ())
});
let response = srv.block_on(srv.get().close_connection().send()).unwrap();
let response = srv.block_on(srv.get("/").force_close().send()).unwrap();
assert!(response.status().is_success());
}

View File

@ -37,7 +37,7 @@ fn test_h1() {
.h1(|_| future::ok::<_, ()>(Response::Ok().finish()))
});
let response = srv.block_on(srv.get().send()).unwrap();
let response = srv.block_on(srv.get("/").send()).unwrap();
assert!(response.status().is_success());
}
@ -55,7 +55,7 @@ fn test_h1_2() {
.map(|_| ())
});
let response = srv.block_on(srv.get().send()).unwrap();
let response = srv.block_on(srv.get("/").send()).unwrap();
assert!(response.status().is_success());
}
@ -98,7 +98,7 @@ fn test_h2() -> std::io::Result<()> {
)
});
let response = srv.block_on(srv.sget().send()).unwrap();
let response = srv.block_on(srv.sget("/").send()).unwrap();
assert!(response.status().is_success());
Ok(())
}
@ -121,7 +121,7 @@ fn test_h2_1() -> std::io::Result<()> {
)
});
let response = srv.block_on(srv.sget().send()).unwrap();
let response = srv.block_on(srv.sget("/").send()).unwrap();
assert!(response.status().is_success());
Ok(())
}
@ -145,7 +145,7 @@ fn test_h2_body() -> std::io::Result<()> {
)
});
let response = srv.block_on(srv.sget().send_body(data.clone())).unwrap();
let response = srv.block_on(srv.sget("/").send_body(data.clone())).unwrap();
assert!(response.status().is_success());
let body = srv.load_body(response).unwrap();
@ -437,7 +437,7 @@ fn test_h1_headers() {
})
});
let response = srv.block_on(srv.get().send()).unwrap();
let response = srv.block_on(srv.get("/").send()).unwrap();
assert!(response.status().is_success());
// read response
@ -482,7 +482,7 @@ fn test_h2_headers() {
}).map_err(|_| ()))
});
let response = srv.block_on(srv.sget().send()).unwrap();
let response = srv.block_on(srv.sget("/").send()).unwrap();
assert!(response.status().is_success());
// read response
@ -518,7 +518,7 @@ fn test_h1_body() {
HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
});
let response = srv.block_on(srv.get().send()).unwrap();
let response = srv.block_on(srv.get("/").send()).unwrap();
assert!(response.status().is_success());
// read response
@ -541,7 +541,7 @@ fn test_h2_body2() {
)
});
let response = srv.block_on(srv.sget().send()).unwrap();
let response = srv.block_on(srv.sget("/").send()).unwrap();
assert!(response.status().is_success());
// read response
@ -555,7 +555,7 @@ fn test_h1_head_empty() {
HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR)))
});
let response = srv.block_on(srv.head().send()).unwrap();
let response = srv.block_on(srv.head("/").send()).unwrap();
assert!(response.status().is_success());
{
@ -586,7 +586,7 @@ fn test_h2_head_empty() {
)
});
let response = srv.block_on(srv.shead().send()).unwrap();
let response = srv.block_on(srv.shead("/").send()).unwrap();
assert!(response.status().is_success());
assert_eq!(response.version(), http::Version::HTTP_2);
@ -611,7 +611,7 @@ fn test_h1_head_binary() {
})
});
let response = srv.block_on(srv.head().send()).unwrap();
let response = srv.block_on(srv.head("/").send()).unwrap();
assert!(response.status().is_success());
{
@ -646,7 +646,7 @@ fn test_h2_head_binary() {
)
});
let response = srv.block_on(srv.shead().send()).unwrap();
let response = srv.block_on(srv.shead("/").send()).unwrap();
assert!(response.status().is_success());
{
@ -668,7 +668,7 @@ fn test_h1_head_binary2() {
HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR)))
});
let response = srv.block_on(srv.head().send()).unwrap();
let response = srv.block_on(srv.head("/").send()).unwrap();
assert!(response.status().is_success());
{
@ -695,7 +695,7 @@ fn test_h2_head_binary2() {
)
});
let response = srv.block_on(srv.shead().send()).unwrap();
let response = srv.block_on(srv.shead("/").send()).unwrap();
assert!(response.status().is_success());
{
@ -719,7 +719,7 @@ fn test_h1_body_length() {
})
});
let response = srv.block_on(srv.get().send()).unwrap();
let response = srv.block_on(srv.get("/").send()).unwrap();
assert!(response.status().is_success());
// read response
@ -747,7 +747,7 @@ fn test_h2_body_length() {
)
});
let response = srv.block_on(srv.sget().send()).unwrap();
let response = srv.block_on(srv.sget("/").send()).unwrap();
assert!(response.status().is_success());
// read response
@ -768,7 +768,7 @@ fn test_h1_body_chunked_explicit() {
})
});
let response = srv.block_on(srv.get().send()).unwrap();
let response = srv.block_on(srv.get("/").send()).unwrap();
assert!(response.status().is_success());
assert_eq!(
response
@ -810,7 +810,7 @@ fn test_h2_body_chunked_explicit() {
)
});
let response = srv.block_on(srv.sget().send()).unwrap();
let response = srv.block_on(srv.sget("/").send()).unwrap();
assert!(response.status().is_success());
assert!(!response.headers().contains_key(header::TRANSFER_ENCODING));
@ -830,7 +830,7 @@ fn test_h1_body_chunked_implicit() {
})
});
let response = srv.block_on(srv.get().send()).unwrap();
let response = srv.block_on(srv.get("/").send()).unwrap();
assert!(response.status().is_success());
assert_eq!(
response
@ -862,7 +862,7 @@ fn test_h1_response_http_error_handling() {
}))
});
let response = srv.block_on(srv.get().send()).unwrap();
let response = srv.block_on(srv.get("/").send()).unwrap();
assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
// read response
@ -895,7 +895,7 @@ fn test_h2_response_http_error_handling() {
)
});
let response = srv.block_on(srv.sget().send()).unwrap();
let response = srv.block_on(srv.sget("/").send()).unwrap();
assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
// read response
@ -910,7 +910,7 @@ fn test_h1_service_error() {
.h1(|_| Err::<Response, Error>(error::ErrorBadRequest("error")))
});
let response = srv.block_on(srv.get().send()).unwrap();
let response = srv.block_on(srv.get("/").send()).unwrap();
assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
// read response
@ -934,7 +934,7 @@ fn test_h2_service_error() {
)
});
let response = srv.block_on(srv.sget().send()).unwrap();
let response = srv.block_on(srv.sget("/").send()).unwrap();
assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
// read response

View File

@ -0,0 +1,5 @@
# Changes
## [0.1.0-alpha.1] - 2019-04-xx
* Split multipart support to separate crate

View File

@ -0,0 +1,34 @@
[package]
name = "actix-multipart"
version = "0.1.0-alpha.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Multipart support for actix web framework."
readme = "README.md"
keywords = ["http", "web", "framework", "async", "futures"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web.git"
documentation = "https://docs.rs/actix-multipart/"
license = "MIT/Apache-2.0"
exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"]
workspace = ".."
edition = "2018"
[lib]
name = "actix_multipart"
path = "src/lib.rs"
[dependencies]
actix-web = "1.0.0-alpha.3"
actix-service = "0.3.4"
bytes = "0.4"
derive_more = "0.14"
httparse = "1.3"
futures = "0.1.25"
log = "0.4"
mime = "0.3"
time = "0.1"
twoway = "0.2"
[dev-dependencies]
actix-rt = "0.2.2"
actix-http = "0.1.0-alpha.3"

View File

@ -0,0 +1 @@
# Multipart support for actix web framework [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](https://meritbadge.herokuapp.com/actix-multipart)](https://crates.io/crates/actix-multipart) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

View File

@ -0,0 +1,46 @@
//! Error and Result module
use actix_web::error::{ParseError, PayloadError};
use actix_web::http::StatusCode;
use actix_web::{HttpResponse, ResponseError};
use derive_more::{Display, From};
/// A set of errors that can occur during parsing multipart streams
#[derive(Debug, Display, From)]
pub enum MultipartError {
/// Content-Type header is not found
#[display(fmt = "No Content-type header found")]
NoContentType,
/// Can not parse Content-Type header
#[display(fmt = "Can not parse Content-Type header")]
ParseContentType,
/// Multipart boundary is not found
#[display(fmt = "Multipart boundary is not found")]
Boundary,
/// Multipart stream is incomplete
#[display(fmt = "Multipart stream is incomplete")]
Incomplete,
/// Error during field parsing
#[display(fmt = "{}", _0)]
Parse(ParseError),
/// Payload error
#[display(fmt = "{}", _0)]
Payload(PayloadError),
}
/// Return `BadRequest` for `MultipartError`
impl ResponseError for MultipartError {
fn error_response(&self) -> HttpResponse {
HttpResponse::new(StatusCode::BAD_REQUEST)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_multipart_error() {
let resp: HttpResponse = MultipartError::Boundary.error_response();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
}
}

View File

@ -0,0 +1,57 @@
//! Multipart payload support
use bytes::Bytes;
use futures::Stream;
use actix_web::dev::ServiceFromRequest;
use actix_web::error::{Error, PayloadError};
use actix_web::FromRequest;
use actix_web::HttpMessage;
use crate::server::Multipart;
/// Get request's payload as multipart stream
///
/// Content-type: multipart/form-data;
///
/// ## Server example
///
/// ```rust
/// # use futures::{Future, Stream};
/// # use futures::future::{ok, result, Either};
/// use actix_web::{web, HttpResponse, Error};
/// use actix_multipart as mp;
///
/// fn index(payload: mp::Multipart) -> impl Future<Item = HttpResponse, Error = Error> {
/// payload.from_err() // <- get multipart stream for current request
/// .and_then(|item| match item { // <- iterate over multipart items
/// mp::Item::Field(field) => {
/// // Field in turn is stream of *Bytes* object
/// Either::A(field.from_err()
/// .fold((), |_, chunk| {
/// println!("-- CHUNK: \n{:?}", std::str::from_utf8(&chunk));
/// Ok::<_, Error>(())
/// }))
/// },
/// mp::Item::Nested(mp) => {
/// // Or item could be nested Multipart stream
/// Either::B(ok(()))
/// }
/// })
/// .fold((), |_, _| Ok::<_, Error>(()))
/// .map(|_| HttpResponse::Ok().into())
/// }
/// # fn main() {}
/// ```
impl<P> FromRequest<P> for Multipart
where
P: Stream<Item = Bytes, Error = PayloadError> + 'static,
{
type Error = Error;
type Future = Result<Multipart, Error>;
#[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
let pl = req.take_payload();
Ok(Multipart::new(req.headers(), pl))
}
}

View File

@ -0,0 +1,6 @@
mod error;
mod extractor;
mod server;
pub use self::error::MultipartError;
pub use self::server::{Field, Item, Multipart};

View File

@ -4,26 +4,22 @@ use std::marker::PhantomData;
use std::rc::Rc;
use std::{cmp, fmt};
use bytes::Bytes;
use bytes::{Bytes, BytesMut};
use futures::task::{current as current_task, Task};
use futures::{Async, Poll, Stream};
use httparse;
use mime;
use crate::error::{Error, MultipartError, ParseError, PayloadError};
use crate::extract::FromRequest;
use crate::http::header::{
use actix_web::error::{ParseError, PayloadError};
use actix_web::http::header::{
self, ContentDisposition, HeaderMap, HeaderName, HeaderValue,
};
use crate::http::HttpTryFrom;
use crate::service::ServiceFromRequest;
use crate::HttpMessage;
use actix_web::http::HttpTryFrom;
use crate::error::MultipartError;
const MAX_HEADERS: usize = 32;
type PayloadBuffer =
actix_http::h1::PayloadBuffer<Box<dyn Stream<Item = Bytes, Error = PayloadError>>>;
/// The server-side implementation of `multipart/form-data` requests.
///
/// This will parse the incoming stream into `MultipartItem` instances via its
@ -37,59 +33,13 @@ pub struct Multipart {
}
/// Multipart item
pub enum MultipartItem {
pub enum Item {
/// Multipart field
Field(MultipartField),
Field(Field),
/// Nested multipart stream
Nested(Multipart),
}
/// Get request's payload as multipart stream
///
/// Content-type: multipart/form-data;
///
/// ## Server example
///
/// ```rust
/// # use futures::{Future, Stream};
/// # use futures::future::{ok, result, Either};
/// use actix_web::{web, HttpResponse, Error};
///
/// fn index(payload: web::Multipart) -> impl Future<Item = HttpResponse, Error = Error> {
/// payload.from_err() // <- get multipart stream for current request
/// .and_then(|item| match item { // <- iterate over multipart items
/// web::MultipartItem::Field(field) => {
/// // Field in turn is stream of *Bytes* object
/// Either::A(field.from_err()
/// .fold((), |_, chunk| {
/// println!("-- CHUNK: \n{:?}", std::str::from_utf8(&chunk));
/// Ok::<_, Error>(())
/// }))
/// },
/// web::MultipartItem::Nested(mp) => {
/// // Or item could be nested Multipart stream
/// Either::B(ok(()))
/// }
/// })
/// .fold((), |_, _| Ok::<_, Error>(()))
/// .map(|_| HttpResponse::Ok().into())
/// }
/// # fn main() {}
/// ```
impl<P> FromRequest<P> for Multipart
where
P: Stream<Item = Bytes, Error = PayloadError> + 'static,
{
type Error = Error;
type Future = Result<Multipart, Error>;
#[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
let pl = req.take_payload();
Ok(Multipart::new(req.headers(), pl))
}
}
enum InnerMultipartItem {
None,
Field(Rc<RefCell<InnerField>>),
@ -163,14 +113,18 @@ impl Multipart {
}
impl Stream for Multipart {
type Item = MultipartItem;
type Item = Item;
type Error = MultipartError;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if let Some(err) = self.error.take() {
Err(err)
} else if self.safety.current() {
self.inner.as_mut().unwrap().borrow_mut().poll(&self.safety)
let mut inner = self.inner.as_mut().unwrap().borrow_mut();
if let Some(payload) = inner.payload.get_mut(&self.safety) {
payload.poll_stream()?;
}
inner.poll(&self.safety)
} else {
Ok(Async::NotReady)
}
@ -178,11 +132,18 @@ impl Stream for Multipart {
}
impl InnerMultipart {
fn read_headers(payload: &mut PayloadBuffer) -> Poll<HeaderMap, MultipartError> {
match payload.read_until(b"\r\n\r\n")? {
Async::NotReady => Ok(Async::NotReady),
Async::Ready(None) => Err(MultipartError::Incomplete),
Async::Ready(Some(bytes)) => {
fn read_headers(
payload: &mut PayloadBuffer,
) -> Result<Option<HeaderMap>, MultipartError> {
match payload.read_until(b"\r\n\r\n") {
None => {
if payload.eof {
Err(MultipartError::Incomplete)
} else {
Ok(None)
}
}
Some(bytes) => {
let mut hdrs = [httparse::EMPTY_HEADER; MAX_HEADERS];
match httparse::parse_headers(&bytes, &mut hdrs) {
Ok(httparse::Status::Complete((_, hdrs))) => {
@ -199,7 +160,7 @@ impl InnerMultipart {
return Err(ParseError::Header.into());
}
}
Ok(Async::Ready(headers))
Ok(Some(headers))
}
Ok(httparse::Status::Partial) => Err(ParseError::Header.into()),
Err(err) => Err(ParseError::from(err).into()),
@ -211,23 +172,28 @@ impl InnerMultipart {
fn read_boundary(
payload: &mut PayloadBuffer,
boundary: &str,
) -> Poll<bool, MultipartError> {
) -> Result<Option<bool>, MultipartError> {
// TODO: need to read epilogue
match payload.readline()? {
Async::NotReady => Ok(Async::NotReady),
Async::Ready(None) => Err(MultipartError::Incomplete),
Async::Ready(Some(chunk)) => {
match payload.readline() {
None => {
if payload.eof {
Err(MultipartError::Incomplete)
} else {
Ok(None)
}
}
Some(chunk) => {
if chunk.len() == boundary.len() + 4
&& &chunk[..2] == b"--"
&& &chunk[2..boundary.len() + 2] == boundary.as_bytes()
{
Ok(Async::Ready(false))
Ok(Some(false))
} else if chunk.len() == boundary.len() + 6
&& &chunk[..2] == b"--"
&& &chunk[2..boundary.len() + 2] == boundary.as_bytes()
&& &chunk[boundary.len() + 2..boundary.len() + 4] == b"--"
{
Ok(Async::Ready(true))
Ok(Some(true))
} else {
Err(MultipartError::Boundary)
}
@ -238,11 +204,11 @@ impl InnerMultipart {
fn skip_until_boundary(
payload: &mut PayloadBuffer,
boundary: &str,
) -> Poll<bool, MultipartError> {
) -> Result<Option<bool>, MultipartError> {
let mut eof = false;
loop {
match payload.readline()? {
Async::Ready(Some(chunk)) => {
match payload.readline() {
Some(chunk) => {
if chunk.is_empty() {
//ValueError("Could not find starting boundary %r"
//% (self._boundary))
@ -267,14 +233,19 @@ impl InnerMultipart {
}
}
}
Async::NotReady => return Ok(Async::NotReady),
Async::Ready(None) => return Err(MultipartError::Incomplete),
None => {
return if payload.eof {
Err(MultipartError::Incomplete)
} else {
Ok(None)
};
}
}
}
Ok(Async::Ready(eof))
Ok(Some(eof))
}
fn poll(&mut self, safety: &Safety) -> Poll<Option<MultipartItem>, MultipartError> {
fn poll(&mut self, safety: &Safety) -> Poll<Option<Item>, MultipartError> {
if self.state == InnerState::Eof {
Ok(Async::Ready(None))
} else {
@ -317,7 +288,7 @@ impl InnerMultipart {
payload,
&self.boundary,
)? {
Async::Ready(eof) => {
Some(eof) => {
if eof {
self.state = InnerState::Eof;
return Ok(Async::Ready(None));
@ -325,14 +296,14 @@ impl InnerMultipart {
self.state = InnerState::Headers;
}
}
Async::NotReady => return Ok(Async::NotReady),
None => return Ok(Async::NotReady),
}
}
// read boundary
InnerState::Boundary => {
match InnerMultipart::read_boundary(payload, &self.boundary)? {
Async::NotReady => return Ok(Async::NotReady),
Async::Ready(eof) => {
None => return Ok(Async::NotReady),
Some(eof) => {
if eof {
self.state = InnerState::Eof;
return Ok(Async::Ready(None));
@ -347,8 +318,7 @@ impl InnerMultipart {
// read field headers for next field
if self.state == InnerState::Headers {
if let Async::Ready(headers) = InnerMultipart::read_headers(payload)?
{
if let Some(headers) = InnerMultipart::read_headers(payload)? {
self.state = InnerState::Boundary;
headers
} else {
@ -389,7 +359,7 @@ impl InnerMultipart {
self.item = InnerMultipartItem::Multipart(Rc::clone(&inner));
Ok(Async::Ready(Some(MultipartItem::Nested(Multipart {
Ok(Async::Ready(Some(Item::Nested(Multipart {
safety: safety.clone(),
error: None,
inner: Some(inner),
@ -402,9 +372,12 @@ impl InnerMultipart {
)?));
self.item = InnerMultipartItem::Field(Rc::clone(&field));
Ok(Async::Ready(Some(MultipartItem::Field(
MultipartField::new(safety.clone(), headers, mt, field),
))))
Ok(Async::Ready(Some(Item::Field(Field::new(
safety.clone(),
headers,
mt,
field,
)))))
}
}
}
@ -418,21 +391,21 @@ impl Drop for InnerMultipart {
}
/// A single field in a multipart stream
pub struct MultipartField {
pub struct Field {
ct: mime::Mime,
headers: HeaderMap,
inner: Rc<RefCell<InnerField>>,
safety: Safety,
}
impl MultipartField {
impl Field {
fn new(
safety: Safety,
headers: HeaderMap,
ct: mime::Mime,
inner: Rc<RefCell<InnerField>>,
) -> Self {
MultipartField {
Field {
ct,
headers,
inner,
@ -463,22 +436,28 @@ impl MultipartField {
}
}
impl Stream for MultipartField {
impl Stream for Field {
type Item = Bytes;
type Error = MultipartError;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if self.safety.current() {
self.inner.borrow_mut().poll(&self.safety)
let mut inner = self.inner.borrow_mut();
if let Some(payload) = inner.payload.as_ref().unwrap().get_mut(&self.safety)
{
payload.poll_stream()?;
}
inner.poll(&self.safety)
} else {
Ok(Async::NotReady)
}
}
}
impl fmt::Debug for MultipartField {
impl fmt::Debug for Field {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "\nMultipartField: {}", self.ct)?;
writeln!(f, "\nField: {}", self.ct)?;
writeln!(f, " boundary: {}", self.inner.borrow().boundary)?;
writeln!(f, " headers:")?;
for (key, val) in self.headers.iter() {
@ -532,10 +511,8 @@ impl InnerField {
if *size == 0 {
Ok(Async::Ready(None))
} else {
match payload.readany() {
Ok(Async::NotReady) => Ok(Async::NotReady),
Ok(Async::Ready(None)) => Err(MultipartError::Incomplete),
Ok(Async::Ready(Some(mut chunk))) => {
match payload.read_max(*size) {
Some(mut chunk) => {
let len = cmp::min(chunk.len() as u64, *size);
*size -= len;
let ch = chunk.split_to(len as usize);
@ -544,7 +521,13 @@ impl InnerField {
}
Ok(Async::Ready(Some(ch)))
}
Err(err) => Err(err.into()),
None => {
if payload.eof && (*size != 0) {
Err(MultipartError::Incomplete)
} else {
Ok(Async::NotReady)
}
}
}
}
}
@ -555,16 +538,26 @@ impl InnerField {
payload: &mut PayloadBuffer,
boundary: &str,
) -> Poll<Option<Bytes>, MultipartError> {
match payload.read_until(b"\r")? {
Async::NotReady => Ok(Async::NotReady),
Async::Ready(None) => Err(MultipartError::Incomplete),
Async::Ready(Some(mut chunk)) => {
match payload.read_until(b"\r") {
None => {
if payload.eof {
Err(MultipartError::Incomplete)
} else {
Ok(Async::NotReady)
}
}
Some(mut chunk) => {
if chunk.len() == 1 {
payload.unprocessed(chunk);
match payload.read_exact(boundary.len() + 4)? {
Async::NotReady => Ok(Async::NotReady),
Async::Ready(None) => Err(MultipartError::Incomplete),
Async::Ready(Some(mut chunk)) => {
match payload.read_exact(boundary.len() + 4) {
None => {
if payload.eof {
Err(MultipartError::Incomplete)
} else {
Ok(Async::NotReady)
}
}
Some(mut chunk) => {
if &chunk[..2] == b"\r\n"
&& &chunk[2..4] == b"--"
&& &chunk[4..] == boundary.as_bytes()
@ -606,10 +599,9 @@ impl InnerField {
Async::Ready(Some(bytes)) => Async::Ready(Some(bytes)),
Async::Ready(None) => {
self.eof = true;
match payload.readline()? {
Async::NotReady => Async::NotReady,
Async::Ready(None) => Async::Ready(None),
Async::Ready(Some(line)) => {
match payload.readline() {
None => Async::Ready(None),
Some(line) => {
if line.as_ref() != b"\r\n" {
log::warn!("multipart field did not read all the data or it is malformed");
}
@ -711,14 +703,86 @@ impl Drop for Safety {
}
}
/// Payload buffer
struct PayloadBuffer {
eof: bool,
buf: BytesMut,
stream: Box<dyn Stream<Item = Bytes, Error = PayloadError>>,
}
impl PayloadBuffer {
/// Create new `PayloadBuffer` instance
fn new<S>(stream: S) -> Self
where
S: Stream<Item = Bytes, Error = PayloadError> + 'static,
{
PayloadBuffer {
eof: false,
buf: BytesMut::new(),
stream: Box::new(stream),
}
}
fn poll_stream(&mut self) -> Result<(), PayloadError> {
loop {
match self.stream.poll()? {
Async::Ready(Some(data)) => self.buf.extend_from_slice(&data),
Async::Ready(None) => {
self.eof = true;
return Ok(());
}
Async::NotReady => return Ok(()),
}
}
}
/// Read exact number of bytes
#[inline]
fn read_exact(&mut self, size: usize) -> Option<Bytes> {
if size <= self.buf.len() {
Some(self.buf.split_to(size).freeze())
} else {
None
}
}
fn read_max(&mut self, size: u64) -> Option<Bytes> {
if !self.buf.is_empty() {
let size = std::cmp::min(self.buf.len() as u64, size) as usize;
Some(self.buf.split_to(size).freeze())
} else {
None
}
}
/// Read until specified ending
pub fn read_until(&mut self, line: &[u8]) -> Option<Bytes> {
twoway::find_bytes(&self.buf, line)
.map(|idx| self.buf.split_to(idx + line.len()).freeze())
}
/// Read bytes until new line delimiter
pub fn readline(&mut self) -> Option<Bytes> {
self.read_until(b"\n")
}
/// Put unprocessed data back to the buffer
pub fn unprocessed(&mut self, data: Bytes) {
let buf = BytesMut::from(data);
let buf = std::mem::replace(&mut self.buf, buf);
self.buf.extend_from_slice(&buf);
}
}
#[cfg(test)]
mod tests {
use actix_http::h1::{Payload, PayloadWriter};
use bytes::Bytes;
use futures::unsync::mpsc;
use super::*;
use crate::http::header::{DispositionParam, DispositionType};
use crate::test::run_on;
use actix_web::http::header::{DispositionParam, DispositionType};
use actix_web::test::run_on;
#[test]
fn test_boundary() {
@ -799,9 +863,9 @@ mod tests {
);
let mut multipart = Multipart::new(&headers, payload);
match multipart.poll() {
Ok(Async::Ready(Some(item))) => match item {
MultipartItem::Field(mut field) => {
match multipart.poll().unwrap() {
Async::Ready(Some(item)) => match item {
Item::Field(mut field) => {
{
let cd = field.content_disposition().unwrap();
assert_eq!(cd.disposition, DispositionType::FormData);
@ -813,12 +877,12 @@ mod tests {
assert_eq!(field.content_type().type_(), mime::TEXT);
assert_eq!(field.content_type().subtype(), mime::PLAIN);
match field.poll() {
Ok(Async::Ready(Some(chunk))) => assert_eq!(chunk, "test"),
match field.poll().unwrap() {
Async::Ready(Some(chunk)) => assert_eq!(chunk, "test"),
_ => unreachable!(),
}
match field.poll() {
Ok(Async::Ready(None)) => (),
match field.poll().unwrap() {
Async::Ready(None) => (),
_ => unreachable!(),
}
}
@ -827,9 +891,9 @@ mod tests {
_ => unreachable!(),
}
match multipart.poll() {
Ok(Async::Ready(Some(item))) => match item {
MultipartItem::Field(mut field) => {
match multipart.poll().unwrap() {
Async::Ready(Some(item)) => match item {
Item::Field(mut field) => {
assert_eq!(field.content_type().type_(), mime::TEXT);
assert_eq!(field.content_type().subtype(), mime::PLAIN);
@ -847,10 +911,110 @@ mod tests {
_ => unreachable!(),
}
match multipart.poll() {
Ok(Async::Ready(None)) => (),
match multipart.poll().unwrap() {
Async::Ready(None) => (),
_ => unreachable!(),
}
});
}
#[test]
fn test_basic() {
run_on(|| {
let (_, payload) = Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(payload.buf.len(), 0);
payload.poll_stream().unwrap();
assert_eq!(None, payload.read_max(1));
})
}
#[test]
fn test_eof() {
run_on(|| {
let (mut sender, payload) = Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(None, payload.read_max(4));
sender.feed_data(Bytes::from("data"));
sender.feed_eof();
payload.poll_stream().unwrap();
assert_eq!(Some(Bytes::from("data")), payload.read_max(4));
assert_eq!(payload.buf.len(), 0);
assert_eq!(None, payload.read_max(1));
assert!(payload.eof);
})
}
#[test]
fn test_err() {
run_on(|| {
let (mut sender, payload) = Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(None, payload.read_max(1));
sender.set_error(PayloadError::Incomplete(None));
payload.poll_stream().err().unwrap();
})
}
#[test]
fn test_readmax() {
run_on(|| {
let (mut sender, payload) = Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
sender.feed_data(Bytes::from("line1"));
sender.feed_data(Bytes::from("line2"));
payload.poll_stream().unwrap();
assert_eq!(payload.buf.len(), 10);
assert_eq!(Some(Bytes::from("line1")), payload.read_max(5));
assert_eq!(payload.buf.len(), 5);
assert_eq!(Some(Bytes::from("line2")), payload.read_max(5));
assert_eq!(payload.buf.len(), 0);
})
}
#[test]
fn test_readexactly() {
run_on(|| {
let (mut sender, payload) = Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(None, payload.read_exact(2));
sender.feed_data(Bytes::from("line1"));
sender.feed_data(Bytes::from("line2"));
payload.poll_stream().unwrap();
assert_eq!(Some(Bytes::from_static(b"li")), payload.read_exact(2));
assert_eq!(payload.buf.len(), 8);
assert_eq!(Some(Bytes::from_static(b"ne1l")), payload.read_exact(4));
assert_eq!(payload.buf.len(), 4);
})
}
#[test]
fn test_readuntil() {
run_on(|| {
let (mut sender, payload) = Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(None, payload.read_until(b"ne"));
sender.feed_data(Bytes::from("line1"));
sender.feed_data(Bytes::from("line2"));
payload.poll_stream().unwrap();
assert_eq!(Some(Bytes::from("line")), payload.read_until(b"ne"));
assert_eq!(payload.buf.len(), 6);
assert_eq!(Some(Bytes::from("1line2")), payload.read_until(b"2"));
assert_eq!(payload.buf.len(), 0);
})
}
}

View File

@ -1,5 +1,9 @@
# Changes
## [0.1.0-alpha.3] - 2019-04-02
* Update actix-web
## [0.1.0-alpha.2] - 2019-03-29
* Update actix-web

View File

@ -1,6 +1,6 @@
[package]
name = "actix-session"
version = "0.1.0-alpha.2"
version = "0.1.0-alpha.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Session for actix web framework."
readme = "README.md"
@ -24,7 +24,7 @@ default = ["cookie-session"]
cookie-session = ["actix-web/secure-cookies"]
[dependencies]
actix-web = "1.0.0-alpha.2"
actix-web = "1.0.0-alpha.3"
actix-service = "0.3.4"
bytes = "0.4"
derive_more = "0.14"

View File

@ -333,6 +333,7 @@ mod tests {
let request = test::TestRequest::get().to_request();
let response = test::block_on(app.call(request)).unwrap();
assert!(response
.response()
.cookies()
.find(|c| c.name() == "actix-session")
.is_some());
@ -352,6 +353,7 @@ mod tests {
let request = test::TestRequest::get().to_request();
let response = test::block_on(app.call(request)).unwrap();
assert!(response
.response()
.cookies()
.find(|c| c.name() == "actix-session")
.is_some());

View File

@ -190,7 +190,7 @@ mod tests {
#[test]
fn session() {
let mut req = test::TestRequest::default().to_service();
let mut req = test::TestRequest::default().to_srv_request();
Session::set_session(
vec![("key".to_string(), "\"value\"".to_string())].into_iter(),

View File

@ -1,5 +1,9 @@
# Changes
## [0.1.0-alpha.3] - 2019-04-02
* Update actix-http and actix-web
## [0.1.0-alpha.2] - 2019-03-29
* Update actix-http and actix-web

View File

@ -1,6 +1,6 @@
[package]
name = "actix-web-actors"
version = "1.0.0-alpha.2"
version = "1.0.0-alpha.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix actors support for actix web framework."
readme = "README.md"
@ -19,12 +19,12 @@ path = "src/lib.rs"
[dependencies]
actix = "0.8.0-alpha.2"
actix-web = "1.0.0-alpha.2"
actix-http = "0.1.0-alpha.2"
actix-web = "1.0.0-alpha.3"
actix-http = "0.1.0-alpha.3"
actix-codec = "0.1.2"
bytes = "0.4"
futures = "0.1.25"
[dev-dependencies]
env_logger = "0.6"
actix-http-test = { version = "0.1.0-alpha.2", features=["ssl"] }
actix-http-test = { version = "0.1.0-alpha.3", features=["ssl"] }

View File

@ -20,7 +20,7 @@ pub use actix_http::ws::{
use actix_web::dev::HttpResponseBuilder;
use actix_web::error::{Error, ErrorInternalServerError, PayloadError};
use actix_web::http::{header, Method, StatusCode};
use actix_web::{HttpMessage, HttpRequest, HttpResponse};
use actix_web::{HttpRequest, HttpResponse};
use bytes::{Bytes, BytesMut};
use futures::sync::oneshot::Sender;
use futures::{Async, Future, Poll, Stream};
@ -64,7 +64,7 @@ pub fn handshake(req: &HttpRequest) -> Result<HttpResponseBuilder, HandshakeErro
}
// Upgrade connection
if !req.upgrade() {
if !req.head().upgrade() {
return Err(HandshakeError::NoConnectionUpgrade);
}

View File

@ -1,12 +1,9 @@
# Changes
## [0.1.0-alpha.3] - 2019-04-xx
## [0.1.0-alpha.3] - 2019-04-02
### Added
* Added `Deref<Target = RequestHead>` for `ClientRequest`.
* Export `MessageBody` type
* `ClientResponse::json()` - Loads and parse `application/json` encoded body
@ -14,12 +11,12 @@
### Changed
* Use non-consuming builder pattern for `ClientRequest`.
* `ClientRequest::json()` accepts reference instead of object.
* `ClientResponse::body()` does not consume response object.
* Renamed `ClientRequest::close_connection()` to `ClientRequest::force_close()`
## [0.1.0-alpha.2] - 2019-03-29

View File

@ -38,7 +38,7 @@ flate2-rust = ["actix-http/flate2-rust"]
[dependencies]
actix-codec = "0.1.1"
actix-service = "0.3.4"
actix-http = "0.1.0-alpa.2"
actix-http = "0.1.0-alpa.3"
base64 = "0.10.1"
bytes = "0.4"
derive_more = "0.14"
@ -55,9 +55,9 @@ openssl = { version="0.10", optional = true }
[dev-dependencies]
actix-rt = "0.2.2"
actix-web = { version = "1.0.0-alpha.2", features=["ssl"] }
actix-http = { version = "0.1.0-alpa.2", features=["ssl"] }
actix-http-test = { version = "0.1.0-alpha.2", features=["ssl"] }
actix-web = { version = "1.0.0-alpha.3", features=["ssl"] }
actix-http = { version = "0.1.0-alpa.3", features=["ssl"] }
actix-http-test = { version = "0.1.0-alpha.3", features=["ssl"] }
actix-utils = "0.3.4"
actix-server = { version = "0.4.1", features=["ssl"] }
brotli2 = { version="0.3.2" }

View File

@ -54,9 +54,6 @@ impl From<HttpError> for WsClientError {
/// A set of errors that can occur during parsing json payloads
#[derive(Debug, Display, From)]
pub enum JsonPayloadError {
/// Payload size is bigger than allowed. (default: 32kB)
#[display(fmt = "Json payload size is bigger than allowed.")]
Overflow,
/// Content type error
#[display(fmt = "Content type error")]
ContentType,

View File

@ -105,7 +105,7 @@ impl Client {
let mut req = ClientRequest::new(method, url, self.0.clone());
for (key, value) in &self.0.headers {
req.set_header_if_none(key.clone(), value.clone());
req = req.set_header_if_none(key.clone(), value.clone());
}
req
}
@ -120,7 +120,7 @@ impl Client {
{
let mut req = self.request(head.method.clone(), url);
for (key, value) in &head.headers {
req.set_header_if_none(key.clone(), value.clone());
req = req.set_header_if_none(key.clone(), value.clone());
}
req
}

View File

@ -17,8 +17,8 @@ use actix_http::cookie::{Cookie, CookieJar};
use actix_http::encoding::Decoder;
use actix_http::http::header::{self, ContentEncoding, Header, IntoHeaderValue};
use actix_http::http::{
uri, ConnectionType, Error as HttpError, HeaderName, HeaderValue, HttpTryFrom,
Method, Uri, Version,
uri, ConnectionType, Error as HttpError, HeaderMap, HeaderName, HeaderValue,
HttpTryFrom, Method, Uri, Version,
};
use actix_http::{Error, Payload, RequestHead};
@ -58,7 +58,7 @@ const HTTPS_ENCODING: &str = "gzip, deflate";
/// }
/// ```
pub struct ClientRequest {
pub(crate) head: Option<RequestHead>,
pub(crate) head: RequestHead,
err: Option<HttpError>,
cookies: Option<CookieJar>,
default_headers: bool,
@ -73,40 +73,36 @@ impl ClientRequest {
where
Uri: HttpTryFrom<U>,
{
let mut req = ClientRequest {
ClientRequest {
config,
head: Some(RequestHead::default()),
head: RequestHead::default(),
err: None,
cookies: None,
timeout: None,
default_headers: true,
response_decompress: true,
};
req.method(method).uri(uri);
req
}
.method(method)
.uri(uri)
}
/// Set HTTP URI of request.
#[inline]
pub fn uri<U>(&mut self, uri: U) -> &mut Self
pub fn uri<U>(mut self, uri: U) -> Self
where
Uri: HttpTryFrom<U>,
{
if let Some(head) = parts(&mut self.head, &self.err) {
match Uri::try_from(uri) {
Ok(uri) => head.uri = uri,
Err(e) => self.err = Some(e.into()),
}
match Uri::try_from(uri) {
Ok(uri) => self.head.uri = uri,
Err(e) => self.err = Some(e.into()),
}
self
}
/// Set HTTP method of this request.
#[inline]
pub fn method(&mut self, method: Method) -> &mut Self {
if let Some(head) = parts(&mut self.head, &self.err) {
head.method = method;
}
pub fn method(mut self, method: Method) -> Self {
self.head.method = method;
self
}
@ -115,13 +111,23 @@ impl ClientRequest {
///
/// By default requests's HTTP version depends on network stream
#[inline]
pub fn version(&mut self, version: Version) -> &mut Self {
if let Some(head) = parts(&mut self.head, &self.err) {
head.version = version;
}
pub fn version(mut self, version: Version) -> Self {
self.head.version = version;
self
}
#[inline]
/// Returns request's headers.
pub fn headers(&self) -> &HeaderMap {
&self.head.headers
}
#[inline]
/// Returns request's mutable headers.
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.head.headers
}
/// Set a header.
///
/// ```rust
@ -135,14 +141,12 @@ impl ClientRequest {
/// # }));
/// }
/// ```
pub fn set<H: Header>(&mut self, hdr: H) -> &mut Self {
if let Some(head) = parts(&mut self.head, &self.err) {
match hdr.try_into() {
Ok(value) => {
head.headers.insert(H::name(), value);
}
Err(e) => self.err = Some(e.into()),
pub fn set<H: Header>(mut self, hdr: H) -> Self {
match hdr.try_into() {
Ok(value) => {
self.head.headers.insert(H::name(), value);
}
Err(e) => self.err = Some(e.into()),
}
self
}
@ -165,106 +169,96 @@ impl ClientRequest {
/// # }));
/// }
/// ```
pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
pub fn header<K, V>(mut self, key: K, value: V) -> Self
where
HeaderName: HttpTryFrom<K>,
V: IntoHeaderValue,
{
if let Some(head) = parts(&mut self.head, &self.err) {
match HeaderName::try_from(key) {
Ok(key) => match value.try_into() {
Ok(value) => {
head.headers.append(key, value);
}
Err(e) => self.err = Some(e.into()),
},
match HeaderName::try_from(key) {
Ok(key) => match value.try_into() {
Ok(value) => {
let _ = self.head.headers.append(key, value);
}
Err(e) => self.err = Some(e.into()),
}
},
Err(e) => self.err = Some(e.into()),
}
self
}
/// Insert a header, replaces existing header.
pub fn set_header<K, V>(&mut self, key: K, value: V) -> &mut Self
pub fn set_header<K, V>(mut self, key: K, value: V) -> Self
where
HeaderName: HttpTryFrom<K>,
V: IntoHeaderValue,
{
if let Some(head) = parts(&mut self.head, &self.err) {
match HeaderName::try_from(key) {
Ok(key) => match value.try_into() {
Ok(value) => {
head.headers.insert(key, value);
}
Err(e) => self.err = Some(e.into()),
},
match HeaderName::try_from(key) {
Ok(key) => match value.try_into() {
Ok(value) => {
let _ = self.head.headers.insert(key, value);
}
Err(e) => self.err = Some(e.into()),
}
},
Err(e) => self.err = Some(e.into()),
}
self
}
/// Insert a header only if it is not yet set.
pub fn set_header_if_none<K, V>(&mut self, key: K, value: V) -> &mut Self
pub fn set_header_if_none<K, V>(mut self, key: K, value: V) -> Self
where
HeaderName: HttpTryFrom<K>,
V: IntoHeaderValue,
{
if let Some(head) = parts(&mut self.head, &self.err) {
match HeaderName::try_from(key) {
Ok(key) => {
if !head.headers.contains_key(&key) {
match value.try_into() {
Ok(value) => {
head.headers.insert(key, value);
}
Err(e) => self.err = Some(e.into()),
match HeaderName::try_from(key) {
Ok(key) => {
if !self.head.headers.contains_key(&key) {
match value.try_into() {
Ok(value) => {
let _ = self.head.headers.insert(key, value);
}
Err(e) => self.err = Some(e.into()),
}
}
Err(e) => self.err = Some(e.into()),
}
Err(e) => self.err = Some(e.into()),
}
self
}
/// Close connection instead of returning it back to connections pool.
/// Force close connection instead of returning it back to connections pool.
/// This setting affect only http/1 connections.
#[inline]
pub fn close_connection(&mut self) -> &mut Self {
if let Some(head) = parts(&mut self.head, &self.err) {
head.set_connection_type(ConnectionType::Close);
}
pub fn force_close(mut self) -> Self {
self.head.set_connection_type(ConnectionType::Close);
self
}
/// Set request's content type
#[inline]
pub fn content_type<V>(&mut self, value: V) -> &mut Self
pub fn content_type<V>(mut self, value: V) -> Self
where
HeaderValue: HttpTryFrom<V>,
{
if let Some(head) = parts(&mut self.head, &self.err) {
match HeaderValue::try_from(value) {
Ok(value) => {
let _ = head.headers.insert(header::CONTENT_TYPE, value);
}
Err(e) => self.err = Some(e.into()),
match HeaderValue::try_from(value) {
Ok(value) => {
let _ = self.head.headers.insert(header::CONTENT_TYPE, value);
}
Err(e) => self.err = Some(e.into()),
}
self
}
/// Set content length
#[inline]
pub fn content_length(&mut self, len: u64) -> &mut Self {
pub fn content_length(self, len: u64) -> Self {
let mut wrt = BytesMut::new().writer();
let _ = write!(wrt, "{}", len);
self.header(header::CONTENT_LENGTH, wrt.get_mut().take().freeze())
}
/// Set HTTP basic authorization header
pub fn basic_auth<U>(&mut self, username: U, password: Option<&str>) -> &mut Self
pub fn basic_auth<U>(self, username: U, password: Option<&str>) -> Self
where
U: fmt::Display,
{
@ -279,7 +273,7 @@ impl ClientRequest {
}
/// Set HTTP bearer authentication header
pub fn bearer_auth<T>(&mut self, token: T) -> &mut Self
pub fn bearer_auth<T>(self, token: T) -> Self
where
T: fmt::Display,
{
@ -311,7 +305,7 @@ impl ClientRequest {
/// }));
/// }
/// ```
pub fn cookie<'c>(&mut self, cookie: Cookie<'c>) -> &mut Self {
pub fn cookie<'c>(mut self, cookie: Cookie<'c>) -> Self {
if self.cookies.is_none() {
let mut jar = CookieJar::new();
jar.add(cookie.into_owned());
@ -324,13 +318,13 @@ impl ClientRequest {
/// Do not add default request headers.
/// By default `Date` and `User-Agent` headers are set.
pub fn no_default_headers(&mut self) -> &mut Self {
pub fn no_default_headers(mut self) -> Self {
self.default_headers = false;
self
}
/// Disable automatic decompress of response's body
pub fn no_decompress(&mut self) -> &mut Self {
pub fn no_decompress(mut self) -> Self {
self.response_decompress = false;
self
}
@ -339,38 +333,38 @@ impl ClientRequest {
///
/// Request timeout is the total time before a response must be received.
/// Default value is 5 seconds.
pub fn timeout(&mut self, timeout: Duration) -> &mut Self {
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
/// This method calls provided closure with builder reference if
/// value is `true`.
pub fn if_true<F>(&mut self, value: bool, f: F) -> &mut Self
pub fn if_true<F>(mut self, value: bool, f: F) -> Self
where
F: FnOnce(&mut ClientRequest),
{
if value {
f(self);
f(&mut self);
}
self
}
/// This method calls provided closure with builder reference if
/// value is `Some`.
pub fn if_some<T, F>(&mut self, value: Option<T>, f: F) -> &mut Self
pub fn if_some<T, F>(mut self, value: Option<T>, f: F) -> Self
where
F: FnOnce(T, &mut ClientRequest),
{
if let Some(val) = value {
f(val, self);
f(val, &mut self);
}
self
}
/// Complete request construction and send body.
pub fn send_body<B>(
&mut self,
mut self,
body: B,
) -> impl Future<
Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>,
@ -383,10 +377,8 @@ impl ClientRequest {
return Either::A(err(e.into()));
}
let mut head = self.head.take().expect("cannot reuse response builder");
// validate uri
let uri = &head.uri;
let uri = &self.head.uri;
if uri.host().is_none() {
return Either::A(err(InvalidUrl::MissingHost.into()));
} else if uri.scheme_part().is_none() {
@ -403,18 +395,18 @@ impl ClientRequest {
// set default headers
if self.default_headers {
// set request host header
if let Some(host) = head.uri.host() {
if !head.headers.contains_key(header::HOST) {
if let Some(host) = self.head.uri.host() {
if !self.head.headers.contains_key(header::HOST) {
let mut wrt = BytesMut::with_capacity(host.len() + 5).writer();
let _ = match head.uri.port_u16() {
let _ = match self.head.uri.port_u16() {
None | Some(80) | Some(443) => write!(wrt, "{}", host),
Some(port) => write!(wrt, "{}:{}", host, port),
};
match wrt.get_mut().take().freeze().try_into() {
Ok(value) => {
head.headers.insert(header::HOST, value);
self.head.headers.insert(header::HOST, value);
}
Err(e) => return Either::A(err(HttpError::from(e).into())),
}
@ -422,32 +414,11 @@ impl ClientRequest {
}
// user agent
self.set_header_if_none(
header::USER_AGENT,
concat!("awc/", env!("CARGO_PKG_VERSION")),
);
}
// enable br only for https
let https = head
.uri
.scheme_part()
.map(|s| s == &uri::Scheme::HTTPS)
.unwrap_or(true);
#[cfg(any(
feature = "brotli",
feature = "flate2-zlib",
feature = "flate2-rust"
))]
{
if https {
self.set_header_if_none(header::ACCEPT_ENCODING, HTTPS_ENCODING);
} else {
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
{
self.set_header_if_none(header::ACCEPT_ENCODING, "gzip, deflate");
}
if !self.head.headers.contains_key(&header::USER_AGENT) {
self.head.headers.insert(
header::USER_AGENT,
HeaderValue::from_static(concat!("awc/", env!("CARGO_PKG_VERSION"))),
);
}
}
@ -459,14 +430,43 @@ impl ClientRequest {
let value = percent_encode(c.value().as_bytes(), USERINFO_ENCODE_SET);
let _ = write!(&mut cookie, "; {}={}", name, value);
}
head.headers.insert(
self.head.headers.insert(
header::COOKIE,
HeaderValue::from_str(&cookie.as_str()[2..]).unwrap(),
);
}
let config = self.config.as_ref();
let response_decompress = self.response_decompress;
let slf = self;
// enable br only for https
#[cfg(any(
feature = "brotli",
feature = "flate2-zlib",
feature = "flate2-rust"
))]
let slf = {
let https = slf
.head
.uri
.scheme_part()
.map(|s| s == &uri::Scheme::HTTPS)
.unwrap_or(true);
if https {
slf.set_header_if_none(header::ACCEPT_ENCODING, HTTPS_ENCODING)
} else {
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
{
slf.set_header_if_none(header::ACCEPT_ENCODING, "gzip, deflate")
}
#[cfg(not(any(feature = "flate2-zlib", feature = "flate2-rust")))]
slf
}
};
let head = slf.head;
let config = slf.config.as_ref();
let response_decompress = slf.response_decompress;
let fut = config
.connector
@ -483,7 +483,7 @@ impl ClientRequest {
});
// set request timeout
if let Some(timeout) = self.timeout.or_else(|| config.timeout.clone()) {
if let Some(timeout) = slf.timeout.or_else(|| config.timeout.clone()) {
Either::B(Either::A(Timeout::new(fut, timeout).map_err(|e| {
if let Some(e) = e.into_inner() {
e
@ -498,7 +498,7 @@ impl ClientRequest {
/// Set a JSON body and generate `ClientRequest`
pub fn send_json<T: Serialize>(
&mut self,
self,
value: &T,
) -> impl Future<
Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>,
@ -510,16 +510,16 @@ impl ClientRequest {
};
// set content-type
self.set_header_if_none(header::CONTENT_TYPE, "application/json");
let slf = self.set_header_if_none(header::CONTENT_TYPE, "application/json");
Either::B(self.send_body(Body::Bytes(Bytes::from(body))))
Either::B(slf.send_body(Body::Bytes(Bytes::from(body))))
}
/// Set a urlencoded body and generate `ClientRequest`
///
/// `ClientRequestBuilder` can not be used after this call.
pub fn send_form<T: Serialize>(
&mut self,
self,
value: &T,
) -> impl Future<
Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>,
@ -531,17 +531,17 @@ impl ClientRequest {
};
// set content-type
self.set_header_if_none(
let slf = self.set_header_if_none(
header::CONTENT_TYPE,
"application/x-www-form-urlencoded",
);
Either::B(self.send_body(Body::Bytes(Bytes::from(body))))
Either::B(slf.send_body(Body::Bytes(Bytes::from(body))))
}
/// Set an streaming body and generate `ClientRequest`.
pub fn send_stream<S, E>(
&mut self,
self,
stream: S,
) -> impl Future<
Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>,
@ -556,7 +556,7 @@ impl ClientRequest {
/// Set an empty body and generate `ClientRequest`.
pub fn send(
&mut self,
self,
) -> impl Future<
Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>,
Error = SendRequestError,
@ -565,48 +565,21 @@ impl ClientRequest {
}
}
impl std::ops::Deref for ClientRequest {
type Target = RequestHead;
fn deref(&self) -> &RequestHead {
self.head.as_ref().expect("cannot reuse response builder")
}
}
impl std::ops::DerefMut for ClientRequest {
fn deref_mut(&mut self) -> &mut RequestHead {
self.head.as_mut().expect("cannot reuse response builder")
}
}
impl fmt::Debug for ClientRequest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let head = self.head.as_ref().expect("cannot reuse response builder");
writeln!(
f,
"\nClientRequest {:?} {}:{}",
head.version, head.method, head.uri
self.head.version, self.head.method, self.head.uri
)?;
writeln!(f, " headers:")?;
for (key, val) in head.headers.iter() {
for (key, val) in self.head.headers.iter() {
writeln!(f, " {:?}: {:?}", key, val)?;
}
Ok(())
}
}
#[inline]
fn parts<'a>(
parts: &'a mut Option<RequestHead>,
err: &Option<HttpError>,
) -> Option<&'a mut RequestHead> {
if err.is_some() {
return None;
}
parts.as_mut()
}
#[cfg(test)]
mod tests {
use super::*;
@ -615,8 +588,7 @@ mod tests {
#[test]
fn test_debug() {
test::run_on(|| {
let mut request = Client::new().get("/");
request.header("x-test", "111");
let request = Client::new().get("/").header("x-test", "111");
let repr = format!("{:?}", request);
assert!(repr.contains("ClientRequest"));
assert!(repr.contains("x-test"));
@ -633,8 +605,6 @@ mod tests {
assert_eq!(
req.head
.as_ref()
.unwrap()
.headers
.get(header::CONTENT_TYPE)
.unwrap()
@ -648,16 +618,14 @@ mod tests {
#[test]
fn test_client_header_override() {
test::run_on(|| {
let mut req = Client::build()
let req = Client::build()
.header(header::CONTENT_TYPE, "111")
.finish()
.get("/");
req.set_header(header::CONTENT_TYPE, "222");
.get("/")
.set_header(header::CONTENT_TYPE, "222");
assert_eq!(
req.head
.as_ref()
.unwrap()
.headers
.get(header::CONTENT_TYPE)
.unwrap()
@ -671,12 +639,11 @@ mod tests {
#[test]
fn client_basic_auth() {
test::run_on(|| {
let mut req = Client::new().get("/");
req.basic_auth("username", Some("password"));
let req = Client::new()
.get("/")
.basic_auth("username", Some("password"));
assert_eq!(
req.head
.as_ref()
.unwrap()
.headers
.get(header::AUTHORIZATION)
.unwrap()
@ -685,12 +652,9 @@ mod tests {
"Basic dXNlcm5hbWU6cGFzc3dvcmQ="
);
let mut req = Client::new().get("/");
req.basic_auth("username", None);
let req = Client::new().get("/").basic_auth("username", None);
assert_eq!(
req.head
.as_ref()
.unwrap()
.headers
.get(header::AUTHORIZATION)
.unwrap()
@ -704,12 +668,9 @@ mod tests {
#[test]
fn client_bearer_auth() {
test::run_on(|| {
let mut req = Client::new().get("/");
req.bearer_auth("someS3cr3tAutht0k3n");
let req = Client::new().get("/").bearer_auth("someS3cr3tAutht0k3n");
assert_eq!(
req.head
.as_ref()
.unwrap()
.headers
.get(header::AUTHORIZATION)
.unwrap()

View File

@ -1,8 +1,9 @@
use std::cell::{Ref, RefMut};
use std::fmt;
use std::marker::PhantomData;
use bytes::{Bytes, BytesMut};
use futures::{Future, Poll, Stream};
use futures::{Async, Future, Poll, Stream};
use actix_http::cookie::Cookie;
use actix_http::error::{CookieParseError, PayloadError};
@ -82,7 +83,7 @@ impl<S> ClientResponse<S> {
}
#[inline]
/// Returns Request's headers.
/// Returns request's headers.
pub fn headers(&self) -> &HeaderMap {
&self.head().headers
}
@ -103,7 +104,7 @@ impl<S> ClientResponse<S> {
impl<S> ClientResponse<S>
where
S: Stream<Item = Bytes, Error = PayloadError> + 'static,
S: Stream<Item = Bytes, Error = PayloadError>,
{
/// Loads http response's body.
pub fn body(&mut self) -> MessageBody<S> {
@ -147,16 +148,14 @@ impl<S> fmt::Debug for ClientResponse<S> {
/// Future that resolves to a complete http message body.
pub struct MessageBody<S> {
limit: usize,
length: Option<usize>,
stream: Option<Payload<S>>,
err: Option<PayloadError>,
fut: Option<Box<Future<Item = Bytes, Error = PayloadError>>>,
fut: Option<ReadBody<S>>,
}
impl<S> MessageBody<S>
where
S: Stream<Item = Bytes, Error = PayloadError> + 'static,
S: Stream<Item = Bytes, Error = PayloadError>,
{
/// Create `MessageBody` for request.
pub fn new(res: &mut ClientResponse<S>) -> MessageBody<S> {
@ -174,24 +173,22 @@ where
}
MessageBody {
limit: 262_144,
length: len,
stream: Some(res.take_payload()),
fut: None,
err: None,
fut: Some(ReadBody::new(res.take_payload(), 262_144)),
}
}
/// Change max size of payload. By default max size is 256Kb
pub fn limit(mut self, limit: usize) -> Self {
self.limit = limit;
if let Some(ref mut fut) = self.fut {
fut.limit = limit;
}
self
}
fn err(e: PayloadError) -> Self {
MessageBody {
stream: None,
limit: 262_144,
fut: None,
err: Some(e),
length: None,
@ -201,44 +198,23 @@ where
impl<S> Future for MessageBody<S>
where
S: Stream<Item = Bytes, Error = PayloadError> + 'static,
S: Stream<Item = Bytes, Error = PayloadError>,
{
type Item = Bytes;
type Error = PayloadError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
if let Some(ref mut fut) = self.fut {
return fut.poll();
}
if let Some(err) = self.err.take() {
return Err(err);
}
if let Some(len) = self.length.take() {
if len > self.limit {
if len > self.fut.as_ref().unwrap().limit {
return Err(PayloadError::Overflow);
}
}
// future
let limit = self.limit;
self.fut = Some(Box::new(
self.stream
.take()
.expect("Can not be used second time")
.from_err()
.fold(BytesMut::with_capacity(8192), move |mut body, chunk| {
if (body.len() + chunk.len()) > limit {
Err(PayloadError::Overflow)
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.map(|body| body.freeze()),
));
self.poll()
self.fut.as_mut().unwrap().poll()
}
}
@ -249,16 +225,15 @@ where
/// * content type is not `application/json`
/// * content length is greater than 64k
pub struct JsonBody<S, U> {
limit: usize,
length: Option<usize>,
stream: Payload<S>,
err: Option<JsonPayloadError>,
fut: Option<Box<Future<Item = U, Error = JsonPayloadError>>>,
fut: Option<ReadBody<S>>,
_t: PhantomData<U>,
}
impl<S, U> JsonBody<S, U>
where
S: Stream<Item = Bytes, Error = PayloadError> + 'static,
S: Stream<Item = Bytes, Error = PayloadError>,
U: DeserializeOwned,
{
/// Create `JsonBody` for request.
@ -271,11 +246,10 @@ where
};
if !json {
return JsonBody {
limit: 65536,
length: None,
stream: Payload::None,
fut: None,
err: Some(JsonPayloadError::ContentType),
_t: PhantomData,
};
}
@ -289,58 +263,84 @@ where
}
JsonBody {
limit: 65536,
length: len,
stream: req.take_payload(),
fut: None,
err: None,
fut: Some(ReadBody::new(req.take_payload(), 65536)),
_t: PhantomData,
}
}
/// Change max size of payload. By default max size is 64Kb
pub fn limit(mut self, limit: usize) -> Self {
self.limit = limit;
if let Some(ref mut fut) = self.fut {
fut.limit = limit;
}
self
}
}
impl<T, U> Future for JsonBody<T, U>
where
T: Stream<Item = Bytes, Error = PayloadError> + 'static,
T: Stream<Item = Bytes, Error = PayloadError>,
U: DeserializeOwned + 'static,
{
type Item = U;
type Error = JsonPayloadError;
fn poll(&mut self) -> Poll<U, JsonPayloadError> {
if let Some(ref mut fut) = self.fut {
return fut.poll();
}
if let Some(err) = self.err.take() {
return Err(err);
}
let limit = self.limit;
if let Some(len) = self.length.take() {
if len > limit {
return Err(JsonPayloadError::Overflow);
if len > self.fut.as_ref().unwrap().limit {
return Err(JsonPayloadError::Payload(PayloadError::Overflow));
}
}
let fut = std::mem::replace(&mut self.stream, Payload::None)
.from_err()
.fold(BytesMut::with_capacity(8192), move |mut body, chunk| {
if (body.len() + chunk.len()) > limit {
Err(JsonPayloadError::Overflow)
} else {
body.extend_from_slice(&chunk);
Ok(body)
let body = futures::try_ready!(self.fut.as_mut().unwrap().poll());
Ok(Async::Ready(serde_json::from_slice::<U>(&body)?))
}
}
struct ReadBody<S> {
stream: Payload<S>,
buf: BytesMut,
limit: usize,
}
impl<S> ReadBody<S> {
fn new(stream: Payload<S>, limit: usize) -> Self {
Self {
stream,
buf: BytesMut::with_capacity(std::cmp::min(limit, 32768)),
limit,
}
}
}
impl<S> Future for ReadBody<S>
where
S: Stream<Item = Bytes, Error = PayloadError>,
{
type Item = Bytes;
type Error = PayloadError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
return match self.stream.poll()? {
Async::Ready(Some(chunk)) => {
if (self.buf.len() + chunk.len()) > self.limit {
Err(PayloadError::Overflow)
} else {
self.buf.extend_from_slice(&chunk);
continue;
}
}
})
.and_then(|body| Ok(serde_json::from_slice::<U>(&body)?));
self.fut = Some(Box::new(fut));
self.poll()
Async::Ready(None) => Ok(Async::Ready(self.buf.take().freeze())),
Async::NotReady => Ok(Async::NotReady),
};
}
}
}
@ -391,8 +391,8 @@ mod tests {
fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {
match err {
JsonPayloadError::Overflow => match other {
JsonPayloadError::Overflow => true,
JsonPayloadError::Payload(PayloadError::Overflow) => match other {
JsonPayloadError::Payload(PayloadError::Overflow) => true,
_ => false,
},
JsonPayloadError::ContentType => match other {
@ -430,7 +430,10 @@ mod tests {
.finish();
let json = block_on(JsonBody::<_, MyObject>::new(&mut req).limit(100));
assert!(json_eq(json.err().unwrap(), JsonPayloadError::Overflow));
assert!(json_eq(
json.err().unwrap(),
JsonPayloadError::Payload(PayloadError::Overflow)
));
let mut req = TestResponse::default()
.header(

View File

@ -44,15 +44,15 @@ fn test_simple() {
))
});
let request = srv.get().header("x-test", "111").send();
let response = srv.block_on(request).unwrap();
let request = srv.get("/").header("x-test", "111").send();
let mut response = srv.block_on(request).unwrap();
assert!(response.status().is_success());
// read response
let bytes = srv.block_on(response.body()).unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
let response = srv.block_on(srv.post().send()).unwrap();
let mut response = srv.block_on(srv.post("/").send()).unwrap();
assert!(response.status().is_success());
// read response
@ -114,7 +114,7 @@ fn test_timeout_override() {
// let mut srv =
// test::TestServer::new(|app| app.handler(|_| HttpResponse::Ok().body(STR)));
// let request = srv.get().header("Connection", "close").finish().unwrap();
// let request = srv.get("/").header("Connection", "close").finish().unwrap();
// let response = srv.execute(request.send()).unwrap();
// assert!(response.status().is_success());
// }
@ -128,7 +128,7 @@ fn test_timeout_override() {
// })
// });
// let request = srv.get().uri(srv.url("/?qp=5").as_str()).finish().unwrap();
// let request = srv.get("/").uri(srv.url("/?qp=5").as_str()).finish().unwrap();
// let response = srv.execute(request.send()).unwrap();
// assert!(response.status().is_success());
@ -139,7 +139,7 @@ fn test_timeout_override() {
// let mut srv =
// test::TestServer::new(|app| app.handler(|_| HttpResponse::Ok().body(STR)));
// let request = srv.get().disable_decompress().finish().unwrap();
// let request = srv.get("/").disable_decompress().finish().unwrap();
// let response = srv.execute(request.send()).unwrap();
// assert!(response.status().is_success());
@ -177,7 +177,7 @@ fn test_client_gzip_encoding() {
});
// client request
let response = srv.block_on(srv.post().send()).unwrap();
let mut response = srv.block_on(srv.post("/").send()).unwrap();
assert!(response.status().is_success());
// read response
@ -200,7 +200,7 @@ fn test_client_gzip_encoding_large() {
});
// client request
let response = srv.block_on(srv.post().send()).unwrap();
let mut response = srv.block_on(srv.post("/").send()).unwrap();
assert!(response.status().is_success());
// read response
@ -229,7 +229,7 @@ fn test_client_gzip_encoding_large_random() {
});
// client request
let response = srv.block_on(srv.post().send_body(data.clone())).unwrap();
let mut response = srv.block_on(srv.post("/").send_body(data.clone())).unwrap();
assert!(response.status().is_success());
// read response
@ -253,7 +253,7 @@ fn test_client_brotli_encoding() {
});
// client request
let response = srv.block_on(srv.post().send_body(STR)).unwrap();
let mut response = srv.block_on(srv.post("/").send_body(STR)).unwrap();
assert!(response.status().is_success());
// read response
@ -375,7 +375,7 @@ fn test_client_brotli_encoding() {
// let body = once(Ok(Bytes::from_static(STR.as_ref())));
// let request = srv.get().body(Body::Streaming(Box::new(body))).unwrap();
// let request = srv.get("/").body(Body::Streaming(Box::new(body))).unwrap();
// let response = srv.execute(request.send()).unwrap();
// assert!(response.status().is_success());
@ -395,7 +395,7 @@ fn test_client_brotli_encoding() {
// })
// });
// let request = srv.get().finish().unwrap();
// let request = srv.get("/").finish().unwrap();
// let response = srv.execute(request.send()).unwrap();
// assert!(response.status().is_success());
@ -459,7 +459,7 @@ fn test_client_cookie_handling() {
))
});
let request = srv.get().cookie(cookie1.clone()).cookie(cookie2.clone());
let request = srv.get("/").cookie(cookie1.clone()).cookie(cookie2.clone());
let response = srv.block_on(request.send()).unwrap();
assert!(response.status().is_success());
let c1 = response.cookie("cookie1").expect("Missing cookie1");
@ -472,7 +472,7 @@ fn test_client_cookie_handling() {
// fn test_default_headers() {
// let srv = test::TestServer::new(|app| app.handler(|_| HttpResponse::Ok().body(STR)));
// let request = srv.get().finish().unwrap();
// let request = srv.get("/").finish().unwrap();
// let repr = format!("{:?}", request);
// assert!(repr.contains("\"accept-encoding\": \"gzip, deflate\""));
// assert!(repr.contains(concat!(
@ -482,7 +482,7 @@ fn test_client_cookie_handling() {
// )));
// let request_override = srv
// .get()
// .get("/")
// .header("User-Agent", "test")
// .header("Accept-Encoding", "over_test")
// .finish()
@ -551,7 +551,7 @@ fn client_basic_auth() {
});
// set authorization header to Basic <base64 encoded username:password>
let request = srv.get().basic_auth("username", Some("password"));
let request = srv.get("/").basic_auth("username", Some("password"));
let response = srv.block_on(request.send()).unwrap();
assert!(response.status().is_success());
}
@ -579,7 +579,7 @@ fn client_bearer_auth() {
});
// set authorization header to Bearer <token>
let request = srv.get().bearer_auth("someS3cr3tAutht0k3n");
let request = srv.get("/").bearer_auth("someS3cr3tAutht0k3n");
let response = srv.block_on(request.send()).unwrap();
assert!(response.status().is_success());
}

View File

@ -15,7 +15,7 @@ use bytes::Bytes;
use futures::{IntoFuture, Stream};
use crate::app_service::{AppChain, AppEntry, AppInit, AppRouting, AppRoutingFactory};
use crate::config::{AppConfig, AppConfigInner};
use crate::config::{AppConfig, AppConfigInner, RouterConfig};
use crate::data::{Data, DataFactory};
use crate::dev::{Payload, PayloadStream, ResourceDef};
use crate::error::{Error, PayloadError};
@ -257,6 +257,55 @@ where
}
}
/// Run external configuration as part of the application building
/// process
///
/// This function is useful for moving parts of configuration to a
/// different module or even library. For example,
/// some of the resource's configuration could be moved to different module.
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::{web, middleware, App, HttpResponse};
///
/// // this function could be located in different module
/// fn config<P>(cfg: &mut web::RouterConfig<P>) {
/// cfg.service(web::resource("/test")
/// .route(web::get().to(|| HttpResponse::Ok()))
/// .route(web::head().to(|| HttpResponse::MethodNotAllowed()))
/// );
/// }
///
/// fn main() {
/// let app = App::new()
/// .wrap(middleware::Logger::default())
/// .configure(config) // <- register resources
/// .route("/index.html", web::get().to(|| HttpResponse::Ok()));
/// }
/// ```
pub fn configure<F>(mut self, f: F) -> AppRouter<T, Out, Body, AppEntry<Out>>
where
F: Fn(&mut RouterConfig<Out>),
{
let mut cfg = RouterConfig::new();
f(&mut cfg);
self.data.extend(cfg.data);
let fref = Rc::new(RefCell::new(None));
AppRouter {
chain: self.chain,
default: None,
endpoint: AppEntry::new(fref.clone()),
factory_ref: fref,
data: self.data,
config: self.config,
services: cfg.services,
external: cfg.external,
_t: PhantomData,
}
}
/// Configure route for a specific path.
///
/// This is a simplified version of the `App::service()` method.
@ -382,6 +431,45 @@ where
InitError = (),
>,
{
/// Run external configuration as part of the application building
/// process
///
/// This function is useful for moving parts of configuration to a
/// different module or even library. For example,
/// some of the resource's configuration could be moved to different module.
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::{web, middleware, App, HttpResponse};
///
/// // this function could be located in different module
/// fn config<P>(cfg: &mut web::RouterConfig<P>) {
/// cfg.service(web::resource("/test")
/// .route(web::get().to(|| HttpResponse::Ok()))
/// .route(web::head().to(|| HttpResponse::MethodNotAllowed()))
/// );
/// }
///
/// fn main() {
/// let app = App::new()
/// .wrap(middleware::Logger::default())
/// .configure(config) // <- register resources
/// .route("/index.html", web::get().to(|| HttpResponse::Ok()));
/// }
/// ```
pub fn configure<F>(mut self, f: F) -> Self
where
F: Fn(&mut RouterConfig<P>),
{
let mut cfg = RouterConfig::new();
f(&mut cfg);
self.data.extend(cfg.data);
self.services.extend(cfg.services);
self.external.extend(cfg.external);
self
}
/// Configure route for a specific path.
///
/// This is a simplified version of the `App::service()` method.

View File

@ -5,11 +5,18 @@ use std::rc::Rc;
use actix_http::Extensions;
use actix_router::ResourceDef;
use actix_service::{boxed, IntoNewService, NewService};
use futures::IntoFuture;
use crate::data::{Data, DataFactory};
use crate::error::Error;
use crate::guard::Guard;
use crate::resource::Resource;
use crate::rmap::ResourceMap;
use crate::service::{ServiceRequest, ServiceResponse};
use crate::route::Route;
use crate::service::{
HttpServiceFactory, ServiceFactory, ServiceFactoryWrapper, ServiceRequest,
ServiceResponse,
};
type Guards = Vec<Box<Guard>>;
type HttpNewService<P> =
@ -157,3 +164,140 @@ impl Default for AppConfigInner {
}
}
}
/// Router config. It is used for external configuration.
/// Part of application configuration could be offloaded
/// to set of external methods. This could help with
/// modularization of big application configuration.
pub struct RouterConfig<P: 'static> {
pub(crate) services: Vec<Box<ServiceFactory<P>>>,
pub(crate) data: Vec<Box<DataFactory>>,
pub(crate) external: Vec<ResourceDef>,
}
impl<P: 'static> RouterConfig<P> {
pub(crate) fn new() -> Self {
Self {
services: Vec::new(),
data: Vec::new(),
external: Vec::new(),
}
}
/// Set application data. Applicatin data could be accessed
/// by using `Data<T>` extractor where `T` is data type.
///
/// This is same as `App::data()` method.
pub fn data<S: 'static>(&mut self, data: S) -> &mut Self {
self.data.push(Box::new(Data::new(data)));
self
}
/// Set application data factory. This function is
/// similar to `.data()` but it accepts data factory. Data object get
/// constructed asynchronously during application initialization.
///
/// This is same as `App::data_dactory()` method.
pub fn data_factory<F, R>(&mut self, data: F) -> &mut Self
where
F: Fn() -> R + 'static,
R: IntoFuture + 'static,
R::Error: std::fmt::Debug,
{
self.data.push(Box::new(data));
self
}
/// Configure route for a specific path.
///
/// This is same as `App::route()` method.
pub fn route(&mut self, path: &str, mut route: Route<P>) -> &mut Self {
self.service(
Resource::new(path)
.add_guards(route.take_guards())
.route(route),
)
}
/// Register http service.
///
/// This is same as `App::service()` method.
pub fn service<F>(&mut self, factory: F) -> &mut Self
where
F: HttpServiceFactory<P> + 'static,
{
self.services
.push(Box::new(ServiceFactoryWrapper::new(factory)));
self
}
/// Register an external resource.
///
/// External resources are useful for URL generation purposes only
/// and are never considered for matching at request time. Calls to
/// `HttpRequest::url_for()` will work as expected.
///
/// This is same as `App::external_service()` method.
pub fn external_resource<N, U>(&mut self, name: N, url: U) -> &mut Self
where
N: AsRef<str>,
U: AsRef<str>,
{
let mut rdef = ResourceDef::new(url.as_ref());
*rdef.name_mut() = name.as_ref().to_string();
self.external.push(rdef);
self
}
}
#[cfg(test)]
mod tests {
use actix_service::Service;
use super::*;
use crate::http::StatusCode;
use crate::test::{block_on, init_service, TestRequest};
use crate::{web, App, HttpResponse};
#[test]
fn test_data() {
let cfg = |cfg: &mut RouterConfig<_>| {
cfg.data(10usize);
};
let mut srv =
init_service(App::new().configure(cfg).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
));
let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::OK);
}
#[test]
fn test_data_factory() {
let cfg = |cfg: &mut RouterConfig<_>| {
cfg.data_factory(|| Ok::<_, ()>(10usize));
};
let mut srv =
init_service(App::new().configure(cfg).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
));
let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let cfg2 = |cfg: &mut RouterConfig<_>| {
cfg.data_factory(|| Ok::<_, ()>(10u32));
};
let mut srv = init_service(
App::new()
.service(web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()))
.configure(cfg2),
);
let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
}

View File

@ -92,7 +92,7 @@ impl<T: 'static, P> FromRequest<P> for Data<T> {
#[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
if let Some(st) = req.config().extensions().get::<Data<T>>() {
if let Some(st) = req.request().config().extensions().get::<Data<T>>() {
Ok(st.clone())
} else {
Err(ErrorInternalServerError(

View File

@ -121,36 +121,6 @@ impl ResponseError for ReadlinesError {
}
}
/// A set of errors that can occur during parsing multipart streams
#[derive(Debug, Display, From)]
pub enum MultipartError {
/// Content-Type header is not found
#[display(fmt = "No Content-type header found")]
NoContentType,
/// Can not parse Content-Type header
#[display(fmt = "Can not parse Content-Type header")]
ParseContentType,
/// Multipart boundary is not found
#[display(fmt = "Multipart boundary is not found")]
Boundary,
/// Multipart stream is incomplete
#[display(fmt = "Multipart stream is incomplete")]
Incomplete,
/// Error during field parsing
#[display(fmt = "{}", _0)]
Parse(ParseError),
/// Payload error
#[display(fmt = "{}", _0)]
Payload(PayloadError),
}
/// Return `BadRequest` for `MultipartError`
impl ResponseError for MultipartError {
fn error_response(&self) -> HttpResponse {
HttpResponse::new(StatusCode::BAD_REQUEST)
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -180,10 +150,4 @@ mod tests {
let resp: HttpResponse = ReadlinesError::EncodingError.error_response();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
}
#[test]
fn test_multipart_error() {
let resp: HttpResponse = MultipartError::Boundary.error_response();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
}
}

View File

@ -73,6 +73,15 @@ where
}
}
impl<F> Guard for F
where
F: Fn(&RequestHead) -> bool,
{
fn check(&self, head: &RequestHead) -> bool {
(self)(head)
}
}
/// Return guard that matches if any of supplied guards.
///
/// ```rust
@ -300,13 +309,13 @@ mod tests {
.to_http_request();
let pred = Header("transfer-encoding", "chunked");
assert!(pred.check(&req));
assert!(pred.check(req.head()));
let pred = Header("transfer-encoding", "other");
assert!(!pred.check(&req));
assert!(!pred.check(req.head()));
let pred = Header("content-type", "other");
assert!(!pred.check(&req));
assert!(!pred.check(req.head()));
}
// #[test]
@ -332,50 +341,50 @@ mod tests {
.method(Method::POST)
.to_http_request();
assert!(Get().check(&req));
assert!(!Get().check(&req2));
assert!(Post().check(&req2));
assert!(!Post().check(&req));
assert!(Get().check(req.head()));
assert!(!Get().check(req2.head()));
assert!(Post().check(req2.head()));
assert!(!Post().check(req.head()));
let r = TestRequest::default().method(Method::PUT).to_http_request();
assert!(Put().check(&r));
assert!(!Put().check(&req));
assert!(Put().check(r.head()));
assert!(!Put().check(req.head()));
let r = TestRequest::default()
.method(Method::DELETE)
.to_http_request();
assert!(Delete().check(&r));
assert!(!Delete().check(&req));
assert!(Delete().check(r.head()));
assert!(!Delete().check(req.head()));
let r = TestRequest::default()
.method(Method::HEAD)
.to_http_request();
assert!(Head().check(&r));
assert!(!Head().check(&req));
assert!(Head().check(r.head()));
assert!(!Head().check(req.head()));
let r = TestRequest::default()
.method(Method::OPTIONS)
.to_http_request();
assert!(Options().check(&r));
assert!(!Options().check(&req));
assert!(Options().check(r.head()));
assert!(!Options().check(req.head()));
let r = TestRequest::default()
.method(Method::CONNECT)
.to_http_request();
assert!(Connect().check(&r));
assert!(!Connect().check(&req));
assert!(Connect().check(r.head()));
assert!(!Connect().check(req.head()));
let r = TestRequest::default()
.method(Method::PATCH)
.to_http_request();
assert!(Patch().check(&r));
assert!(!Patch().check(&req));
assert!(Patch().check(r.head()));
assert!(!Patch().check(req.head()));
let r = TestRequest::default()
.method(Method::TRACE)
.to_http_request();
assert!(Trace().check(&r));
assert!(!Trace().check(&req));
assert!(Trace().check(r.head()));
assert!(!Trace().check(req.head()));
}
#[test]
@ -384,13 +393,13 @@ mod tests {
.method(Method::TRACE)
.to_http_request();
assert!(Not(Get()).check(&r));
assert!(!Not(Trace()).check(&r));
assert!(Not(Get()).check(r.head()));
assert!(!Not(Trace()).check(r.head()));
assert!(All(Trace()).and(Trace()).check(&r));
assert!(!All(Get()).and(Trace()).check(&r));
assert!(All(Trace()).and(Trace()).check(r.head()));
assert!(!All(Get()).and(Trace()).check(r.head()));
assert!(Any(Get()).or(Trace()).check(&r));
assert!(!Any(Get()).or(Get()).check(&r));
assert!(Any(Get()).or(Trace()).check(r.head()));
assert!(!Any(Get()).or(Get()).check(r.head()));
}
}

View File

@ -113,7 +113,7 @@ where
fn call(&mut self, req: ServiceRequest<P>) -> Self::Future {
// negotiate content-encoding
let encoding = if let Some(val) = req.headers.get(ACCEPT_ENCODING) {
let encoding = if let Some(val) = req.headers().get(ACCEPT_ENCODING) {
if let Ok(enc) = val.to_str() {
AcceptEncoding::parse(enc, self.encoding)
} else {
@ -157,7 +157,7 @@ where
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let resp = futures::try_ready!(self.fut.poll());
let enc = if let Some(enc) = resp.head().extensions().get::<Enc>() {
let enc = if let Some(enc) = resp.response().extensions().get::<Enc>() {
enc.0
} else {
self.encoding

View File

@ -51,7 +51,7 @@ use crate::error::{ResponseError, Result};
use crate::http::header::{self, HeaderName, HeaderValue};
use crate::http::{self, HttpTryFrom, Method, StatusCode, Uri};
use crate::service::{ServiceRequest, ServiceResponse};
use crate::{HttpMessage, HttpResponse};
use crate::HttpResponse;
/// A set of errors that can occur during processing CORS
#[derive(Debug, Display)]
@ -702,9 +702,9 @@ where
if self.inner.preflight && Method::OPTIONS == *req.method() {
if let Err(e) = self
.inner
.validate_origin(&req)
.and_then(|_| self.inner.validate_allowed_method(&req))
.and_then(|_| self.inner.validate_allowed_headers(&req))
.validate_origin(req.head())
.and_then(|_| self.inner.validate_allowed_method(req.head()))
.and_then(|_| self.inner.validate_allowed_headers(req.head()))
{
return Either::A(ok(req.error_response(e)));
}
@ -739,7 +739,7 @@ where
let _ = resp.header(header::ACCESS_CONTROL_ALLOW_HEADERS, headers);
})
.if_some(
self.inner.access_control_allow_origin(&req),
self.inner.access_control_allow_origin(req.head()),
|origin, resp| {
let _ = resp.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin);
},
@ -762,7 +762,7 @@ where
Either::A(ok(req.into_response(res)))
} else if req.headers().contains_key(header::ORIGIN) {
// Only check requests with a origin header.
if let Err(e) = self.inner.validate_origin(&req) {
if let Err(e) = self.inner.validate_origin(req.head()) {
return Either::A(ok(req.error_response(e)));
}
@ -771,7 +771,7 @@ where
Either::B(Either::B(Box::new(self.service.call(req).and_then(
move |mut res| {
if let Some(origin) =
inner.access_control_allow_origin(&res.request())
inner.access_control_allow_origin(res.request().head())
{
res.headers_mut()
.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin.clone());
@ -869,8 +869,8 @@ mod tests {
.method(Method::OPTIONS)
.to_srv_request();
assert!(cors.inner.validate_allowed_method(&req).is_err());
assert!(cors.inner.validate_allowed_headers(&req).is_err());
assert!(cors.inner.validate_allowed_method(req.head()).is_err());
assert!(cors.inner.validate_allowed_headers(req.head()).is_err());
let resp = test::call_success(&mut cors, req);
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
@ -879,8 +879,8 @@ mod tests {
.method(Method::OPTIONS)
.to_srv_request();
assert!(cors.inner.validate_allowed_method(&req).is_err());
assert!(cors.inner.validate_allowed_headers(&req).is_err());
assert!(cors.inner.validate_allowed_method(req.head()).is_err());
assert!(cors.inner.validate_allowed_headers(req.head()).is_err());
let req = TestRequest::with_header("Origin", "https://www.example.com")
.header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST")
@ -961,9 +961,9 @@ mod tests {
let req = TestRequest::with_header("Origin", "https://www.unknown.com")
.method(Method::GET)
.to_srv_request();
cors.inner.validate_origin(&req).unwrap();
cors.inner.validate_allowed_method(&req).unwrap();
cors.inner.validate_allowed_headers(&req).unwrap();
cors.inner.validate_origin(req.head()).unwrap();
cors.inner.validate_allowed_method(req.head()).unwrap();
cors.inner.validate_allowed_headers(req.head()).unwrap();
}
#[test]

View File

@ -10,7 +10,6 @@ use futures::{Async, Poll, Stream};
use crate::dev::Payload;
use crate::error::{Error, PayloadError};
use crate::service::ServiceRequest;
use crate::HttpMessage;
/// `Middleware` for decompressing request's payload.
/// `Decompress` middleware must be added with `App::chain()` method.

View File

@ -148,7 +148,7 @@ impl<P> FromRequest<P> for Identity {
#[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
Ok(Identity(req.clone()))
Ok(Identity(req.request().clone()))
}
}
@ -507,7 +507,7 @@ mod tests {
let resp =
test::call_success(&mut srv, TestRequest::with_uri("/login").to_request());
assert_eq!(resp.status(), StatusCode::OK);
let c = resp.cookies().next().unwrap().to_owned();
let c = resp.response().cookies().next().unwrap().to_owned();
let resp = test::call_success(
&mut srv,

View File

@ -15,7 +15,7 @@ use time;
use crate::dev::{BodySize, MessageBody, ResponseBody};
use crate::error::{Error, Result};
use crate::service::{ServiceRequest, ServiceResponse};
use crate::{HttpMessage, HttpResponse};
use crate::HttpResponse;
/// `Middleware` for logging request and response info to the terminal.
///
@ -203,7 +203,7 @@ where
if let Some(ref mut format) = self.format {
for unit in &mut format.0 {
unit.render_response(&res);
unit.render_response(res.response());
}
}

View File

@ -1,6 +1,5 @@
use std::cell::{Ref, RefMut};
use std::fmt;
use std::ops::Deref;
use std::rc::Rc;
use actix_http::http::{HeaderMap, Method, Uri, Version};
@ -66,6 +65,12 @@ impl HttpRequest {
self.head().version
}
#[inline]
/// Returns request's headers.
pub fn headers(&self) -> &HeaderMap {
&self.head().headers
}
/// The target path of this Request.
#[inline]
pub fn path(&self) -> &str {
@ -111,6 +116,18 @@ impl HttpRequest {
}
}
/// Request extensions
#[inline]
pub fn extensions(&self) -> Ref<Extensions> {
self.head().extensions()
}
/// Mutable reference to a the request's extensions
#[inline]
pub fn extensions_mut(&self) -> RefMut<Extensions> {
self.head().extensions_mut()
}
/// Generate url for named resource
///
/// ```rust
@ -154,15 +171,7 @@ impl HttpRequest {
/// Get *ConnectionInfo* for the current request.
#[inline]
pub fn connection_info(&self) -> Ref<ConnectionInfo> {
ConnectionInfo::get(&*self, &*self.config())
}
}
impl Deref for HttpRequest {
type Target = RequestHead;
fn deref(&self) -> &RequestHead {
self.head()
ConnectionInfo::get(self.head(), &*self.config())
}
}
@ -219,7 +228,7 @@ impl<P> FromRequest<P> for HttpRequest {
#[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
Ok(req.clone())
Ok(req.request().clone())
}
}

View File

@ -313,7 +313,7 @@ pub(crate) mod tests {
let req = TestRequest::with_uri("/some").to_request();
let resp = TestRequest::block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::OK);
match resp.body() {
match resp.response().body() {
ResponseBody::Body(Body::Bytes(ref b)) => {
let bytes: Bytes = b.clone().into();
assert_eq!(bytes, Bytes::from_static(b"some"));

View File

@ -693,7 +693,7 @@ mod tests {
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::OK);
match resp.body() {
match resp.response().body() {
ResponseBody::Body(Body::Bytes(ref b)) => {
let bytes: Bytes = b.clone().into();
assert_eq!(bytes, Bytes::from_static(b"project: project1"));
@ -799,7 +799,7 @@ mod tests {
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::CREATED);
match resp.body() {
match resp.response().body() {
ResponseBody::Body(Body::Bytes(ref b)) => {
let bytes: Bytes = b.clone().into();
assert_eq!(bytes, Bytes::from_static(b"project: project_1"));
@ -826,7 +826,7 @@ mod tests {
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::CREATED);
match resp.body() {
match resp.response().body() {
ResponseBody::Body(Body::Bytes(ref b)) => {
let bytes: Bytes = b.clone().into();
assert_eq!(bytes, Bytes::from_static(b"project: test - 1"));

View File

@ -4,7 +4,7 @@ use std::marker::PhantomData;
use std::rc::Rc;
use actix_http::body::{Body, MessageBody, ResponseBody};
use actix_http::http::{HeaderMap, Method, Uri, Version};
use actix_http::http::{HeaderMap, Method, StatusCode, Uri, Version};
use actix_http::{
Error, Extensions, HttpMessage, Payload, PayloadStream, Request, RequestHead,
Response, ResponseHead,
@ -123,7 +123,13 @@ impl<P> ServiceRequest<P> {
}
#[inline]
/// Returns mutable Request's headers.
/// Returns request's headers.
pub fn headers(&self) -> &HeaderMap {
&self.head().headers
}
#[inline]
/// Returns mutable request's headers.
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.head_mut().headers
}
@ -202,20 +208,6 @@ impl<P> HttpMessage for ServiceRequest<P> {
}
}
impl<P> std::ops::Deref for ServiceRequest<P> {
type Target = RequestHead;
fn deref(&self) -> &RequestHead {
self.req.head()
}
}
impl<P> std::ops::DerefMut for ServiceRequest<P> {
fn deref_mut(&mut self) -> &mut RequestHead {
self.head_mut()
}
}
impl<P> fmt::Debug for ServiceRequest<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(
@ -255,11 +247,19 @@ impl<P> ServiceFromRequest<P> {
}
#[inline]
/// Get reference to inner HttpRequest
pub fn request(&self) -> &HttpRequest {
&self.req
}
#[inline]
/// Convert this request into a HttpRequest
pub fn into_request(self) -> HttpRequest {
self.req
}
#[inline]
/// Get match information for this request
pub fn match_info_mut(&mut self) -> &mut Path<Url> {
&mut self.req.path
}
@ -281,14 +281,6 @@ impl<P> ServiceFromRequest<P> {
}
}
impl<P> std::ops::Deref for ServiceFromRequest<P> {
type Target = HttpRequest;
fn deref(&self) -> &HttpRequest {
&self.req
}
}
impl<P> HttpMessage for ServiceFromRequest<P> {
type Stream = P;
@ -366,6 +358,24 @@ impl<B> ServiceResponse<B> {
&mut self.response
}
/// Get the response status code
#[inline]
pub fn status(&self) -> StatusCode {
self.response.status()
}
#[inline]
/// Returns response's headers.
pub fn headers(&self) -> &HeaderMap {
self.response.headers()
}
#[inline]
/// Returns mutable response's headers.
pub fn headers_mut(&mut self) -> &mut HeaderMap {
self.response.headers_mut()
}
/// Execute closure and in case of error convert it to response.
pub fn checked_expr<F, E>(mut self, f: F) -> Self
where
@ -402,20 +412,6 @@ impl<B> ServiceResponse<B> {
}
}
impl<B> std::ops::Deref for ServiceResponse<B> {
type Target = Response<B>;
fn deref(&self) -> &Response<B> {
self.response()
}
}
impl<B> std::ops::DerefMut for ServiceResponse<B> {
fn deref_mut(&mut self) -> &mut Response<B> {
self.response_mut()
}
}
impl<B> Into<Response<B>> for ServiceResponse<B> {
fn into(self) -> Response<B> {
self.response

View File

@ -80,7 +80,7 @@ where
#[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
let req2 = req.clone();
let req2 = req.request().clone();
let (limit, err) = req
.route_data::<FormConfig>()
.map(|c| (c.limit, c.ehandler.clone()))

View File

@ -174,7 +174,7 @@ where
#[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
let req2 = req.clone();
let req2 = req.request().clone();
let (limit, err) = req
.route_data::<JsonConfig>()
.map(|c| (c.limit, c.ehandler.clone()))

View File

@ -2,7 +2,6 @@
pub(crate) mod form;
pub(crate) mod json;
mod multipart;
mod path;
pub(crate) mod payload;
mod query;
@ -10,7 +9,6 @@ pub(crate) mod readlines;
pub use self::form::{Form, FormConfig};
pub use self::json::{Json, JsonConfig};
pub use self::multipart::{Multipart, MultipartField, MultipartItem};
pub use self::path::Path;
pub use self::payload::{Payload, PayloadConfig};
pub use self::query::Query;

View File

@ -170,7 +170,7 @@ where
#[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
Self::extract(req).map_err(ErrorNotFound)
Self::extract(req.request()).map_err(ErrorNotFound)
}
}

View File

@ -119,7 +119,7 @@ where
#[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
serde_urlencoded::from_str::<T>(req.query_string())
serde_urlencoded::from_str::<T>(req.request().query_string())
.map(|val| Ok(Query(val)))
.unwrap_or_else(|e| Err(e.into()))
}

View File

@ -13,6 +13,7 @@ use crate::responder::Responder;
use crate::route::Route;
use crate::scope::Scope;
pub use crate::config::RouterConfig;
pub use crate::data::{Data, RouteData};
pub use crate::request::HttpRequest;
pub use crate::types::*;

View File

@ -1,5 +1,10 @@
# Changes
## [0.1.0-alpha.3] - 2019-04-02
* Request functions accept path #743
## [0.1.0-alpha.2] - 2019-03-29
* Added TestServerRuntime::load_body() method

View File

@ -1,6 +1,6 @@
[package]
name = "actix-http-test"
version = "0.1.0-alpha.2"
version = "0.1.0-alpha.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix http test server"
readme = "README.md"
@ -35,7 +35,7 @@ actix-rt = "0.2.1"
actix-service = "0.3.4"
actix-server = "0.4.0"
actix-utils = "0.3.4"
awc = "0.1.0-alpha.2"
awc = "0.1.0-alpha.3"
base64 = "0.10"
bytes = "0.4"
@ -53,3 +53,7 @@ time = "0.1"
tokio-tcp = "0.1"
tokio-timer = "0.2"
openssl = { version="0.10", optional = true }
[dev-dependencies]
actix-web = "1.0.0-alpa.3"
actix-http = "0.1.0-alpa.3"

View File

@ -36,7 +36,7 @@ use net2::TcpBuilder;
/// )
/// );
///
/// let req = srv.get();
/// let req = srv.get("/");
/// let response = srv.block_on(req.send()).unwrap();
/// assert!(response.status().is_success());
/// }
@ -195,7 +195,7 @@ impl TestServerRuntime {
pub fn load_body<S>(
&mut self,
response: ClientResponse<S>,
mut response: ClientResponse<S>,
) -> Result<Bytes, PayloadError>
where
S: Stream<Item = Bytes, Error = PayloadError> + 'static,

View File

@ -54,7 +54,7 @@ fn test_body() {
)
});
let mut response = srv.block_on(srv.get().send()).unwrap();
let mut response = srv.block_on(srv.get("/").send()).unwrap();
assert!(response.status().is_success());
// read response
@ -73,7 +73,7 @@ fn test_body_gzip() {
)
});
let mut response = srv.block_on(srv.get().no_decompress().send()).unwrap();
let mut response = srv.block_on(srv.get("/").no_decompress().send()).unwrap();
assert!(response.status().is_success());
// read response
@ -111,7 +111,7 @@ fn test_body_encoding_override() {
});
// Builder
let mut response = srv.block_on(srv.get().no_decompress().send()).unwrap();
let mut response = srv.block_on(srv.get("/").no_decompress().send()).unwrap();
assert!(response.status().is_success());
// read response
@ -161,7 +161,7 @@ fn test_body_gzip_large() {
)
});
let mut response = srv.block_on(srv.get().no_decompress().send()).unwrap();
let mut response = srv.block_on(srv.get("/").no_decompress().send()).unwrap();
assert!(response.status().is_success());
// read response
@ -195,7 +195,7 @@ fn test_body_gzip_large_random() {
)
});
let mut response = srv.block_on(srv.get().no_decompress().send()).unwrap();
let mut response = srv.block_on(srv.get("/").no_decompress().send()).unwrap();
assert!(response.status().is_success());
// read response
@ -224,7 +224,7 @@ fn test_body_chunked_implicit() {
)
});
let mut response = srv.block_on(srv.get().no_decompress().send()).unwrap();
let mut response = srv.block_on(srv.get("/").no_decompress().send()).unwrap();
assert!(response.status().is_success());
assert_eq!(
response.headers().get(TRANSFER_ENCODING).unwrap(),
@ -258,7 +258,7 @@ fn test_body_br_streaming() {
let mut response = srv
.block_on(
srv.get()
srv.get("/")
.header(ACCEPT_ENCODING, "br")
.no_decompress()
.send(),
@ -284,7 +284,7 @@ fn test_head_binary() {
)))
});
let mut response = srv.block_on(srv.head().send()).unwrap();
let mut response = srv.block_on(srv.head("/").send()).unwrap();
assert!(response.status().is_success());
{
@ -310,7 +310,7 @@ fn test_no_chunking() {
))))
});
let mut response = srv.block_on(srv.get().send()).unwrap();
let mut response = srv.block_on(srv.get("/").send()).unwrap();
assert!(response.status().is_success());
assert!(!response.headers().contains_key(TRANSFER_ENCODING));
@ -333,7 +333,7 @@ fn test_body_deflate() {
});
// client request
let mut response = srv.block_on(srv.get().no_decompress().send()).unwrap();
let mut response = srv.block_on(srv.get("/").no_decompress().send()).unwrap();
assert!(response.status().is_success());
// read response
@ -362,7 +362,7 @@ fn test_body_brotli() {
// client request
let mut response = srv
.block_on(
srv.get()
srv.get("/")
.header(ACCEPT_ENCODING, "br")
.no_decompress()
.send(),
@ -398,7 +398,7 @@ fn test_encoding() {
let enc = e.finish().unwrap();
let request = srv
.post()
.post("/")
.header(CONTENT_ENCODING, "gzip")
.send_body(enc.clone());
let mut response = srv.block_on(request).unwrap();
@ -427,7 +427,7 @@ fn test_gzip_encoding() {
let enc = e.finish().unwrap();
let request = srv
.post()
.post("/")
.header(CONTENT_ENCODING, "gzip")
.send_body(enc.clone());
let mut response = srv.block_on(request).unwrap();
@ -457,7 +457,7 @@ fn test_gzip_encoding_large() {
let enc = e.finish().unwrap();
let request = srv
.post()
.post("/")
.header(CONTENT_ENCODING, "gzip")
.send_body(enc.clone());
let mut response = srv.block_on(request).unwrap();
@ -491,7 +491,7 @@ fn test_reading_gzip_encoding_large_random() {
let enc = e.finish().unwrap();
let request = srv
.post()
.post("/")
.header(CONTENT_ENCODING, "gzip")
.send_body(enc.clone());
let mut response = srv.block_on(request).unwrap();
@ -521,7 +521,7 @@ fn test_reading_deflate_encoding() {
// client request
let request = srv
.post()
.post("/")
.header(CONTENT_ENCODING, "deflate")
.send_body(enc.clone());
let mut response = srv.block_on(request).unwrap();
@ -551,7 +551,7 @@ fn test_reading_deflate_encoding_large() {
// client request
let request = srv
.post()
.post("/")
.header(CONTENT_ENCODING, "deflate")
.send_body(enc.clone());
let mut response = srv.block_on(request).unwrap();
@ -585,7 +585,7 @@ fn test_reading_deflate_encoding_large_random() {
// client request
let request = srv
.post()
.post("/")
.header(CONTENT_ENCODING, "deflate")
.send_body(enc.clone());
let mut response = srv.block_on(request).unwrap();
@ -615,7 +615,7 @@ fn test_brotli_encoding() {
// client request
let request = srv
.post()
.post("/")
.header(CONTENT_ENCODING, "br")
.send_body(enc.clone());
let mut response = srv.block_on(request).unwrap();
@ -645,7 +645,7 @@ fn test_brotli_encoding_large() {
// client request
let request = srv
.post()
.post("/")
.header(CONTENT_ENCODING, "br")
.send_body(enc.clone());
let mut response = srv.block_on(request).unwrap();
@ -912,7 +912,7 @@ fn test_reading_deflate_encoding_large_random_ssl() {
// .finish();
// let second_cookie = http::Cookie::new("second", "second_value");
// let request = srv.get().finish().unwrap();
// let request = srv.get("/").finish().unwrap();
// let response = srv.execute(request.send()).unwrap();
// assert!(response.status().is_success());