diff --git a/guide/src/qs_10.md b/guide/src/qs_10.md index 9af3301e..10e5c7bc 100644 --- a/guide/src/qs_10.md +++ b/guide/src/qs_10.md @@ -85,3 +85,5 @@ fn main() { ``` ## User sessions + +[WIP] diff --git a/guide/src/qs_13.md b/guide/src/qs_13.md index a529fb9b..ee0c21f1 100644 --- a/guide/src/qs_13.md +++ b/guide/src/qs_13.md @@ -1,10 +1,10 @@ -# HTTP/2 +# HTTP/2.0 -Actix web automatically upgrades connection to *HTTP/2* if possible. +Actix web automatically upgrades connection to *HTTP/2.0* if possible. ## Negotiation -*HTTP/2* protocol over tls without prior knowlage requires +*HTTP/2.0* protocol over tls without prior knowlage requires [tls alpn](https://tools.ietf.org/html/rfc7301). At the moment only `rust-openssl` has support. Turn on `alpn` feature to enable `alpn` negotiation. With enable `alpn` feature `HttpServer` provides @@ -32,7 +32,7 @@ fn main() { } ``` -Upgrade to *HTTP/2* schema described in +Upgrade to *HTTP/2.0* schema described in [rfc section 3.2](https://http2.github.io/http2-spec/#rfc.section.3.2) is not supported. Starting *HTTP/2* with prior knowledge is supported for both clear text connection and tls connection. [rfc section 3.4](https://http2.github.io/http2-spec/#rfc.section.3.4) diff --git a/guide/src/qs_2.md b/guide/src/qs_2.md index 1b28892e..0c29f527 100644 --- a/guide/src/qs_2.md +++ b/guide/src/qs_2.md @@ -49,7 +49,7 @@ request handler with the application's `resource` on a particular *HTTP method* # } # fn main() { let app = Application::new() - .resource("/", |r| r.method(Method::GET).f(index)) + .resource("/", |r| r.f(index)) .finish(); # } ``` diff --git a/guide/src/qs_3_5.md b/guide/src/qs_3_5.md index da21e3ce..5bb06fdf 100644 --- a/guide/src/qs_3_5.md +++ b/guide/src/qs_3_5.md @@ -1,5 +1,6 @@ # Server + ## Multi-threading Http server automatically starts number of http workers, by default @@ -52,7 +53,7 @@ fn main() { } ``` -Note on *HTTP/2* protocol over tls without prior knowlage, it requires +Note on *HTTP/2.0* protocol over tls without prior knowlage, it requires [tls alpn](https://tools.ietf.org/html/rfc7301). At the moment only `openssl` has `alpn ` support. @@ -99,6 +100,7 @@ use actix_web::*; fn index(req: HttpRequest) -> HttpResponse { HTTPOk.build() .connection_type(headers::ConnectionType::Close) // <- Close connection + .force_close() // <- Alternative method .finish().unwrap() } # fn main() {} diff --git a/guide/src/qs_7.md b/guide/src/qs_7.md index 3e3fd8f7..7e8bd08a 100644 --- a/guide/src/qs_7.md +++ b/guide/src/qs_7.md @@ -82,3 +82,45 @@ fn main() { .finish(); } ``` + +## Chunked transfer encoding + +Actix automatically decode *chunked* encoding. `HttpRequest::payload()` already contains +decoded bytes stream. If request payload compressed with one of supported +compression codecs (br, gzip, deflate) bytes stream get decompressed. + +Chunked encoding on response could be enabled with `HttpResponseBuilder::chunked()` method. +But this takes effect only for `Body::Streaming(BodyStream)` or `Body::StreamingContext` bodies. +Also if response payload compression is enabled and streaming body is used, chunked encoding +get enabled automatically. + +Enabling chunked encoding for *HTTP/2.0* responses is forbidden. + +```rust +# extern crate actix_web; +use actix_web::*; +use actix_web::headers::ContentEncoding; + +fn index(req: HttpRequest) -> HttpResponse { + HttpResponse::Ok() + .chunked() + .body(Body::Streaming(Payload::empty().stream())).unwrap() +} +# fn main() {} +``` + +## Cookies + +[WIP] + +## Multipart body + +[WIP] + +## Urlencoded body + +[WIP] + +## Streaming request + +[WIP] diff --git a/guide/src/qs_9.md b/guide/src/qs_9.md index 7627a157..cde41c74 100644 --- a/guide/src/qs_9.md +++ b/guide/src/qs_9.md @@ -1 +1,4 @@ # WebSockets + +[WIP] + diff --git a/src/body.rs b/src/body.rs index 34c06dd3..b9e6676e 100644 --- a/src/body.rs +++ b/src/body.rs @@ -6,8 +6,8 @@ use futures::Stream; use error::Error; -pub(crate) type BodyStream = Box>; - +/// Type represent streaming body +pub type BodyStream = Box>; /// Represents various types of http message body. pub enum Body { diff --git a/src/error.rs b/src/error.rs index 37c0d0e5..a44863ff 100644 --- a/src/error.rs +++ b/src/error.rs @@ -213,6 +213,9 @@ impl From for PayloadError { } } +/// `InternalServerError` for `PayloadError` +impl ResponseError for PayloadError {} + /// Return `BadRequest` for `cookie::ParseError` impl ResponseError for cookie::ParseError { fn error_response(&self) -> HttpResponse { diff --git a/src/h1.rs b/src/h1.rs index 0f35f131..fef40e56 100644 --- a/src/h1.rs +++ b/src/h1.rs @@ -254,7 +254,7 @@ impl Http1 if self.keepalive_timer.is_none() { trace!("Start keep-alive timer"); let mut to = Timeout::new( - Duration::new(keep_alive as u64, 0), + Duration::new(keep_alive, 0), Arbiter::handle()).unwrap(); // register timeout let _ = to.poll(); diff --git a/src/h2.rs b/src/h2.rs index 87566277..9dd85a93 100644 --- a/src/h2.rs +++ b/src/h2.rs @@ -159,7 +159,7 @@ impl Http2 if keep_alive > 0 && self.keepalive_timer.is_none() { trace!("Start keep-alive timer"); let mut timeout = Timeout::new( - Duration::new(keep_alive as u64, 0), + Duration::new(keep_alive, 0), Arbiter::handle()).unwrap(); // register timeout let _ = timeout.poll(); diff --git a/src/httpresponse.rs b/src/httpresponse.rs index 3d3ca4cc..a60411d2 100644 --- a/src/httpresponse.rs +++ b/src/httpresponse.rs @@ -309,7 +309,7 @@ impl HttpResponseBuilder { /// Enables automatic chunked transfer encoding #[inline] - pub fn enable_chunked(&mut self) -> &mut Self { + pub fn chunked(&mut self) -> &mut Self { if let Some(parts) = parts(&mut self.response, &self.err) { parts.chunked = true; } diff --git a/src/lib.rs b/src/lib.rs index 7c05015e..4623b7a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,6 +128,7 @@ pub mod dev { //! use actix_web::dev::*; //! ``` + pub use body::BodyStream; pub use info::ConnectionInfo; pub use handler::Handler; pub use router::{Router, Pattern}; diff --git a/src/payload.rs b/src/payload.rs index 3aff4216..6d81e2f6 100644 --- a/src/payload.rs +++ b/src/payload.rs @@ -7,6 +7,7 @@ use bytes::{Bytes, BytesMut}; use futures::{Async, Poll, Stream}; use futures::task::{Task, current as current_task}; +use body::BodyStream; use actix::ResponseType; use error::PayloadError; @@ -121,6 +122,11 @@ impl Payload { pub fn set_buffer_size(&self, size: usize) { self.inner.borrow_mut().set_buffer_size(size) } + + /// Convert payload into BodyStream + pub fn stream(self) -> BodyStream { + Box::new(self.map(|item| item.0).map_err(|e| e.into())) + } } impl Stream for Payload { diff --git a/src/server.rs b/src/server.rs index 24c8318f..50d47d2e 100644 --- a/src/server.rs +++ b/src/server.rs @@ -94,7 +94,7 @@ pub struct HttpServer io: PhantomData, addr: PhantomData, threads: usize, - keep_alive: Option, + keep_alive: Option, factory: Arc U + Send + Sync>, workers: Vec>>, } @@ -152,7 +152,7 @@ impl HttpServer /// - `Some(0)` - disable /// /// - `None` - use `SO_KEEPALIVE` socket option - pub fn keep_alive(mut self, val: Option) -> Self { + pub fn keep_alive(mut self, val: Option) -> Self { self.keep_alive = val; self } @@ -240,7 +240,7 @@ impl HttpServer let (tx, rx) = mpsc::unbounded::>(); let h = handler.clone(); - let ka = self.keep_alive.clone(); + let ka = self.keep_alive; let factory = Arc::clone(&self.factory); let addr = Arbiter::start(move |ctx: &mut Context<_>| { let mut apps: Vec<_> = (*factory)() @@ -396,7 +396,7 @@ impl Handler, io::Error> for HttpServer -> Response> { Arbiter::handle().spawn( - HttpChannel::new(Rc::clone(&self.h.as_ref().unwrap()), msg.io, msg.peer, msg.http2)); + HttpChannel::new(Rc::clone(self.h.as_ref().unwrap()), msg.io, msg.peer, msg.http2)); Self::empty() } } @@ -412,21 +412,21 @@ struct Worker { pub(crate) struct WorkerSettings { h: Vec, - keep_alive: Option, + keep_alive: Option, } impl WorkerSettings { pub fn handlers(&self) -> &Vec { &self.h } - pub fn keep_alive(&self) -> Option { + pub fn keep_alive(&self) -> Option { self.keep_alive } } impl Worker { - fn new(h: Vec, handler: StreamHandlerType, keep_alive: Option) -> Worker { + fn new(h: Vec, handler: StreamHandlerType, keep_alive: Option) -> Worker { Worker { h: Rc::new(WorkerSettings{h: h, keep_alive: keep_alive}), handler: handler, @@ -456,10 +456,10 @@ impl Handler> for Worker fn handle(&mut self, msg: IoStream, _: &mut Context) -> Response> { - if let None = self.h.keep_alive { - if msg.io.set_keepalive(Some(Duration::new(75, 0))).is_err() { - error!("Can not set socket keep-alive option"); - } + if self.h.keep_alive.is_none() && + msg.io.set_keepalive(Some(Duration::new(75, 0))).is_err() + { + error!("Can not set socket keep-alive option"); } self.handler.handle(Rc::clone(&self.h), msg); Self::empty()