diff --git a/CHANGES.md b/CHANGES.md index 5974ee69a..07991142e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,20 +1,26 @@ # Changes -## [1.0.0] - 2019-05-xx +## [1.0.0] - 2019-06-05 ### Add +* Add `Scope::configure()` method. + * Add `ServiceRequest::set_payload()` method. * Add `test::TestRequest::set_json()` convenience method to automatically serialize data and set header in test requests. +* Add macros for head, options, trace, connect and patch http methods + ### Changes * Drop an unnecessary `Option<_>` indirection around `ServerBuilder` from `HttpServer`. #863 ### Fixed +* Fix Logger request time format, and use rfc3339. #867 + * Clear http requests pool on app service drop #860 diff --git a/Cargo.toml b/Cargo.toml index 0bf6e7c9b..cd0e94589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "1.0.0-rc" +version = "1.0.0" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" @@ -72,11 +72,11 @@ actix-service = "0.4.0" actix-utils = "0.4.1" actix-router = "0.1.5" actix-rt = "0.2.2" -actix-web-codegen = "0.1.0" -actix-http = "0.2.0" +actix-web-codegen = "0.1.2" +actix-http = "0.2.3" actix-server = "0.5.1" actix-server-config = "0.1.1" -actix-threadpool = "0.1.0" +actix-threadpool = "0.1.1" awc = { version = "0.2.0", optional = true } bytes = "0.4" @@ -100,9 +100,9 @@ openssl = { version="0.10", optional = true } rustls = { version = "0.15", optional = true } [dev-dependencies] -actix-http = { version = "0.2.0", features=["ssl", "brotli", "flate2-zlib"] } +actix-http = { version = "0.2.3", features=["ssl", "brotli", "flate2-zlib"] } actix-http-test = { version = "0.2.0", features=["ssl"] } -actix-files = { version = "0.1.0-beta.4" } +actix-files = { version = "0.1.1" } rand = "0.6" env_logger = "0.6" serde_derive = "1.0" diff --git a/MIGRATION.md b/MIGRATION.md index 73669ddb8..1736ee65d 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -238,6 +238,17 @@ * Actors support have been moved to `actix-web-actors` crate +* Custom Error + + Instead of error_response method alone, ResponseError now provides two methods: error_response and render_response respectively. Where, error_response creates the error response and render_response returns the error response to the caller. + + Simplest migration from 0.7 to 1.0 shall include below method to the custom implementation of ResponseError: + + ```rust + fn render_response(&self) -> HttpResponse { + self.error_response() + } + ``` ## 0.7.15 diff --git a/README.md b/README.md index fc8f78b86..cae737b68 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust. * Multipart streams * Static assets * SSL support with OpenSSL or Rustls -* Middlewares ([Logger, Session, CORS, CSRF, etc](https://actix.rs/docs/middleware/)) +* Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/)) * Includes an asynchronous [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html) * Supports [Actix actor framework](https://github.com/actix/actix) @@ -22,7 +22,7 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust. * [API Documentation (0.7)](https://docs.rs/actix-web/0.7.19/actix_web/) * [Chat on gitter](https://gitter.im/actix/actix) * Cargo package: [actix-web](https://crates.io/crates/actix-web) -* Minimum supported Rust version: 1.32 or later +* Minimum supported Rust version: 1.34 or later ## Example diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index e8457f42f..862a59442 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,6 +1,14 @@ # Changes -## [0.1.0] - 2019-05-xx +## [0.1.2] - 2019-06-06 + +* Fix ring dependency from actix-web default features for #741. + +## [0.1.1] - 2019-06-01 + +* Static files are incorrectly served as both chunked and with length #812 + +## [0.1.0] - 2019-05-25 * NamedFile last-modified check always fails due to nano-seconds in file modified date #820 diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 6b065758d..b1428a22b 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.1.0-beta.4" +version = "0.1.2" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" @@ -18,7 +18,8 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = "1.0.0-beta.5" +actix-web = { version = "1.0.0", default-features = false } +actix-http = "0.2.3" actix-service = "0.4.0" bitflags = "1" bytes = "0.4" @@ -31,4 +32,4 @@ percent-encoding = "1.0" v_htmlescape = "0.4" [dev-dependencies] -actix-web = { version = "1.0.0-beta.5", features=["ssl"] } +actix-web = { version = "1.0.0", features=["ssl"] } diff --git a/actix-files/LICENSE-APACHE b/actix-files/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/actix-files/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/actix-files/LICENSE-MIT b/actix-files/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/actix-files/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index 2298e35af..29e9eee41 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -11,6 +11,7 @@ use bitflags::bitflags; use mime; use mime_guess::guess_mime_type; +use actix_http::body::SizedStream; use actix_web::http::header::{ self, ContentDisposition, DispositionParam, DispositionType, }; @@ -434,7 +435,7 @@ impl Responder for NamedFile { if offset != 0 || length != self.md.len() { return Ok(resp.status(StatusCode::PARTIAL_CONTENT).streaming(reader)); }; - Ok(resp.streaming(reader)) + Ok(resp.body(SizedStream::new(length, reader))) } } } diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 61c211b26..d0c75da76 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,5 +1,32 @@ # Changes +## [0.2.3] - 2019-06-02 + +### Added + +* Debug impl for ResponseBuilder + +* From SizedStream and BodyStream for Body + +### Changed + +* SizedStream uses u64 + + +## [0.2.2] - 2019-05-29 + +### Fixed + +* Parse incoming stream before closing stream on disconnect #868 + + +## [0.2.1] - 2019-05-25 + +### Fixed + +* Handle socket read disconnect + + ## [0.2.0] - 2019-05-12 ### Changed diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index fabf7fe92..1847a5ba2 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "0.2.0" +version = "0.2.3" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -47,7 +47,7 @@ secure-cookies = ["ring"] actix-service = "0.4.0" actix-codec = "0.1.2" actix-connect = "0.2.0" -actix-utils = "0.4.0" +actix-utils = "0.4.1" actix-server-config = "0.1.1" actix-threadpool = "0.1.0" @@ -81,7 +81,7 @@ time = "0.1.42" tokio-tcp = "0.1.3" tokio-timer = "0.2.8" tokio-current-thread = "0.1" -trust-dns-resolver = { version="0.11.0", default-features = false } +trust-dns-resolver = { version="0.11.1", default-features = false } # for secure cookie ring = { version = "0.14.6", optional = true } diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index 0652dd274..e728cdb98 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -234,6 +234,25 @@ impl From for Body { } } +impl From> for Body +where + S: Stream + 'static, +{ + fn from(s: SizedStream) -> Body { + Body::from_message(s) + } +} + +impl From> for Body +where + S: Stream + 'static, + E: Into + 'static, +{ + fn from(s: BodyStream) -> Body { + Body::from_message(s) + } +} + impl MessageBody for Bytes { fn size(&self) -> BodySize { BodySize::Sized(self.len()) @@ -366,7 +385,7 @@ where /// Type represent streaming body. This body implementation should be used /// if total size of stream is known. Data get sent as is without using transfer encoding. pub struct SizedStream { - size: usize, + size: u64, stream: S, } @@ -374,7 +393,7 @@ impl SizedStream where S: Stream, { - pub fn new(size: usize, stream: S) -> Self { + pub fn new(size: u64, stream: S) -> Self { SizedStream { size, stream } } } @@ -384,7 +403,7 @@ where S: Stream, { fn size(&self) -> BodySize { - BodySize::Sized(self.size) + BodySize::Sized64(self.size) } fn poll_next(&mut self) -> Poll, Error> { diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index ec717ae07..220984f8d 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -583,6 +583,9 @@ where self.ka_timer = Some(Delay::new(interval)); } else { self.flags.insert(Flags::READ_DISCONNECT); + if let Some(mut payload) = self.payload.take() { + payload.set_error(PayloadError::Incomplete(None)); + } return Ok(()); } } else { @@ -690,15 +693,21 @@ where } } else { // read socket into a buf - if !inner.flags.contains(Flags::READ_DISCONNECT) { - if let Some(true) = + let should_disconnect = + if !inner.flags.contains(Flags::READ_DISCONNECT) { read_available(&mut inner.io, &mut inner.read_buf)? - { - inner.flags.insert(Flags::READ_DISCONNECT) - } - } + } else { + None + }; inner.poll_request()?; + if let Some(true) = should_disconnect { + inner.flags.insert(Flags::READ_DISCONNECT); + if let Some(mut payload) = inner.payload.take() { + payload.feed_eof(); + } + }; + loop { if inner.write_buf.remaining_mut() < LW_BUFFER_SIZE { inner.write_buf.reserve(HW_BUFFER_SIZE); diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index fd51e54c7..ce986a472 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -764,6 +764,25 @@ impl IntoFuture for ResponseBuilder { } } +impl fmt::Debug for ResponseBuilder { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let head = self.head.as_ref().unwrap(); + + let res = writeln!( + f, + "\nResponseBuilder {:?} {}{}", + head.version, + head.status, + head.reason.unwrap_or(""), + ); + let _ = writeln!(f, " headers:"); + for (key, val) in head.headers.iter() { + let _ = writeln!(f, " {:?}: {:?}", key, val); + } + res + } +} + /// Helper converters impl, E: Into> From> for Response { fn from(res: Result) -> Self { diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index d0c5e3526..4a679f4b9 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -9,9 +9,9 @@ use actix_service::{new_service_cfg, service_fn, NewService}; use bytes::{Bytes, BytesMut}; use futures::future::{self, ok, Future}; use futures::stream::{once, Stream}; +use regex::Regex; use tokio_timer::sleep; -use actix_http::body::Body; use actix_http::error::PayloadError; use actix_http::{ body, error, http, http::header, Error, HttpService, KeepAlive, Request, Response, @@ -215,6 +215,54 @@ fn test_expect_continue_h1() { assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n")); } +#[test] +fn test_chunked_payload() { + let chunk_sizes = vec![32768, 32, 32768]; + let total_size: usize = chunk_sizes.iter().sum(); + + let srv = TestServer::new(|| { + HttpService::build().h1(|mut request: Request| { + request + .take_payload() + .map_err(|e| panic!(format!("Error reading payload: {}", e))) + .fold(0usize, |acc, chunk| future::ok::<_, ()>(acc + chunk.len())) + .map(|req_size| Response::Ok().body(format!("size={}", req_size))) + }) + }); + + let returned_size = { + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream + .write_all(b"POST /test HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"); + + for chunk_size in chunk_sizes.iter() { + let mut bytes = Vec::new(); + let random_bytes: Vec = + (0..*chunk_size).map(|_| rand::random::()).collect(); + + bytes.extend(format!("{:X}\r\n", chunk_size).as_bytes()); + bytes.extend(&random_bytes[..]); + bytes.extend(b"\r\n"); + let _ = stream.write_all(&bytes); + } + + let _ = stream.write_all(b"0\r\n\r\n"); + stream.shutdown(net::Shutdown::Write).unwrap(); + + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + + let re = Regex::new(r"size=(\d+)").unwrap(); + let size: usize = match re.captures(&data) { + Some(caps) => caps.get(1).unwrap().as_str().parse().unwrap(), + None => panic!(format!("Failed to find size in HTTP Response: {}", data)), + }; + size + }; + + assert_eq!(returned_size, total_size); +} + #[test] fn test_slow_request() { let srv = TestServer::new(|| { @@ -775,8 +823,7 @@ fn test_h1_body_length() { HttpService::build().h1(|_| { let body = once(Ok(Bytes::from_static(STR.as_ref()))); ok::<_, ()>( - Response::Ok() - .body(Body::from_message(body::SizedStream::new(STR.len(), body))), + Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), ) }) }); @@ -801,9 +848,10 @@ fn test_h2_body_length() { HttpService::build() .h2(|_| { let body = once(Ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>(Response::Ok().body(Body::from_message( - body::SizedStream::new(STR.len(), body), - ))) + ok::<_, ()>( + Response::Ok() + .body(body::SizedStream::new(STR.len() as u64, body)), + ) }) .map_err(|_| ()), ) diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index 3a959890a..b0d8f285e 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -1,12 +1,24 @@ # Changes +## [0.1.3] - 2019-06-06 + +* Fix ring dependency from actix-web default features for #741. + +## [0.1.2] - 2019-06-02 + +* Fix boundary parsing #876 + +## [0.1.1] - 2019-05-25 + +* Fix disconnect handling #834 + ## [0.1.0] - 2019-05-18 * Release ## [0.1.0-beta.4] - 2019-05-12 -* Handle cancellation of uploads #834 #736 +* Handle cancellation of uploads #736 * Upgrade to actix-web 1.0.0-beta.4 diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index ca1ff9c91..d377be1f4 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-multipart" -version = "0.1.0" +version = "0.1.3" authors = ["Nikolay Kim "] description = "Multipart support for actix web framework." readme = "README.md" @@ -18,7 +18,7 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = "1.0.0-rc" +actix-web = { version = "1.0.0", default-features = false } actix-service = "0.4.0" bytes = "0.4" derive_more = "0.14" @@ -31,4 +31,4 @@ twoway = "0.2" [dev-dependencies] actix-rt = "0.2.2" -actix-http = "0.2.0" \ No newline at end of file +actix-http = "0.2.2" \ No newline at end of file diff --git a/actix-multipart/LICENSE-APACHE b/actix-multipart/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/actix-multipart/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/actix-multipart/LICENSE-MIT b/actix-multipart/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/actix-multipart/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/actix-multipart/src/server.rs b/actix-multipart/src/server.rs index 7d746ea2f..e1a5543d4 100644 --- a/actix-multipart/src/server.rs +++ b/actix-multipart/src/server.rs @@ -128,7 +128,7 @@ impl InnerMultipart { fn read_headers( payload: &mut PayloadBuffer, ) -> Result, MultipartError> { - match payload.read_until(b"\r\n\r\n") { + match payload.read_until(b"\r\n\r\n")? { None => { if payload.eof { Err(MultipartError::Incomplete) @@ -167,7 +167,7 @@ impl InnerMultipart { boundary: &str, ) -> Result, MultipartError> { // TODO: need to read epilogue - match payload.readline() { + match payload.readline()? { None => { if payload.eof { Ok(Some(true)) @@ -200,7 +200,7 @@ impl InnerMultipart { ) -> Result, MultipartError> { let mut eof = false; loop { - match payload.readline() { + match payload.readline()? { Some(chunk) => { if chunk.is_empty() { return Err(MultipartError::Boundary); @@ -481,7 +481,7 @@ impl InnerField { if *size == 0 { Ok(Async::Ready(None)) } else { - match payload.read_max(*size) { + match payload.read_max(*size)? { Some(mut chunk) => { let len = cmp::min(chunk.len() as u64, *size); *size -= len; @@ -512,7 +512,11 @@ impl InnerField { let len = payload.buf.len(); if len == 0 { - return Ok(Async::NotReady); + return if payload.eof { + Err(MultipartError::Incomplete) + } else { + Ok(Async::NotReady) + }; } // check boundary @@ -533,8 +537,6 @@ impl InnerField { if &payload.buf[b_len..b_size] == boundary.as_bytes() { // found boundary return Ok(Async::Ready(None)); - } else { - pos = b_size; } } } @@ -572,7 +574,7 @@ impl InnerField { } } } else { - return Ok(Async::Ready(Some(payload.buf.take().freeze()))); + Ok(Async::Ready(Some(payload.buf.take().freeze()))) }; } } @@ -597,7 +599,7 @@ impl InnerField { } } - match payload.readline() { + match payload.readline()? { None => Async::Ready(None), Some(line) => { if line.as_ref() != b"\r\n" { @@ -749,23 +751,31 @@ impl PayloadBuffer { } } - fn read_max(&mut self, size: u64) -> Option { + fn read_max(&mut self, size: u64) -> Result, MultipartError> { if !self.buf.is_empty() { let size = std::cmp::min(self.buf.len() as u64, size) as usize; - Some(self.buf.split_to(size).freeze()) + Ok(Some(self.buf.split_to(size).freeze())) + } else if self.eof { + Err(MultipartError::Incomplete) } else { - None + Ok(None) } } /// Read until specified ending - pub fn read_until(&mut self, line: &[u8]) -> Option { - twoway::find_bytes(&self.buf, line) - .map(|idx| self.buf.split_to(idx + line.len()).freeze()) + pub fn read_until(&mut self, line: &[u8]) -> Result, MultipartError> { + let res = twoway::find_bytes(&self.buf, line) + .map(|idx| self.buf.split_to(idx + line.len()).freeze()); + + if res.is_none() && self.eof { + Err(MultipartError::Incomplete) + } else { + Ok(res) + } } /// Read bytes until new line delimiter - pub fn readline(&mut self) -> Option { + pub fn readline(&mut self) -> Result, MultipartError> { self.read_until(b"\n") } @@ -991,7 +1001,7 @@ mod tests { assert_eq!(payload.buf.len(), 0); payload.poll_stream().unwrap(); - assert_eq!(None, payload.read_max(1)); + assert_eq!(None, payload.read_max(1).unwrap()); }) } @@ -1001,14 +1011,14 @@ mod tests { let (mut sender, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); - assert_eq!(None, payload.read_max(4)); + assert_eq!(None, payload.read_max(4).unwrap()); sender.feed_data(Bytes::from("data")); sender.feed_eof(); payload.poll_stream().unwrap(); - assert_eq!(Some(Bytes::from("data")), payload.read_max(4)); + assert_eq!(Some(Bytes::from("data")), payload.read_max(4).unwrap()); assert_eq!(payload.buf.len(), 0); - assert_eq!(None, payload.read_max(1)); + assert!(payload.read_max(1).is_err()); assert!(payload.eof); }) } @@ -1018,7 +1028,7 @@ mod tests { run_on(|| { let (mut sender, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); - assert_eq!(None, payload.read_max(1)); + assert_eq!(None, payload.read_max(1).unwrap()); sender.set_error(PayloadError::Incomplete(None)); payload.poll_stream().err().unwrap(); }) @@ -1035,10 +1045,10 @@ mod tests { payload.poll_stream().unwrap(); assert_eq!(payload.buf.len(), 10); - assert_eq!(Some(Bytes::from("line1")), payload.read_max(5)); + assert_eq!(Some(Bytes::from("line1")), payload.read_max(5).unwrap()); assert_eq!(payload.buf.len(), 5); - assert_eq!(Some(Bytes::from("line2")), payload.read_max(5)); + assert_eq!(Some(Bytes::from("line2")), payload.read_max(5).unwrap()); assert_eq!(payload.buf.len(), 0); }) } @@ -1069,16 +1079,22 @@ mod tests { let (mut sender, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); - assert_eq!(None, payload.read_until(b"ne")); + assert_eq!(None, payload.read_until(b"ne").unwrap()); sender.feed_data(Bytes::from("line1")); sender.feed_data(Bytes::from("line2")); payload.poll_stream().unwrap(); - assert_eq!(Some(Bytes::from("line")), payload.read_until(b"ne")); + assert_eq!( + Some(Bytes::from("line")), + payload.read_until(b"ne").unwrap() + ); assert_eq!(payload.buf.len(), 6); - assert_eq!(Some(Bytes::from("1line2")), payload.read_until(b"2")); + assert_eq!( + Some(Bytes::from("1line2")), + payload.read_until(b"2").unwrap() + ); assert_eq!(payload.buf.len(), 0); }) } diff --git a/actix-session/CHANGES.md b/actix-session/CHANGES.md index 10727ae35..10aea8700 100644 --- a/actix-session/CHANGES.md +++ b/actix-session/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.1.1] - 2019-06-03 + +* Fix optional cookie session support + ## [0.1.0] - 2019-05-18 * Use actix-web 1.0.0-rc diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index b0ef1e2b4..1101ceffc 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-session" -version = "0.1.0" +version = "0.1.1" authors = ["Nikolay Kim "] description = "Session for actix web framework." readme = "README.md" diff --git a/actix-session/LICENSE-APACHE b/actix-session/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/actix-session/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/actix-session/LICENSE-MIT b/actix-session/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/actix-session/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs index 0e7831442..fb316f394 100644 --- a/actix-session/src/lib.rs +++ b/actix-session/src/lib.rs @@ -52,7 +52,9 @@ use serde::de::DeserializeOwned; use serde::Serialize; use serde_json; +#[cfg(feature = "cookie-session")] mod cookie; +#[cfg(feature = "cookie-session")] pub use crate::cookie::CookieSession; /// The high-level interface you use to modify session data. diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md index 34592aafc..89b4be818 100644 --- a/actix-web-actors/CHANGES.md +++ b/actix-web-actors/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [1.0.0] - 2019-05-29 + +* Update actix-http and actix-web + ## [0.1.0-alpha.3] - 2019-04-02 * Update actix-http and actix-web diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 0341641a3..565b53a57 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-actors" -version = "1.0.0-beta.4" +version = "1.0.0" authors = ["Nikolay Kim "] description = "Actix actors support for actix web framework." readme = "README.md" @@ -18,9 +18,9 @@ name = "actix_web_actors" path = "src/lib.rs" [dependencies] -actix = "0.8.2" -actix-web = "1.0.0-beta.5" -actix-http = "0.2.0" +actix = "0.8.3" +actix-web = "1.0.0-rc" +actix-http = "0.2.2" actix-codec = "0.1.2" bytes = "0.4" futures = "0.1.25" diff --git a/actix-web-actors/LICENSE-APACHE b/actix-web-actors/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/actix-web-actors/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/actix-web-actors/LICENSE-MIT b/actix-web-actors/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/actix-web-actors/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index 4f2d1c865..7cc0c164f 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -1,5 +1,13 @@ # Changes +## [0.1.2] - 2019-06-04 + +* Add macros for head, options, trace, connect and patch http methods + +## [0.1.1] - 2019-06-01 + +* Add syn "extra-traits" feature + ## [0.1.0] - 2019-05-18 * Release diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 5ca9f416d..23e9a432f 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-codegen" -version = "0.1.0" +version = "0.1.2" description = "Actix web proc macros" readme = "README.md" authors = ["Nikolay Kim "] @@ -12,11 +12,11 @@ workspace = ".." proc-macro = true [dependencies] -quote = "0.6" -syn = { version = "0.15", features = ["full", "parsing"] } +quote = "0.6.12" +syn = { version = "0.15.34", features = ["full", "parsing", "extra-traits"] } [dev-dependencies] -actix-web = { version = "1.0.0-beta.5" } -actix-http = { version = "0.2.0", features=["ssl"] } +actix-web = { version = "1.0.0-rc" } +actix-http = { version = "0.2.2", features=["ssl"] } actix-http-test = { version = "0.2.0", features=["ssl"] } futures = { version = "0.1" } diff --git a/actix-web-codegen/LICENSE-APACHE b/actix-web-codegen/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/actix-web-codegen/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/actix-web-codegen/LICENSE-MIT b/actix-web-codegen/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/actix-web-codegen/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/actix-web-codegen/src/lib.rs b/actix-web-codegen/src/lib.rs index 70cde90e4..b3ae7dd9b 100644 --- a/actix-web-codegen/src/lib.rs +++ b/actix-web-codegen/src/lib.rs @@ -11,6 +11,11 @@ //! - [post](attr.post.html) //! - [put](attr.put.html) //! - [delete](attr.delete.html) +//! - [head](attr.head.html) +//! - [connect](attr.connect.html) +//! - [options](attr.options.html) +//! - [trace](attr.trace.html) +//! - [patch](attr.patch.html) //! //! ### Attributes: //! @@ -92,3 +97,63 @@ pub fn delete(args: TokenStream, input: TokenStream) -> TokenStream { let gen = route::Args::new(&args, input, route::GuardType::Delete); gen.generate() } + +/// Creates route handler with `HEAD` method guard. +/// +/// Syntax: `#[head("path"[, attributes])]` +/// +/// Attributes are the same as in [head](attr.head.html) +#[proc_macro_attribute] +pub fn head(args: TokenStream, input: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as syn::AttributeArgs); + let gen = route::Args::new(&args, input, route::GuardType::Head); + gen.generate() +} + +/// Creates route handler with `CONNECT` method guard. +/// +/// Syntax: `#[connect("path"[, attributes])]` +/// +/// Attributes are the same as in [connect](attr.connect.html) +#[proc_macro_attribute] +pub fn connect(args: TokenStream, input: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as syn::AttributeArgs); + let gen = route::Args::new(&args, input, route::GuardType::Connect); + gen.generate() +} + +/// Creates route handler with `OPTIONS` method guard. +/// +/// Syntax: `#[options("path"[, attributes])]` +/// +/// Attributes are the same as in [options](attr.options.html) +#[proc_macro_attribute] +pub fn options(args: TokenStream, input: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as syn::AttributeArgs); + let gen = route::Args::new(&args, input, route::GuardType::Options); + gen.generate() +} + +/// Creates route handler with `TRACE` method guard. +/// +/// Syntax: `#[trace("path"[, attributes])]` +/// +/// Attributes are the same as in [trace](attr.trace.html) +#[proc_macro_attribute] +pub fn trace(args: TokenStream, input: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as syn::AttributeArgs); + let gen = route::Args::new(&args, input, route::GuardType::Trace); + gen.generate() +} + +/// Creates route handler with `PATCH` method guard. +/// +/// Syntax: `#[patch("path"[, attributes])]` +/// +/// Attributes are the same as in [patch](attr.patch.html) +#[proc_macro_attribute] +pub fn patch(args: TokenStream, input: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as syn::AttributeArgs); + let gen = route::Args::new(&args, input, route::GuardType::Patch); + gen.generate() +} diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index 1a5f79298..268adecb0 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -25,6 +25,11 @@ pub enum GuardType { Post, Put, Delete, + Head, + Connect, + Options, + Trace, + Patch, } impl fmt::Display for GuardType { @@ -34,6 +39,11 @@ impl fmt::Display for GuardType { &GuardType::Post => write!(f, "Post"), &GuardType::Put => write!(f, "Put"), &GuardType::Delete => write!(f, "Delete"), + &GuardType::Head => write!(f, "Head"), + &GuardType::Connect => write!(f, "Connect"), + &GuardType::Options => write!(f, "Options"), + &GuardType::Trace => write!(f, "Trace"), + &GuardType::Patch => write!(f, "Patch"), } } } diff --git a/actix-web-codegen/tests/test_macro.rs b/actix-web-codegen/tests/test_macro.rs index cd899d48d..f02b82f00 100644 --- a/actix-web-codegen/tests/test_macro.rs +++ b/actix-web-codegen/tests/test_macro.rs @@ -1,7 +1,7 @@ use actix_http::HttpService; use actix_http_test::TestServer; use actix_web::{http, web::Path, App, HttpResponse, Responder}; -use actix_web_codegen::{delete, get, post, put}; +use actix_web_codegen::{connect, delete, get, head, options, patch, post, put, trace}; use futures::{future, Future}; #[get("/test")] @@ -14,11 +14,36 @@ fn put_test() -> impl Responder { HttpResponse::Created() } +#[patch("/test")] +fn patch_test() -> impl Responder { + HttpResponse::Ok() +} + #[post("/test")] fn post_test() -> impl Responder { HttpResponse::NoContent() } +#[head("/test")] +fn head_test() -> impl Responder { + HttpResponse::Ok() +} + +#[connect("/test")] +fn connect_test() -> impl Responder { + HttpResponse::Ok() +} + +#[options("/test")] +fn options_test() -> impl Responder { + HttpResponse::Ok() +} + +#[trace("/test")] +fn trace_test() -> impl Responder { + HttpResponse::Ok() +} + #[get("/test")] fn auto_async() -> impl Future { future::ok(HttpResponse::Ok().finish()) @@ -75,6 +100,11 @@ fn test_body() { App::new() .service(post_test) .service(put_test) + .service(head_test) + .service(connect_test) + .service(options_test) + .service(trace_test) + .service(patch_test) .service(test), ) }); @@ -82,6 +112,26 @@ fn test_body() { let response = srv.block_on(request.send()).unwrap(); assert!(response.status().is_success()); + let request = srv.request(http::Method::HEAD, srv.url("/test")); + let response = srv.block_on(request.send()).unwrap(); + assert!(response.status().is_success()); + + let request = srv.request(http::Method::CONNECT, srv.url("/test")); + let response = srv.block_on(request.send()).unwrap(); + assert!(response.status().is_success()); + + let request = srv.request(http::Method::OPTIONS, srv.url("/test")); + let response = srv.block_on(request.send()).unwrap(); + assert!(response.status().is_success()); + + let request = srv.request(http::Method::TRACE, srv.url("/test")); + let response = srv.block_on(request.send()).unwrap(); + assert!(response.status().is_success()); + + let request = srv.request(http::Method::PATCH, srv.url("/test")); + let response = srv.block_on(request.send()).unwrap(); + assert!(response.status().is_success()); + let request = srv.request(http::Method::PUT, srv.url("/test")); let response = srv.block_on(request.send()).unwrap(); assert!(response.status().is_success()); diff --git a/awc/CHANGES.md b/awc/CHANGES.md index f36b0bb3a..b5f64dd37 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,5 +1,11 @@ # Changes +## [0.2.1] - 2019-06-05 + +### Added + +* Add license files + ## [0.2.0] - 2019-05-12 ### Added diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 2112185c8..cad52033e 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "0.2.0" +version = "0.2.1" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" @@ -41,7 +41,7 @@ flate2-rust = ["actix-http/flate2-rust"] [dependencies] actix-codec = "0.1.2" actix-service = "0.4.0" -actix-http = "0.2.0" +actix-http = "0.2.3" base64 = "0.10.1" bytes = "0.4" derive_more = "0.14" @@ -58,11 +58,11 @@ openssl = { version="0.10", optional = true } [dev-dependencies] actix-rt = "0.2.2" -actix-web = { version = "1.0.0-beta.4", features=["ssl"] } -actix-http = { version = "0.2.0", features=["ssl"] } +actix-web = { version = "1.0.0-rc", features=["ssl"] } +actix-http = { version = "0.2.3", features=["ssl"] } actix-http-test = { version = "0.2.0", features=["ssl"] } -actix-utils = "0.4.0" -actix-server = { version = "0.5.0", features=["ssl"] } +actix-utils = "0.4.1" +actix-server = { version = "0.5.1", features=["ssl"] } brotli2 = { version="0.3.2" } flate2 = { version="1.0.2" } env_logger = "0.6" diff --git a/awc/LICENSE-APACHE b/awc/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/awc/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/awc/LICENSE-MIT b/awc/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/awc/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/src/app.rs b/src/app.rs index 1568d5fca..4f8b283e1 100644 --- a/src/app.rs +++ b/src/app.rs @@ -100,6 +100,13 @@ where self } + /// Set application data. Application data could be accessed + /// by using `Data` extractor where `T` is data type. + pub fn register_data(mut self, data: Data) -> Self { + self.data.push(Box::new(data)); + self + } + /// Run external configuration as part of the application building /// process /// diff --git a/src/app_service.rs b/src/app_service.rs index 88e97de1e..5a9731bf2 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -239,7 +239,6 @@ where { fn drop(&mut self) { self.pool.clear(); - println!("DROP: APP-INIT-ENTRY"); } } @@ -436,7 +435,6 @@ mod tests { impl Drop for DropData { fn drop(&mut self) { self.0.store(true, Ordering::Relaxed); - println!("Dropping!"); } } diff --git a/src/config.rs b/src/config.rs index bc33da9df..8de43f36c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -188,7 +188,7 @@ impl ServiceConfig { } } - /// Set application data. Applicatin data could be accessed + /// Set application data. Application data could be accessed /// by using `Data` extractor where `T` is data type. /// /// This is same as `App::data()` method. diff --git a/src/data.rs b/src/data.rs index 1328c4ef6..9fd8b67fc 100644 --- a/src/data.rs +++ b/src/data.rs @@ -54,7 +54,7 @@ pub(crate) trait DataFactory { /// /// let app = App::new() /// // Store `MyData` in application storage. -/// .data(data.clone()) +/// .register_data(data.clone()) /// .service( /// web::resource("/index.html").route( /// web::get().to(index))); @@ -130,6 +130,7 @@ impl DataFactory for Data { mod tests { use actix_service::Service; + use super::*; use crate::http::StatusCode; use crate::test::{block_on, init_service, TestRequest}; use crate::{web, App, HttpResponse}; @@ -154,6 +155,26 @@ mod tests { assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); } + #[test] + fn test_register_data_extractor() { + let mut srv = + init_service(App::new().register_data(Data::new(10usize)).service( + web::resource("/").to(|_: web::Data| HttpResponse::Ok()), + )); + + let req = TestRequest::default().to_request(); + let resp = block_on(srv.call(req)).unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + + let mut srv = + init_service(App::new().register_data(Data::new(10u32)).service( + web::resource("/").to(|_: web::Data| HttpResponse::Ok()), + )); + let req = TestRequest::default().to_request(); + let resp = block_on(srv.call(req)).unwrap(); + assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); + } + #[test] fn test_route_data_extractor() { let mut srv = diff --git a/src/lib.rs b/src/lib.rs index f84dbd5be..f0bf01bc9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,15 +66,15 @@ //! //! ## Package feature //! -//! * `client` - enables http client +//! * `client` - enables http client (default enabled) //! * `ssl` - enables ssl support via `openssl` crate, supports `http/2` //! * `rust-tls` - enables ssl support via `rustls` crate, supports `http/2` //! * `secure-cookies` - enables secure cookies support, includes `ring` crate as -//! dependency +//! dependency (default enabled) //! * `brotli` - enables `brotli` compression support, requires `c` -//! compiler +//! compiler (default enabled) //! * `flate2-zlib` - enables `gzip`, `deflate` compression support, requires -//! `c` compiler +//! `c` compiler (default enabled) //! * `flate2-rust` - experimental rust based implementation for //! `gzip`, `deflate` compression. //! @@ -111,7 +111,7 @@ pub use actix_web_codegen::*; // re-export for convenience pub use actix_http::Response as HttpResponse; -pub use actix_http::{cookie, http, Error, HttpMessage, ResponseError, Result}; +pub use actix_http::{body, cookie, http, Error, HttpMessage, ResponseError, Result}; pub use crate::app::App; pub use crate::extract::FromRequest; @@ -143,7 +143,7 @@ pub mod dev { pub use crate::types::json::JsonBody; pub use crate::types::readlines::Readlines; - pub use actix_http::body::{Body, BodySize, MessageBody, ResponseBody}; + pub use actix_http::body::{Body, BodySize, MessageBody, ResponseBody, SizedStream}; pub use actix_http::encoding::Decoder as Decompress; pub use actix_http::ResponseBuilder as HttpResponseBuilder; pub use actix_http::{ diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index 5d0b615e1..d47e45023 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -53,7 +53,7 @@ use crate::HttpResponse; /// /// `%a` Remote IP-address (IP-address of proxy if using reverse proxy) /// -/// `%t` Time when the request was started to process +/// `%t` Time when the request was started to process (in rfc3339 format) /// /// `%r` First line of request /// @@ -417,10 +417,7 @@ impl FormatText { } FormatText::UrlPath => *self = FormatText::Str(format!("{}", req.path())), FormatText::RequestTime => { - *self = FormatText::Str(format!( - "{:?}", - now.strftime("[%d/%b/%Y:%H:%M:%S %z]").unwrap() - )) + *self = FormatText::Str(format!("{}", now.rfc3339())) } FormatText::RequestHeader(ref name) => { let s = if let Some(val) = req.headers().get(name) { @@ -547,4 +544,29 @@ mod tests { assert!(s.contains("200 1024")); assert!(s.contains("ACTIX-WEB")); } + + #[test] + fn test_request_time_format() { + let mut format = Format::new("%t"); + let req = TestRequest::default().to_srv_request(); + + let now = time::now(); + for unit in &mut format.0 { + unit.render_request(now, &req); + } + + let resp = HttpResponse::build(StatusCode::OK).force_close().finish(); + for unit in &mut format.0 { + unit.render_response(&resp); + } + + let render = |fmt: &mut Formatter| { + for unit in &format.0 { + unit.render(fmt, 1024, now)?; + } + Ok(()) + }; + let s = format!("{}", FormatDisplay(&render)); + assert!(s.contains(&format!("{}", now.rfc3339()))); + } } diff --git a/src/rmap.rs b/src/rmap.rs index 35fe8ee32..6a543b75c 100644 --- a/src/rmap.rs +++ b/src/rmap.rs @@ -122,7 +122,9 @@ impl ResourceMap { I: AsRef, { if let Some(pattern) = self.named.get(name) { - self.fill_root(path, elements)?; + if pattern.pattern().starts_with("/") { + self.fill_root(path, elements)?; + } if pattern.resource_path(path, elements) { Ok(Some(())) } else { diff --git a/src/scope.rs b/src/scope.rs index 84f34dae6..ad97fcb62 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -11,6 +11,7 @@ use actix_service::{ use futures::future::{ok, Either, Future, FutureResult}; use futures::{Async, IntoFuture, Poll}; +use crate::config::ServiceConfig; use crate::data::Data; use crate::dev::{AppService, HttpServiceFactory}; use crate::error::Error; @@ -66,6 +67,7 @@ pub struct Scope { services: Vec>, guards: Vec>, default: Rc>>>, + external: Vec, factory_ref: Rc>>, } @@ -80,6 +82,7 @@ impl Scope { guards: Vec::new(), services: Vec::new(), default: Rc::new(RefCell::new(None)), + external: Vec::new(), factory_ref: fref, } } @@ -153,6 +156,56 @@ where self } + /// Run external configuration as part of the scope building + /// process + /// + /// This function is useful for moving parts of configuration to a + /// different module or even library. For example, + /// some of the resource's configuration could be moved to different module. + /// + /// ```rust + /// # extern crate actix_web; + /// use actix_web::{web, middleware, App, HttpResponse}; + /// + /// // this function could be located in different module + /// fn config(cfg: &mut web::ServiceConfig) { + /// cfg.service(web::resource("/test") + /// .route(web::get().to(|| HttpResponse::Ok())) + /// .route(web::head().to(|| HttpResponse::MethodNotAllowed())) + /// ); + /// } + /// + /// fn main() { + /// let app = App::new() + /// .wrap(middleware::Logger::default()) + /// .service( + /// web::scope("/api") + /// .configure(config) + /// ) + /// .route("/index.html", web::get().to(|| HttpResponse::Ok())); + /// } + /// ``` + pub fn configure(mut self, f: F) -> Self + where + F: FnOnce(&mut ServiceConfig), + { + let mut cfg = ServiceConfig::new(); + f(&mut cfg); + self.services.extend(cfg.services); + self.external.extend(cfg.external); + + if !cfg.data.is_empty() { + let mut data = self.data.unwrap_or_else(|| Extensions::new()); + + for value in cfg.data.iter() { + value.create(&mut data); + } + + self.data = Some(data); + } + self + } + /// Register http service. /// /// This is similar to `App's` service registration. @@ -281,6 +334,7 @@ where guards: self.guards, services: self.services, default: self.default, + external: self.external, factory_ref: self.factory_ref, } } @@ -359,6 +413,11 @@ where let mut rmap = ResourceMap::new(ResourceDef::root_prefix(&self.rdef)); + // external resources + for mut rdef in std::mem::replace(&mut self.external, Vec::new()) { + rmap.add(&mut rdef, None); + } + // custom app data storage if let Some(ref mut ext) = self.data { config.set_service_data(ext); @@ -594,7 +653,7 @@ mod tests { use crate::dev::{Body, ResponseBody}; use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::service::{ServiceRequest, ServiceResponse}; - use crate::test::{block_on, call_service, init_service, TestRequest}; + use crate::test::{block_on, call_service, init_service, read_body, TestRequest}; use crate::{guard, web, App, Error, HttpRequest, HttpResponse}; #[test] @@ -1022,4 +1081,58 @@ mod tests { let resp = call_service(&mut srv, req); assert_eq!(resp.status(), StatusCode::OK); } + + #[test] + fn test_scope_config() { + let mut srv = + init_service(App::new().service(web::scope("/app").configure(|s| { + s.route("/path1", web::get().to(|| HttpResponse::Ok())); + }))); + + let req = TestRequest::with_uri("/app/path1").to_request(); + let resp = block_on(srv.call(req)).unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + } + + #[test] + fn test_scope_config_2() { + let mut srv = + init_service(App::new().service(web::scope("/app").configure(|s| { + s.service(web::scope("/v1").configure(|s| { + s.route("/", web::get().to(|| HttpResponse::Ok())); + })); + }))); + + let req = TestRequest::with_uri("/app/v1/").to_request(); + let resp = block_on(srv.call(req)).unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + } + + #[test] + fn test_url_for_external() { + let mut srv = + init_service(App::new().service(web::scope("/app").configure(|s| { + s.service(web::scope("/v1").configure(|s| { + s.external_resource( + "youtube", + "https://youtube.com/watch/{video_id}", + ); + s.route( + "/", + web::get().to(|req: HttpRequest| { + HttpResponse::Ok().body(format!( + "{}", + req.url_for("youtube", &["xxxxxx"]).unwrap().as_str() + )) + }), + ); + })); + }))); + + let req = TestRequest::with_uri("/app/v1/").to_request(); + let resp = block_on(srv.call(req)).unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + let body = read_body(resp); + assert_eq!(body, &b"https://youtube.com/watch/xxxxxx"[..]); + } } diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index 8704a64c2..a31937909 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.2.1] - 2019-06-05 + +* Add license files + ## [0.2.0] - 2019-05-12 * Update awc and actix-http deps diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 8567b745e..a8f4425ba 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "0.2.0" +version = "0.2.1" authors = ["Nikolay Kim "] description = "Actix http test server" readme = "README.md" @@ -33,9 +33,9 @@ ssl = ["openssl", "actix-server/ssl", "awc/ssl"] actix-codec = "0.1.2" actix-rt = "0.2.2" actix-service = "0.4.0" -actix-server = "0.5.0" -actix-utils = "0.4.0" -awc = "0.2.0" +actix-server = "0.5.1" +actix-utils = "0.4.1" +awc = "0.2.1" base64 = "0.10" bytes = "0.4" @@ -55,5 +55,5 @@ tokio-timer = "0.2" openssl = { version="0.10", optional = true } [dev-dependencies] -actix-web = "1.0.0-beta.4" -actix-http = "0.2.0" +actix-web = "1.0.0-rc" +actix-http = "0.2.3" diff --git a/test-server/LICENSE-APACHE b/test-server/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/test-server/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/test-server/LICENSE-MIT b/test-server/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/test-server/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file