mirror of
https://github.com/fafhrd91/actix-web
synced 2025-07-22 17:07:19 +02:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b4ed564e5d | ||
|
80fbc2e9ec | ||
|
f58065082e | ||
|
6048817ba7 | ||
|
e408b68744 | ||
|
b878613e10 | ||
|
85b275bb2b | ||
|
d6abd2fe22 | ||
|
b79a9aaec7 | ||
|
b9586b3f71 | ||
|
d3b12d885e | ||
|
f21386708a | ||
|
b48a2d4d7b | ||
|
35b754a3ab | ||
|
1079c5c562 | ||
|
f4bb7efa89 | ||
|
0099091e96 | ||
|
c352a69d54 | ||
|
f5347ec897 | ||
|
b367f07d56 | ||
|
6a75a3d683 | ||
|
56b924e155 |
26
CHANGES.md
26
CHANGES.md
@@ -1,5 +1,31 @@
|
||||
# Changes
|
||||
|
||||
## [0.7.2] - 2018-07-26
|
||||
|
||||
### Added
|
||||
|
||||
* Add implementation of `FromRequest<S>` for `Option<T>` and `Result<T, Error>`
|
||||
|
||||
* Allow to handle application prefix, i.e. allow to handle `/app` path
|
||||
for application with `/app` prefix.
|
||||
Check [`App::prefix()`](https://actix.rs/actix-web/actix_web/struct.App.html#method.prefix)
|
||||
api doc.
|
||||
|
||||
* Add `CookieSessionBackend::http_only` method to set `HttpOnly` directive of cookies
|
||||
|
||||
### Changed
|
||||
|
||||
* Upgrade to cookie 0.11
|
||||
|
||||
* Removed the timestamp from the default logger middleware
|
||||
|
||||
### Fixed
|
||||
|
||||
* Missing response header "content-encoding" #421
|
||||
|
||||
* Fix stream draining for http/2 connections #290
|
||||
|
||||
|
||||
## [0.7.1] - 2018-07-21
|
||||
|
||||
### Fixed
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-web"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
|
||||
readme = "README.md"
|
||||
@@ -56,7 +56,7 @@ base64 = "0.9"
|
||||
bitflags = "1.0"
|
||||
h2 = "0.1"
|
||||
htmlescape = "0.3"
|
||||
http = "^0.1.5"
|
||||
http = "^0.1.8"
|
||||
httparse = "1.3"
|
||||
log = "0.4"
|
||||
mime = "0.3"
|
||||
@@ -76,7 +76,7 @@ lazy_static = "1.0"
|
||||
lazycell = "1.0.0"
|
||||
parking_lot = "0.6"
|
||||
url = { version="1.7", features=["query_encoding"] }
|
||||
cookie = { version="0.10", features=["percent-encode"] }
|
||||
cookie = { version="0.11", features=["percent-encode"] }
|
||||
brotli2 = { version="^0.3.2", optional = true }
|
||||
flate2 = { version="1.0", optional = true, default-features = false }
|
||||
|
||||
|
@@ -12,12 +12,7 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust.
|
||||
* Multipart streams
|
||||
* Static assets
|
||||
* SSL support with OpenSSL or `native-tls`
|
||||
* Middlewares ([Logger](https://actix.rs/book/actix-web/sec-9-middlewares.html#logging),
|
||||
[Session](https://actix.rs/book/actix-web/sec-9-middlewares.html#user-sessions),
|
||||
[Redis sessions](https://github.com/actix/actix-redis),
|
||||
[DefaultHeaders](https://actix.rs/book/actix-web/sec-9-middlewares.html#default-headers),
|
||||
[CORS](https://actix.rs/actix-web/actix_web/middleware/cors/index.html),
|
||||
[CSRF](https://actix.rs/actix-web/actix_web/middleware/csrf/index.html))
|
||||
* Middlewares ([Logger, Session, CORS, CSRF, etc](https://actix.rs/docs/middleware/))
|
||||
* Includes an asynchronous [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html)
|
||||
* Built on top of [Actix actor framework](https://github.com/actix/actix)
|
||||
|
||||
|
@@ -171,7 +171,9 @@ where
|
||||
/// In the following example only requests with an `/app/` path
|
||||
/// prefix get handled. Requests with path `/app/test/` would be
|
||||
/// handled, while requests with the paths `/application` or
|
||||
/// `/other/...` would return `NOT FOUND`.
|
||||
/// `/other/...` would return `NOT FOUND`. It is also possible to
|
||||
/// handle `/app` path, to do this you can register resource for
|
||||
/// empty string `""`
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate actix_web;
|
||||
@@ -180,6 +182,8 @@ where
|
||||
/// fn main() {
|
||||
/// let app = App::new()
|
||||
/// .prefix("/app")
|
||||
/// .resource("", |r| r.f(|_| HttpResponse::Ok())) // <- handle `/app` path
|
||||
/// .resource("/", |r| r.f(|_| HttpResponse::Ok())) // <- handle `/app/` path
|
||||
/// .resource("/test", |r| {
|
||||
/// r.get().f(|_| HttpResponse::Ok());
|
||||
/// r.head().f(|_| HttpResponse::MethodNotAllowed());
|
||||
@@ -610,7 +614,6 @@ impl<S: 'static> Iterator for App<S> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use body::{Binary, Body};
|
||||
use fs;
|
||||
use http::StatusCode;
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
@@ -823,6 +826,23 @@ mod tests {
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_option_responder() {
|
||||
let app = App::new()
|
||||
.resource("/none", |r| r.f(|_| -> Option<&'static str> { None }))
|
||||
.resource("/some", |r| r.f(|_| Some("some")))
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/none").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/some").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
assert_eq!(resp.as_msg().body(), &Body::Binary(Binary::Slice(b"some")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_filter() {
|
||||
let mut srv = TestServer::with_factory(|| {
|
||||
@@ -841,19 +861,21 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_option_responder() {
|
||||
let app = App::new()
|
||||
.resource("/none", |r| r.f(|_| -> Option<&'static str> { None }))
|
||||
.resource("/some", |r| r.f(|_| Some("some")))
|
||||
.finish();
|
||||
fn test_prefix_root() {
|
||||
let mut srv = TestServer::with_factory(|| {
|
||||
App::new()
|
||||
.prefix("/test")
|
||||
.resource("/", |r| r.f(|_| HttpResponse::Ok()))
|
||||
.resource("", |r| r.f(|_| HttpResponse::Created()))
|
||||
});
|
||||
|
||||
let req = TestRequest::with_uri("/none").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
let request = srv.get().uri(srv.url("/test/")).finish().unwrap();
|
||||
let response = srv.execute(request.send()).unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/some").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
assert_eq!(resp.as_msg().body(), &Body::Binary(Binary::Slice(b"some")));
|
||||
let request = srv.get().uri(srv.url("/test")).finish().unwrap();
|
||||
let response = srv.execute(request.send()).unwrap();
|
||||
assert_eq!(response.status(), StatusCode::CREATED);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -599,7 +599,7 @@ impl ClientConnector {
|
||||
}
|
||||
Acquire::Available => {
|
||||
// create new connection
|
||||
self.connect_waiter(key.clone(), waiter, ctx);
|
||||
self.connect_waiter(&key, waiter, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -608,7 +608,8 @@ impl ClientConnector {
|
||||
self.waiters = Some(act_waiters);
|
||||
}
|
||||
|
||||
fn connect_waiter(&mut self, key: Key, waiter: Waiter, ctx: &mut Context<Self>) {
|
||||
fn connect_waiter(&mut self, key: &Key, waiter: Waiter, ctx: &mut Context<Self>) {
|
||||
let key = key.clone();
|
||||
let conn = AcquiredConn(key.clone(), Some(self.acq_tx.clone()));
|
||||
|
||||
let key2 = key.clone();
|
||||
@@ -635,7 +636,7 @@ impl ClientConnector {
|
||||
act.connector
|
||||
.connect_async(&key.host, stream)
|
||||
.into_actor(act)
|
||||
.then(move |res, act, _| {
|
||||
.then(move |res, _, _| {
|
||||
match res {
|
||||
Err(e) => {
|
||||
let _ = waiter.tx.send(Err(
|
||||
@@ -828,7 +829,7 @@ impl Handler<Connect> for ClientConnector {
|
||||
wait,
|
||||
conn_timeout,
|
||||
};
|
||||
self.connect_waiter(key.clone(), waiter, ctx);
|
||||
self.connect_waiter(&key, waiter, ctx);
|
||||
|
||||
return ActorResponse::async(
|
||||
rx.map_err(|_| ClientConnectorError::Disconnected)
|
||||
@@ -885,7 +886,7 @@ impl Handler<Connect> for ClientConnector {
|
||||
wait,
|
||||
conn_timeout,
|
||||
};
|
||||
self.connect_waiter(key.clone(), waiter, ctx);
|
||||
self.connect_waiter(&key, waiter, ctx);
|
||||
|
||||
ActorResponse::async(
|
||||
rx.map_err(|_| ClientConnectorError::Disconnected)
|
||||
|
@@ -216,7 +216,7 @@ impl Future for SendRequest {
|
||||
|
||||
match pl.parse() {
|
||||
Ok(Async::Ready(mut resp)) => {
|
||||
if self.req.method() == &Method::HEAD {
|
||||
if self.req.method() == Method::HEAD {
|
||||
pl.parser.take();
|
||||
}
|
||||
resp.set_pipeline(pl);
|
||||
|
217
src/extractor.rs
217
src/extractor.rs
@@ -6,7 +6,7 @@ use std::{fmt, str};
|
||||
use bytes::Bytes;
|
||||
use encoding::all::UTF_8;
|
||||
use encoding::types::{DecoderTrap, Encoding};
|
||||
use futures::{Async, Future, Poll};
|
||||
use futures::{future, Async, Future, Poll};
|
||||
use mime::Mime;
|
||||
use serde::de::{self, DeserializeOwned};
|
||||
use serde_urlencoded;
|
||||
@@ -17,6 +17,7 @@ use handler::{AsyncResult, FromRequest};
|
||||
use httpmessage::{HttpMessage, MessageBody, UrlEncoded};
|
||||
use httprequest::HttpRequest;
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
/// Extract typed information from the request's path.
|
||||
///
|
||||
/// ## Example
|
||||
@@ -128,6 +129,7 @@ impl<T: fmt::Display> fmt::Display for Path<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
/// Extract typed information from from the request's query.
|
||||
///
|
||||
/// ## Example
|
||||
@@ -215,6 +217,7 @@ impl<T: fmt::Display> fmt::Display for Query<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
/// Extract typed information from the request's body.
|
||||
///
|
||||
/// To extract typed information from request's body, the type `T` must
|
||||
@@ -455,6 +458,126 @@ impl<S: 'static> FromRequest<S> for String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Optionally extract a field from the request
|
||||
///
|
||||
/// If the FromRequest for T fails, return None rather than returning an error response
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate actix_web;
|
||||
/// extern crate rand;
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{http, App, Result, HttpRequest, Error, FromRequest};
|
||||
/// use actix_web::error::ErrorBadRequest;
|
||||
///
|
||||
/// #[derive(Debug, Deserialize)]
|
||||
/// struct Thing { name: String }
|
||||
///
|
||||
/// impl<S> FromRequest<S> for Thing {
|
||||
/// type Config = ();
|
||||
/// type Result = Result<Thing, Error>;
|
||||
///
|
||||
/// #[inline]
|
||||
/// fn from_request(req: &HttpRequest<S>, _cfg: &Self::Config) -> Self::Result {
|
||||
/// if rand::random() {
|
||||
/// Ok(Thing { name: "thingy".into() })
|
||||
/// } else {
|
||||
/// Err(ErrorBadRequest("no luck"))
|
||||
/// }
|
||||
///
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// extract text data from request
|
||||
/// fn index(supplied_thing: Option<Thing>) -> Result<String> {
|
||||
/// match supplied_thing {
|
||||
/// // Puns not intended
|
||||
/// Some(thing) => Ok(format!("Got something: {:?}", thing)),
|
||||
/// None => Ok(format!("No thing!"))
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new().resource("/users/:first", |r| {
|
||||
/// r.method(http::Method::POST).with(index)
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
impl<T: 'static, S: 'static> FromRequest<S> for Option<T>
|
||||
where
|
||||
T: FromRequest<S>,
|
||||
{
|
||||
type Config = T::Config;
|
||||
type Result = Box<Future<Item = Option<T>, Error = Error>>;
|
||||
|
||||
#[inline]
|
||||
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
|
||||
Box::new(T::from_request(req, cfg).into().then(|r| match r {
|
||||
Ok(v) => future::ok(Some(v)),
|
||||
Err(_) => future::ok(None),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Optionally extract a field from the request or extract the Error if unsuccessful
|
||||
///
|
||||
/// If the FromRequest for T fails, inject Err into handler rather than returning an error response
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate actix_web;
|
||||
/// extern crate rand;
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{http, App, Result, HttpRequest, Error, FromRequest};
|
||||
/// use actix_web::error::ErrorBadRequest;
|
||||
///
|
||||
/// #[derive(Debug, Deserialize)]
|
||||
/// struct Thing { name: String }
|
||||
///
|
||||
/// impl<S> FromRequest<S> for Thing {
|
||||
/// type Config = ();
|
||||
/// type Result = Result<Thing, Error>;
|
||||
///
|
||||
/// #[inline]
|
||||
/// fn from_request(req: &HttpRequest<S>, _cfg: &Self::Config) -> Self::Result {
|
||||
/// if rand::random() {
|
||||
/// Ok(Thing { name: "thingy".into() })
|
||||
/// } else {
|
||||
/// Err(ErrorBadRequest("no luck"))
|
||||
/// }
|
||||
///
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// extract text data from request
|
||||
/// fn index(supplied_thing: Result<Thing>) -> Result<String> {
|
||||
/// match supplied_thing {
|
||||
/// Ok(thing) => Ok(format!("Got thing: {:?}", thing)),
|
||||
/// Err(e) => Ok(format!("Error extracting thing: {}", e))
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new().resource("/users/:first", |r| {
|
||||
/// r.method(http::Method::POST).with(index)
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
impl<T: 'static, S: 'static> FromRequest<S> for Result<T, Error>
|
||||
where
|
||||
T: FromRequest<S>,
|
||||
{
|
||||
type Config = T::Config;
|
||||
type Result = Box<Future<Item = Result<T, Error>, Error = Error>>;
|
||||
|
||||
#[inline]
|
||||
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
|
||||
Box::new(T::from_request(req, cfg).into().then(future::ok))
|
||||
}
|
||||
}
|
||||
|
||||
/// Payload configuration for request's payload.
|
||||
pub struct PayloadConfig {
|
||||
limit: usize,
|
||||
@@ -680,6 +803,98 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_option() {
|
||||
let req = TestRequest::with_header(
|
||||
header::CONTENT_TYPE,
|
||||
"application/x-www-form-urlencoded",
|
||||
).finish();
|
||||
|
||||
let mut cfg = FormConfig::default();
|
||||
cfg.limit(4096);
|
||||
|
||||
match Option::<Form<Info>>::from_request(&req, &cfg)
|
||||
.poll()
|
||||
.unwrap()
|
||||
{
|
||||
Async::Ready(r) => assert_eq!(r, None),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let req = TestRequest::with_header(
|
||||
header::CONTENT_TYPE,
|
||||
"application/x-www-form-urlencoded",
|
||||
).header(header::CONTENT_LENGTH, "9")
|
||||
.set_payload(Bytes::from_static(b"hello=world"))
|
||||
.finish();
|
||||
|
||||
match Option::<Form<Info>>::from_request(&req, &cfg)
|
||||
.poll()
|
||||
.unwrap()
|
||||
{
|
||||
Async::Ready(r) => assert_eq!(
|
||||
r,
|
||||
Some(Form(Info {
|
||||
hello: "world".into()
|
||||
}))
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let req = TestRequest::with_header(
|
||||
header::CONTENT_TYPE,
|
||||
"application/x-www-form-urlencoded",
|
||||
).header(header::CONTENT_LENGTH, "9")
|
||||
.set_payload(Bytes::from_static(b"bye=world"))
|
||||
.finish();
|
||||
|
||||
match Option::<Form<Info>>::from_request(&req, &cfg)
|
||||
.poll()
|
||||
.unwrap()
|
||||
{
|
||||
Async::Ready(r) => assert_eq!(r, None),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_result() {
|
||||
let req = TestRequest::with_header(
|
||||
header::CONTENT_TYPE,
|
||||
"application/x-www-form-urlencoded",
|
||||
).header(header::CONTENT_LENGTH, "11")
|
||||
.set_payload(Bytes::from_static(b"hello=world"))
|
||||
.finish();
|
||||
|
||||
match Result::<Form<Info>, Error>::from_request(&req, &FormConfig::default())
|
||||
.poll()
|
||||
.unwrap()
|
||||
{
|
||||
Async::Ready(Ok(r)) => assert_eq!(
|
||||
r,
|
||||
Form(Info {
|
||||
hello: "world".into()
|
||||
})
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let req = TestRequest::with_header(
|
||||
header::CONTENT_TYPE,
|
||||
"application/x-www-form-urlencoded",
|
||||
).header(header::CONTENT_LENGTH, "9")
|
||||
.set_payload(Bytes::from_static(b"bye=world"))
|
||||
.finish();
|
||||
|
||||
match Result::<Form<Info>, Error>::from_request(&req, &FormConfig::default())
|
||||
.poll()
|
||||
.unwrap()
|
||||
{
|
||||
Async::Ready(r) => assert!(r.is_err()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_payload_config() {
|
||||
let req = TestRequest::default().finish();
|
||||
|
@@ -70,9 +70,9 @@
|
||||
//! dependency
|
||||
//! * `brotli` - enables `brotli` compression support, requires `c`
|
||||
//! compiler
|
||||
//! * `flate-c` - enables `gzip`, `deflate` compression support, requires
|
||||
//! * `flate2-c` - enables `gzip`, `deflate` compression support, requires
|
||||
//! `c` compiler
|
||||
//! * `flate-rust` - experimental rust based implementation for
|
||||
//! * `flate2-rust` - experimental rust based implementation for
|
||||
//! `gzip`, `deflate` compression.
|
||||
//!
|
||||
#![cfg_attr(actix_nightly, feature(
|
||||
|
@@ -25,7 +25,7 @@ use middleware::{Finished, Middleware, Started};
|
||||
/// default format:
|
||||
///
|
||||
/// ```ignore
|
||||
/// %a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
|
||||
/// %a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
|
||||
/// ```
|
||||
/// ```rust
|
||||
/// # extern crate actix_web;
|
||||
@@ -94,7 +94,7 @@ impl Default for Logger {
|
||||
/// Create `Logger` middleware with format:
|
||||
///
|
||||
/// ```ignore
|
||||
/// %a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
|
||||
/// %a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
|
||||
/// ```
|
||||
fn default() -> Logger {
|
||||
Logger {
|
||||
@@ -143,7 +143,7 @@ struct Format(Vec<FormatText>);
|
||||
impl Default for Format {
|
||||
/// Return the default formatting style for the `Logger`:
|
||||
fn default() -> Format {
|
||||
Format::new(r#"%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T"#)
|
||||
Format::new(r#"%a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T"#)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -358,6 +358,7 @@ struct CookieSessionInner {
|
||||
path: String,
|
||||
domain: Option<String>,
|
||||
secure: bool,
|
||||
http_only: bool,
|
||||
max_age: Option<Duration>,
|
||||
same_site: Option<SameSite>,
|
||||
}
|
||||
@@ -371,6 +372,7 @@ impl CookieSessionInner {
|
||||
path: "/".to_owned(),
|
||||
domain: None,
|
||||
secure: true,
|
||||
http_only: true,
|
||||
max_age: None,
|
||||
same_site: None,
|
||||
}
|
||||
@@ -388,7 +390,7 @@ impl CookieSessionInner {
|
||||
let mut cookie = Cookie::new(self.name.clone(), value);
|
||||
cookie.set_path(self.path.clone());
|
||||
cookie.set_secure(self.secure);
|
||||
cookie.set_http_only(true);
|
||||
cookie.set_http_only(self.http_only);
|
||||
|
||||
if let Some(ref domain) = self.domain {
|
||||
cookie.set_domain(domain.clone());
|
||||
@@ -532,6 +534,12 @@ impl CookieSessionBackend {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `http_only` field in the session cookie being built.
|
||||
pub fn http_only(mut self, value: bool) -> CookieSessionBackend {
|
||||
Rc::get_mut(&mut self.0).unwrap().http_only = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `same_site` field in the session cookie being built.
|
||||
pub fn same_site(mut self, value: SameSite) -> CookieSessionBackend {
|
||||
Rc::get_mut(&mut self.0).unwrap().same_site = Some(value);
|
||||
|
@@ -409,7 +409,7 @@ struct ProcessResponse<S, H> {
|
||||
_h: PhantomData<H>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum RunningState {
|
||||
Running,
|
||||
Paused,
|
||||
|
@@ -463,7 +463,7 @@ impl ResourceDef {
|
||||
///
|
||||
/// Panics if path pattern is wrong.
|
||||
pub fn new(path: &str) -> Self {
|
||||
ResourceDef::with_prefix(path, "/", false)
|
||||
ResourceDef::with_prefix(path, if path.is_empty() { "" } else { "/" }, false)
|
||||
}
|
||||
|
||||
/// Parse path pattern and create new `Resource` instance.
|
||||
|
@@ -155,7 +155,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if !item.flags.contains(EntryFlags::WRITE_DONE) {
|
||||
if item.flags.contains(EntryFlags::FINISHED)
|
||||
&& !item.flags.contains(EntryFlags::WRITE_DONE)
|
||||
{
|
||||
match item.stream.poll_completed(false) {
|
||||
Ok(Async::NotReady) => (),
|
||||
Ok(Async::Ready(_)) => {
|
||||
|
@@ -8,16 +8,18 @@ use modhttp::Response;
|
||||
use std::rc::Rc;
|
||||
use std::{cmp, io};
|
||||
|
||||
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING};
|
||||
use http::{HttpTryFrom, Method, Version};
|
||||
|
||||
use super::helpers;
|
||||
use super::message::Request;
|
||||
use super::output::{Output, ResponseInfo};
|
||||
use super::output::{Output, ResponseInfo, ResponseLength};
|
||||
use super::settings::WorkerSettings;
|
||||
use super::{Writer, WriterState, MAX_WRITE_BUFFER_SIZE};
|
||||
use body::{Binary, Body};
|
||||
use header::ContentEncoding;
|
||||
use http::header::{
|
||||
HeaderValue, CONNECTION, CONTENT_ENCODING, CONTENT_LENGTH, DATE, TRANSFER_ENCODING,
|
||||
};
|
||||
use httpresponse::HttpResponse;
|
||||
|
||||
const CHUNK_SIZE: usize = 16_384;
|
||||
@@ -92,50 +94,63 @@ impl<H: 'static> Writer for H2Writer<H> {
|
||||
let mut info = ResponseInfo::new(req.inner.method == Method::HEAD);
|
||||
self.buffer.for_server(&mut info, &req.inner, msg, encoding);
|
||||
|
||||
// http2 specific
|
||||
msg.headers_mut().remove(CONNECTION);
|
||||
msg.headers_mut().remove(TRANSFER_ENCODING);
|
||||
|
||||
// using helpers::date is quite a lot faster
|
||||
if !msg.headers().contains_key(DATE) {
|
||||
let mut bytes = BytesMut::with_capacity(29);
|
||||
self.settings.set_date(&mut bytes, false);
|
||||
msg.headers_mut()
|
||||
.insert(DATE, HeaderValue::try_from(bytes.freeze()).unwrap());
|
||||
}
|
||||
|
||||
let body = msg.replace_body(Body::Empty);
|
||||
match body {
|
||||
Body::Binary(ref bytes) => {
|
||||
if bytes.is_empty() {
|
||||
msg.headers_mut()
|
||||
.insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
|
||||
self.flags.insert(Flags::EOF);
|
||||
} else {
|
||||
let mut val = BytesMut::new();
|
||||
helpers::convert_usize(bytes.len(), &mut val);
|
||||
let l = val.len();
|
||||
msg.headers_mut().insert(
|
||||
CONTENT_LENGTH,
|
||||
HeaderValue::try_from(val.split_to(l - 2).freeze()).unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Body::Empty => {
|
||||
self.flags.insert(Flags::EOF);
|
||||
msg.headers_mut()
|
||||
.insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let mut has_date = false;
|
||||
let mut resp = Response::new(());
|
||||
*resp.status_mut() = msg.status();
|
||||
*resp.version_mut() = Version::HTTP_2;
|
||||
for (key, value) in msg.headers().iter() {
|
||||
match *key {
|
||||
// http2 specific
|
||||
CONNECTION | TRANSFER_ENCODING => continue,
|
||||
CONTENT_ENCODING => if encoding != ContentEncoding::Identity {
|
||||
continue;
|
||||
},
|
||||
CONTENT_LENGTH => match info.length {
|
||||
ResponseLength::None => (),
|
||||
_ => continue,
|
||||
},
|
||||
DATE => has_date = true,
|
||||
_ => (),
|
||||
}
|
||||
resp.headers_mut().insert(key, value.clone());
|
||||
}
|
||||
|
||||
// set date header
|
||||
if !has_date {
|
||||
let mut bytes = BytesMut::with_capacity(29);
|
||||
self.settings.set_date(&mut bytes, false);
|
||||
resp.headers_mut()
|
||||
.insert(DATE, HeaderValue::try_from(bytes.freeze()).unwrap());
|
||||
}
|
||||
|
||||
// content length
|
||||
match info.length {
|
||||
ResponseLength::Zero => {
|
||||
resp.headers_mut()
|
||||
.insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
|
||||
self.flags.insert(Flags::EOF);
|
||||
}
|
||||
ResponseLength::Length(len) => {
|
||||
let mut val = BytesMut::new();
|
||||
helpers::convert_usize(len, &mut val);
|
||||
let l = val.len();
|
||||
resp.headers_mut().insert(
|
||||
CONTENT_LENGTH,
|
||||
HeaderValue::try_from(val.split_to(l - 2).freeze()).unwrap(),
|
||||
);
|
||||
}
|
||||
ResponseLength::Length64(len) => {
|
||||
let l = format!("{}", len);
|
||||
resp.headers_mut()
|
||||
.insert(CONTENT_LENGTH, HeaderValue::try_from(l.as_str()).unwrap());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
if let Some(ce) = info.content_encoding {
|
||||
resp.headers_mut()
|
||||
.insert(CONTENT_ENCODING, HeaderValue::try_from(ce).unwrap());
|
||||
}
|
||||
|
||||
match self
|
||||
.respond
|
||||
.send_response(resp, self.flags.contains(Flags::EOF))
|
||||
@@ -146,6 +161,7 @@ impl<H: 'static> Writer for H2Writer<H> {
|
||||
|
||||
trace!("Response: {:?}", msg);
|
||||
|
||||
let body = msg.replace_body(Body::Empty);
|
||||
if let Body::Binary(bytes) = body {
|
||||
if bytes.is_empty() {
|
||||
Ok(WriterState::Done)
|
||||
@@ -229,14 +245,18 @@ impl<H: 'static> Writer for H2Writer<H> {
|
||||
let cap = cmp::min(self.buffer.len(), CHUNK_SIZE);
|
||||
stream.reserve_capacity(cap);
|
||||
} else {
|
||||
if eof {
|
||||
stream.reserve_capacity(0);
|
||||
continue;
|
||||
}
|
||||
self.flags.remove(Flags::RESERVED);
|
||||
return Ok(Async::NotReady);
|
||||
return Ok(Async::Ready(()));
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Async::NotReady)
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user