1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-27 17:52:56 +01:00

added default headers middleware

This commit is contained in:
Nikolay Kim 2017-12-03 20:47:15 -08:00
parent d35be02587
commit 57fd35ffc1
7 changed files with 209 additions and 63 deletions

View File

@ -21,7 +21,6 @@ fn main() {
"guide/src/qs_7.md",
"guide/src/qs_9.md",
"guide/src/qs_10.md",
"guide/src/qs_11.md",
"guide/src/qs_12.md",
"guide/src/qs_13.md",
]);

View File

@ -8,7 +8,6 @@
- [Application state](./qs_6.md)
- [Request & Response](./qs_7.md)
- [WebSockets](./qs_9.md)
- [User sessions](./qs_10.md)
- [Logging](./qs_11.md)
- [Middlewares](./qs_10.md)
- [Static file handling](./qs_12.md)
- [HTTP/2](./qs_13.md)

View File

@ -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

View File

@ -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']

View File

@ -303,6 +303,7 @@ impl HttpResponseBuilder {
/// By default `ContentEncoding::Auto` is used, which automatically
/// negotiates content encoding based on request's `Accept-Encoding` headers.
/// To enforce specific encoding, use specific ContentEncoding` value.
#[inline]
pub fn content_encoding(&mut self, enc: ContentEncoding) -> &mut Self {
if let Some(parts) = parts(&mut self.parts, &self.err) {
parts.encoding = enc;
@ -311,6 +312,7 @@ impl HttpResponseBuilder {
}
/// Set connection type
#[inline]
pub fn connection_type(&mut self, conn: ConnectionType) -> &mut Self {
if let Some(parts) = parts(&mut self.parts, &self.err) {
parts.connection_type = Some(conn);
@ -319,16 +321,19 @@ impl HttpResponseBuilder {
}
/// Set connection type to Upgrade
#[inline]
pub fn upgrade(&mut self) -> &mut Self {
self.connection_type(ConnectionType::Upgrade)
}
/// Force close connection, even if it is marked as keep-alive
#[inline]
pub fn force_close(&mut self) -> &mut Self {
self.connection_type(ConnectionType::Close)
}
/// Enables automatic chunked transfer encoding
#[inline]
pub fn enable_chunked(&mut self) -> &mut Self {
if let Some(parts) = parts(&mut self.parts, &self.err) {
parts.chunked = true;
@ -337,6 +342,7 @@ impl HttpResponseBuilder {
}
/// Set response content type
#[inline]
pub fn content_type<V>(&mut self, value: V) -> &mut Self
where HeaderValue: HttpTryFrom<V>
{

View 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");
}
}

View File

@ -7,7 +7,9 @@ use httpresponse::HttpResponse;
mod logger;
mod session;
mod defaultheaders;
pub use self::logger::Logger;
pub use self::defaultheaders::{DefaultHeaders, DefaultHeadersBuilder};
pub use self::session::{RequestSession, Session, SessionImpl, SessionBackend, SessionStorage,
CookieSessionError, CookieSessionBackend, CookieSessionBackendBuilder};