mirror of
https://github.com/fafhrd91/actix-web
synced 2025-07-30 12:06:41 +02:00
Compare commits
17 Commits
http-test-
...
files-v0.1
Author | SHA1 | Date | |
---|---|---|---|
|
a464ffc23d | ||
|
4de2e8a898 | ||
|
0f09415469 | ||
|
f089cf185b | ||
|
15d3c1ae81 | ||
|
fba31d4e0a | ||
|
f81ae37677 | ||
|
5169d306ae | ||
|
4f3e97fff8 | ||
|
3ff01a9fc4 | ||
|
3d4e45a0e5 | ||
|
c659c33919 | ||
|
959f7754b2 | ||
|
23f04c4f38 | ||
|
d9af8f66ba | ||
|
aa39b8ca6f | ||
|
58c7065f08 |
21
CHANGES.md
21
CHANGES.md
@@ -1,15 +1,32 @@
|
||||
# Changes
|
||||
|
||||
## [1.0.8] - 2019-09-xx
|
||||
## [1.0.9] - 2019-xx-xx
|
||||
|
||||
### Added
|
||||
|
||||
* Add `middleware::Conditon` that conditionally enables another middleware
|
||||
* Add `Payload::into_inner` method and make stored `def::Payload` public. (#1110)
|
||||
|
||||
## [1.0.8] - 2019-09-25
|
||||
|
||||
### Added
|
||||
|
||||
* Add `Scope::register_data` and `Resource::register_data` methods, parallel to
|
||||
`App::register_data`.
|
||||
|
||||
* Add `middleware::Condition` that conditionally enables another middleware
|
||||
|
||||
* Allow to re-construct `ServiceRequest` from `HttpRequest` and `Payload`
|
||||
|
||||
* Add `HttpServer::listen_uds` for ability to listen on UDS FD rather than path,
|
||||
which is useful for example with systemd.
|
||||
|
||||
### Changed
|
||||
|
||||
* Make UrlEncodedError::Overflow more informativve
|
||||
|
||||
* Use actix-testing for testing utils
|
||||
|
||||
|
||||
## [1.0.7] - 2019-08-29
|
||||
|
||||
### Fixed
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-web"
|
||||
version = "1.0.7"
|
||||
version = "1.0.8"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
|
||||
readme = "README.md"
|
||||
@@ -79,10 +79,11 @@ actix-router = "0.1.5"
|
||||
actix-rt = "0.2.4"
|
||||
actix-web-codegen = "0.1.2"
|
||||
actix-http = "0.2.9"
|
||||
actix-server = "0.6.0"
|
||||
actix-server = "0.6.1"
|
||||
actix-server-config = "0.1.2"
|
||||
actix-testing = "0.1.0"
|
||||
actix-threadpool = "0.1.1"
|
||||
awc = { version = "0.2.4", optional = true }
|
||||
awc = { version = "0.2.7", optional = true }
|
||||
|
||||
bytes = "0.4"
|
||||
derive_more = "0.15.0"
|
||||
|
@@ -1,11 +1,14 @@
|
||||
# Changes
|
||||
|
||||
## [0.1.5] - unreleased
|
||||
## [0.1.5] - 2019-10-08
|
||||
|
||||
* Bump up `mime_guess` crate version to 2.0.1
|
||||
|
||||
* Bump up `percent-encoding` crate version to 2.1
|
||||
|
||||
* Allow user defined request guards for `Files` #1113
|
||||
|
||||
|
||||
## [0.1.4] - 2019-07-20
|
||||
|
||||
* Allow to disable `Content-Disposition` header #686
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-files"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Static files support for actix web."
|
||||
readme = "README.md"
|
||||
@@ -18,7 +18,7 @@ name = "actix_files"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
actix-web = { version = "1.0.2", default-features = false }
|
||||
actix-web = { version = "1.0.8", default-features = false }
|
||||
actix-http = "0.2.9"
|
||||
actix-service = "0.4.1"
|
||||
bitflags = "1"
|
||||
@@ -32,4 +32,4 @@ percent-encoding = "2.1"
|
||||
v_htmlescape = "0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
actix-web = { version = "1.0.2", features=["ssl"] }
|
||||
actix-web = { version = "1.0.8", features=["ssl"] }
|
||||
|
@@ -16,7 +16,9 @@ use actix_web::dev::{
|
||||
ServiceResponse,
|
||||
};
|
||||
use actix_web::error::{BlockingError, Error, ErrorInternalServerError};
|
||||
use actix_web::http::header::DispositionType;
|
||||
use actix_web::guard::Guard;
|
||||
use actix_web::http::header::{self, DispositionType};
|
||||
use actix_web::http::Method;
|
||||
use actix_web::{web, FromRequest, HttpRequest, HttpResponse, Responder};
|
||||
use bytes::Bytes;
|
||||
use futures::future::{ok, Either, FutureResult};
|
||||
@@ -235,6 +237,7 @@ pub struct Files {
|
||||
renderer: Rc<DirectoryRenderer>,
|
||||
mime_override: Option<Rc<MimeOverride>>,
|
||||
file_flags: named::Flags,
|
||||
guards: Option<Rc<Box<dyn Guard>>>,
|
||||
}
|
||||
|
||||
impl Clone for Files {
|
||||
@@ -248,6 +251,7 @@ impl Clone for Files {
|
||||
file_flags: self.file_flags,
|
||||
path: self.path.clone(),
|
||||
mime_override: self.mime_override.clone(),
|
||||
guards: self.guards.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,6 +277,7 @@ impl Files {
|
||||
renderer: Rc::new(directory_listing),
|
||||
mime_override: None,
|
||||
file_flags: named::Flags::default(),
|
||||
guards: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,6 +336,15 @@ impl Files {
|
||||
self
|
||||
}
|
||||
|
||||
/// Specifies custom guards to use for directory listings and files.
|
||||
///
|
||||
/// Default behaviour allows GET and HEAD.
|
||||
#[inline]
|
||||
pub fn use_guards<G: Guard + 'static>(mut self, guards: G) -> Self {
|
||||
self.guards = Some(Rc::new(Box::new(guards)));
|
||||
self
|
||||
}
|
||||
|
||||
/// Disable `Content-Disposition` header.
|
||||
///
|
||||
/// By default Content-Disposition` header is enabled.
|
||||
@@ -392,6 +406,7 @@ impl NewService for Files {
|
||||
renderer: self.renderer.clone(),
|
||||
mime_override: self.mime_override.clone(),
|
||||
file_flags: self.file_flags,
|
||||
guards: self.guards.clone(),
|
||||
};
|
||||
|
||||
if let Some(ref default) = *self.default.borrow() {
|
||||
@@ -418,6 +433,7 @@ pub struct FilesService {
|
||||
renderer: Rc<DirectoryRenderer>,
|
||||
mime_override: Option<Rc<MimeOverride>>,
|
||||
file_flags: named::Flags,
|
||||
guards: Option<Rc<Box<dyn Guard>>>,
|
||||
}
|
||||
|
||||
impl FilesService {
|
||||
@@ -454,6 +470,25 @@ impl Service for FilesService {
|
||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
||||
// let (req, pl) = req.into_parts();
|
||||
|
||||
let is_method_valid = if let Some(guard) = &self.guards {
|
||||
// execute user defined guards
|
||||
(**guard).check(req.head())
|
||||
} else {
|
||||
// default behaviour
|
||||
match *req.method() {
|
||||
Method::HEAD | Method::GET => true,
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
|
||||
if !is_method_valid {
|
||||
return Either::A(ok(req.into_response(
|
||||
actix_web::HttpResponse::MethodNotAllowed()
|
||||
.header(header::CONTENT_TYPE, "text/plain")
|
||||
.body("Request did not meet this resource's requirements."),
|
||||
)));
|
||||
}
|
||||
|
||||
let real_path = match PathBufWrp::get_pathbuf(req.match_info().path()) {
|
||||
Ok(item) => item,
|
||||
Err(e) => return Either::A(ok(req.error_response(e))),
|
||||
@@ -576,6 +611,7 @@ mod tests {
|
||||
use bytes::BytesMut;
|
||||
|
||||
use super::*;
|
||||
use actix_web::guard;
|
||||
use actix_web::http::header::{
|
||||
self, ContentDisposition, DispositionParam, DispositionType,
|
||||
};
|
||||
@@ -1010,20 +1046,41 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_not_allowed() {
|
||||
let file = NamedFile::open("Cargo.toml").unwrap();
|
||||
fn test_files_not_allowed() {
|
||||
let mut srv = test::init_service(App::new().service(Files::new("/", ".")));
|
||||
|
||||
let req = TestRequest::default()
|
||||
.uri("/Cargo.toml")
|
||||
.method(Method::POST)
|
||||
.to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
.to_request();
|
||||
|
||||
let resp = test::call_service(&mut srv, req);
|
||||
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||
|
||||
let file = NamedFile::open("Cargo.toml").unwrap();
|
||||
let req = TestRequest::default().method(Method::PUT).to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
let mut srv = test::init_service(App::new().service(Files::new("/", ".")));
|
||||
let req = TestRequest::default()
|
||||
.method(Method::PUT)
|
||||
.uri("/Cargo.toml")
|
||||
.to_request();
|
||||
let resp = test::call_service(&mut srv, req);
|
||||
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_files_guards() {
|
||||
let mut srv = test::init_service(
|
||||
App::new().service(Files::new("/", ".").use_guards(guard::Post())),
|
||||
);
|
||||
|
||||
let req = TestRequest::default()
|
||||
.uri("/Cargo.toml")
|
||||
.method(Method::POST)
|
||||
.to_request();
|
||||
|
||||
let resp = test::call_service(&mut srv, req);
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_content_encoding() {
|
||||
let mut srv = test::init_service(App::new().wrap(Compress::default()).service(
|
||||
|
@@ -15,7 +15,7 @@ use actix_http::body::SizedStream;
|
||||
use actix_web::http::header::{
|
||||
self, ContentDisposition, DispositionParam, DispositionType,
|
||||
};
|
||||
use actix_web::http::{ContentEncoding, Method, StatusCode};
|
||||
use actix_web::http::{ContentEncoding, StatusCode};
|
||||
use actix_web::middleware::BodyEncoding;
|
||||
use actix_web::{Error, HttpMessage, HttpRequest, HttpResponse, Responder};
|
||||
|
||||
@@ -324,16 +324,6 @@ impl Responder for NamedFile {
|
||||
return Ok(resp.streaming(reader));
|
||||
}
|
||||
|
||||
match *req.method() {
|
||||
Method::HEAD | Method::GET => (),
|
||||
_ => {
|
||||
return Ok(HttpResponse::MethodNotAllowed()
|
||||
.header(header::CONTENT_TYPE, "text/plain")
|
||||
.header(header::ALLOW, "GET, HEAD")
|
||||
.body("This resource only supports GET and HEAD."));
|
||||
}
|
||||
}
|
||||
|
||||
let etag = if self.flags.contains(Flags::ETAG) {
|
||||
self.etag()
|
||||
} else {
|
||||
|
@@ -1,6 +1,17 @@
|
||||
# Changes
|
||||
|
||||
## [0.2.11] - 2019-09-11
|
||||
## Not released yet
|
||||
|
||||
### Added
|
||||
|
||||
* Add support for serde_json::Value to be passed as argument to ResponseBuilder.body()
|
||||
|
||||
### Fixed
|
||||
|
||||
* To be compatible with non-English error responses, `ResponseError` rendered with `text/plain; charset=utf-8` header #1118
|
||||
|
||||
|
||||
## [0.2.10] - 2019-09-11
|
||||
|
||||
### Added
|
||||
|
||||
|
@@ -234,6 +234,12 @@ impl From<BytesMut> for Body {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Value> for Body {
|
||||
fn from(v: serde_json::Value) -> Body {
|
||||
Body::Bytes(v.to_string().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<SizedStream<S>> for Body
|
||||
where
|
||||
S: Stream<Item = Bytes, Error = Error> + 'static,
|
||||
@@ -548,4 +554,17 @@ mod tests {
|
||||
assert!(format!("{:?}", Body::Empty).contains("Body::Empty"));
|
||||
assert!(format!("{:?}", Body::Bytes(Bytes::from_static(b"1"))).contains("1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_json() {
|
||||
use serde_json::json;
|
||||
assert_eq!(
|
||||
Body::from(serde_json::Value::String("test".into())).size(),
|
||||
BodySize::Sized(6)
|
||||
);
|
||||
assert_eq!(
|
||||
Body::from(json!({"test-key":"test-value"})).size(),
|
||||
BodySize::Sized(25)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -212,7 +212,7 @@ where
|
||||
pub fn finish(
|
||||
self,
|
||||
) -> impl Service<Request = Connect, Response = impl Connection, Error = ConnectError>
|
||||
+ Clone {
|
||||
+ Clone {
|
||||
#[cfg(not(any(feature = "ssl", feature = "rust-tls")))]
|
||||
{
|
||||
let connector = TimeoutService::new(
|
||||
|
@@ -75,7 +75,7 @@ pub trait ResponseError: fmt::Debug + fmt::Display {
|
||||
let _ = write!(Writer(&mut buf), "{}", self);
|
||||
resp.headers_mut().insert(
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("text/plain"),
|
||||
header::HeaderValue::from_static("text/plain; charset=utf-8"),
|
||||
);
|
||||
resp.set_body(Body::from(buf))
|
||||
}
|
||||
@@ -536,7 +536,7 @@ where
|
||||
let _ = write!(Writer(&mut buf), "{}", self);
|
||||
res.headers_mut().insert(
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("text/plain"),
|
||||
header::HeaderValue::from_static("text/plain; charset=utf-8"),
|
||||
);
|
||||
res.set_body(Body::from(buf))
|
||||
}
|
||||
|
@@ -16,7 +16,6 @@ use crate::httpmessage::HttpMessage;
|
||||
mod common;
|
||||
pub(crate) mod map;
|
||||
mod shared;
|
||||
#[doc(hidden)]
|
||||
pub use self::common::*;
|
||||
#[doc(hidden)]
|
||||
pub use self::shared::*;
|
||||
|
@@ -992,6 +992,14 @@ mod tests {
|
||||
assert_eq!(resp.body().get_ref(), b"[\"v1\",\"v2\",\"v3\"]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_json_in_body() {
|
||||
use serde_json::json;
|
||||
let resp =
|
||||
Response::build(StatusCode::OK).body(json!({"test-key":"test-value"}));
|
||||
assert_eq!(resp.body().get_ref(), br#"{"test-key":"test-value"}"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_response() {
|
||||
let resp: Response = "test".into();
|
||||
|
@@ -1,5 +1,13 @@
|
||||
# Changes
|
||||
|
||||
|
||||
## [0.2.7] - 2019-09-25
|
||||
|
||||
### Added
|
||||
|
||||
* Remaining getter methods for `ClientRequest`'s private `head` field #1101
|
||||
|
||||
|
||||
## [0.2.6] - 2019-09-12
|
||||
|
||||
### Added
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "awc"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Actix http client."
|
||||
readme = "README.md"
|
||||
|
@@ -96,7 +96,7 @@ impl ClientRequest {
|
||||
self
|
||||
}
|
||||
|
||||
/// Get HTTP URI of request
|
||||
/// Get HTTP URI of request.
|
||||
pub fn get_uri(&self) -> &Uri {
|
||||
&self.head.uri
|
||||
}
|
||||
@@ -132,6 +132,16 @@ impl ClientRequest {
|
||||
self
|
||||
}
|
||||
|
||||
/// Get HTTP version of this request.
|
||||
pub fn get_version(&self) -> &Version {
|
||||
&self.head.version
|
||||
}
|
||||
|
||||
/// Get peer address of this request.
|
||||
pub fn get_peer_addr(&self) -> &Option<net::SocketAddr> {
|
||||
&self.head.peer_addr
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Returns request's headers.
|
||||
pub fn headers(&self) -> &HeaderMap {
|
||||
|
@@ -46,9 +46,9 @@ pub trait FromRequest: Sized {
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest};
|
||||
/// use actix_web::error::ErrorBadRequest;
|
||||
/// use serde_derive::Deserialize;
|
||||
/// use rand;
|
||||
///
|
||||
/// #[derive(Debug, Deserialize)]
|
||||
@@ -119,9 +119,9 @@ where
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest};
|
||||
/// use actix_web::error::ErrorBadRequest;
|
||||
/// use serde_derive::Deserialize;
|
||||
/// use rand;
|
||||
///
|
||||
/// #[derive(Debug, Deserialize)]
|
||||
|
20
src/guard.rs
20
src/guard.rs
@@ -1,16 +1,16 @@
|
||||
//! Route match guards.
|
||||
//!
|
||||
//! Guards are one of the way how actix-web router chooses
|
||||
//! handler service. In essence it just function that accepts
|
||||
//! reference to a `RequestHead` instance and returns boolean.
|
||||
//! Guards are one of the ways how actix-web router chooses a
|
||||
//! handler service. In essence it is just a function that accepts a
|
||||
//! reference to a `RequestHead` instance and returns a boolean.
|
||||
//! It is possible to add guards to *scopes*, *resources*
|
||||
//! and *routes*. Actix provide several guards by default, like various
|
||||
//! http methods, header, etc. To become a guard, type must implement `Guard`
|
||||
//! trait. Simple functions coulds guards as well.
|
||||
//!
|
||||
//! Guard can not modify request object. But it is possible to
|
||||
//! to store extra attributes on a request by using `Extensions` container.
|
||||
//! Extensions container available via `RequestHead::extensions()` method.
|
||||
//! Guards can not modify the request object. But it is possible
|
||||
//! to store extra attributes on a request by using the `Extensions` container.
|
||||
//! Extensions containers are available via the `RequestHead::extensions()` method.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use actix_web::{web, http, dev, guard, App, HttpResponse};
|
||||
@@ -29,11 +29,11 @@
|
||||
use actix_http::http::{self, header, uri::Uri, HttpTryFrom};
|
||||
use actix_http::RequestHead;
|
||||
|
||||
/// Trait defines resource guards. Guards are used for routes selection.
|
||||
/// Trait defines resource guards. Guards are used for route selection.
|
||||
///
|
||||
/// Guard can not modify request object. But it is possible to
|
||||
/// to store extra attributes on request by using `Extensions` container,
|
||||
/// Extensions container available via `RequestHead::extensions()` method.
|
||||
/// Guards can not modify the request object. But it is possible
|
||||
/// to store extra attributes on a request by using the `Extensions` container.
|
||||
/// Extensions containers are available via the `RequestHead::extensions()` method.
|
||||
pub trait Guard {
|
||||
/// Check if request matches predicate
|
||||
fn check(&self, request: &RequestHead) -> bool;
|
||||
|
@@ -155,9 +155,9 @@ impl ConnectionInfo {
|
||||
&self.host
|
||||
}
|
||||
|
||||
/// Remote IP of client initiated HTTP request.
|
||||
/// Remote socket addr of client initiated HTTP request.
|
||||
///
|
||||
/// The IP is resolved through the following headers, in this order:
|
||||
/// The addr is resolved through the following headers, in this order:
|
||||
///
|
||||
/// - Forwarded
|
||||
/// - X-Forwarded-For
|
||||
|
@@ -271,8 +271,8 @@ impl Drop for HttpRequest {
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{web, App, HttpRequest};
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// /// extract `Thing` from request
|
||||
/// fn index(req: HttpRequest) -> String {
|
||||
|
@@ -189,11 +189,21 @@ where
|
||||
/// ));
|
||||
/// }
|
||||
/// ```
|
||||
pub fn data<U: 'static>(mut self, data: U) -> Self {
|
||||
pub fn data<U: 'static>(self, data: U) -> Self {
|
||||
self.register_data(Data::new(data))
|
||||
}
|
||||
|
||||
/// Set or override application data.
|
||||
///
|
||||
/// This method has the same effect as [`Resource::data`](#method.data),
|
||||
/// except that instead of taking a value of some type `T`, it expects a
|
||||
/// value of type `Data<T>`. Use a `Data<T>` extractor to retrieve its
|
||||
/// value.
|
||||
pub fn register_data<U: 'static>(mut self, data: Data<U>) -> Self {
|
||||
if self.data.is_none() {
|
||||
self.data = Some(Extensions::new());
|
||||
}
|
||||
self.data.as_mut().unwrap().insert(Data::new(data));
|
||||
self.data.as_mut().unwrap().insert(data);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -763,4 +773,34 @@ mod tests {
|
||||
let resp = call_service(&mut srv, req);
|
||||
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data() {
|
||||
let mut srv = init_service(
|
||||
App::new()
|
||||
.data(1.0f64)
|
||||
.data(1usize)
|
||||
.register_data(web::Data::new('-'))
|
||||
.service(
|
||||
web::resource("/test")
|
||||
.data(10usize)
|
||||
.register_data(web::Data::new('*'))
|
||||
.guard(guard::Get())
|
||||
.to(
|
||||
|data1: web::Data<usize>,
|
||||
data2: web::Data<char>,
|
||||
data3: web::Data<f64>| {
|
||||
assert_eq!(*data1, 10);
|
||||
assert_eq!(*data2, '*');
|
||||
assert_eq!(*data3, 1.0);
|
||||
HttpResponse::Ok()
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
let req = TestRequest::get().uri("/test").to_request();
|
||||
let resp = call_service(&mut srv, req);
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
}
|
||||
|
@@ -178,8 +178,8 @@ impl Route {
|
||||
/// Set handler function, use request extractors for parameters.
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{web, http, App};
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Info {
|
||||
@@ -239,9 +239,9 @@ impl Route {
|
||||
///
|
||||
/// ```rust
|
||||
/// # use futures::future::ok;
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{web, App, Error};
|
||||
/// use futures::Future;
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Info {
|
||||
|
35
src/scope.rs
35
src/scope.rs
@@ -148,11 +148,20 @@ where
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
pub fn data<U: 'static>(mut self, data: U) -> Self {
|
||||
pub fn data<U: 'static>(self, data: U) -> Self {
|
||||
self.register_data(Data::new(data))
|
||||
}
|
||||
|
||||
/// Set or override application data.
|
||||
///
|
||||
/// This method has the same effect as [`Scope::data`](#method.data), except
|
||||
/// that instead of taking a value of some type `T`, it expects a value of
|
||||
/// type `Data<T>`. Use a `Data<T>` extractor to retrieve its value.
|
||||
pub fn register_data<U: 'static>(mut self, data: Data<U>) -> Self {
|
||||
if self.data.is_none() {
|
||||
self.data = Some(Extensions::new());
|
||||
}
|
||||
self.data.as_mut().unwrap().insert(Data::new(data));
|
||||
self.data.as_mut().unwrap().insert(data);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -1082,6 +1091,28 @@ mod tests {
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_override_register_data() {
|
||||
let mut srv = init_service(
|
||||
App::new().register_data(web::Data::new(1usize)).service(
|
||||
web::scope("app")
|
||||
.register_data(web::Data::new(10usize))
|
||||
.route(
|
||||
"/t",
|
||||
web::get().to(|data: web::Data<usize>| {
|
||||
assert_eq!(*data, 10);
|
||||
let _ = data.clone();
|
||||
HttpResponse::Ok()
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
let req = TestRequest::with_uri("/app/t").to_request();
|
||||
let resp = call_service(&mut srv, req);
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_config() {
|
||||
let mut srv =
|
||||
|
@@ -435,6 +435,37 @@ where
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[cfg(feature = "uds")]
|
||||
/// Start listening for unix domain connections on existing listener.
|
||||
///
|
||||
/// This method is available with `uds` feature.
|
||||
pub fn listen_uds(
|
||||
mut self,
|
||||
lst: std::os::unix::net::UnixListener,
|
||||
) -> io::Result<Self> {
|
||||
let cfg = self.config.clone();
|
||||
let factory = self.factory.clone();
|
||||
// todo duplicated:
|
||||
self.sockets.push(Socket {
|
||||
scheme: "http",
|
||||
addr: net::SocketAddr::new(
|
||||
net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)),
|
||||
8080,
|
||||
),
|
||||
});
|
||||
|
||||
let addr = format!("actix-web-service-{:?}", lst.local_addr()?);
|
||||
|
||||
self.builder = self.builder.listen_uds(addr, lst, move || {
|
||||
let c = cfg.lock();
|
||||
HttpService::build()
|
||||
.keep_alive(c.keep_alive)
|
||||
.client_timeout(c.client_timeout)
|
||||
.finish(factory())
|
||||
})?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[cfg(feature = "uds")]
|
||||
/// Start listening for incoming unix domain connections.
|
||||
///
|
||||
|
77
src/test.rs
77
src/test.rs
@@ -1,5 +1,4 @@
|
||||
//! Various helpers for Actix applications to use during testing.
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use actix_http::http::header::{ContentType, Header, HeaderName, IntoHeaderValue};
|
||||
@@ -7,17 +6,17 @@ use actix_http::http::{HttpTryFrom, Method, StatusCode, Uri, Version};
|
||||
use actix_http::test::TestRequest as HttpTestRequest;
|
||||
use actix_http::{cookie::Cookie, Extensions, Request};
|
||||
use actix_router::{Path, ResourceDef, Url};
|
||||
use actix_rt::{System, SystemRunner};
|
||||
use actix_server_config::ServerConfig;
|
||||
use actix_service::{IntoNewService, IntoService, NewService, Service};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::future::{lazy, ok, Future, IntoFuture};
|
||||
use futures::future::{ok, Future};
|
||||
use futures::Stream;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use serde_json;
|
||||
|
||||
pub use actix_http::test::TestBuffer;
|
||||
pub use actix_testing::{block_fn, block_on, run_on};
|
||||
|
||||
use crate::config::{AppConfig, AppConfigInner};
|
||||
use crate::data::Data;
|
||||
@@ -27,78 +26,6 @@ use crate::rmap::ResourceMap;
|
||||
use crate::service::{ServiceRequest, ServiceResponse};
|
||||
use crate::{Error, HttpRequest, HttpResponse};
|
||||
|
||||
thread_local! {
|
||||
static RT: RefCell<Inner> = {
|
||||
RefCell::new(Inner(Some(System::builder().build())))
|
||||
};
|
||||
}
|
||||
|
||||
struct Inner(Option<SystemRunner>);
|
||||
|
||||
impl Inner {
|
||||
fn get_mut(&mut self) -> &mut SystemRunner {
|
||||
self.0.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Inner {
|
||||
fn drop(&mut self) {
|
||||
std::mem::forget(self.0.take().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the provided future, blocking the current thread until the future
|
||||
/// completes.
|
||||
///
|
||||
/// This function can be used to synchronously block the current thread
|
||||
/// until the provided `future` has resolved either successfully or with an
|
||||
/// error. The result of the future is then returned from this function
|
||||
/// call.
|
||||
///
|
||||
/// Note that this function is intended to be used only for testing purpose.
|
||||
/// This function panics on nested call.
|
||||
pub fn block_on<F>(f: F) -> Result<F::Item, F::Error>
|
||||
where
|
||||
F: IntoFuture,
|
||||
{
|
||||
RT.with(move |rt| rt.borrow_mut().get_mut().block_on(f.into_future()))
|
||||
}
|
||||
|
||||
/// Runs the provided function, blocking the current thread until the result
|
||||
/// future completes.
|
||||
///
|
||||
/// This function can be used to synchronously block the current thread
|
||||
/// until the provided `future` has resolved either successfully or with an
|
||||
/// error. The result of the future is then returned from this function
|
||||
/// call.
|
||||
///
|
||||
/// Note that this function is intended to be used only for testing purpose.
|
||||
/// This function panics on nested call.
|
||||
pub fn block_fn<F, R>(f: F) -> Result<R::Item, R::Error>
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
R: IntoFuture,
|
||||
{
|
||||
RT.with(move |rt| rt.borrow_mut().get_mut().block_on(lazy(f)))
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Runs the provided function, with runtime enabled.
|
||||
///
|
||||
/// Note that this function is intended to be used only for testing purpose.
|
||||
/// This function panics on nested call.
|
||||
pub fn run_on<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
RT.with(move |rt| {
|
||||
rt.borrow_mut()
|
||||
.get_mut()
|
||||
.block_on(lazy(|| Ok::<_, ()>(f())))
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Create service that always responds with `HttpResponse::Ok()`
|
||||
pub fn ok_service(
|
||||
) -> impl Service<Request = ServiceRequest, Response = ServiceResponse<Body>, Error = Error>
|
||||
|
@@ -35,9 +35,8 @@ use crate::responder::Responder;
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # extern crate actix_web;
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{web, App};
|
||||
/// use actix_web::web;
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct FormData {
|
||||
@@ -61,9 +60,9 @@ use crate::responder::Responder;
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate serde_derive;
|
||||
/// # use actix_web::*;
|
||||
/// #
|
||||
/// use actix_web::*;
|
||||
/// use serde_derive::Serialize;
|
||||
///
|
||||
/// #[derive(Serialize)]
|
||||
/// struct SomeForm {
|
||||
/// name: String,
|
||||
@@ -167,8 +166,8 @@ impl<T: Serialize> Responder for Form<T> {
|
||||
/// Form extractor configuration
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{web, App, FromRequest, Result};
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct FormData {
|
||||
|
@@ -33,8 +33,8 @@ use crate::responder::Responder;
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{web, App};
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Info {
|
||||
@@ -60,9 +60,9 @@ use crate::responder::Responder;
|
||||
/// trait from *serde*.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate serde_derive;
|
||||
/// # use actix_web::*;
|
||||
/// #
|
||||
/// use actix_web::*;
|
||||
/// use serde_derive::Serialize;
|
||||
///
|
||||
/// #[derive(Serialize)]
|
||||
/// struct MyObj {
|
||||
/// name: String,
|
||||
@@ -144,8 +144,8 @@ impl<T: Serialize> Responder for Json<T> {
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{web, App};
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Info {
|
||||
@@ -203,8 +203,8 @@ where
|
||||
/// Json extractor configuration
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{error, web, App, FromRequest, HttpResponse};
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Info {
|
||||
|
@@ -39,8 +39,8 @@ use crate::FromRequest;
|
||||
/// implements `Deserialize` trait from *serde*.
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{web, App, Error};
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Info {
|
||||
@@ -134,8 +134,8 @@ impl<T: fmt::Display> fmt::Display for Path<T> {
|
||||
/// implements `Deserialize` trait from *serde*.
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{web, App, Error};
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Info {
|
||||
@@ -190,10 +190,9 @@ where
|
||||
/// Path extractor configuration
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_derive;
|
||||
/// use actix_web::web::PathConfig;
|
||||
/// use actix_web::{error, web, App, FromRequest, HttpResponse};
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize, Debug)]
|
||||
/// enum Folder {
|
||||
|
@@ -43,7 +43,14 @@ use crate::request::HttpRequest;
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Payload(crate::dev::Payload);
|
||||
pub struct Payload(pub crate::dev::Payload);
|
||||
|
||||
impl Payload {
|
||||
/// Deconstruct to a inner value
|
||||
pub fn into_inner(self) -> crate::dev::Payload {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for Payload {
|
||||
type Item = Bytes;
|
||||
|
@@ -21,8 +21,8 @@ use crate::request::HttpRequest;
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{web, App};
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// #[derive(Debug, Deserialize)]
|
||||
/// pub enum ResponseType {
|
||||
@@ -99,8 +99,8 @@ impl<T: fmt::Display> fmt::Display for Query<T> {
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{web, App};
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// #[derive(Debug, Deserialize)]
|
||||
/// pub enum ResponseType {
|
||||
@@ -169,8 +169,8 @@ where
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{error, web, App, FromRequest, HttpResponse};
|
||||
/// use serde_derive::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Info {
|
||||
|
Reference in New Issue
Block a user