mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into master
This commit is contained in:
commit
ca9775f6e6
|
@ -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"
|
||||
|
|
18
CHANGES.md
18
CHANGES.md
|
@ -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
|
||||
|
||||
|
|
18
Cargo.toml
18
Cargo.toml
|
@ -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" }
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# Changes
|
||||
|
||||
## [0.1.0-alpha.1] - 2019-04-xx
|
||||
|
||||
* Split multipart support to separate crate
|
|
@ -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"
|
|
@ -0,0 +1 @@
|
|||
# Multipart support for actix web framework [](https://travis-ci.org/actix/actix-web) [](https://codecov.io/gh/actix/actix-web) [](https://crates.io/crates/actix-multipart) [](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
mod error;
|
||||
mod extractor;
|
||||
mod server;
|
||||
|
||||
pub use self::error::MultipartError;
|
||||
pub use self::server::{Field, Item, Multipart};
|
|
@ -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);
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
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,15 +141,13 @@ impl ClientRequest {
|
|||
/// # }));
|
||||
/// }
|
||||
/// ```
|
||||
pub fn set<H: Header>(&mut self, hdr: H) -> &mut Self {
|
||||
if let Some(head) = parts(&mut self.head, &self.err) {
|
||||
pub fn set<H: Header>(mut self, hdr: H) -> Self {
|
||||
match hdr.try_into() {
|
||||
Ok(value) => {
|
||||
head.headers.insert(H::name(), value);
|
||||
self.head.headers.insert(H::name(), value);
|
||||
}
|
||||
Err(e) => self.err = Some(e.into()),
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -165,58 +169,53 @@ 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);
|
||||
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);
|
||||
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) {
|
||||
if !self.head.headers.contains_key(&key) {
|
||||
match value.try_into() {
|
||||
Ok(value) => {
|
||||
head.headers.insert(key, value);
|
||||
let _ = self.head.headers.insert(key, value);
|
||||
}
|
||||
Err(e) => self.err = Some(e.into()),
|
||||
}
|
||||
|
@ -224,47 +223,42 @@ impl ClientRequest {
|
|||
}
|
||||
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);
|
||||
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,33 +414,12 @@ impl ClientRequest {
|
|||
}
|
||||
|
||||
// user agent
|
||||
self.set_header_if_none(
|
||||
if !self.head.headers.contains_key(&header::USER_AGENT) {
|
||||
self.head.headers.insert(
|
||||
header::USER_AGENT,
|
||||
concat!("awc/", env!("CARGO_PKG_VERSION")),
|
||||
HeaderValue::from_static(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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set cookies
|
||||
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
Async::Ready(None) => Ok(Async::Ready(self.buf.take().freeze())),
|
||||
Async::NotReady => Ok(Async::NotReady),
|
||||
};
|
||||
}
|
||||
})
|
||||
.and_then(|body| Ok(serde_json::from_slice::<U>(&body)?));
|
||||
self.fut = Some(Box::new(fut));
|
||||
self.poll()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
90
src/app.rs
90
src/app.rs
|
@ -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.
|
||||
|
|
146
src/config.rs
146
src/config.rs
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
36
src/error.rs
36
src/error.rs
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
63
src/guard.rs
63
src/guard.rs
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
Loading…
Reference in New Issue