diff --git a/Cargo.toml b/Cargo.toml index 74aa284e4..4c3b11e28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ members = [ "awc", "actix-http", "actix-files", - "actix-framed", "actix-multipart", "actix-web-actors", "actix-web-codegen", diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml deleted file mode 100644 index 494dd6f42..000000000 --- a/actix-framed/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "actix-framed" -version = "0.3.0" -authors = ["Nikolay Kim "] -description = "Actix framed app server" -readme = "README.md" -keywords = ["http", "web", "framework", "async", "futures"] -homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-web.git" -documentation = "https://docs.rs/actix-framed/" -categories = ["network-programming", "asynchronous", - "web-programming::http-server", - "web-programming::websocket"] -license = "MIT/Apache-2.0" -edition = "2018" - -[lib] -name = "actix_framed" -path = "src/lib.rs" - -[dependencies] -actix-codec = "0.2.0" -actix-service = "1.0.1" -actix-router = "0.2.1" -actix-rt = "1.0.0" -actix-http = "2.0.0-alpha.4" - -bytes = "0.5.3" -futures-util = { version = "0.3.5", default-features = false } -pin-project = "0.4.6" -log = "0.4" - -[dev-dependencies] -actix-server = "1.0.0" -actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] } -actix-http-test = { version = "2.0.0-alpha.1", features = ["openssl"] } -actix-utils = "1.0.3" diff --git a/actix-framed/LICENSE-APACHE b/actix-framed/LICENSE-APACHE deleted file mode 100644 index 6cdf2d16c..000000000 --- a/actix-framed/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2017-NOW Nikolay Kim - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/actix-framed/LICENSE-MIT b/actix-framed/LICENSE-MIT deleted file mode 100644 index 0f80296ae..000000000 --- a/actix-framed/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2017 Nikolay Kim - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/actix-framed/README.md b/actix-framed/README.md index 3a5ea0596..a4eaadf21 100644 --- a/actix-framed/README.md +++ b/actix-framed/README.md @@ -1,8 +1,3 @@ -# Framed app for actix web [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](https://meritbadge.herokuapp.com/actix-framed)](https://crates.io/crates/actix-framed) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +# Framed app for actix web -## Documentation & community resources - -* [API Documentation](https://docs.rs/actix-framed/) -* [Chat on gitter](https://gitter.im/actix/actix) -* Cargo package: [actix-framed](https://crates.io/crates/actix-framed) -* Minimum supported Rust version: 1.40 or later +**This crate has been deprecated and removed.** diff --git a/actix-framed/changes.md b/actix-framed/changes.md deleted file mode 100644 index 1c5d31fa2..000000000 --- a/actix-framed/changes.md +++ /dev/null @@ -1,28 +0,0 @@ -# Changes - -## [Unreleased] - 2020-xx-xx - -* Bump minimum supported Rust version to 1.40 - -## [0.3.0] - 2019-12-25 - -* Migrate to actix-http 1.0 - -## [0.2.1] - 2019-07-20 - -* Remove unneeded actix-utils dependency - - -## [0.2.0] - 2019-05-12 - -* Update dependencies - - -## [0.1.0] - 2019-04-16 - -* Update tests - - -## [0.1.0-alpha.1] - 2019-04-12 - -* Initial release diff --git a/actix-framed/src/app.rs b/actix-framed/src/app.rs deleted file mode 100644 index 2fddbc039..000000000 --- a/actix-framed/src/app.rs +++ /dev/null @@ -1,221 +0,0 @@ -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; - -use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use actix_http::h1::{Codec, SendResponse}; -use actix_http::{Error, Request, Response}; -use actix_router::{Path, Router, Url}; -use actix_service::{IntoServiceFactory, Service, ServiceFactory}; -use futures_util::future::{ok, FutureExt, LocalBoxFuture}; - -use crate::helpers::{BoxedHttpNewService, BoxedHttpService, HttpNewService}; -use crate::request::FramedRequest; -use crate::state::State; - -type BoxedResponse = LocalBoxFuture<'static, Result<(), Error>>; - -pub trait HttpServiceFactory { - type Factory: ServiceFactory; - - fn path(&self) -> &str; - - fn create(self) -> Self::Factory; -} - -/// Application builder -pub struct FramedApp { - state: State, - services: Vec<(String, BoxedHttpNewService>)>, -} - -impl FramedApp { - pub fn new() -> Self { - FramedApp { - state: State::new(()), - services: Vec::new(), - } - } -} - -impl FramedApp { - pub fn with(state: S) -> FramedApp { - FramedApp { - services: Vec::new(), - state: State::new(state), - } - } - - pub fn service(mut self, factory: U) -> Self - where - U: HttpServiceFactory, - U::Factory: ServiceFactory< - Config = (), - Request = FramedRequest, - Response = (), - Error = Error, - InitError = (), - > + 'static, - ::Future: 'static, - ::Service: Service< - Request = FramedRequest, - Response = (), - Error = Error, - Future = LocalBoxFuture<'static, Result<(), Error>>, - >, - { - let path = factory.path().to_string(); - self.services - .push((path, Box::new(HttpNewService::new(factory.create())))); - self - } -} - -impl IntoServiceFactory> for FramedApp -where - T: AsyncRead + AsyncWrite + Unpin + 'static, - S: 'static, -{ - fn into_factory(self) -> FramedAppFactory { - FramedAppFactory { - state: self.state, - services: Rc::new(self.services), - } - } -} - -#[derive(Clone)] -pub struct FramedAppFactory { - state: State, - services: Rc>)>>, -} - -impl ServiceFactory for FramedAppFactory -where - T: AsyncRead + AsyncWrite + Unpin + 'static, - S: 'static, -{ - type Config = (); - type Request = (Request, Framed); - type Response = (); - type Error = Error; - type InitError = (); - type Service = FramedAppService; - type Future = CreateService; - - fn new_service(&self, _: ()) -> Self::Future { - CreateService { - fut: self - .services - .iter() - .map(|(path, service)| { - CreateServiceItem::Future( - Some(path.clone()), - service.new_service(()), - ) - }) - .collect(), - state: self.state.clone(), - } - } -} - -#[doc(hidden)] -pub struct CreateService { - fut: Vec>, - state: State, -} - -enum CreateServiceItem { - Future( - Option, - LocalBoxFuture<'static, Result>, ()>>, - ), - Service(String, BoxedHttpService>), -} - -impl Future for CreateService -where - T: AsyncRead + AsyncWrite + Unpin, -{ - type Output = Result, ()>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let mut done = true; - - // poll http services - for item in &mut self.fut { - let res = match item { - CreateServiceItem::Future(ref mut path, ref mut fut) => { - match Pin::new(fut).poll(cx) { - Poll::Ready(Ok(service)) => { - Some((path.take().unwrap(), service)) - } - Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), - Poll::Pending => { - done = false; - None - } - } - } - CreateServiceItem::Service(_, _) => continue, - }; - - if let Some((path, service)) = res { - *item = CreateServiceItem::Service(path, service); - } - } - - if done { - let router = self - .fut - .drain(..) - .fold(Router::build(), |mut router, item| { - match item { - CreateServiceItem::Service(path, service) => { - router.path(&path, service); - } - CreateServiceItem::Future(_, _) => unreachable!(), - } - router - }); - Poll::Ready(Ok(FramedAppService { - router: router.finish(), - state: self.state.clone(), - })) - } else { - Poll::Pending - } - } -} - -pub struct FramedAppService { - state: State, - router: Router>>, -} - -impl Service for FramedAppService -where - T: AsyncRead + AsyncWrite + Unpin, -{ - type Request = (Request, Framed); - type Response = (); - type Error = Error; - type Future = BoxedResponse; - - fn poll_ready(&mut self, _: &mut Context) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, (req, framed): (Request, Framed)) -> Self::Future { - let mut path = Path::new(Url::new(req.uri().clone())); - - if let Some((srv, _info)) = self.router.recognize_mut(&mut path) { - return srv.call(FramedRequest::new(req, framed, path, self.state.clone())); - } - SendResponse::new(framed, Response::NotFound().finish()) - .then(|_| ok(())) - .boxed_local() - } -} diff --git a/actix-framed/src/helpers.rs b/actix-framed/src/helpers.rs deleted file mode 100644 index d83736a52..000000000 --- a/actix-framed/src/helpers.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::task::{Context, Poll}; - -use actix_http::Error; -use actix_service::{Service, ServiceFactory}; -use futures_util::future::{FutureExt, LocalBoxFuture}; - -pub(crate) type BoxedHttpService = Box< - dyn Service< - Request = Req, - Response = (), - Error = Error, - Future = LocalBoxFuture<'static, Result<(), Error>>, - >, ->; - -pub(crate) type BoxedHttpNewService = Box< - dyn ServiceFactory< - Config = (), - Request = Req, - Response = (), - Error = Error, - InitError = (), - Service = BoxedHttpService, - Future = LocalBoxFuture<'static, Result, ()>>, - >, ->; - -pub(crate) struct HttpNewService(T); - -impl HttpNewService -where - T: ServiceFactory, - T::Response: 'static, - T::Future: 'static, - T::Service: Service>> + 'static, - ::Future: 'static, -{ - pub fn new(service: T) -> Self { - HttpNewService(service) - } -} - -impl ServiceFactory for HttpNewService -where - T: ServiceFactory, - T::Request: 'static, - T::Future: 'static, - T::Service: Service>> + 'static, - ::Future: 'static, -{ - type Config = (); - type Request = T::Request; - type Response = (); - type Error = Error; - type InitError = (); - type Service = BoxedHttpService; - type Future = LocalBoxFuture<'static, Result>; - - fn new_service(&self, _: ()) -> Self::Future { - let fut = self.0.new_service(()); - - async move { - fut.await.map_err(|_| ()).map(|service| { - let service: BoxedHttpService<_> = - Box::new(HttpServiceWrapper { service }); - service - }) - } - .boxed_local() - } -} - -struct HttpServiceWrapper { - service: T, -} - -impl Service for HttpServiceWrapper -where - T: Service< - Response = (), - Future = LocalBoxFuture<'static, Result<(), Error>>, - Error = Error, - >, - T::Request: 'static, -{ - type Request = T::Request; - type Response = (); - type Error = Error; - type Future = LocalBoxFuture<'static, Result<(), Error>>; - - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { - self.service.poll_ready(cx) - } - - fn call(&mut self, req: Self::Request) -> Self::Future { - self.service.call(req) - } -} diff --git a/actix-framed/src/lib.rs b/actix-framed/src/lib.rs deleted file mode 100644 index 9d2c71250..000000000 --- a/actix-framed/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![allow(clippy::type_complexity, clippy::new_without_default, dead_code)] -mod app; -mod helpers; -mod request; -mod route; -mod service; -mod state; -pub mod test; - -// re-export for convenience -pub use actix_http::{http, Error, HttpMessage, Response, ResponseError}; - -pub use self::app::{FramedApp, FramedAppService}; -pub use self::request::FramedRequest; -pub use self::route::FramedRoute; -pub use self::service::{SendError, VerifyWebSockets}; -pub use self::state::State; diff --git a/actix-framed/src/request.rs b/actix-framed/src/request.rs deleted file mode 100644 index 9a6cc8e39..000000000 --- a/actix-framed/src/request.rs +++ /dev/null @@ -1,172 +0,0 @@ -use std::cell::{Ref, RefMut}; - -use actix_codec::Framed; -use actix_http::http::{HeaderMap, Method, Uri, Version}; -use actix_http::{h1::Codec, Extensions, Request, RequestHead}; -use actix_router::{Path, Url}; - -use crate::state::State; - -pub struct FramedRequest { - req: Request, - framed: Framed, - state: State, - pub(crate) path: Path, -} - -impl FramedRequest { - pub fn new( - req: Request, - framed: Framed, - path: Path, - state: State, - ) -> Self { - Self { - req, - framed, - state, - path, - } - } -} - -impl FramedRequest { - /// Split request into a parts - pub fn into_parts(self) -> (Request, Framed, State) { - (self.req, self.framed, self.state) - } - - /// This method returns reference to the request head - #[inline] - pub fn head(&self) -> &RequestHead { - self.req.head() - } - - /// This method returns mutable reference to the request head. - /// panics if multiple references of http request exists. - #[inline] - pub fn head_mut(&mut self) -> &mut RequestHead { - self.req.head_mut() - } - - /// Shared application state - #[inline] - pub fn state(&self) -> &S { - self.state.get_ref() - } - - /// Request's uri. - #[inline] - pub fn uri(&self) -> &Uri { - &self.head().uri - } - - /// Read the Request method. - #[inline] - pub fn method(&self) -> &Method { - &self.head().method - } - - /// Read the Request Version. - #[inline] - pub fn version(&self) -> Version { - self.head().version - } - - #[inline] - /// Returns request's headers. - pub fn headers(&self) -> &HeaderMap { - &self.head().headers - } - - /// The target path of this Request. - #[inline] - pub fn path(&self) -> &str { - self.head().uri.path() - } - - /// The query string in the URL. - /// - /// E.g., id=10 - #[inline] - pub fn query_string(&self) -> &str { - if let Some(query) = self.uri().query().as_ref() { - query - } else { - "" - } - } - - /// Get a reference to the Path parameters. - /// - /// Params is a container for url parameters. - /// A variable segment is specified in the form `{identifier}`, - /// where the identifier can be used later in a request handler to - /// access the matched value for that segment. - #[inline] - pub fn match_info(&self) -> &Path { - &self.path - } - - /// Request extensions - #[inline] - pub fn extensions(&self) -> Ref { - self.head().extensions() - } - - /// Mutable reference to a the request's extensions - #[inline] - pub fn extensions_mut(&self) -> RefMut { - self.head().extensions_mut() - } -} - -#[cfg(test)] -mod tests { - use std::convert::TryFrom; - - use actix_http::http::{HeaderName, HeaderValue}; - use actix_http::test::{TestBuffer, TestRequest}; - - use super::*; - - #[test] - fn test_request() { - let buf = TestBuffer::empty(); - let framed = Framed::new(buf, Codec::default()); - let req = TestRequest::with_uri("/index.html?q=1") - .header("content-type", "test") - .finish(); - let path = Path::new(Url::new(req.uri().clone())); - - let mut freq = FramedRequest::new(req, framed, path, State::new(10u8)); - assert_eq!(*freq.state(), 10); - assert_eq!(freq.version(), Version::HTTP_11); - assert_eq!(freq.method(), Method::GET); - assert_eq!(freq.path(), "/index.html"); - assert_eq!(freq.query_string(), "q=1"); - assert_eq!( - freq.headers() - .get("content-type") - .unwrap() - .to_str() - .unwrap(), - "test" - ); - - freq.head_mut().headers.insert( - HeaderName::try_from("x-hdr").unwrap(), - HeaderValue::from_static("test"), - ); - assert_eq!( - freq.headers().get("x-hdr").unwrap().to_str().unwrap(), - "test" - ); - - freq.extensions_mut().insert(100usize); - assert_eq!(*freq.extensions().get::().unwrap(), 100usize); - - let (_, _, state) = freq.into_parts(); - assert_eq!(*state, 10); - } -} diff --git a/actix-framed/src/route.rs b/actix-framed/src/route.rs deleted file mode 100644 index 7b9004b64..000000000 --- a/actix-framed/src/route.rs +++ /dev/null @@ -1,159 +0,0 @@ -use std::fmt; -use std::future::Future; -use std::marker::PhantomData; -use std::task::{Context, Poll}; - -use actix_codec::{AsyncRead, AsyncWrite}; -use actix_http::{http::Method, Error}; -use actix_service::{Service, ServiceFactory}; -use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready}; -use log::error; - -use crate::app::HttpServiceFactory; -use crate::request::FramedRequest; - -/// Resource route definition -/// -/// Route uses builder-like pattern for configuration. -/// If handler is not explicitly set, default *404 Not Found* handler is used. -pub struct FramedRoute { - handler: F, - pattern: String, - methods: Vec, - state: PhantomData<(Io, S, R, E)>, -} - -impl FramedRoute { - pub fn new(pattern: &str) -> Self { - FramedRoute { - handler: (), - pattern: pattern.to_string(), - methods: Vec::new(), - state: PhantomData, - } - } - - pub fn get(path: &str) -> FramedRoute { - FramedRoute::new(path).method(Method::GET) - } - - pub fn post(path: &str) -> FramedRoute { - FramedRoute::new(path).method(Method::POST) - } - - pub fn put(path: &str) -> FramedRoute { - FramedRoute::new(path).method(Method::PUT) - } - - pub fn delete(path: &str) -> FramedRoute { - FramedRoute::new(path).method(Method::DELETE) - } - - pub fn method(mut self, method: Method) -> Self { - self.methods.push(method); - self - } - - pub fn to(self, handler: F) -> FramedRoute - where - F: FnMut(FramedRequest) -> R, - R: Future> + 'static, - - E: fmt::Debug, - { - FramedRoute { - handler, - pattern: self.pattern, - methods: self.methods, - state: PhantomData, - } - } -} - -impl HttpServiceFactory for FramedRoute -where - Io: AsyncRead + AsyncWrite + 'static, - F: FnMut(FramedRequest) -> R + Clone, - R: Future> + 'static, - E: fmt::Display, -{ - type Factory = FramedRouteFactory; - - fn path(&self) -> &str { - &self.pattern - } - - fn create(self) -> Self::Factory { - FramedRouteFactory { - handler: self.handler, - methods: self.methods, - _t: PhantomData, - } - } -} - -pub struct FramedRouteFactory { - handler: F, - methods: Vec, - _t: PhantomData<(Io, S, R, E)>, -} - -impl ServiceFactory for FramedRouteFactory -where - Io: AsyncRead + AsyncWrite + 'static, - F: FnMut(FramedRequest) -> R + Clone, - R: Future> + 'static, - E: fmt::Display, -{ - type Config = (); - type Request = FramedRequest; - type Response = (); - type Error = Error; - type InitError = (); - type Service = FramedRouteService; - type Future = Ready>; - - fn new_service(&self, _: ()) -> Self::Future { - ok(FramedRouteService { - handler: self.handler.clone(), - methods: self.methods.clone(), - _t: PhantomData, - }) - } -} - -pub struct FramedRouteService { - handler: F, - methods: Vec, - _t: PhantomData<(Io, S, R, E)>, -} - -impl Service for FramedRouteService -where - Io: AsyncRead + AsyncWrite + 'static, - F: FnMut(FramedRequest) -> R + Clone, - R: Future> + 'static, - E: fmt::Display, -{ - type Request = FramedRequest; - type Response = (); - type Error = Error; - type Future = LocalBoxFuture<'static, Result<(), Error>>; - - fn poll_ready(&mut self, _: &mut Context) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: FramedRequest) -> Self::Future { - let fut = (self.handler)(req); - - async move { - let res = fut.await; - if let Err(e) = res { - error!("Error in request handler: {}", e); - } - Ok(()) - } - .boxed_local() - } -} diff --git a/actix-framed/src/service.rs b/actix-framed/src/service.rs deleted file mode 100644 index dd61f298d..000000000 --- a/actix-framed/src/service.rs +++ /dev/null @@ -1,156 +0,0 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; - -use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use actix_http::body::BodySize; -use actix_http::error::ResponseError; -use actix_http::h1::{Codec, Message}; -use actix_http::ws::{verify_handshake, HandshakeError}; -use actix_http::{Request, Response}; -use actix_service::{Service, ServiceFactory}; -use futures_util::future::{err, ok, Either, Ready}; - -/// Service that verifies incoming request if it is valid websocket -/// upgrade request. In case of error returns `HandshakeError` -pub struct VerifyWebSockets { - _t: PhantomData<(T, C)>, -} - -impl Default for VerifyWebSockets { - fn default() -> Self { - VerifyWebSockets { _t: PhantomData } - } -} - -impl ServiceFactory for VerifyWebSockets { - type Config = C; - type Request = (Request, Framed); - type Response = (Request, Framed); - type Error = (HandshakeError, Framed); - type InitError = (); - type Service = VerifyWebSockets; - type Future = Ready>; - - fn new_service(&self, _: C) -> Self::Future { - ok(VerifyWebSockets { _t: PhantomData }) - } -} - -impl Service for VerifyWebSockets { - type Request = (Request, Framed); - type Response = (Request, Framed); - type Error = (HandshakeError, Framed); - type Future = Ready>; - - fn poll_ready(&mut self, _: &mut Context) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, (req, framed): (Request, Framed)) -> Self::Future { - match verify_handshake(req.head()) { - Err(e) => err((e, framed)), - Ok(_) => ok((req, framed)), - } - } -} - -/// Send http/1 error response -pub struct SendError(PhantomData<(T, R, E, C)>); - -impl Default for SendError -where - T: AsyncRead + AsyncWrite, - E: ResponseError, -{ - fn default() -> Self { - SendError(PhantomData) - } -} - -impl ServiceFactory for SendError -where - T: AsyncRead + AsyncWrite + Unpin + 'static, - R: 'static, - E: ResponseError + 'static, -{ - type Config = C; - type Request = Result)>; - type Response = R; - type Error = (E, Framed); - type InitError = (); - type Service = SendError; - type Future = Ready>; - - fn new_service(&self, _: C) -> Self::Future { - ok(SendError(PhantomData)) - } -} - -impl Service for SendError -where - T: AsyncRead + AsyncWrite + Unpin + 'static, - R: 'static, - E: ResponseError + 'static, -{ - type Request = Result)>; - type Response = R; - type Error = (E, Framed); - type Future = Either)>>, SendErrorFut>; - - fn poll_ready(&mut self, _: &mut Context) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Result)>) -> Self::Future { - match req { - Ok(r) => Either::Left(ok(r)), - Err((e, framed)) => { - let res = e.error_response().drop_body(); - Either::Right(SendErrorFut { - framed: Some(framed), - res: Some((res, BodySize::Empty).into()), - err: Some(e), - _t: PhantomData, - }) - } - } - } -} - -#[pin_project::pin_project] -pub struct SendErrorFut { - res: Option, BodySize)>>, - framed: Option>, - err: Option, - _t: PhantomData, -} - -impl Future for SendErrorFut -where - E: ResponseError, - T: AsyncRead + AsyncWrite + Unpin, -{ - type Output = Result)>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { - if let Some(res) = self.res.take() { - if self.framed.as_mut().unwrap().write(res).is_err() { - return Poll::Ready(Err(( - self.err.take().unwrap(), - self.framed.take().unwrap(), - ))); - } - } - match self.framed.as_mut().unwrap().flush(cx) { - Poll::Ready(Ok(_)) => { - Poll::Ready(Err((self.err.take().unwrap(), self.framed.take().unwrap()))) - } - Poll::Ready(Err(_)) => { - Poll::Ready(Err((self.err.take().unwrap(), self.framed.take().unwrap()))) - } - Poll::Pending => Poll::Pending, - } - } -} diff --git a/actix-framed/src/state.rs b/actix-framed/src/state.rs deleted file mode 100644 index 600a639ca..000000000 --- a/actix-framed/src/state.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::ops::Deref; -use std::sync::Arc; - -/// Application state -pub struct State(Arc); - -impl State { - pub fn new(state: S) -> State { - State(Arc::new(state)) - } - - pub fn get_ref(&self) -> &S { - self.0.as_ref() - } -} - -impl Deref for State { - type Target = S; - - fn deref(&self) -> &S { - self.0.as_ref() - } -} - -impl Clone for State { - fn clone(&self) -> State { - State(self.0.clone()) - } -} diff --git a/actix-framed/src/test.rs b/actix-framed/src/test.rs deleted file mode 100644 index b8029531e..000000000 --- a/actix-framed/src/test.rs +++ /dev/null @@ -1,155 +0,0 @@ -//! Various helpers for Actix applications to use during testing. -use std::convert::TryFrom; -use std::future::Future; - -use actix_codec::Framed; -use actix_http::h1::Codec; -use actix_http::http::header::{Header, HeaderName, IntoHeaderValue}; -use actix_http::http::{Error as HttpError, Method, Uri, Version}; -use actix_http::test::{TestBuffer, TestRequest as HttpTestRequest}; -use actix_router::{Path, Url}; - -use crate::{FramedRequest, State}; - -/// Test `Request` builder. -pub struct TestRequest { - req: HttpTestRequest, - path: Path, - state: State, -} - -impl Default for TestRequest<()> { - fn default() -> TestRequest { - TestRequest { - req: HttpTestRequest::default(), - path: Path::new(Url::new(Uri::default())), - state: State::new(()), - } - } -} - -impl TestRequest<()> { - /// Create TestRequest and set request uri - pub fn with_uri(path: &str) -> Self { - Self::get().uri(path) - } - - /// Create TestRequest and set header - pub fn with_hdr(hdr: H) -> Self { - Self::default().set(hdr) - } - - /// Create TestRequest and set header - pub fn with_header(key: K, value: V) -> Self - where - HeaderName: TryFrom, - >::Error: Into, - V: IntoHeaderValue, - { - Self::default().header(key, value) - } - - /// Create TestRequest and set method to `Method::GET` - pub fn get() -> Self { - Self::default().method(Method::GET) - } - - /// Create TestRequest and set method to `Method::POST` - pub fn post() -> Self { - Self::default().method(Method::POST) - } -} - -impl TestRequest { - /// Create TestRequest and set request uri - pub fn with_state(state: S) -> TestRequest { - let req = TestRequest::get(); - TestRequest { - state: State::new(state), - req: req.req, - path: req.path, - } - } - - /// Set HTTP version of this request - pub fn version(mut self, ver: Version) -> Self { - self.req.version(ver); - self - } - - /// Set HTTP method of this request - pub fn method(mut self, meth: Method) -> Self { - self.req.method(meth); - self - } - - /// Set HTTP Uri of this request - pub fn uri(mut self, path: &str) -> Self { - self.req.uri(path); - self - } - - /// Set a header - pub fn set(mut self, hdr: H) -> Self { - self.req.set(hdr); - self - } - - /// Set a header - pub fn header(mut self, key: K, value: V) -> Self - where - HeaderName: TryFrom, - >::Error: Into, - V: IntoHeaderValue, - { - self.req.header(key, value); - self - } - - /// Set request path pattern parameter - pub fn param(mut self, name: &'static str, value: &'static str) -> Self { - self.path.add_static(name, value); - self - } - - /// Complete request creation and generate `Request` instance - pub fn finish(mut self) -> FramedRequest { - let req = self.req.finish(); - self.path.get_mut().update(req.uri()); - let framed = Framed::new(TestBuffer::empty(), Codec::default()); - FramedRequest::new(req, framed, self.path, self.state) - } - - /// This method generates `FramedRequest` instance and executes async handler - pub async fn run(self, f: F) -> Result - where - F: FnOnce(FramedRequest) -> R, - R: Future>, - { - f(self.finish()).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test() { - let req = TestRequest::with_uri("/index.html") - .header("x-test", "test") - .param("test", "123") - .finish(); - - assert_eq!(*req.state(), ()); - assert_eq!(req.version(), Version::HTTP_11); - assert_eq!(req.method(), Method::GET); - assert_eq!(req.path(), "/index.html"); - assert_eq!(req.query_string(), ""); - assert_eq!( - req.headers().get("x-test").unwrap().to_str().unwrap(), - "test" - ); - assert_eq!(&req.match_info()["test"], "123"); - } -} diff --git a/actix-framed/tests/test_server.rs b/actix-framed/tests/test_server.rs deleted file mode 100644 index ec6897861..000000000 --- a/actix-framed/tests/test_server.rs +++ /dev/null @@ -1,161 +0,0 @@ -use actix_codec::{AsyncRead, AsyncWrite}; -use actix_http::{body, http::StatusCode, ws, Error, HttpService, Response}; -use actix_http_test::test_server; -use actix_service::{pipeline_factory, IntoServiceFactory, ServiceFactory}; -use actix_utils::framed::Dispatcher; -use bytes::Bytes; -use futures_util::{future, SinkExt, StreamExt}; - -use actix_framed::{FramedApp, FramedRequest, FramedRoute, SendError, VerifyWebSockets}; - -async fn ws_service( - req: FramedRequest, -) -> Result<(), Error> { - let (req, mut framed, _) = req.into_parts(); - let res = ws::handshake(req.head()).unwrap().message_body(()); - - framed - .send((res, body::BodySize::None).into()) - .await - .unwrap(); - Dispatcher::new(framed.into_framed(ws::Codec::new()), service) - .await - .unwrap(); - - Ok(()) -} - -async fn service(msg: ws::Frame) -> Result { - let msg = match msg { - ws::Frame::Ping(msg) => ws::Message::Pong(msg), - ws::Frame::Text(text) => { - ws::Message::Text(String::from_utf8_lossy(&text).to_string()) - } - ws::Frame::Binary(bin) => ws::Message::Binary(bin), - ws::Frame::Close(reason) => ws::Message::Close(reason), - _ => panic!(), - }; - Ok(msg) -} - -#[actix_rt::test] -async fn test_simple() { - let mut srv = test_server(|| { - HttpService::build() - .upgrade( - FramedApp::new().service(FramedRoute::get("/index.html").to(ws_service)), - ) - .finish(|_| future::ok::<_, Error>(Response::NotFound())) - .tcp() - }) - .await; - - assert!(srv.ws_at("/test").await.is_err()); - - // client service - let mut framed = srv.ws_at("/index.html").await.unwrap(); - framed - .send(ws::Message::Text("text".to_string())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Text(Bytes::from_static(b"text")) - ); - - framed - .send(ws::Message::Binary("text".into())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Binary(Bytes::from_static(b"text")) - ); - - framed.send(ws::Message::Ping("text".into())).await.unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Pong("text".to_string().into()) - ); - - framed - .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) - .await - .unwrap(); - - let (item, _) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Close(Some(ws::CloseCode::Normal.into())) - ); -} - -#[actix_rt::test] -async fn test_service() { - let mut srv = test_server(|| { - pipeline_factory(actix_http::h1::OneRequest::new().map_err(|_| ())).and_then( - pipeline_factory( - pipeline_factory(VerifyWebSockets::default()) - .then(SendError::default()) - .map_err(|_| ()), - ) - .and_then( - FramedApp::new() - .service(FramedRoute::get("/index.html").to(ws_service)) - .into_factory() - .map_err(|_| ()), - ), - ) - }) - .await; - - // non ws request - let res = srv.get("/index.html").send().await.unwrap(); - assert_eq!(res.status(), StatusCode::BAD_REQUEST); - - // not found - assert!(srv.ws_at("/test").await.is_err()); - - // client service - let mut framed = srv.ws_at("/index.html").await.unwrap(); - framed - .send(ws::Message::Text("text".to_string())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Text(Bytes::from_static(b"text")) - ); - - framed - .send(ws::Message::Binary("text".into())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Binary(Bytes::from_static(b"text")) - ); - - framed.send(ws::Message::Ping("text".into())).await.unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Pong("text".to_string().into()) - ); - - framed - .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) - .await - .unwrap(); - - let (item, _) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Close(Some(ws::CloseCode::Normal.into())) - ); -}