1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-07-21 00:29:56 +02:00

Compare commits

...

17 Commits

Author SHA1 Message Date
Nikolay Kim
db0091ba6f disable server test for windows 2018-05-21 21:01:52 -07:00
Nikolay Kim
2159158c30 Fix streaming response with body compression 2018-05-21 20:50:10 -07:00
Nikolay Kim
76d790425f bump version 2018-05-21 19:07:56 -07:00
Nikolay Kim
90968d4333 Drop connection if request's payload is not fulle consumed #236 2018-05-21 18:54:17 -07:00
Nikolay Kim
577a509875 increase delay 2018-05-21 16:12:33 -07:00
Nikolay Kim
a9728abfc8 run coverage report on 1.24 2018-05-20 21:10:50 -07:00
Nikolay Kim
14d1b8e2b6 prepare release 2018-05-20 21:09:54 -07:00
Nikolay Kim
285c73e95e Re-use tcp listener on pause/resume 2018-05-20 20:47:20 -07:00
Nikolay Kim
483db7028c expose low level data 2018-05-20 20:37:19 -07:00
Nikolay Kim
082ff46041 Fix scope resource path extractor #234 2018-05-20 17:04:23 -07:00
Nikolay Kim
f32e8f22c8 Merge pull request #231 from qrvaelet/ranges
NamedFile: range upgrade
2018-05-20 09:18:46 -07:00
Nikolay Kim
766dde7c42 Merge branch 'master' into ranges 2018-05-20 08:51:07 -07:00
qrvaelet
b68687044e range header syntax fix, change range to content-range in responses, enabled accept ranges, tests for content-range, content-length, and range status code 2018-05-20 17:40:36 +02:00
Nikolay Kim
c9e84e9dd3 Merge pull request #233 from sindreij/patch-1
Fix some typos in server/srv.rs
2018-05-20 06:19:53 -07:00
Sindre Johansen
0126ac46fc Fix some typos in server/srv.rs
Hello! This looks like a great library, thanks for creating it! While reading through the documentation I found a few typos.
2018-05-20 14:43:26 +02:00
Nikolay Kim
9b7ea836d0 bump version 2018-05-17 18:34:09 -07:00
Nikolay Kim
537b420d35 Fix compilation with --no-default-features 2018-05-17 18:33:48 -07:00
16 changed files with 337 additions and 81 deletions

View File

@@ -31,12 +31,12 @@ before_script:
script: script:
- | - |
if [[ "$TRAVIS_RUST_VERSION" != "beta" ]]; then if [[ "$TRAVIS_RUST_VERSION" != "1.24.0" ]]; then
cargo clean cargo clean
cargo test --features="alpn,tls" -- --nocapture cargo test --features="alpn,tls" -- --nocapture
fi fi
- | - |
if [[ "$TRAVIS_RUST_VERSION" == "beta" ]]; then if [[ "$TRAVIS_RUST_VERSION" == "1.24.0" ]]; then
bash <(curl https://raw.githubusercontent.com/xd009642/tarpaulin/master/travis-install.sh) bash <(curl https://raw.githubusercontent.com/xd009642/tarpaulin/master/travis-install.sh)
USE_SKEPTIC=1 cargo tarpaulin --out Xml --no-count USE_SKEPTIC=1 cargo tarpaulin --out Xml --no-count
bash <(curl -s https://codecov.io/bash) bash <(curl -s https://codecov.io/bash)

View File

@@ -1,5 +1,24 @@
# Changes # Changes
## 0.6.9 (2018-05-22)
* Drop connection if request's payload is not fully consumed #236
* Fix streaming response with body compression
## 0.6.8 (2018-05-20)
* Fix scope resource path extractor #234
* Re-use tcp listener on pause/resume
## 0.6.7 (2018-05-17)
* Fix compilation with --no-default-features
## 0.6.6 (2018-05-17) ## 0.6.6 (2018-05-17)
* Panic during middleware execution #226 * Panic during middleware execution #226

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "actix-web" name = "actix-web"
version = "0.6.6" version = "0.6.9"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
readme = "README.md" readme = "README.md"

135
src/fs.rs
View File

@@ -20,7 +20,7 @@ use mime_guess::{get_mime_type, guess_mime_type};
use error::Error; use error::Error;
use handler::{AsyncResult, Handler, Responder, RouteHandler, WrapHandler}; use handler::{AsyncResult, Handler, Responder, RouteHandler, WrapHandler};
use header; use header;
use http::{HttpRange, Method, StatusCode}; use http::{HttpRange, Method, StatusCode, ContentEncoding};
use httpmessage::HttpMessage; use httpmessage::HttpMessage;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
@@ -289,30 +289,29 @@ impl Responder for NamedFile {
resp.set(header::ETag(etag)); resp.set(header::ETag(etag));
}); });
// TODO: Debug, enabling "accept-ranges: bytes" causes problems with resp.header(header::ACCEPT_RANGES, "bytes");
// certain clients when not using the ranges header.
//resp.header(header::ACCEPT_RANGES, format!("bytes"));
let mut length = self.md.len(); let mut length = self.md.len();
let mut offset = 0; let mut offset = 0;
// check for ranges header // check for range header
if let Some(ranges) = req.headers().get(header::RANGE) { if let Some(ranges) = req.headers().get(header::RANGE) {
if let Ok(rangesheader) = ranges.to_str() { if let Ok(rangesheader) = ranges.to_str() {
if let Ok(rangesvec) = HttpRange::parse(rangesheader, length) { if let Ok(rangesvec) = HttpRange::parse(rangesheader, length) {
length = rangesvec[0].length - 1; length = rangesvec[0].length;
offset = rangesvec[0].start; offset = rangesvec[0].start;
resp.content_encoding(ContentEncoding::Identity);
resp.header( resp.header(
header::RANGE, header::CONTENT_RANGE,
format!( format!(
"bytes={}-{}/{}", "bytes {}-{}/{}",
offset, offset,
offset + length, offset + length - 1,
self.md.len() self.md.len()
), )
); );
} else { } else {
resp.header(header::RANGE, format!("*/{}", length)); resp.header(header::CONTENT_RANGE, format!("bytes */{}", length));
return Ok(resp.status(StatusCode::RANGE_NOT_SATISFIABLE).finish()); return Ok(resp.status(StatusCode::RANGE_NOT_SATISFIABLE).finish());
}; };
} else { } else {
@@ -778,6 +777,7 @@ mod tests {
App::new().handler("test", StaticFiles::new(".").index_file("Cargo.toml")) App::new().handler("test", StaticFiles::new(".").index_file("Cargo.toml"))
}); });
// Valid range header
let request = srv let request = srv
.get() .get()
.uri(srv.url("/t%65st/Cargo.toml")) .uri(srv.url("/t%65st/Cargo.toml"))
@@ -787,10 +787,21 @@ mod tests {
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT); assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);
// Invalid range header
let request = srv
.get()
.uri(srv.url("/t%65st/Cargo.toml"))
.header(header::RANGE, "bytes=1-0")
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
} }
#[test] #[test]
fn test_named_file_ranges_headers() { fn test_named_file_content_range_headers() {
let mut srv = test::TestServer::with_factory(|| { let mut srv = test::TestServer::with_factory(|| {
App::new().handler( App::new().handler(
"test", "test",
@@ -798,13 +809,64 @@ mod tests {
) )
}); });
// Valid range header
let request = srv let request = srv
.get() .get()
.uri(srv.url("/t%65st/tests/test.binary")) .uri(srv.url("/t%65st/tests/test.binary"))
.header(header::RANGE, "bytes=10-20") .header(header::RANGE, "bytes=10-20")
.finish() .finish()
.unwrap(); .unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
let contentrange = response
.headers()
.get(header::CONTENT_RANGE)
.unwrap()
.to_str()
.unwrap();
assert_eq!(contentrange, "bytes 10-20/100");
// Invalid range header
let request = srv
.get()
.uri(srv.url("/t%65st/tests/test.binary"))
.header(header::RANGE, "bytes=10-5")
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap();
let contentrange = response
.headers()
.get(header::CONTENT_RANGE)
.unwrap()
.to_str()
.unwrap();
assert_eq!(contentrange, "bytes */100");
}
#[test]
fn test_named_file_content_length_headers() {
let mut srv = test::TestServer::with_factory(|| {
App::new().handler(
"test",
StaticFiles::new(".").index_file("tests/test.binary"),
)
});
// Valid range header
let request = srv
.get()
.uri(srv.url("/t%65st/tests/test.binary"))
.header(header::RANGE, "bytes=10-20")
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap();
let contentlength = response let contentlength = response
.headers() .headers()
.get(header::CONTENT_LENGTH) .get(header::CONTENT_LENGTH)
@@ -812,23 +874,62 @@ mod tests {
.to_str() .to_str()
.unwrap(); .unwrap();
assert_eq!(contentlength, "10"); assert_eq!(contentlength, "11");
// Invalid range header
let request = srv let request = srv
.get() .get()
.uri(srv.url("/t%65st/tests/test.binary")) .uri(srv.url("/t%65st/tests/test.binary"))
.header(header::RANGE, "bytes=10-20") .header(header::RANGE, "bytes=10-8")
.finish() .finish()
.unwrap(); .unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
let range = response
let contentlength = response
.headers() .headers()
.get(header::RANGE) .get(header::CONTENT_LENGTH)
.unwrap() .unwrap()
.to_str() .to_str()
.unwrap(); .unwrap();
assert_eq!(range, "bytes=10-20/100"); assert_eq!(contentlength, "0");
// Without range header
let request = srv
.get()
.uri(srv.url("/t%65st/tests/test.binary"))
.no_default_headers()
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap();
let contentlength = response
.headers()
.get(header::CONTENT_LENGTH)
.unwrap()
.to_str()
.unwrap();
assert_eq!(contentlength, "100");
// chunked
let request = srv
.get()
.uri(srv.url("/t%65st/tests/test.binary"))
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap();
let te = response
.headers()
.get(header::TRANSFER_ENCODING)
.unwrap()
.to_str()
.unwrap();
assert_eq!(te, "chunked");
} }
#[test] #[test]

View File

@@ -58,6 +58,16 @@ impl<'a> Params<'a> {
self.0.push((name, value)); self.0.push((name, value));
} }
pub(crate) fn remove(&mut self, name: &str)
{
for idx in (0..self.0.len()).rev() {
if self.0[idx].0 == name {
self.0.remove(idx);
return
}
}
}
/// Check if there are any matched patterns /// Check if there are any matched patterns
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.0.is_empty() self.0.is_empty()

View File

@@ -336,6 +336,7 @@ impl<S: 'static> RouteHandler<S> for Scope<S> {
if pattern.match_with_params(path, req.match_info_mut()) { if pattern.match_with_params(path, req.match_info_mut()) {
let default = unsafe { &mut *self.default.as_ref().get() }; let default = unsafe { &mut *self.default.as_ref().get() };
req.match_info_mut().remove("tail");
if self.middlewares.is_empty() { if self.middlewares.is_empty() {
let resource = unsafe { &mut *resource.get() }; let resource = unsafe { &mut *resource.get() };
return resource.handle(req, Some(default)); return resource.handle(req, Some(default));

View File

@@ -33,6 +33,7 @@ pub(crate) enum PayloadType {
} }
impl PayloadType { impl PayloadType {
#[cfg(any(feature = "brotli", feature = "flate2"))]
pub fn new(headers: &HeaderMap, sender: PayloadSender) -> PayloadType { pub fn new(headers: &HeaderMap, sender: PayloadSender) -> PayloadType {
// check content-encoding // check content-encoding
let enc = if let Some(enc) = headers.get(CONTENT_ENCODING) { let enc = if let Some(enc) = headers.get(CONTENT_ENCODING) {
@@ -52,6 +53,11 @@ impl PayloadType {
_ => PayloadType::Encoding(Box::new(EncodedPayload::new(sender, enc))), _ => PayloadType::Encoding(Box::new(EncodedPayload::new(sender, enc))),
} }
} }
#[cfg(not(any(feature = "brotli", feature = "flate2")))]
pub fn new(headers: &HeaderMap, sender: PayloadSender) -> PayloadType {
PayloadType::Sender(sender)
}
} }
impl PayloadWriter for PayloadType { impl PayloadWriter for PayloadType {
@@ -399,6 +405,7 @@ impl ContentEncoder {
// Enable content encoding only if response does not contain Content-Encoding // Enable content encoding only if response does not contain Content-Encoding
// header // header
#[cfg(any(feature = "brotli", feature = "flate2"))]
let mut encoding = if has_body { let mut encoding = if has_body {
let encoding = match response_encoding { let encoding = match response_encoding {
ContentEncoding::Auto => { ContentEncoding::Auto => {
@@ -425,6 +432,8 @@ impl ContentEncoder {
} else { } else {
ContentEncoding::Identity ContentEncoding::Identity
}; };
#[cfg(not(any(feature = "brotli", feature = "flate2")))]
let mut encoding = ContentEncoding::Identity;
#[cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))] #[cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))]
let mut transfer = match resp.body() { let mut transfer = match resp.body() {
@@ -435,6 +444,8 @@ impl ContentEncoder {
TransferEncoding::length(0, buf) TransferEncoding::length(0, buf)
} }
&Body::Binary(_) => { &Body::Binary(_) => {
#[cfg(any(feature = "brotli", feature = "flate2"))]
{
if !(encoding == ContentEncoding::Identity if !(encoding == ContentEncoding::Identity
|| encoding == ContentEncoding::Auto) || encoding == ContentEncoding::Auto)
{ {
@@ -446,10 +457,9 @@ impl ContentEncoder {
DeflateEncoder::new(transfer, Compression::fast()), DeflateEncoder::new(transfer, Compression::fast()),
), ),
#[cfg(feature = "flate2")] #[cfg(feature = "flate2")]
ContentEncoding::Gzip => ContentEncoder::Gzip(GzEncoder::new( ContentEncoding::Gzip => ContentEncoder::Gzip(
transfer, GzEncoder::new(transfer, Compression::fast()),
Compression::fast(), ),
)),
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
ContentEncoding::Br => { ContentEncoding::Br => {
ContentEncoder::Br(BrotliEncoder::new(transfer, 3)) ContentEncoder::Br(BrotliEncoder::new(transfer, 3))
@@ -470,6 +480,7 @@ impl ContentEncoder {
encoding = ContentEncoding::Identity; encoding = ContentEncoding::Identity;
resp.replace_body(Binary::from(body)); resp.replace_body(Binary::from(body));
} }
}
if is_head { if is_head {
let mut b = BytesMut::new(); let mut b = BytesMut::new();
@@ -494,6 +505,11 @@ impl ContentEncoder {
} }
TransferEncoding::eof(buf) TransferEncoding::eof(buf)
} else { } else {
if !(encoding == ContentEncoding::Identity
|| encoding == ContentEncoding::Auto)
{
resp.headers_mut().remove(CONTENT_LENGTH);
}
ContentEncoder::streaming_encoding(buf, version, resp) ContentEncoder::streaming_encoding(buf, version, resp)
} }
} }

View File

@@ -270,7 +270,12 @@ where
debug!("Error sending data: {}", err); debug!("Error sending data: {}", err);
return Err(()); return Err(());
} }
_ => (), Ok(Async::Ready(_)) => {
// non consumed payload in that case close connection
if self.payload.is_some() && self.tasks.is_empty() {
return Ok(Async::Ready(false))
}
}
} }
} }

View File

@@ -1,6 +1,6 @@
#![cfg_attr(feature = "cargo-clippy", allow(redundant_field_names))] #![cfg_attr(feature = "cargo-clippy", allow(redundant_field_names))]
use bytes::BufMut; use bytes::{BytesMut, BufMut};
use futures::{Async, Poll}; use futures::{Async, Poll};
use std::io; use std::io;
use std::rc::Rc; use std::rc::Rc;
@@ -45,7 +45,7 @@ impl<T: AsyncWrite, H: 'static> H1Writer<T, H> {
stream: T, buf: SharedBytes, settings: Rc<WorkerSettings<H>>, stream: T, buf: SharedBytes, settings: Rc<WorkerSettings<H>>,
) -> H1Writer<T, H> { ) -> H1Writer<T, H> {
H1Writer { H1Writer {
flags: Flags::empty(), flags: Flags::KEEPALIVE,
encoder: ContentEncoder::empty(buf.clone()), encoder: ContentEncoder::empty(buf.clone()),
written: 0, written: 0,
headers_size: 0, headers_size: 0,
@@ -62,7 +62,7 @@ impl<T: AsyncWrite, H: 'static> H1Writer<T, H> {
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.written = 0; self.written = 0;
self.flags = Flags::empty(); self.flags = Flags::KEEPALIVE;
} }
pub fn disconnected(&mut self) { pub fn disconnected(&mut self) {
@@ -100,6 +100,16 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
self.written self.written
} }
#[inline]
fn set_date(&self, dst: &mut BytesMut) {
self.settings.set_date(dst)
}
#[inline]
fn buffer(&self) -> &mut BytesMut {
self.buffer.get_mut()
}
fn start( fn start(
&mut self, req: &mut HttpInnerMessage, msg: &mut HttpResponse, &mut self, req: &mut HttpInnerMessage, msg: &mut HttpResponse,
encoding: ContentEncoding, encoding: ContentEncoding,
@@ -108,9 +118,9 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
self.encoder = self.encoder =
ContentEncoder::for_server(self.buffer.clone(), req, msg, encoding); ContentEncoder::for_server(self.buffer.clone(), req, msg, encoding);
if msg.keep_alive().unwrap_or_else(|| req.keep_alive()) { if msg.keep_alive().unwrap_or_else(|| req.keep_alive()) {
self.flags.insert(Flags::STARTED | Flags::KEEPALIVE); self.flags = Flags::STARTED | Flags::KEEPALIVE;
} else { } else {
self.flags.insert(Flags::STARTED); self.flags = Flags::STARTED;
} }
// Connection upgrade // Connection upgrade

View File

@@ -71,6 +71,16 @@ impl<H: 'static> Writer for H2Writer<H> {
self.written self.written
} }
#[inline]
fn set_date(&self, dst: &mut BytesMut) {
self.settings.set_date(dst)
}
#[inline]
fn buffer(&self) -> &mut BytesMut {
self.buffer.get_mut()
}
fn start( fn start(
&mut self, req: &mut HttpInnerMessage, msg: &mut HttpResponse, &mut self, req: &mut HttpInnerMessage, msg: &mut HttpResponse,
encoding: ContentEncoding, encoding: ContentEncoding,

View File

@@ -143,7 +143,7 @@ pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesM
} }
/// NOTE: bytes object has to contain enough space /// NOTE: bytes object has to contain enough space
pub(crate) fn write_content_length(mut n: usize, bytes: &mut BytesMut) { pub fn write_content_length(mut n: usize, bytes: &mut BytesMut) {
if n < 10 { if n < 10 {
let mut buf: [u8; 21] = [ let mut buf: [u8; 21] = [
b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l', b'e', b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l', b'e',

View File

@@ -3,7 +3,8 @@ use std::net::Shutdown;
use std::{io, time}; use std::{io, time};
use actix; use actix;
use futures::Poll; use bytes::BytesMut;
use futures::{Async, Poll};
use tokio_core::net::TcpStream; use tokio_core::net::TcpStream;
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
@@ -24,6 +25,9 @@ mod worker;
pub use self::settings::ServerSettings; pub use self::settings::ServerSettings;
pub use self::srv::HttpServer; pub use self::srv::HttpServer;
#[doc(hidden)]
pub use self::helpers::write_content_length;
use body::Binary; use body::Binary;
use error::Error; use error::Error;
use header::ContentEncoding; use header::ContentEncoding;
@@ -132,13 +136,15 @@ impl HttpHandler for Box<HttpHandler> {
#[doc(hidden)] #[doc(hidden)]
pub trait HttpHandlerTask { pub trait HttpHandlerTask {
/// Poll task, this method is used before or after *io* object is available /// Poll task, this method is used before or after *io* object is available
fn poll(&mut self) -> Poll<(), Error>; fn poll(&mut self) -> Poll<(), Error>{
Ok(Async::Ready(()))
}
/// Poll task when *io* object is available /// Poll task when *io* object is available
fn poll_io(&mut self, io: &mut Writer) -> Poll<bool, Error>; fn poll_io(&mut self, io: &mut Writer) -> Poll<bool, Error>;
/// Connection is disconnected /// Connection is disconnected
fn disconnected(&mut self); fn disconnected(&mut self) {}
} }
/// Conversion helper trait /// Conversion helper trait
@@ -168,8 +174,16 @@ pub enum WriterState {
#[doc(hidden)] #[doc(hidden)]
/// Stream writer /// Stream writer
pub trait Writer { pub trait Writer {
/// number of bytes written to the stream
fn written(&self) -> u64; fn written(&self) -> u64;
#[doc(hidden)]
fn set_date(&self, st: &mut BytesMut);
#[doc(hidden)]
#[cfg_attr(feature = "cargo-clippy", allow(mut_from_ref))]
fn buffer(&self) -> &mut BytesMut;
fn start( fn start(
&mut self, req: &mut HttpInnerMessage, resp: &mut HttpResponse, &mut self, req: &mut HttpInnerMessage, resp: &mut HttpResponse,
encoding: ContentEncoding, encoding: ContentEncoding,

View File

@@ -309,7 +309,7 @@ where
/// The socket address to bind /// The socket address to bind
/// ///
/// To mind multiple addresses this method can be call multiple times. /// To bind multiple addresses this method can be called multiple times.
pub fn bind<S: net::ToSocketAddrs>(mut self, addr: S) -> io::Result<Self> { pub fn bind<S: net::ToSocketAddrs>(mut self, addr: S) -> io::Result<Self> {
let sockets = self.bind2(addr)?; let sockets = self.bind2(addr)?;
self.sockets.extend(sockets); self.sockets.extend(sockets);
@@ -319,7 +319,7 @@ where
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
/// The ssl socket address to bind /// The ssl socket address to bind
/// ///
/// To mind multiple addresses this method can be call multiple times. /// To bind multiple addresses this method can be called multiple times.
pub fn bind_tls<S: net::ToSocketAddrs>( pub fn bind_tls<S: net::ToSocketAddrs>(
mut self, addr: S, acceptor: TlsAcceptor, mut self, addr: S, acceptor: TlsAcceptor,
) -> io::Result<Self> { ) -> io::Result<Self> {
@@ -450,7 +450,6 @@ impl<H: IntoHttpHandler> HttpServer<H> {
self.accept.push(start_accept_thread( self.accept.push(start_accept_thread(
token, token,
sock, sock,
self.backlog,
tx.clone(), tx.clone(),
socks.clone(), socks.clone(),
workers.clone(), workers.clone(),
@@ -782,7 +781,7 @@ enum Command {
} }
fn start_accept_thread( fn start_accept_thread(
token: usize, sock: Socket, backlog: i32, srv: mpsc::UnboundedSender<ServerCommand>, token: usize, sock: Socket, srv: mpsc::UnboundedSender<ServerCommand>,
socks: Slab<SocketInfo>, socks: Slab<SocketInfo>,
mut workers: Vec<(usize, mpsc::UnboundedSender<Conn<net::TcpStream>>)>, mut workers: Vec<(usize, mpsc::UnboundedSender<Conn<net::TcpStream>>)>,
) -> (mio::SetReadiness, sync_mpsc::Sender<Command>) { ) -> (mio::SetReadiness, sync_mpsc::Sender<Command>) {
@@ -892,8 +891,8 @@ fn start_accept_thread(
}, },
CMD => match rx.try_recv() { CMD => match rx.try_recv() {
Ok(cmd) => match cmd { Ok(cmd) => match cmd {
Command::Pause => if let Some(server) = server.take() { Command::Pause => if let Some(ref server) = server {
if let Err(err) = poll.deregister(&server) { if let Err(err) = poll.deregister(server) {
error!( error!(
"Can not deregister server socket {}", "Can not deregister server socket {}",
err err
@@ -906,15 +905,6 @@ fn start_accept_thread(
} }
}, },
Command::Resume => { Command::Resume => {
let lst = create_tcp_listener(addr, backlog)
.expect("Can not create net::TcpListener");
server = Some(
mio::net::TcpListener::from_std(lst).expect(
"Can not create mio::net::TcpListener",
),
);
if let Some(ref server) = server { if let Some(ref server) = server {
if let Err(err) = poll.register( if let Err(err) = poll.register(
server, server,

View File

@@ -239,9 +239,9 @@ impl StreamHandlerType {
match *self { match *self {
StreamHandlerType::Normal => "http", StreamHandlerType::Normal => "http",
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
StreamHandlerType::Tls(ref acceptor) => "https", StreamHandlerType::Tls(_) => "https",
#[cfg(feature = "alpn")] #[cfg(feature = "alpn")]
StreamHandlerType::Alpn(ref acceptor) => "https", StreamHandlerType::Alpn(_) => "https",
} }
} }
} }

View File

@@ -368,6 +368,80 @@ fn test_path_and_query_extractor2_async4() {
assert_eq!(response.status(), StatusCode::BAD_REQUEST); assert_eq!(response.status(), StatusCode::BAD_REQUEST);
} }
#[test]
fn test_scope_and_path_extractor() {
let mut srv = test::TestServer::with_factory(move || {
App::new().scope("/sc", |scope| {
scope.resource("/{num}/index.html", |r| {
r.route()
.with(|p: Path<(usize,)>| {
format!("Welcome {}!", p.0)
})
})
})
});
// client request
let request = srv
.get()
.uri(srv.url("/sc/10/index.html"))
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success());
// read response
let bytes = srv.execute(response.body()).unwrap();
assert_eq!(bytes, Bytes::from_static(b"Welcome 10!"));
// client request
let request = srv
.get()
.uri(srv.url("/sc/test1/index.html"))
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[test]
fn test_nested_scope_and_path_extractor() {
let mut srv = test::TestServer::with_factory(move || {
App::new().scope("/sc", |scope| {
scope.nested("/{num}", |scope| {
scope.resource("/{num}/index.html", |r| {
r.route()
.with(|p: Path<(usize, usize)>| {
format!("Welcome {} {}!", p.0, p.1)
})
})
})
})
});
// client request
let request = srv
.get()
.uri(srv.url("/sc/10/12/index.html"))
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success());
// read response
let bytes = srv.execute(response.body()).unwrap();
assert_eq!(bytes, Bytes::from_static(b"Welcome 10 12!"));
// client request
let request = srv
.get()
.uri(srv.url("/sc/10/test1/index.html"))
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[cfg(actix_impl_trait)] #[cfg(actix_impl_trait)]
fn test_impl_trait( fn test_impl_trait(
data: (Json<Value>, Path<PParam>, Query<PParam>), data: (Json<Value>, Path<PParam>, Query<PParam>),

View File

@@ -54,6 +54,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World"; Hello World Hello World Hello World Hello World Hello World";
#[test] #[test]
#[cfg(unix)]
fn test_start() { fn test_start() {
let _ = test::TestServer::unused_addr(); let _ = test::TestServer::unused_addr();
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
@@ -86,12 +87,17 @@ fn test_start() {
// pause // pause
let _ = srv_addr.send(server::PauseServer).wait(); let _ = srv_addr.send(server::PauseServer).wait();
thread::sleep(time::Duration::from_millis(200)); thread::sleep(time::Duration::from_millis(200));
assert!(net::TcpStream::connect(addr).is_err()); {
let req = client::ClientRequest::get(format!("http://{}/", addr).as_str())
.timeout(time::Duration::from_millis(200))
.finish()
.unwrap();
assert!(sys.run_until_complete(req.send()).is_err());
}
// resume // resume
let _ = srv_addr.send(server::ResumeServer).wait(); let _ = srv_addr.send(server::ResumeServer).wait();
thread::sleep(time::Duration::from_millis(200)); thread::sleep(time::Duration::from_millis(400));
{ {
let req = client::ClientRequest::get(format!("http://{}/", addr).as_str()) let req = client::ClientRequest::get(format!("http://{}/", addr).as_str())
.finish() .finish()