mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-28 01:32:57 +01:00
added default headers middleware
This commit is contained in:
parent
d35be02587
commit
57fd35ffc1
1
build.rs
1
build.rs
@ -21,7 +21,6 @@ fn main() {
|
|||||||
"guide/src/qs_7.md",
|
"guide/src/qs_7.md",
|
||||||
"guide/src/qs_9.md",
|
"guide/src/qs_9.md",
|
||||||
"guide/src/qs_10.md",
|
"guide/src/qs_10.md",
|
||||||
"guide/src/qs_11.md",
|
|
||||||
"guide/src/qs_12.md",
|
"guide/src/qs_12.md",
|
||||||
"guide/src/qs_13.md",
|
"guide/src/qs_13.md",
|
||||||
]);
|
]);
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
- [Application state](./qs_6.md)
|
- [Application state](./qs_6.md)
|
||||||
- [Request & Response](./qs_7.md)
|
- [Request & Response](./qs_7.md)
|
||||||
- [WebSockets](./qs_9.md)
|
- [WebSockets](./qs_9.md)
|
||||||
- [User sessions](./qs_10.md)
|
- [Middlewares](./qs_10.md)
|
||||||
- [Logging](./qs_11.md)
|
|
||||||
- [Static file handling](./qs_12.md)
|
- [Static file handling](./qs_12.md)
|
||||||
- [HTTP/2](./qs_13.md)
|
- [HTTP/2](./qs_13.md)
|
||||||
|
@ -1 +1,87 @@
|
|||||||
# User sessions
|
# Middlewares
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
Logging is implemented as middleware. Middlewares get executed in same order as registraton order.
|
||||||
|
It is common to register logging middleware as first middleware for application.
|
||||||
|
Logging middleware has to be registered for each application.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
Create `Logger` middlewares with the specified `format`.
|
||||||
|
Default `Logger` could be created with `default` method, it uses the default format:
|
||||||
|
|
||||||
|
```ignore
|
||||||
|
%a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T
|
||||||
|
```
|
||||||
|
```rust
|
||||||
|
extern crate actix_web;
|
||||||
|
use actix_web::Application;
|
||||||
|
use actix_web::middlewares::Logger;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Application::default("/")
|
||||||
|
.middleware(Logger::default())
|
||||||
|
.middleware(Logger::new("%a %{User-Agent}i"))
|
||||||
|
.finish();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is example of default logging format:
|
||||||
|
|
||||||
|
```
|
||||||
|
INFO:actix_web::middlewares::logger: 127.0.0.1:59934 [02/Dec/2017:00:21:43 -0800] "GET / HTTP/1.1" 302 0 "-" "curl/7.54.0" 0.000397
|
||||||
|
INFO:actix_web::middlewares::logger: 127.0.0.1:59947 [02/Dec/2017:00:22:40 -0800] "GET /index.html HTTP/1.1" 200 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:57.0) Gecko/20100101 Firefox/57.0" 0.000646
|
||||||
|
```
|
||||||
|
|
||||||
|
### Format
|
||||||
|
|
||||||
|
`%%` The percent sign
|
||||||
|
|
||||||
|
`%a` Remote IP-address (IP-address of proxy if using reverse proxy)
|
||||||
|
|
||||||
|
`%t` Time when the request was started to process
|
||||||
|
|
||||||
|
`%P` The process ID of the child that serviced the request
|
||||||
|
|
||||||
|
`%r` First line of request
|
||||||
|
|
||||||
|
`%s` Response status code
|
||||||
|
|
||||||
|
`%b` Size of response in bytes, including HTTP headers
|
||||||
|
|
||||||
|
`%T` Time taken to serve the request, in seconds with floating fraction in .06f format
|
||||||
|
|
||||||
|
`%D` Time taken to serve the request, in milliseconds
|
||||||
|
|
||||||
|
`%{FOO}i` request.headers['FOO']
|
||||||
|
|
||||||
|
`%{FOO}o` response.headers['FOO']
|
||||||
|
|
||||||
|
`%{FOO}e` os.environ['FOO']
|
||||||
|
|
||||||
|
|
||||||
|
## Default headers
|
||||||
|
|
||||||
|
It is possible to set default response headers with `DefaultHeaders` middleware.
|
||||||
|
*DefaultHeaders* middleware does not set header if response headers already contains it.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
extern crate actix_web;
|
||||||
|
use actix_web::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let app = Application::default("/")
|
||||||
|
.middleware(
|
||||||
|
middlewares::DefaultHeaders::build()
|
||||||
|
.header("X-Version", "0.2")
|
||||||
|
.finish())
|
||||||
|
.resource("/test", |r| {
|
||||||
|
r.get(|req| httpcodes::HTTPOk);
|
||||||
|
r.handler(Method::HEAD, |req| httpcodes::HTTPMethodNotAllowed);
|
||||||
|
})
|
||||||
|
.finish();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## User sessions
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
# Logging
|
|
||||||
|
|
||||||
Logging is implemented as middleware. Middlewares get executed in same order as registraton order.
|
|
||||||
It is common to register logging middleware as first middleware for application.
|
|
||||||
Logging middleware has to be registered for each application.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Create `Logger` middlewares with the specified `format`.
|
|
||||||
Default `Logger` could be created with `default` method, it uses the default format:
|
|
||||||
|
|
||||||
```ignore
|
|
||||||
%a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T
|
|
||||||
```
|
|
||||||
```rust
|
|
||||||
extern crate actix_web;
|
|
||||||
use actix_web::Application;
|
|
||||||
use actix_web::middlewares::Logger;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
Application::default("/")
|
|
||||||
.middleware(Logger::default())
|
|
||||||
.middleware(Logger::new("%a %{User-Agent}i"))
|
|
||||||
.finish();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Here is example of default logging format:
|
|
||||||
|
|
||||||
```
|
|
||||||
INFO:actix_web::middlewares::logger: 127.0.0.1:59934 [02/Dec/2017:00:21:43 -0800] "GET / HTTP/1.1" 302 0 "-" "curl/7.54.0" 0.000397
|
|
||||||
INFO:actix_web::middlewares::logger: 127.0.0.1:59947 [02/Dec/2017:00:22:40 -0800] "GET /index.html HTTP/1.1" 200 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:57.0) Gecko/20100101 Firefox/57.0" 0.000646
|
|
||||||
```
|
|
||||||
|
|
||||||
## Format
|
|
||||||
|
|
||||||
`%%` The percent sign
|
|
||||||
|
|
||||||
`%a` Remote IP-address (IP-address of proxy if using reverse proxy)
|
|
||||||
|
|
||||||
`%t` Time when the request was started to process
|
|
||||||
|
|
||||||
`%P` The process ID of the child that serviced the request
|
|
||||||
|
|
||||||
`%r` First line of request
|
|
||||||
|
|
||||||
`%s` Response status code
|
|
||||||
|
|
||||||
`%b` Size of response in bytes, including HTTP headers
|
|
||||||
|
|
||||||
`%T` Time taken to serve the request, in seconds with floating fraction in .06f format
|
|
||||||
|
|
||||||
`%D` Time taken to serve the request, in milliseconds
|
|
||||||
|
|
||||||
`%{FOO}i` request.headers['FOO']
|
|
||||||
|
|
||||||
`%{FOO}o` response.headers['FOO']
|
|
||||||
|
|
||||||
`%{FOO}e` os.environ['FOO']
|
|
@ -303,6 +303,7 @@ impl HttpResponseBuilder {
|
|||||||
/// By default `ContentEncoding::Auto` is used, which automatically
|
/// By default `ContentEncoding::Auto` is used, which automatically
|
||||||
/// negotiates content encoding based on request's `Accept-Encoding` headers.
|
/// negotiates content encoding based on request's `Accept-Encoding` headers.
|
||||||
/// To enforce specific encoding, use specific ContentEncoding` value.
|
/// To enforce specific encoding, use specific ContentEncoding` value.
|
||||||
|
#[inline]
|
||||||
pub fn content_encoding(&mut self, enc: ContentEncoding) -> &mut Self {
|
pub fn content_encoding(&mut self, enc: ContentEncoding) -> &mut Self {
|
||||||
if let Some(parts) = parts(&mut self.parts, &self.err) {
|
if let Some(parts) = parts(&mut self.parts, &self.err) {
|
||||||
parts.encoding = enc;
|
parts.encoding = enc;
|
||||||
@ -311,6 +312,7 @@ impl HttpResponseBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set connection type
|
/// Set connection type
|
||||||
|
#[inline]
|
||||||
pub fn connection_type(&mut self, conn: ConnectionType) -> &mut Self {
|
pub fn connection_type(&mut self, conn: ConnectionType) -> &mut Self {
|
||||||
if let Some(parts) = parts(&mut self.parts, &self.err) {
|
if let Some(parts) = parts(&mut self.parts, &self.err) {
|
||||||
parts.connection_type = Some(conn);
|
parts.connection_type = Some(conn);
|
||||||
@ -319,16 +321,19 @@ impl HttpResponseBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set connection type to Upgrade
|
/// Set connection type to Upgrade
|
||||||
|
#[inline]
|
||||||
pub fn upgrade(&mut self) -> &mut Self {
|
pub fn upgrade(&mut self) -> &mut Self {
|
||||||
self.connection_type(ConnectionType::Upgrade)
|
self.connection_type(ConnectionType::Upgrade)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Force close connection, even if it is marked as keep-alive
|
/// Force close connection, even if it is marked as keep-alive
|
||||||
|
#[inline]
|
||||||
pub fn force_close(&mut self) -> &mut Self {
|
pub fn force_close(&mut self) -> &mut Self {
|
||||||
self.connection_type(ConnectionType::Close)
|
self.connection_type(ConnectionType::Close)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enables automatic chunked transfer encoding
|
/// Enables automatic chunked transfer encoding
|
||||||
|
#[inline]
|
||||||
pub fn enable_chunked(&mut self) -> &mut Self {
|
pub fn enable_chunked(&mut self) -> &mut Self {
|
||||||
if let Some(parts) = parts(&mut self.parts, &self.err) {
|
if let Some(parts) = parts(&mut self.parts, &self.err) {
|
||||||
parts.chunked = true;
|
parts.chunked = true;
|
||||||
@ -337,6 +342,7 @@ impl HttpResponseBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set response content type
|
/// Set response content type
|
||||||
|
#[inline]
|
||||||
pub fn content_type<V>(&mut self, value: V) -> &mut Self
|
pub fn content_type<V>(&mut self, value: V) -> &mut Self
|
||||||
where HeaderValue: HttpTryFrom<V>
|
where HeaderValue: HttpTryFrom<V>
|
||||||
{
|
{
|
||||||
|
113
src/middlewares/defaultheaders.rs
Normal file
113
src/middlewares/defaultheaders.rs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
//! Default response headers
|
||||||
|
use http::{HeaderMap, HttpTryFrom};
|
||||||
|
use http::header::{HeaderName, HeaderValue};
|
||||||
|
|
||||||
|
use httprequest::HttpRequest;
|
||||||
|
use httpresponse::HttpResponse;
|
||||||
|
use middlewares::{Response, Middleware};
|
||||||
|
|
||||||
|
/// `Middleware` for setting default response headers.
|
||||||
|
///
|
||||||
|
/// This middleware does not set header if response headers already contains it.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// extern crate actix_web;
|
||||||
|
/// use actix_web::*;
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = Application::default("/")
|
||||||
|
/// .middleware(
|
||||||
|
/// middlewares::DefaultHeaders::build()
|
||||||
|
/// .header("X-Version", "0.2")
|
||||||
|
/// .finish())
|
||||||
|
/// .resource("/test", |r| {
|
||||||
|
/// r.get(|req| httpcodes::HTTPOk);
|
||||||
|
/// r.handler(Method::HEAD, |req| httpcodes::HTTPMethodNotAllowed);
|
||||||
|
/// })
|
||||||
|
/// .finish();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub struct DefaultHeaders(HeaderMap);
|
||||||
|
|
||||||
|
impl DefaultHeaders {
|
||||||
|
pub fn build() -> DefaultHeadersBuilder {
|
||||||
|
DefaultHeadersBuilder{headers: Some(HeaderMap::new())}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Middleware for DefaultHeaders {
|
||||||
|
|
||||||
|
fn response(&self, _: &mut HttpRequest, mut resp: HttpResponse) -> Response {
|
||||||
|
for (key, value) in self.0.iter() {
|
||||||
|
if !resp.headers().contains_key(key) {
|
||||||
|
resp.headers_mut().insert(key, value.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Response::Done(resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Structure that follows the builder pattern for building `DefaultHeaders` middleware.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DefaultHeadersBuilder {
|
||||||
|
headers: Option<HeaderMap>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DefaultHeadersBuilder {
|
||||||
|
|
||||||
|
/// Set a header.
|
||||||
|
#[inline]
|
||||||
|
#[cfg_attr(feature = "cargo-clippy", allow(match_wild_err_arm))]
|
||||||
|
pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
|
||||||
|
where HeaderName: HttpTryFrom<K>,
|
||||||
|
HeaderValue: HttpTryFrom<V>
|
||||||
|
{
|
||||||
|
if let Some(ref mut headers) = self.headers {
|
||||||
|
match HeaderName::try_from(key) {
|
||||||
|
Ok(key) => {
|
||||||
|
match HeaderValue::try_from(value) {
|
||||||
|
Ok(value) => { headers.append(key, value); }
|
||||||
|
Err(_) => panic!("Can not create header value"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => panic!("Can not create header name"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finishes building and returns the built `DefaultHeaders` middleware.
|
||||||
|
pub fn finish(&mut self) -> DefaultHeaders {
|
||||||
|
let headers = self.headers.take().expect("cannot reuse middleware builder");
|
||||||
|
DefaultHeaders(headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use http::header::CONTENT_TYPE;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_default_headers() {
|
||||||
|
let mw = DefaultHeaders::build()
|
||||||
|
.header(CONTENT_TYPE, "0001")
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let mut req = HttpRequest::default();
|
||||||
|
|
||||||
|
let resp = HttpResponse::Ok().finish().unwrap();
|
||||||
|
let resp = match mw.response(&mut req, resp) {
|
||||||
|
Response::Done(resp) => resp,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
|
||||||
|
|
||||||
|
let resp = HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish().unwrap();
|
||||||
|
let resp = match mw.response(&mut req, resp) {
|
||||||
|
Response::Done(resp) => resp,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0002");
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,9 @@ use httpresponse::HttpResponse;
|
|||||||
|
|
||||||
mod logger;
|
mod logger;
|
||||||
mod session;
|
mod session;
|
||||||
|
mod defaultheaders;
|
||||||
pub use self::logger::Logger;
|
pub use self::logger::Logger;
|
||||||
|
pub use self::defaultheaders::{DefaultHeaders, DefaultHeadersBuilder};
|
||||||
pub use self::session::{RequestSession, Session, SessionImpl, SessionBackend, SessionStorage,
|
pub use self::session::{RequestSession, Session, SessionImpl, SessionBackend, SessionStorage,
|
||||||
CookieSessionError, CookieSessionBackend, CookieSessionBackendBuilder};
|
CookieSessionError, CookieSessionBackend, CookieSessionBackendBuilder};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user