From eb1f01bad32dd851ebcc32abe9c1f9867d843f7b Mon Sep 17 00:00:00 2001 From: Ilya Grigoriev Date: Sat, 9 Mar 2024 17:25:45 -0800 Subject: [PATCH 01/24] Fixup to e3b2201f: prevent open::that from blocking, fix a typo, minor code simplification (#764) * Fixup to e3b2201f: fix a typo, minor code simplification * Fixup to e3b2201f: use `that_detached` to open browser `open::that` can block, for example when `firefox` is the default browser on Linux and no instance of `firefox` is running. --- .../local-server-with-browser/src/main.rs | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/examples/poem/local-server-with-browser/src/main.rs b/examples/poem/local-server-with-browser/src/main.rs index 417456a331..ae36067e24 100644 --- a/examples/poem/local-server-with-browser/src/main.rs +++ b/examples/poem/local-server-with-browser/src/main.rs @@ -19,8 +19,8 @@ async fn main() -> Result<(), std::io::Error> { // To test port assignment, run two instances of this example at once. // - // For ports <1024, running with administrator priveleges would be needed on - // Unix. For port 0, the OS would assign a port and we'd need to find out + // For ports <1024, running with administrator priveledges would be needed + // on Unix. For port 0, the OS would assign a port and we'd need to find out // what that port's number is. let (min_port, max_port) = (8080, 8085); // Using 127.0.0.1 instead of 0.0.0.0 for security; a local server should @@ -32,25 +32,30 @@ async fn main() -> Result<(), std::io::Error> { if port > max_port { return Err(error.unwrap()); } - let listener = TcpListener::bind(format!("{hostname}:{}", port)); + let listener = TcpListener::bind(format!("{hostname}:{port}")); match listener.into_acceptor().await { Ok(a) => break a, - Err(err) => { - // Most likely, another application is bound to this port. - eprintln!("Couldn't bind to port {port}."); - error = Some(err) - } + Err(err) => error = Some(err), }; + // Most likely, another application is bound to this port. + eprintln!("Couldn't bind to port {port}."); port += 1; }; // Now that the acceptor exists, the browser should be able to connect + eprintln!("Listening at {hostname}:{port}."); let http_address = format!("http://{hostname}:{port}/"); - eprintln!("Listening at {http_address}."); - eprint!("Trying to launch a browser at {http_address}..."); - match open::that(&http_address) { - Ok(_) => eprintln!(" Success!"), - Err(err) => eprintln!("\nFailed to launch a browser: {err}"), + eprintln!("Trying to launch a browser at {http_address}..."); + // We use `open::that_detached` so that launching, for example, a new + // instance of firefox on Linux does not block. This will report success + // even if the browser exits with a non-zero error code. + // + // You can alternatively consider using `tokio::spawn_blocking` and + // `open::that`. Note that in cases when `open::that` blocks, exiting the + // server process may also kill the browser process. + match open::that_detached(&http_address) { + Ok(()) => { /* Ok() doesn't mean much with `that_detached`. */ } + Err(err) => eprintln!("Failed to launch a browser: {err}"), } Server::new_with_acceptor(acceptor).run(app).await?; From b1bf63404781a6fdcd7c14e4affd9e3bd5585ed6 Mon Sep 17 00:00:00 2001 From: Yiyu Lin Date: Sun, 10 Mar 2024 10:23:54 +0800 Subject: [PATCH 02/24] chore(poem,lambda): bump deps and doc fix (#767) --- README.md | 2 +- poem-grpc/README.md | 2 +- poem-lambda/CHANGELOG.md | 4 ++++ poem-lambda/Cargo.toml | 2 +- poem-lambda/README.md | 2 +- poem-openapi/README.md | 2 +- poem/CHANGELOG.md | 6 ++++++ poem/Cargo.toml | 6 +++--- poem/README.md | 2 +- 9 files changed, 19 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3524863bb5..09befe5795 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Unsafe Rust forbidden - + rustc 1.74.0+ diff --git a/poem-grpc/README.md b/poem-grpc/README.md index a4b0bf61b2..f7b241d1dd 100644 --- a/poem-grpc/README.md +++ b/poem-grpc/README.md @@ -20,7 +20,7 @@ Unsafe Rust forbidden - + rustc 1.74.0+ diff --git a/poem-lambda/CHANGELOG.md b/poem-lambda/CHANGELOG.md index 153a124fe3..e8fe601d99 100644 --- a/poem-lambda/CHANGELOG.md +++ b/poem-lambda/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# [4.0.1] unreleased + +- Bump `lambda_http` from `0.9` to `0.10` + # [1.3.47] 2022-10-19 - Bump lambda_http from `0.6.0` to `0.7.0` diff --git a/poem-lambda/Cargo.toml b/poem-lambda/Cargo.toml index aea09499b9..777cc3ff08 100644 --- a/poem-lambda/Cargo.toml +++ b/poem-lambda/Cargo.toml @@ -21,7 +21,7 @@ categories = [ [dependencies] poem = { workspace = true, default-features = false } -lambda_http = { version = "0.9.0" } +lambda_http = { version = "0.10.0" } [dev-dependencies] tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } diff --git a/poem-lambda/README.md b/poem-lambda/README.md index 9a59b62ddc..c96b89c1c2 100644 --- a/poem-lambda/README.md +++ b/poem-lambda/README.md @@ -20,7 +20,7 @@ Unsafe Rust forbidden - + rustc 1.74.0+ diff --git a/poem-openapi/README.md b/poem-openapi/README.md index add11cd751..96b7b01484 100644 --- a/poem-openapi/README.md +++ b/poem-openapi/README.md @@ -21,7 +21,7 @@ Unsafe Rust forbidden - + rustc 1.74.0+ diff --git a/poem/CHANGELOG.md b/poem/CHANGELOG.md index 8cd0415ef6..8fffebcf06 100644 --- a/poem/CHANGELOG.md +++ b/poem/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# [unrealsed] + +- bump `priority-queue` to 2.0 +- bump `x509-parser` to 0.16 +- bump `nix` to 0.28 + # [2.0.1] 2024-03-04 - update rustls-pemfile to 2.0 [#736](https://github.com/poem-web/poem/pull/736) diff --git a/poem/Cargo.toml b/poem/Cargo.toml index f472b86f98..f4684d7707 100644 --- a/poem/Cargo.toml +++ b/poem/Cargo.toml @@ -136,7 +136,7 @@ libopentelemetry = { package = "opentelemetry", version = "0.21.0", features = [ "metrics", ], optional = true } libtempfile = { package = "tempfile", version = "3.2.0", optional = true } -priority-queue = { version = "1.2.0", optional = true } +priority-queue = { version = "2.0.2", optional = true } tokio-native-tls = { version = "0.3.0", optional = true } tokio-openssl = { version = "0.6.3", optional = true } openssl = { version = "0.10.56", optional = true } @@ -152,7 +152,7 @@ intl-memoizer = { version = "0.5.1", optional = true } ring = { version = "0.17.7", optional = true } reqwest = { workspace = true, features = ["json"], optional = true } rcgen = { version = "0.12.0", optional = true } -x509-parser = { version = "0.15.0", optional = true } +x509-parser = { version = "0.16.0", optional = true } tokio-metrics = { version = "0.3.0", optional = true } rust-embed = { version = "8.0", optional = true } hex = { version = "0.4", optional = true } @@ -165,7 +165,7 @@ anyhow = { version = "1.0.0", optional = true } eyre06 = { package = "eyre", version = "0.6", optional = true } [target.'cfg(unix)'.dependencies] -nix = { version = "0.27.1", features = ["fs", "user"] } +nix = { version = "0.28.0", features = ["fs", "user"] } [dev-dependencies] async-stream = "0.3.2" diff --git a/poem/README.md b/poem/README.md index f556dea289..7f3bd6b019 100644 --- a/poem/README.md +++ b/poem/README.md @@ -20,7 +20,7 @@ Unsafe Rust forbidden - + rustc 1.74.0+ From d67f4bc0acabdaf38308ec25ac631f7a3fa206e0 Mon Sep 17 00:00:00 2001 From: Sunli Date: Sat, 23 Mar 2024 14:23:11 +0800 Subject: [PATCH 03/24] use AFIT instead of `async_trait` --- examples/disabled/tonic/src/main.rs | 1 - examples/grpc/helloworld/src/main.rs | 1 - examples/grpc/jsoncodec/src/main.rs | 1 - examples/grpc/middleware/src/main.rs | 1 - examples/grpc/reflection/src/main.rs | 1 - examples/grpc/routeguide/src/main.rs | 1 - examples/openapi/auth-multiple/src/main.rs | 3 + .../content-type-accept/src/bcs_payload.rs | 1 - .../openapi/custom-payload/src/bcs_payload.rs | 1 - examples/poem/basic-auth/src/main.rs | 1 - examples/poem/middleware/src/main.rs | 5 +- poem-derive/src/lib.rs | 1 - poem-grpc-build/src/client.rs | 2 +- poem-grpc-build/src/server.rs | 13 +--- poem-grpc/README.md | 1 - poem-grpc/src/client.rs | 22 +++--- poem-grpc/src/health.rs | 1 - poem-grpc/src/reflection.rs | 1 - poem-grpc/src/service.rs | 23 +++--- poem-grpc/src/test_harness.rs | 1 - poem-openapi-derive/src/common_args.rs | 20 +---- poem-openapi-derive/src/enum.rs | 1 - poem-openapi-derive/src/multipart.rs | 1 - poem-openapi-derive/src/newtype.rs | 1 - poem-openapi/src/payload/base64_payload.rs | 2 - poem-openapi/src/payload/binary.rs | 3 - poem-openapi/src/payload/form.rs | 1 - poem-openapi/src/payload/html.rs | 1 - poem-openapi/src/payload/json.rs | 1 - poem-openapi/src/payload/mod.rs | 8 +- poem-openapi/src/payload/plain_text.rs | 1 - poem-openapi/src/payload/xml.rs | 1 - poem-openapi/src/payload/yaml.rs | 1 - poem-openapi/src/types/binary.rs | 2 - poem-openapi/src/types/external/bool.rs | 1 - poem-openapi/src/types/external/bson.rs | 1 - poem-openapi/src/types/external/btreeset.rs | 6 +- poem-openapi/src/types/external/chrono.rs | 2 - poem-openapi/src/types/external/decimal.rs | 1 - poem-openapi/src/types/external/floats.rs | 1 - poem-openapi/src/types/external/hashset.rs | 7 +- poem-openapi/src/types/external/humantime.rs | 1 - .../src/types/external/humantime_wrapper.rs | 1 - poem-openapi/src/types/external/integers.rs | 2 - poem-openapi/src/types/external/ip.rs | 1 - poem-openapi/src/types/external/optional.rs | 1 - poem-openapi/src/types/external/regex.rs | 1 - poem-openapi/src/types/external/string.rs | 1 - poem-openapi/src/types/external/time.rs | 2 - poem-openapi/src/types/external/uri.rs | 1 - poem-openapi/src/types/external/url.rs | 1 - poem-openapi/src/types/external/uuid.rs | 1 - poem-openapi/src/types/external/vec.rs | 1 - poem-openapi/src/types/maybe_undefined.rs | 1 - poem-openapi/src/types/mod.rs | 15 ++-- poem-openapi/src/types/multipart/json.rs | 1 - poem-openapi/src/types/multipart/upload.rs | 1 - poem/src/endpoint/after.rs | 1 - poem/src/endpoint/and_then.rs | 1 - poem/src/endpoint/around.rs | 1 - poem/src/endpoint/before.rs | 1 - poem/src/endpoint/catch_all_error.rs | 1 - poem/src/endpoint/catch_error.rs | 1 - poem/src/endpoint/embed.rs | 3 - poem/src/endpoint/endpoint.rs | 63 +++++++++++---- poem/src/endpoint/inspect_all_err.rs | 1 - poem/src/endpoint/inspect_err.rs | 1 - poem/src/endpoint/map.rs | 1 - poem/src/endpoint/map_to_response.rs | 1 - poem/src/endpoint/mod.rs | 5 +- poem/src/endpoint/prometheus_exporter.rs | 1 - poem/src/endpoint/static_files.rs | 2 - poem/src/endpoint/to_response.rs | 1 - poem/src/endpoint/tower_compat.rs | 1 - poem/src/listener/acme/endpoint.rs | 1 - poem/src/listener/acme/listener.rs | 2 - poem/src/listener/combined.rs | 1 - poem/src/listener/mod.rs | 7 +- poem/src/listener/native_tls.rs | 1 - poem/src/listener/openssl_tls.rs | 1 - poem/src/listener/rustls.rs | 1 - poem/src/listener/tcp.rs | 1 - poem/src/middleware/add_data.rs | 1 - poem/src/middleware/catch_panic.rs | 1 - poem/src/middleware/compression.rs | 1 - poem/src/middleware/cookie_jar_manager.rs | 1 - poem/src/middleware/cors.rs | 1 - poem/src/middleware/csrf.rs | 1 - poem/src/middleware/force_https.rs | 1 - poem/src/middleware/mod.rs | 1 - poem/src/middleware/normalize_path.rs | 1 - poem/src/middleware/opentelemetry_metrics.rs | 1 - poem/src/middleware/opentelemetry_tracing.rs | 1 - poem/src/middleware/propagate_header.rs | 1 - poem/src/middleware/sensitive_header.rs | 1 - poem/src/middleware/set_header.rs | 1 - poem/src/middleware/size_limit.rs | 1 - poem/src/middleware/tokio_metrics_mw.rs | 1 - poem/src/middleware/tower_compat.rs | 1 - poem/src/middleware/tracing_mw.rs | 1 - poem/src/route/router.rs | 14 ++-- poem/src/route/router_domain.rs | 1 - poem/src/route/router_method.rs | 35 +++++---- poem/src/route/router_scheme.rs | 1 - poem/src/server.rs | 5 +- poem/src/session/cookie_session.rs | 1 - poem/src/session/memory_storage.rs | 58 ++++++++------ poem/src/session/redis_storage.rs | 77 +++++++++++-------- poem/src/session/server_session.rs | 1 - poem/src/session/session_storage.rs | 23 +++--- 110 files changed, 235 insertions(+), 277 deletions(-) diff --git a/examples/disabled/tonic/src/main.rs b/examples/disabled/tonic/src/main.rs index 5893a32246..4615f0db37 100644 --- a/examples/disabled/tonic/src/main.rs +++ b/examples/disabled/tonic/src/main.rs @@ -12,7 +12,6 @@ pub mod hello_world { pub struct MyGreeter; -#[tonic::async_trait] impl Greeter for MyGreeter { async fn say_hello( &self, diff --git a/examples/grpc/helloworld/src/main.rs b/examples/grpc/helloworld/src/main.rs index f147740868..986c525f3f 100644 --- a/examples/grpc/helloworld/src/main.rs +++ b/examples/grpc/helloworld/src/main.rs @@ -5,7 +5,6 @@ poem_grpc::include_proto!("helloworld"); struct GreeterService; -#[poem::async_trait] impl Greeter for GreeterService { async fn say_hello( &self, diff --git a/examples/grpc/jsoncodec/src/main.rs b/examples/grpc/jsoncodec/src/main.rs index d1359216a0..83229e4747 100644 --- a/examples/grpc/jsoncodec/src/main.rs +++ b/examples/grpc/jsoncodec/src/main.rs @@ -5,7 +5,6 @@ poem_grpc::include_proto!("helloworld"); struct GreeterService; -#[poem::async_trait] impl Greeter for GreeterService { async fn say_hello( &self, diff --git a/examples/grpc/middleware/src/main.rs b/examples/grpc/middleware/src/main.rs index ad437bfa9b..d96330c763 100644 --- a/examples/grpc/middleware/src/main.rs +++ b/examples/grpc/middleware/src/main.rs @@ -7,7 +7,6 @@ poem_grpc::include_proto!("helloworld"); struct GreeterService; -#[poem::async_trait] impl Greeter for GreeterService { async fn say_hello( &self, diff --git a/examples/grpc/reflection/src/main.rs b/examples/grpc/reflection/src/main.rs index 504d3fb033..f42661d9c2 100644 --- a/examples/grpc/reflection/src/main.rs +++ b/examples/grpc/reflection/src/main.rs @@ -6,7 +6,6 @@ const FILE_DESCRIPTOR_SET: &[u8] = poem_grpc::include_file_descriptor_set!("hell struct GreeterService; -#[poem::async_trait] impl Greeter for GreeterService { async fn say_hello( &self, diff --git a/examples/grpc/routeguide/src/main.rs b/examples/grpc/routeguide/src/main.rs index b6a3494079..969a2df56b 100644 --- a/examples/grpc/routeguide/src/main.rs +++ b/examples/grpc/routeguide/src/main.rs @@ -12,7 +12,6 @@ struct RouteGuideService { features: Arc>, } -#[poem::async_trait] impl RouteGuide for RouteGuideService { async fn get_feature(&self, request: Request) -> Result, Status> { for feature in &self.features[..] { diff --git a/examples/openapi/auth-multiple/src/main.rs b/examples/openapi/auth-multiple/src/main.rs index 04b057c376..042fe0b18f 100644 --- a/examples/openapi/auth-multiple/src/main.rs +++ b/examples/openapi/auth-multiple/src/main.rs @@ -76,6 +76,9 @@ impl Api { auth1: MyApiKeyAuthorization, auth2: MyBasicAuthorization, ) -> Result> { + if auth1.0.username != "test" { + return Err(Error::from_status(StatusCode::UNAUTHORIZED)); + } if auth2.0.username != "test" || auth2.0.password != "123456" { return Err(Error::from_status(StatusCode::UNAUTHORIZED)); } diff --git a/examples/openapi/content-type-accept/src/bcs_payload.rs b/examples/openapi/content-type-accept/src/bcs_payload.rs index 8024d19c18..543e9fe3e5 100644 --- a/examples/openapi/content-type-accept/src/bcs_payload.rs +++ b/examples/openapi/content-type-accept/src/bcs_payload.rs @@ -50,7 +50,6 @@ impl Payload for Bcs { } } -#[poem::async_trait] impl Deserialize<'b>> ParsePayload for Bcs { const IS_REQUIRED: bool = true; diff --git a/examples/openapi/custom-payload/src/bcs_payload.rs b/examples/openapi/custom-payload/src/bcs_payload.rs index 8024d19c18..543e9fe3e5 100644 --- a/examples/openapi/custom-payload/src/bcs_payload.rs +++ b/examples/openapi/custom-payload/src/bcs_payload.rs @@ -50,7 +50,6 @@ impl Payload for Bcs { } } -#[poem::async_trait] impl Deserialize<'b>> ParsePayload for Bcs { const IS_REQUIRED: bool = true; diff --git a/examples/poem/basic-auth/src/main.rs b/examples/poem/basic-auth/src/main.rs index 1d7a7ed5b4..2f8ed95127 100644 --- a/examples/poem/basic-auth/src/main.rs +++ b/examples/poem/basic-auth/src/main.rs @@ -32,7 +32,6 @@ struct BasicAuthEndpoint { password: String, } -#[poem::async_trait] impl Endpoint for BasicAuthEndpoint { type Output = E::Output; diff --git a/examples/poem/middleware/src/main.rs b/examples/poem/middleware/src/main.rs index 9d840ee52b..0a0ce95186 100644 --- a/examples/poem/middleware/src/main.rs +++ b/examples/poem/middleware/src/main.rs @@ -1,6 +1,6 @@ use poem::{ - async_trait, get, handler, listener::TcpListener, Endpoint, EndpointExt, IntoResponse, - Middleware, Request, Response, Result, Route, Server, + get, handler, listener::TcpListener, Endpoint, EndpointExt, IntoResponse, Middleware, Request, + Response, Result, Route, Server, }; struct Log; @@ -15,7 +15,6 @@ impl Middleware for Log { struct LogImpl(E); -#[async_trait] impl Endpoint for LogImpl { type Output = Response; diff --git a/poem-derive/src/lib.rs b/poem-derive/src/lib.rs index 47a730194d..ed12849991 100644 --- a/poem-derive/src/lib.rs +++ b/poem-derive/src/lib.rs @@ -100,7 +100,6 @@ fn generate_handler(internal: bool, input: TokenStream) -> Result { #[allow(non_camel_case_types)] #def_struct - #[#crate_name::async_trait] impl #impl_generics #crate_name::Endpoint for #ident #type_generics #where_clause { type Output = #crate_name::Response; diff --git a/poem-grpc-build/src/client.rs b/poem-grpc-build/src/client.rs index 1a849626fe..391e41ad88 100644 --- a/poem-grpc-build/src/client.rs +++ b/poem-grpc-build/src/client.rs @@ -110,7 +110,7 @@ pub(crate) fn generate(config: &GrpcConfig, service: &Service, buf: &mut String) pub fn with(mut self, middleware: M) -> Self where - M: ::poem::Middleware<::std::sync::Arc + 'static>>, + M: ::poem::Middleware<::std::sync::Arc + 'static>>, M::Output: 'static, { self.cli = self.cli.with(middleware); diff --git a/poem-grpc-build/src/server.rs b/poem-grpc-build/src/server.rs index 72f2bc13fb..9326e266ad 100644 --- a/poem-grpc-build/src/server.rs +++ b/poem-grpc-build/src/server.rs @@ -61,25 +61,25 @@ pub(crate) fn generate(config: &GrpcConfig, service: &Service, buf: &mut String) match (method.client_streaming, method.server_streaming) { (false, false) => { trait_methods.push(quote! { - async fn #method_ident(&self, request: #crate_name::Request<#input_type>) -> ::std::result::Result<#crate_name::Response<#output_type>, #crate_name::Status>; + fn #method_ident(&self, request: #crate_name::Request<#input_type>) -> impl ::std::future::Future, #crate_name::Status>> + Send; }); endpoints.push(generate_unary(&codec_list, method_info)); } (true, false) => { trait_methods.push(quote! { - async fn #method_ident(&self, request: #crate_name::Request<#crate_name::Streaming<#input_type>>) -> ::std::result::Result<#crate_name::Response<#output_type>, #crate_name::Status>; + fn #method_ident(&self, request: #crate_name::Request<#crate_name::Streaming<#input_type>>) -> impl ::std::future::Future, #crate_name::Status>> + Send; }); endpoints.push(generate_client_streaming(&codec_list, method_info)); } (false, true) => { trait_methods.push(quote! { - async fn #method_ident(&self, request: #crate_name::Request<#input_type>) -> ::std::result::Result<#crate_name::Response<#crate_name::Streaming<#output_type>>, #crate_name::Status>; + fn #method_ident(&self, request: #crate_name::Request<#input_type>) -> impl ::std::future::Future>, #crate_name::Status>> + Send; }); endpoints.push(generate_server_streaming(&codec_list, method_info)); } (true, true) => { trait_methods.push(quote! { - async fn #method_ident(&self, request: #crate_name::Request<#crate_name::Streaming<#input_type>>) -> ::std::result::Result<#crate_name::Response<#crate_name::Streaming<#output_type>>, #crate_name::Status>; + fn #method_ident(&self, request: #crate_name::Request<#crate_name::Streaming<#input_type>>) -> impl ::std::future::Future>, #crate_name::Status>> + Send; }); endpoints.push(generate_bidirectional_streaming(&codec_list, method_info)); } @@ -94,7 +94,6 @@ pub(crate) fn generate(config: &GrpcConfig, service: &Service, buf: &mut String) let token_stream = quote! { #[allow(unused_imports)] - #[::poem::async_trait] pub trait #service_ident: Send + Sync + 'static { #(#trait_methods)* } @@ -200,7 +199,6 @@ fn generate_unary(codec_list: &[Path], method_info: MethodInfo) -> TokenStream { #[allow(non_camel_case_types)] struct #proxy_service_ident(::std::sync::Arc); - #[::poem::async_trait] impl #crate_name::service::UnaryService<#input_type> for #proxy_service_ident { type Response = #output_type; @@ -245,7 +243,6 @@ fn generate_client_streaming(codec_list: &[Path], method_info: MethodInfo) -> To #[allow(non_camel_case_types)] struct #proxy_service_ident(::std::sync::Arc); - #[::poem::async_trait] impl #crate_name::service::ClientStreamingService<#input_type> for #proxy_service_ident { type Response = #output_type; @@ -290,7 +287,6 @@ fn generate_server_streaming(codec_list: &[Path], method_info: MethodInfo) -> To #[allow(non_camel_case_types)] struct #proxy_service_ident(::std::sync::Arc); - #[::poem::async_trait] impl #crate_name::service::ServerStreamingService<#input_type> for #proxy_service_ident { type Response = #output_type; @@ -335,7 +331,6 @@ fn generate_bidirectional_streaming(codec_list: &[Path], method_info: MethodInfo #[allow(non_camel_case_types)] struct #proxy_service_ident(::std::sync::Arc); - #[::poem::async_trait] impl #crate_name::service::BidirectionalStreamingService<#input_type> for #proxy_service_ident { type Response = #output_type; diff --git a/poem-grpc/README.md b/poem-grpc/README.md index f7b241d1dd..001a1eba4c 100644 --- a/poem-grpc/README.md +++ b/poem-grpc/README.md @@ -36,7 +36,6 @@ poem_grpc::include_proto!("helloworld"); struct GreeterService; -#[poem::async_trait] impl Greeter for GreeterService { async fn say_hello( &self, diff --git a/poem-grpc/src/client.rs b/poem-grpc/src/client.rs index dbef21c1b2..3f657ac737 100644 --- a/poem-grpc/src/client.rs +++ b/poem-grpc/src/client.rs @@ -5,9 +5,11 @@ use futures_util::TryStreamExt; use http_body_util::BodyExt; use hyper_util::{client::legacy::Client, rt::TokioExecutor}; use poem::{ + endpoint::{DynEndpoint, DynEndpointWrapper}, http::{ - header, header::InvalidHeaderValue, uri::InvalidUri, Extensions, HeaderValue, Method, - StatusCode, Uri, Version, + header::{self, InvalidHeaderValue}, + uri::InvalidUri, + Extensions, HeaderValue, Method, StatusCode, Uri, Version, }, Endpoint, EndpointExt, IntoEndpoint, Middleware, Request as HttpRequest, Response as HttpResponse, @@ -152,7 +154,7 @@ impl ClientConfigBuilder { #[doc(hidden)] #[derive(Clone)] pub struct GrpcClient { - ep: Arc + 'static>, + ep: Arc + 'static>, } impl GrpcClient { @@ -170,16 +172,18 @@ impl GrpcClient { ::Output: 'static, { Self { - ep: Arc::new(ep.map_to_response()), + ep: Arc::new(DynEndpointWrapper(ep.map_to_response())), } } pub fn with(mut self, middleware: M) -> Self where - M: Middleware + 'static>>, + M: Middleware + 'static>>, M::Output: 'static, { - self.ep = Arc::new(middleware.transform(self.ep).map_to_response()); + self.ep = Arc::new(DynEndpointWrapper( + middleware.transform(self.ep).map_to_response(), + )); self } @@ -395,7 +399,7 @@ fn make_uri(base_uri: &Uri, path: &Uri) -> Uri { fn create_client_endpoint( config: ClientConfig, -) -> Arc + 'static> { +) -> Arc + 'static> { let mut config = config; let cli = Client::builder(TokioExecutor::new()) .http2_only(true) @@ -403,7 +407,7 @@ fn create_client_endpoint( let config = Arc::new(config); - Arc::new(poem::endpoint::make(move |request| { + Arc::new(DynEndpointWrapper(poem::endpoint::make(move |request| { let config = config.clone(); let cli = cli.clone(); async move { @@ -443,5 +447,5 @@ fn create_client_endpoint( body.map_err(IoError::other), ))) } - })) + }))) } diff --git a/poem-grpc/src/health.rs b/poem-grpc/src/health.rs index 21cd7a5f81..849c14d21b 100644 --- a/poem-grpc/src/health.rs +++ b/poem-grpc/src/health.rs @@ -70,7 +70,6 @@ impl HealthReporter { } } -#[poem::async_trait] impl proto::Health for HealthService { async fn check( &self, diff --git a/poem-grpc/src/reflection.rs b/poem-grpc/src/reflection.rs index dc28e3b77c..34e455349d 100644 --- a/poem-grpc/src/reflection.rs +++ b/poem-grpc/src/reflection.rs @@ -80,7 +80,6 @@ struct ServerReflectionService { state: Arc, } -#[poem::async_trait] impl proto::ServerReflection for ServerReflectionService { async fn server_reflection_info( &self, diff --git a/poem-grpc/src/service.rs b/poem-grpc/src/service.rs index 48f930e093..38fe35cf6b 100644 --- a/poem-grpc/src/service.rs +++ b/poem-grpc/src/service.rs @@ -1,3 +1,5 @@ +use std::future::Future; + use crate::{status::Status, streaming::Streaming, Request, Response}; /// Represent a GRPC service @@ -6,39 +8,38 @@ pub trait Service { const NAME: &'static str; } -#[poem::async_trait] pub trait UnaryService { type Response; - async fn call(&self, request: Request) -> Result, Status>; + fn call( + &self, + request: Request, + ) -> impl Future, Status>> + Send; } -#[poem::async_trait] pub trait ClientStreamingService { type Response; - async fn call( + fn call( &self, request: Request>, - ) -> Result, Status>; + ) -> impl Future, Status>> + Send; } -#[poem::async_trait] pub trait ServerStreamingService { type Response; - async fn call( + fn call( &self, request: Request, - ) -> Result>, Status>; + ) -> impl Future>, Status>> + Send; } -#[poem::async_trait] pub trait BidirectionalStreamingService { type Response; - async fn call( + fn call( &self, request: Request>, - ) -> Result>, Status>; + ) -> impl Future>, Status>> + Send; } diff --git a/poem-grpc/src/test_harness.rs b/poem-grpc/src/test_harness.rs index ca49b1cdef..ed5f326646 100644 --- a/poem-grpc/src/test_harness.rs +++ b/poem-grpc/src/test_harness.rs @@ -10,7 +10,6 @@ use crate::{Request, Response, Status, Streaming}; pub(crate) struct TestHarnessService; -#[poem::async_trait] impl TestHarness for TestHarnessService { async fn unary(&self, req: Request) -> Result, Status> { Ok(Response::new(ValueResponse { diff --git a/poem-openapi-derive/src/common_args.rs b/poem-openapi-derive/src/common_args.rs index 891810effa..a469f9a897 100644 --- a/poem-openapi-derive/src/common_args.rs +++ b/poem-openapi-derive/src/common_args.rs @@ -1,7 +1,7 @@ -use darling::{ast::NestedMeta, util::SpannedValue, FromMeta}; +use darling::{util::SpannedValue, FromMeta}; use proc_macro2::TokenStream; use quote::quote; -use syn::{Lit, Meta, Path}; +use syn::{Lit, Path}; #[derive(Debug, Copy, Clone, FromMeta)] #[allow(clippy::enum_variant_names)] @@ -94,22 +94,6 @@ pub(crate) fn apply_rename_rule_variant(rule: Option, variant: Strin } } -pub(crate) struct PathList(pub(crate) Vec); - -impl FromMeta for PathList { - fn from_list(items: &[NestedMeta]) -> darling::Result { - let mut res = Vec::new(); - for item in items { - if let NestedMeta::Meta(Meta::Path(p)) = item { - res.push(p.clone()); - } else { - return Err(darling::Error::custom("Invalid path list")); - } - } - Ok(PathList(res)) - } -} - #[derive(Debug, Copy, Clone, FromMeta, Eq, PartialEq, Hash)] #[darling(rename_all = "lowercase")] pub(crate) enum APIMethod { diff --git a/poem-openapi-derive/src/enum.rs b/poem-openapi-derive/src/enum.rs index 8da70c0182..8d2f89ff9f 100644 --- a/poem-openapi-derive/src/enum.rs +++ b/poem-openapi-derive/src/enum.rs @@ -192,7 +192,6 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult { } } - #[#crate_name::__private::poem::async_trait] impl #crate_name::types::ParseFromMultipartField for #ident { async fn parse_from_multipart(field: ::std::option::Option<#crate_name::__private::poem::web::Field>) -> #crate_name::types::ParseResult { use poem_openapi::types::ParseFromParameter; diff --git a/poem-openapi-derive/src/multipart.rs b/poem-openapi-derive/src/multipart.rs index 76b41c8129..112a75cbbd 100644 --- a/poem-openapi-derive/src/multipart.rs +++ b/poem-openapi-derive/src/multipart.rs @@ -242,7 +242,6 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult { } } - #[#crate_name::__private::poem::async_trait] impl #impl_generics #crate_name::payload::ParsePayload for #ident #ty_generics #where_clause { const IS_REQUIRED: bool = true; diff --git a/poem-openapi-derive/src/newtype.rs b/poem-openapi-derive/src/newtype.rs index 66bd72df97..1c5ae31d87 100644 --- a/poem-openapi-derive/src/newtype.rs +++ b/poem-openapi-derive/src/newtype.rs @@ -135,7 +135,6 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult { let from_multipart = if args.from_multipart { Some(quote! { - #[#crate_name::__private::poem::async_trait] impl #impl_generics #crate_name::types::ParseFromMultipartField for #ident #ty_generics #where_clause { async fn parse_from_multipart(field: ::std::option::Option<#crate_name::__private::poem::web::Field>) -> #crate_name::types::ParseResult { let value = ::std::result::Result::map_err(<#inner_ty as #crate_name::types::ParseFromMultipartField>::parse_from_multipart(field).await, poem_openapi::types::ParseError::propagate)?; diff --git a/poem-openapi/src/payload/base64_payload.rs b/poem-openapi/src/payload/base64_payload.rs index 92fa554cdc..4ee10c3ff6 100644 --- a/poem-openapi/src/payload/base64_payload.rs +++ b/poem-openapi/src/payload/base64_payload.rs @@ -119,7 +119,6 @@ async fn read_base64(body: &mut RequestBody) -> Result> { Ok(data) } -#[poem::async_trait] impl ParsePayload for Base64> { const IS_REQUIRED: bool = true; @@ -128,7 +127,6 @@ impl ParsePayload for Base64> { } } -#[poem::async_trait] impl ParsePayload for Base64 { const IS_REQUIRED: bool = true; diff --git a/poem-openapi/src/payload/binary.rs b/poem-openapi/src/payload/binary.rs index a1da94d1c4..1932d0de02 100644 --- a/poem-openapi/src/payload/binary.rs +++ b/poem-openapi/src/payload/binary.rs @@ -103,7 +103,6 @@ impl Payload for Binary { } } -#[poem::async_trait] impl ParsePayload for Binary> { const IS_REQUIRED: bool = true; @@ -112,7 +111,6 @@ impl ParsePayload for Binary> { } } -#[poem::async_trait] impl ParsePayload for Binary { const IS_REQUIRED: bool = true; @@ -121,7 +119,6 @@ impl ParsePayload for Binary { } } -#[poem::async_trait] impl ParsePayload for Binary { const IS_REQUIRED: bool = true; diff --git a/poem-openapi/src/payload/form.rs b/poem-openapi/src/payload/form.rs index 717a87040e..42b3c0784c 100644 --- a/poem-openapi/src/payload/form.rs +++ b/poem-openapi/src/payload/form.rs @@ -49,7 +49,6 @@ impl Payload for Form { } } -#[poem::async_trait] impl ParsePayload for Form { const IS_REQUIRED: bool = true; diff --git a/poem-openapi/src/payload/html.rs b/poem-openapi/src/payload/html.rs index 1736d881b6..ed32d9402e 100644 --- a/poem-openapi/src/payload/html.rs +++ b/poem-openapi/src/payload/html.rs @@ -43,7 +43,6 @@ impl Payload for Html { } } -#[poem::async_trait] impl ParsePayload for Html { const IS_REQUIRED: bool = true; diff --git a/poem-openapi/src/payload/json.rs b/poem-openapi/src/payload/json.rs index 17d7b31c37..f588d93ce2 100644 --- a/poem-openapi/src/payload/json.rs +++ b/poem-openapi/src/payload/json.rs @@ -50,7 +50,6 @@ impl Payload for Json { } } -#[poem::async_trait] impl ParsePayload for Json { const IS_REQUIRED: bool = true; diff --git a/poem-openapi/src/payload/mod.rs b/poem-openapi/src/payload/mod.rs index bb35f13212..836c74092f 100644 --- a/poem-openapi/src/payload/mod.rs +++ b/poem-openapi/src/payload/mod.rs @@ -12,6 +12,8 @@ mod response; mod xml; mod yaml; +use std::future::Future; + use poem::{Request, RequestBody, Result}; pub use self::{ @@ -48,11 +50,13 @@ pub trait Payload: Send { } /// Represents a payload that can parse from HTTP request. -#[poem::async_trait] pub trait ParsePayload: Sized { /// If it is `true`, it means that this payload is required. const IS_REQUIRED: bool; /// Parse the payload object from the HTTP request. - async fn from_request(request: &Request, body: &mut RequestBody) -> Result; + fn from_request( + request: &Request, + body: &mut RequestBody, + ) -> impl Future> + Send; } diff --git a/poem-openapi/src/payload/plain_text.rs b/poem-openapi/src/payload/plain_text.rs index ca92aad556..8adcc40337 100644 --- a/poem-openapi/src/payload/plain_text.rs +++ b/poem-openapi/src/payload/plain_text.rs @@ -43,7 +43,6 @@ impl Payload for PlainText { } } -#[poem::async_trait] impl ParsePayload for PlainText { const IS_REQUIRED: bool = true; diff --git a/poem-openapi/src/payload/xml.rs b/poem-openapi/src/payload/xml.rs index a0b6ecfb77..ea7935d87f 100644 --- a/poem-openapi/src/payload/xml.rs +++ b/poem-openapi/src/payload/xml.rs @@ -50,7 +50,6 @@ impl Payload for Xml { } } -#[poem::async_trait] impl ParsePayload for Xml { const IS_REQUIRED: bool = true; diff --git a/poem-openapi/src/payload/yaml.rs b/poem-openapi/src/payload/yaml.rs index cae4b4ae01..c39cc236d4 100644 --- a/poem-openapi/src/payload/yaml.rs +++ b/poem-openapi/src/payload/yaml.rs @@ -50,7 +50,6 @@ impl Payload for Yaml { } } -#[poem::async_trait] impl ParsePayload for Yaml { const IS_REQUIRED: bool = true; diff --git a/poem-openapi/src/types/binary.rs b/poem-openapi/src/types/binary.rs index f3e59337d1..5be5f949af 100644 --- a/poem-openapi/src/types/binary.rs +++ b/poem-openapi/src/types/binary.rs @@ -59,7 +59,6 @@ impl + Send + Sync> Type for Binary { } } -#[poem::async_trait] impl ParseFromMultipartField for Binary> { async fn parse_from_multipart(field: Option) -> ParseResult { match field { @@ -69,7 +68,6 @@ impl ParseFromMultipartField for Binary> { } } -#[poem::async_trait] impl ParseFromMultipartField for Binary { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/bool.rs b/poem-openapi/src/types/external/bool.rs index 6461f749a9..290408bd0a 100644 --- a/poem-openapi/src/types/external/bool.rs +++ b/poem-openapi/src/types/external/bool.rs @@ -54,7 +54,6 @@ impl ParseFromParameter for bool { } } -#[poem::async_trait] impl ParseFromMultipartField for bool { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/bson.rs b/poem-openapi/src/types/external/bson.rs index 15f8c00fad..f51e0fdbee 100644 --- a/poem-openapi/src/types/external/bson.rs +++ b/poem-openapi/src/types/external/bson.rs @@ -51,7 +51,6 @@ impl ParseFromParameter for ObjectId { } } -#[poem::async_trait] impl ParseFromMultipartField for ObjectId { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/btreeset.rs b/poem-openapi/src/types/external/btreeset.rs index 5f65dac4e1..76a239887e 100644 --- a/poem-openapi/src/types/external/btreeset.rs +++ b/poem-openapi/src/types/external/btreeset.rs @@ -83,8 +83,10 @@ impl ParseFromParameter for BTreeSet { } } -#[poem::async_trait] -impl ParseFromMultipartField for BTreeSet { +impl ParseFromMultipartField for BTreeSet +where + T: ParseFromMultipartField + Ord, +{ async fn parse_from_multipart(field: Option) -> ParseResult { match field { Some(field) => { diff --git a/poem-openapi/src/types/external/chrono.rs b/poem-openapi/src/types/external/chrono.rs index b110b44993..5e80eda550 100644 --- a/poem-openapi/src/types/external/chrono.rs +++ b/poem-openapi/src/types/external/chrono.rs @@ -57,7 +57,6 @@ macro_rules! impl_datetime_types { } } - #[poem::async_trait] impl ParseFromMultipartField for $ty { async fn parse_from_multipart(field: Option) -> ParseResult { match field { @@ -124,7 +123,6 @@ macro_rules! impl_naive_datetime_types { } } - #[poem::async_trait] impl ParseFromMultipartField for $ty { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/decimal.rs b/poem-openapi/src/types/external/decimal.rs index f9016a4eaf..4cf675f1b0 100644 --- a/poem-openapi/src/types/external/decimal.rs +++ b/poem-openapi/src/types/external/decimal.rs @@ -67,7 +67,6 @@ impl ParseFromParameter for Decimal { } } -#[poem::async_trait] impl ParseFromMultipartField for Decimal { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/floats.rs b/poem-openapi/src/types/external/floats.rs index 4aedee8452..cb387f3874 100644 --- a/poem-openapi/src/types/external/floats.rs +++ b/poem-openapi/src/types/external/floats.rs @@ -60,7 +60,6 @@ macro_rules! impl_type_for_floats { } } - #[poem::async_trait] impl ParseFromMultipartField for $ty { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/hashset.rs b/poem-openapi/src/types/external/hashset.rs index 2a67ba4811..627fa2ee16 100644 --- a/poem-openapi/src/types/external/hashset.rs +++ b/poem-openapi/src/types/external/hashset.rs @@ -91,9 +91,10 @@ impl } } -#[poem::async_trait] -impl - ParseFromMultipartField for HashSet +impl ParseFromMultipartField for HashSet +where + T: ParseFromMultipartField + Hash + Eq, + R: Send + Sync + Default + BuildHasher, { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/humantime.rs b/poem-openapi/src/types/external/humantime.rs index be80ec5c08..2c4d0e2a85 100644 --- a/poem-openapi/src/types/external/humantime.rs +++ b/poem-openapi/src/types/external/humantime.rs @@ -54,7 +54,6 @@ impl ParseFromParameter for Duration { } } -#[poem::async_trait] impl ParseFromMultipartField for Duration { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/humantime_wrapper.rs b/poem-openapi/src/types/external/humantime_wrapper.rs index 82df997c9b..7619f09049 100644 --- a/poem-openapi/src/types/external/humantime_wrapper.rs +++ b/poem-openapi/src/types/external/humantime_wrapper.rs @@ -55,7 +55,6 @@ impl ParseFromParameter for Duration { } } -#[poem::async_trait] impl ParseFromMultipartField for Duration { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/integers.rs b/poem-openapi/src/types/external/integers.rs index d040126fb7..547366bba0 100644 --- a/poem-openapi/src/types/external/integers.rs +++ b/poem-openapi/src/types/external/integers.rs @@ -92,7 +92,6 @@ macro_rules! impl_type_for_integers { } } - #[poem::async_trait] impl ParseFromMultipartField for $ty { async fn parse_from_multipart(field: Option) -> ParseResult { match field { @@ -185,7 +184,6 @@ macro_rules! impl_type_for_unsigneds { } } - #[poem::async_trait] impl ParseFromMultipartField for $ty { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/ip.rs b/poem-openapi/src/types/external/ip.rs index 66d579d492..490d9e0d36 100644 --- a/poem-openapi/src/types/external/ip.rs +++ b/poem-openapi/src/types/external/ip.rs @@ -72,7 +72,6 @@ macro_rules! impl_type_for_ip { } } - #[poem::async_trait] impl ParseFromMultipartField for $ty { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/optional.rs b/poem-openapi/src/types/external/optional.rs index 1c7b90fb9f..a841daebd6 100644 --- a/poem-openapi/src/types/external/optional.rs +++ b/poem-openapi/src/types/external/optional.rs @@ -83,7 +83,6 @@ impl ParseFromParameter for Option { } } -#[poem::async_trait] impl ParseFromMultipartField for Option { async fn parse_from_multipart(value: Option) -> ParseResult { match value { diff --git a/poem-openapi/src/types/external/regex.rs b/poem-openapi/src/types/external/regex.rs index 702540c8db..c1cd1ad7ce 100644 --- a/poem-openapi/src/types/external/regex.rs +++ b/poem-openapi/src/types/external/regex.rs @@ -55,7 +55,6 @@ impl ParseFromParameter for Regex { } } -#[poem::async_trait] impl ParseFromMultipartField for Regex { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/string.rs b/poem-openapi/src/types/external/string.rs index 19181561c6..27c09b7b8a 100644 --- a/poem-openapi/src/types/external/string.rs +++ b/poem-openapi/src/types/external/string.rs @@ -59,7 +59,6 @@ impl ParseFromParameter for String { } } -#[poem::async_trait] impl ParseFromMultipartField for String { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/time.rs b/poem-openapi/src/types/external/time.rs index 5e8686f1f6..51f0dc37c8 100644 --- a/poem-openapi/src/types/external/time.rs +++ b/poem-openapi/src/types/external/time.rs @@ -58,7 +58,6 @@ impl ParseFromParameter for OffsetDateTime { } } -#[poem::async_trait] impl ParseFromMultipartField for OffsetDateTime { async fn parse_from_multipart(field: Option) -> ParseResult { match field { @@ -119,7 +118,6 @@ macro_rules! impl_naive_datetime_types { } } - #[poem::async_trait] impl ParseFromMultipartField for $ty { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/uri.rs b/poem-openapi/src/types/external/uri.rs index 133a68cd09..e83e9c0e4e 100644 --- a/poem-openapi/src/types/external/uri.rs +++ b/poem-openapi/src/types/external/uri.rs @@ -57,7 +57,6 @@ impl ParseFromParameter for Uri { } } -#[poem::async_trait] impl ParseFromMultipartField for Uri { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/url.rs b/poem-openapi/src/types/external/url.rs index 1df2e2298e..3d7904e872 100644 --- a/poem-openapi/src/types/external/url.rs +++ b/poem-openapi/src/types/external/url.rs @@ -55,7 +55,6 @@ impl ParseFromParameter for Url { } } -#[poem::async_trait] impl ParseFromMultipartField for Url { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/uuid.rs b/poem-openapi/src/types/external/uuid.rs index f7aa5cda9a..e1d228f2ef 100644 --- a/poem-openapi/src/types/external/uuid.rs +++ b/poem-openapi/src/types/external/uuid.rs @@ -55,7 +55,6 @@ impl ParseFromParameter for Uuid { } } -#[poem::async_trait] impl ParseFromMultipartField for Uuid { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/external/vec.rs b/poem-openapi/src/types/external/vec.rs index 8cc4d3a332..13458d8d84 100644 --- a/poem-openapi/src/types/external/vec.rs +++ b/poem-openapi/src/types/external/vec.rs @@ -83,7 +83,6 @@ impl ParseFromParameter for Vec { } } -#[poem::async_trait] impl ParseFromMultipartField for Vec { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem-openapi/src/types/maybe_undefined.rs b/poem-openapi/src/types/maybe_undefined.rs index e95882a8ed..48eda7755e 100644 --- a/poem-openapi/src/types/maybe_undefined.rs +++ b/poem-openapi/src/types/maybe_undefined.rs @@ -384,7 +384,6 @@ impl ParseFromParameter for MaybeUndefined { } } -#[poem::async_trait] impl ParseFromMultipartField for MaybeUndefined { async fn parse_from_multipart(value: Option) -> ParseResult { match value { diff --git a/poem-openapi/src/types/mod.rs b/poem-openapi/src/types/mod.rs index d240366ba2..a69f894ee2 100644 --- a/poem-openapi/src/types/mod.rs +++ b/poem-openapi/src/types/mod.rs @@ -10,7 +10,7 @@ mod string_types; pub mod multipart; -use std::{borrow::Cow, sync::Arc}; +use std::{borrow::Cow, future::Future, sync::Arc}; pub use any::Any; pub use base64_type::Base64; @@ -138,14 +138,18 @@ pub trait ParseFromParameter: Sized + Type { } /// Represents a type that can parsing from multipart. -#[poem::async_trait] pub trait ParseFromMultipartField: Sized + Type { /// Parse from multipart field. - async fn parse_from_multipart(field: Option) -> ParseResult; + fn parse_from_multipart( + field: Option, + ) -> impl Future> + Send; /// Parse from repeated multipart field. - async fn parse_from_repeated_field(self, _field: PoemField) -> ParseResult { - Err(ParseError::::custom("repeated field")) + fn parse_from_repeated_field( + self, + _field: PoemField, + ) -> impl Future> + Send { + async move { Err(ParseError::::custom("repeated field")) } } } @@ -380,7 +384,6 @@ impl ParseFromParameter for Box { } } -#[poem::async_trait] impl ParseFromMultipartField for Box { async fn parse_from_multipart(field: Option) -> ParseResult { T::parse_from_multipart(field) diff --git a/poem-openapi/src/types/multipart/json.rs b/poem-openapi/src/types/multipart/json.rs index 93b5d8b6c7..085c2eab08 100644 --- a/poem-openapi/src/types/multipart/json.rs +++ b/poem-openapi/src/types/multipart/json.rs @@ -44,7 +44,6 @@ impl Type for JsonField { } } -#[poem::async_trait] impl ParseFromMultipartField for JsonField { async fn parse_from_multipart(field: Option) -> ParseResult { let value = match field { diff --git a/poem-openapi/src/types/multipart/upload.rs b/poem-openapi/src/types/multipart/upload.rs index e41d0107d6..909e7e8bfb 100644 --- a/poem-openapi/src/types/multipart/upload.rs +++ b/poem-openapi/src/types/multipart/upload.rs @@ -98,7 +98,6 @@ impl Type for Upload { } } -#[poem::async_trait] impl ParseFromMultipartField for Upload { async fn parse_from_multipart(field: Option) -> ParseResult { match field { diff --git a/poem/src/endpoint/after.rs b/poem/src/endpoint/after.rs index 5612662fac..c74eebadf1 100644 --- a/poem/src/endpoint/after.rs +++ b/poem/src/endpoint/after.rs @@ -15,7 +15,6 @@ impl After { } } -#[async_trait::async_trait] impl Endpoint for After where E: Endpoint, diff --git a/poem/src/endpoint/and_then.rs b/poem/src/endpoint/and_then.rs index 5f70f77f70..4ae8820001 100644 --- a/poem/src/endpoint/and_then.rs +++ b/poem/src/endpoint/and_then.rs @@ -15,7 +15,6 @@ impl AndThen { } } -#[async_trait::async_trait] impl Endpoint for AndThen where E: Endpoint, diff --git a/poem/src/endpoint/around.rs b/poem/src/endpoint/around.rs index 21a2552a34..6d2d42515c 100644 --- a/poem/src/endpoint/around.rs +++ b/poem/src/endpoint/around.rs @@ -18,7 +18,6 @@ impl Around { } } -#[async_trait::async_trait] impl Endpoint for Around where E: Endpoint, diff --git a/poem/src/endpoint/before.rs b/poem/src/endpoint/before.rs index 3aa652a617..631e0a194a 100644 --- a/poem/src/endpoint/before.rs +++ b/poem/src/endpoint/before.rs @@ -15,7 +15,6 @@ impl Before { } } -#[async_trait::async_trait] impl Endpoint for Before where E: Endpoint, diff --git a/poem/src/endpoint/catch_all_error.rs b/poem/src/endpoint/catch_all_error.rs index 5cbb9b0a9c..1c0cdece66 100644 --- a/poem/src/endpoint/catch_all_error.rs +++ b/poem/src/endpoint/catch_all_error.rs @@ -21,7 +21,6 @@ impl CatchAllError { } } -#[async_trait::async_trait] impl Endpoint for CatchAllError where E: Endpoint, diff --git a/poem/src/endpoint/catch_error.rs b/poem/src/endpoint/catch_error.rs index befaf22e64..64c7215940 100644 --- a/poem/src/endpoint/catch_error.rs +++ b/poem/src/endpoint/catch_error.rs @@ -22,7 +22,6 @@ impl CatchError { } } -#[async_trait::async_trait] impl Endpoint for CatchError where E: Endpoint, diff --git a/poem/src/endpoint/embed.rs b/poem/src/endpoint/embed.rs index 07555995f8..d07aa8544f 100644 --- a/poem/src/endpoint/embed.rs +++ b/poem/src/endpoint/embed.rs @@ -1,6 +1,5 @@ use std::marker::PhantomData; -use async_trait::async_trait; use rust_embed::RustEmbed; use crate::{ @@ -26,7 +25,6 @@ impl EmbeddedFileEndpoint { } } -#[async_trait] impl Endpoint for EmbeddedFileEndpoint { type Output = Response; @@ -81,7 +79,6 @@ impl EmbeddedFilesEndpoint { } } -#[async_trait] impl Endpoint for EmbeddedFilesEndpoint { type Output = Response; diff --git a/poem/src/endpoint/endpoint.rs b/poem/src/endpoint/endpoint.rs index 2e12d17fa9..fbd6ba6e32 100644 --- a/poem/src/endpoint/endpoint.rs +++ b/poem/src/endpoint/endpoint.rs @@ -1,5 +1,7 @@ use std::{future::Future, marker::PhantomData, sync::Arc}; +use futures_util::{future::BoxFuture, FutureExt}; + use super::{ After, AndThen, Around, Before, CatchAllError, CatchError, InspectAllError, InspectError, Map, MapToResponse, ToResponse, @@ -11,13 +13,12 @@ use crate::{ }; /// An HTTP request handler. -#[async_trait::async_trait] pub trait Endpoint: Send + Sync { /// Represents the response of the endpoint. type Output: IntoResponse; /// Get the response to the request. - async fn call(&self, req: Request) -> Result; + fn call(&self, req: Request) -> impl Future> + Send; /// Get the response to the request and return a [`Response`]. /// @@ -45,11 +46,13 @@ pub trait Endpoint: Send + Sync { /// .assert_status(StatusCode::NOT_FOUND); /// # }); /// ``` - async fn get_response(&self, req: Request) -> Response { - self.call(req) - .await - .map(IntoResponse::into_response) - .unwrap_or_else(|err| err.into_response()) + fn get_response(&self, req: Request) -> impl Future + Send { + async move { + self.call(req) + .await + .map(IntoResponse::into_response) + .unwrap_or_else(|err| err.into_response()) + } } } @@ -58,7 +61,6 @@ struct SyncFnEndpoint { f: F, } -#[async_trait::async_trait] impl Endpoint for SyncFnEndpoint where F: Fn(Request) -> R + Send + Sync, @@ -77,7 +79,6 @@ struct AsyncFnEndpoint { f: F, } -#[async_trait::async_trait] impl Endpoint for AsyncFnEndpoint where F: Fn(Request) -> Fut + Sync + Send, @@ -98,7 +99,6 @@ pub enum EitherEndpoint { B(B), } -#[async_trait::async_trait] impl Endpoint for EitherEndpoint where A: Endpoint, @@ -175,7 +175,6 @@ where } } -#[async_trait::async_trait] impl Endpoint for &T { type Output = T::Output; @@ -184,7 +183,6 @@ impl Endpoint for &T { } } -#[async_trait::async_trait] impl Endpoint for Box { type Output = T::Output; @@ -193,7 +191,6 @@ impl Endpoint for Box { } } -#[async_trait::async_trait] impl Endpoint for Arc { type Output = T::Output; @@ -202,9 +199,45 @@ impl Endpoint for Arc { } } +/// A `endpoint` that can be dynamically dispatched. +pub trait DynEndpoint: Send + Sync { + /// Represents the response of the endpoint. + type Output: IntoResponse; + + /// Get the response to the request. + fn call(&self, req: Request) -> BoxFuture>; +} + +/// A [`Endpoint`] wrapper used to implement [`DynEndpoint`]. +pub struct DynEndpointWrapper(pub E); + +impl DynEndpoint for DynEndpointWrapper +where + E: Endpoint, +{ + type Output = E::Output; + + #[inline] + fn call(&self, req: Request) -> BoxFuture> { + self.0.call(req).boxed() + } +} + +impl Endpoint for dyn DynEndpoint + '_ +where + T: IntoResponse, +{ + type Output = T; + + #[inline] + async fn call(&self, req: Request) -> Result { + DynEndpoint::call(self, req).await + } +} + /// An owned dynamically typed `Endpoint` for use in cases where you can’t /// statically type your result or need to add some indirection. -pub type BoxEndpoint<'a, T = Response> = Box + 'a>; +pub type BoxEndpoint<'a, T = Response> = Box + 'a>; /// Extension trait for [`Endpoint`]. pub trait EndpointExt: IntoEndpoint { @@ -213,7 +246,7 @@ pub trait EndpointExt: IntoEndpoint { where Self: Sized + 'a, { - Box::new(self.into_endpoint()) + Box::new(DynEndpointWrapper(self.into_endpoint())) } /// Use middleware to transform this endpoint. diff --git a/poem/src/endpoint/inspect_all_err.rs b/poem/src/endpoint/inspect_all_err.rs index 6d1a6da39b..2377ce9eff 100644 --- a/poem/src/endpoint/inspect_all_err.rs +++ b/poem/src/endpoint/inspect_all_err.rs @@ -14,7 +14,6 @@ impl InspectAllError { } } -#[async_trait::async_trait] impl Endpoint for InspectAllError where E: Endpoint, diff --git a/poem/src/endpoint/inspect_err.rs b/poem/src/endpoint/inspect_err.rs index 630ea40fb0..1ded32a205 100644 --- a/poem/src/endpoint/inspect_err.rs +++ b/poem/src/endpoint/inspect_err.rs @@ -21,7 +21,6 @@ impl InspectError { } } -#[async_trait::async_trait] impl Endpoint for InspectError where E: Endpoint, diff --git a/poem/src/endpoint/map.rs b/poem/src/endpoint/map.rs index 55bfa3200d..27b81bcfdd 100644 --- a/poem/src/endpoint/map.rs +++ b/poem/src/endpoint/map.rs @@ -15,7 +15,6 @@ impl Map { } } -#[async_trait::async_trait] impl Endpoint for Map where E: Endpoint, diff --git a/poem/src/endpoint/map_to_response.rs b/poem/src/endpoint/map_to_response.rs index 5395aeaad3..e7f9c26d74 100644 --- a/poem/src/endpoint/map_to_response.rs +++ b/poem/src/endpoint/map_to_response.rs @@ -13,7 +13,6 @@ impl MapToResponse { } } -#[async_trait::async_trait] impl Endpoint for MapToResponse { type Output = Response; diff --git a/poem/src/endpoint/mod.rs b/poem/src/endpoint/mod.rs index 2e6cf93fab..9c3960f456 100644 --- a/poem/src/endpoint/mod.rs +++ b/poem/src/endpoint/mod.rs @@ -30,7 +30,10 @@ pub use catch_all_error::CatchAllError; pub use catch_error::CatchError; #[cfg(feature = "embed")] pub use embed::{EmbeddedFileEndpoint, EmbeddedFilesEndpoint}; -pub use endpoint::{make, make_sync, BoxEndpoint, Endpoint, EndpointExt, IntoEndpoint}; +pub use endpoint::{ + make, make_sync, BoxEndpoint, DynEndpoint, DynEndpointWrapper, Endpoint, EndpointExt, + IntoEndpoint, +}; pub use inspect_all_err::InspectAllError; pub use inspect_err::InspectError; pub use map::Map; diff --git a/poem/src/endpoint/prometheus_exporter.rs b/poem/src/endpoint/prometheus_exporter.rs index b04d48d812..bcad498f7d 100644 --- a/poem/src/endpoint/prometheus_exporter.rs +++ b/poem/src/endpoint/prometheus_exporter.rs @@ -43,7 +43,6 @@ pub struct PrometheusExporterEndpoint { registry: Registry, } -#[async_trait::async_trait] impl Endpoint for PrometheusExporterEndpoint { type Output = Response; diff --git a/poem/src/endpoint/static_files.rs b/poem/src/endpoint/static_files.rs index 60723fd37c..79cdb505cf 100644 --- a/poem/src/endpoint/static_files.rs +++ b/poem/src/endpoint/static_files.rs @@ -161,7 +161,6 @@ impl StaticFilesEndpoint { } } -#[async_trait::async_trait] impl Endpoint for StaticFilesEndpoint { type Output = Response; @@ -315,7 +314,6 @@ impl StaticFileEndpoint { } } -#[async_trait::async_trait] impl Endpoint for StaticFileEndpoint { type Output = Response; diff --git a/poem/src/endpoint/to_response.rs b/poem/src/endpoint/to_response.rs index b0a3abcb8e..ed4a893df6 100644 --- a/poem/src/endpoint/to_response.rs +++ b/poem/src/endpoint/to_response.rs @@ -13,7 +13,6 @@ impl ToResponse { } } -#[async_trait::async_trait] impl Endpoint for ToResponse { type Output = Response; diff --git a/poem/src/endpoint/tower_compat.rs b/poem/src/endpoint/tower_compat.rs index c1519dc2bb..86b4fdd6f2 100644 --- a/poem/src/endpoint/tower_compat.rs +++ b/poem/src/endpoint/tower_compat.rs @@ -38,7 +38,6 @@ impl TowerCompatExt for T {} #[cfg_attr(docsrs, doc(cfg(feature = "tower-compat")))] pub struct TowerCompatEndpoint(Svc); -#[async_trait::async_trait] impl Endpoint for TowerCompatEndpoint where ResBody: hyper::body::Body + Send + Sync + 'static, diff --git a/poem/src/listener/acme/endpoint.rs b/poem/src/listener/acme/endpoint.rs index ba6bdca608..2ede375aa4 100644 --- a/poem/src/listener/acme/endpoint.rs +++ b/poem/src/listener/acme/endpoint.rs @@ -38,7 +38,6 @@ pub struct Http01Endpoint { pub keys: Http01TokensMap, } -#[async_trait::async_trait] impl Endpoint for Http01Endpoint { type Output = Response; diff --git a/poem/src/listener/acme/listener.rs b/poem/src/listener/acme/listener.rs index 1f5c950d05..e5115fc13f 100644 --- a/poem/src/listener/acme/listener.rs +++ b/poem/src/listener/acme/listener.rs @@ -76,7 +76,6 @@ impl ResolvedCertListener { } } -#[async_trait::async_trait] impl Listener for ResolvedCertListener { type Acceptor = AutoCertAcceptor; @@ -97,7 +96,6 @@ impl AutoCertListener { } } -#[async_trait::async_trait] impl Listener for AutoCertListener { type Acceptor = AutoCertAcceptor; diff --git a/poem/src/listener/combined.rs b/poem/src/listener/combined.rs index 6a0e707970..1a309f9221 100644 --- a/poem/src/listener/combined.rs +++ b/poem/src/listener/combined.rs @@ -24,7 +24,6 @@ impl Combined { } } -#[async_trait::async_trait] impl Listener for Combined { type Acceptor = Combined; diff --git a/poem/src/listener/mod.rs b/poem/src/listener/mod.rs index 2e59598ca4..6b5bfda60a 100644 --- a/poem/src/listener/mod.rs +++ b/poem/src/listener/mod.rs @@ -25,7 +25,7 @@ use std::{ task::{Context, Poll}, }; -use futures_util::{future::BoxFuture, FutureExt, TryFutureExt}; +use futures_util::{future::BoxFuture, Future, FutureExt, TryFutureExt}; use http::uri::Scheme; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf, Result as IoResult}; @@ -132,7 +132,7 @@ pub trait Listener: Send { type Acceptor: Acceptor; /// Create a acceptor instance. - async fn into_acceptor(self) -> IoResult; + fn into_acceptor(self) -> impl Future> + Send; /// Combine two listeners. /// @@ -232,7 +232,6 @@ pub trait Listener: Send { } } -#[async_trait::async_trait] impl Listener for Infallible { type Acceptor = Infallible; @@ -241,7 +240,6 @@ impl Listener for Infallible { } } -#[async_trait::async_trait] impl Listener for Box { type Acceptor = T::Acceptor; @@ -334,7 +332,6 @@ impl BoxListener { } } -#[async_trait::async_trait] impl Listener for BoxListener { type Acceptor = BoxAcceptor; diff --git a/poem/src/listener/native_tls.rs b/poem/src/listener/native_tls.rs index f76071d658..deb2be01b7 100644 --- a/poem/src/listener/native_tls.rs +++ b/poem/src/listener/native_tls.rs @@ -103,7 +103,6 @@ where } } -#[async_trait::async_trait] impl> Listener for NativeTlsListener { type Acceptor = NativeTlsAcceptor>; diff --git a/poem/src/listener/openssl_tls.rs b/poem/src/listener/openssl_tls.rs index 090f3cc018..935cec6933 100644 --- a/poem/src/listener/openssl_tls.rs +++ b/poem/src/listener/openssl_tls.rs @@ -148,7 +148,6 @@ where } } -#[async_trait::async_trait] impl> Listener for OpensslTlsListener { type Acceptor = OpensslTlsAcceptor>; diff --git a/poem/src/listener/rustls.rs b/poem/src/listener/rustls.rs index 40e8a8aadb..23aa526a05 100644 --- a/poem/src/listener/rustls.rs +++ b/poem/src/listener/rustls.rs @@ -320,7 +320,6 @@ where } } -#[async_trait::async_trait] impl> Listener for RustlsListener { type Acceptor = RustlsAcceptor>; diff --git a/poem/src/listener/tcp.rs b/poem/src/listener/tcp.rs index b464ef6c66..a11ca2346a 100644 --- a/poem/src/listener/tcp.rs +++ b/poem/src/listener/tcp.rs @@ -23,7 +23,6 @@ impl TcpListener { } } -#[async_trait::async_trait] impl Listener for TcpListener { type Acceptor = TcpAcceptor; diff --git a/poem/src/middleware/add_data.rs b/poem/src/middleware/add_data.rs index dec6e0cf70..1e07e79d4c 100644 --- a/poem/src/middleware/add_data.rs +++ b/poem/src/middleware/add_data.rs @@ -33,7 +33,6 @@ pub struct AddDataEndpoint { value: T, } -#[async_trait::async_trait] impl Endpoint for AddDataEndpoint where E: Endpoint, diff --git a/poem/src/middleware/catch_panic.rs b/poem/src/middleware/catch_panic.rs index 45b1edafcc..adc0294450 100644 --- a/poem/src/middleware/catch_panic.rs +++ b/poem/src/middleware/catch_panic.rs @@ -128,7 +128,6 @@ pub struct CatchPanicEndpoint { panic_handler: H, } -#[async_trait::async_trait] impl Endpoint for CatchPanicEndpoint { type Output = Response; diff --git a/poem/src/middleware/compression.rs b/poem/src/middleware/compression.rs index dfb030a8c7..2ea4fb2ac8 100644 --- a/poem/src/middleware/compression.rs +++ b/poem/src/middleware/compression.rs @@ -138,7 +138,6 @@ fn coding_priority(c: &ContentCoding) -> u8 { } } -#[async_trait::async_trait] impl Endpoint for CompressionEndpoint { type Output = Response; diff --git a/poem/src/middleware/cookie_jar_manager.rs b/poem/src/middleware/cookie_jar_manager.rs index 328e899ce2..5a4da25478 100644 --- a/poem/src/middleware/cookie_jar_manager.rs +++ b/poem/src/middleware/cookie_jar_manager.rs @@ -49,7 +49,6 @@ pub struct CookieJarManagerEndpoint { key: Option>, } -#[async_trait::async_trait] impl Endpoint for CookieJarManagerEndpoint { type Output = Response; diff --git a/poem/src/middleware/cors.rs b/poem/src/middleware/cors.rs index 7329fda0c0..57a30feda3 100644 --- a/poem/src/middleware/cors.rs +++ b/poem/src/middleware/cors.rs @@ -349,7 +349,6 @@ impl CorsEndpoint { } } -#[async_trait::async_trait] impl Endpoint for CorsEndpoint { type Output = Response; diff --git a/poem/src/middleware/csrf.rs b/poem/src/middleware/csrf.rs index 44002b570c..63b3e87731 100644 --- a/poem/src/middleware/csrf.rs +++ b/poem/src/middleware/csrf.rs @@ -199,7 +199,6 @@ impl CsrfEndpoint { } } -#[async_trait::async_trait] impl Endpoint for CsrfEndpoint { type Output = E::Output; diff --git a/poem/src/middleware/force_https.rs b/poem/src/middleware/force_https.rs index 9cfab0f209..0d8a209b78 100644 --- a/poem/src/middleware/force_https.rs +++ b/poem/src/middleware/force_https.rs @@ -60,7 +60,6 @@ pub struct ForceHttpsEndpoint { filter_fn: Option, } -#[async_trait::async_trait] impl Endpoint for ForceHttpsEndpoint where E: Endpoint, diff --git a/poem/src/middleware/mod.rs b/poem/src/middleware/mod.rs index 2885952b86..4b00bd02cb 100644 --- a/poem/src/middleware/mod.rs +++ b/poem/src/middleware/mod.rs @@ -225,7 +225,6 @@ mod tests { value: HeaderValue, } - #[async_trait::async_trait] impl Endpoint for AddHeader { type Output = Response; diff --git a/poem/src/middleware/normalize_path.rs b/poem/src/middleware/normalize_path.rs index 247a3882f3..f903e0acea 100644 --- a/poem/src/middleware/normalize_path.rs +++ b/poem/src/middleware/normalize_path.rs @@ -78,7 +78,6 @@ pub struct NormalizePathEndpoint { style: TrailingSlash, } -#[async_trait::async_trait] impl Endpoint for NormalizePathEndpoint { type Output = E::Output; diff --git a/poem/src/middleware/opentelemetry_metrics.rs b/poem/src/middleware/opentelemetry_metrics.rs index 2131169ff6..02d2cb7d17 100644 --- a/poem/src/middleware/opentelemetry_metrics.rs +++ b/poem/src/middleware/opentelemetry_metrics.rs @@ -69,7 +69,6 @@ pub struct OpenTelemetryMetricsEndpoint { inner: E, } -#[async_trait::async_trait] impl Endpoint for OpenTelemetryMetricsEndpoint { type Output = Response; diff --git a/poem/src/middleware/opentelemetry_tracing.rs b/poem/src/middleware/opentelemetry_tracing.rs index aaedde9024..a171358851 100644 --- a/poem/src/middleware/opentelemetry_tracing.rs +++ b/poem/src/middleware/opentelemetry_tracing.rs @@ -67,7 +67,6 @@ impl<'a> Extractor for HeaderExtractor<'a> { } } -#[async_trait::async_trait] impl Endpoint for OpenTelemetryTracingEndpoint where T: Tracer + Send + Sync, diff --git a/poem/src/middleware/propagate_header.rs b/poem/src/middleware/propagate_header.rs index c09c69cffd..d96de9f9c8 100644 --- a/poem/src/middleware/propagate_header.rs +++ b/poem/src/middleware/propagate_header.rs @@ -47,7 +47,6 @@ pub struct PropagateHeaderEndpoint { headers: HashSet, } -#[async_trait::async_trait] impl Endpoint for PropagateHeaderEndpoint { type Output = Response; diff --git a/poem/src/middleware/sensitive_header.rs b/poem/src/middleware/sensitive_header.rs index 1ba7f25557..631daf6d66 100644 --- a/poem/src/middleware/sensitive_header.rs +++ b/poem/src/middleware/sensitive_header.rs @@ -91,7 +91,6 @@ pub struct SensitiveHeaderEndpoint { applied_to: AppliedTo, } -#[async_trait::async_trait] impl Endpoint for SensitiveHeaderEndpoint { type Output = Response; diff --git a/poem/src/middleware/set_header.rs b/poem/src/middleware/set_header.rs index 7ae28f6249..d6aca5f8d3 100644 --- a/poem/src/middleware/set_header.rs +++ b/poem/src/middleware/set_header.rs @@ -109,7 +109,6 @@ pub struct SetHeaderEndpoint { actions: Vec, } -#[async_trait::async_trait] impl Endpoint for SetHeaderEndpoint { type Output = Response; diff --git a/poem/src/middleware/size_limit.rs b/poem/src/middleware/size_limit.rs index f7156b014a..737116966f 100644 --- a/poem/src/middleware/size_limit.rs +++ b/poem/src/middleware/size_limit.rs @@ -38,7 +38,6 @@ pub struct SizeLimitEndpoint { max_size: usize, } -#[async_trait::async_trait] impl Endpoint for SizeLimitEndpoint { type Output = E::Output; diff --git a/poem/src/middleware/tokio_metrics_mw.rs b/poem/src/middleware/tokio_metrics_mw.rs index e7c6a29e4c..8fd33870bc 100644 --- a/poem/src/middleware/tokio_metrics_mw.rs +++ b/poem/src/middleware/tokio_metrics_mw.rs @@ -77,7 +77,6 @@ pub struct TokioMetricsEndpoint { monitor: TaskMonitor, } -#[async_trait::async_trait] impl Endpoint for TokioMetricsEndpoint { type Output = Response; diff --git a/poem/src/middleware/tower_compat.rs b/poem/src/middleware/tower_compat.rs index 1e13ce2e70..e0cc059a90 100644 --- a/poem/src/middleware/tower_compat.rs +++ b/poem/src/middleware/tower_compat.rs @@ -81,7 +81,6 @@ where /// An tower service to endpoint adapter. pub struct TowerServiceToEndpoint>(Buffer); -#[async_trait::async_trait] impl Endpoint for TowerServiceToEndpoint where Svc: Service + Send + 'static, diff --git a/poem/src/middleware/tracing_mw.rs b/poem/src/middleware/tracing_mw.rs index 5b7a0e642a..4ca0799e1d 100644 --- a/poem/src/middleware/tracing_mw.rs +++ b/poem/src/middleware/tracing_mw.rs @@ -24,7 +24,6 @@ pub struct TracingEndpoint { inner: E, } -#[async_trait::async_trait] impl Endpoint for TracingEndpoint { type Output = Response; diff --git a/poem/src/route/router.rs b/poem/src/route/router.rs index fd6980f6e1..490f978069 100644 --- a/poem/src/route/router.rs +++ b/poem/src/route/router.rs @@ -244,7 +244,6 @@ impl Route { prefix_for_path_pattern: usize, } - #[async_trait::async_trait] impl Endpoint for Nest { type Output = Response; @@ -301,22 +300,24 @@ impl Route { self.tree.add( &format!("{path}*--poem-rest"), - Box::new(Nest { + Nest { inner: ep.clone(), root: false, prefix_len, prefix_for_path_pattern, - }), + } + .boxed(), )?; self.tree.add( &path[..path.len() - 1], - Box::new(Nest { + Nest { inner: ep, root: true, prefix_len, prefix_for_path_pattern, - }), + } + .boxed(), )?; Ok(self) @@ -327,7 +328,6 @@ impl Route { #[derive(Debug, Clone)] pub struct PathPattern(pub Arc); -#[async_trait::async_trait] impl Endpoint for Route { type Output = Response; @@ -652,7 +652,6 @@ mod tests { inner: E, } - #[async_trait::async_trait] impl Endpoint for PathPatternSpyEndpoint { type Output = Response; @@ -723,7 +722,6 @@ mod tests { struct ErrorEndpoint; - #[async_trait::async_trait] impl Endpoint for ErrorEndpoint { type Output = Response; diff --git a/poem/src/route/router_domain.rs b/poem/src/route/router_domain.rs index 3946e850cf..001374a37c 100644 --- a/poem/src/route/router_domain.rs +++ b/poem/src/route/router_domain.rs @@ -87,7 +87,6 @@ impl RouteDomain { } } -#[async_trait::async_trait] impl Endpoint for RouteDomain { type Output = Response; diff --git a/poem/src/route/router_method.rs b/poem/src/route/router_method.rs index 37b70467bb..f572c8cb3b 100644 --- a/poem/src/route/router_method.rs +++ b/poem/src/route/router_method.rs @@ -1,3 +1,5 @@ +use std::future::Future; + use crate::{ endpoint::BoxEndpoint, error::MethodNotAllowedError, http::Method, Endpoint, EndpointExt, IntoEndpoint, Request, Response, Result, @@ -163,26 +165,27 @@ impl RouteMethod { } } -#[async_trait::async_trait] impl Endpoint for RouteMethod { type Output = Response; - async fn call(&self, mut req: Request) -> Result { - match self - .methods - .iter() - .find(|(method, _)| method == req.method()) - .map(|(_, ep)| ep) - { - Some(ep) => ep.call(req).await, - None => { - if req.method() == Method::HEAD { - req.set_method(Method::GET); - let mut resp = self.call(req).await?; - resp.set_body(()); - return Ok(resp); + fn call(&self, mut req: Request) -> impl Future> + Send { + async move { + match self + .methods + .iter() + .find(|(method, _)| method == req.method()) + .map(|(_, ep)| ep) + { + Some(ep) => ep.call(req).await, + None => { + if req.method() == Method::HEAD { + req.set_method(Method::GET); + let mut resp = Box::pin(self.call(req)).await?; + resp.set_body(()); + return Ok(resp); + } + Err(MethodNotAllowedError.into()) } - Err(MethodNotAllowedError.into()) } } } diff --git a/poem/src/route/router_scheme.rs b/poem/src/route/router_scheme.rs index 65e320cd43..6faa6709ec 100644 --- a/poem/src/route/router_scheme.rs +++ b/poem/src/route/router_scheme.rs @@ -72,7 +72,6 @@ impl RouteScheme { } } -#[async_trait::async_trait] impl Endpoint for RouteScheme { type Output = Response; diff --git a/poem/src/server.rs b/poem/src/server.rs index 824340fd0b..5a2a0e3b0f 100644 --- a/poem/src/server.rs +++ b/poem/src/server.rs @@ -23,6 +23,7 @@ use tokio::{ use tokio_util::sync::CancellationToken; use crate::{ + endpoint::{DynEndpoint, DynEndpointWrapper}, listener::{Acceptor, AcceptorExt, Listener}, web::{LocalAddr, RemoteAddr}, Endpoint, EndpointExt, IntoEndpoint, Response, @@ -109,7 +110,7 @@ where E: IntoEndpoint, E::Endpoint: 'static, { - let ep = Arc::new(ep.into_endpoint().map_to_response()); + let ep = Arc::new(DynEndpointWrapper(ep.into_endpoint().map_to_response())); let Server { listener, name, @@ -313,7 +314,7 @@ async fn serve_connection( local_addr: LocalAddr, remote_addr: RemoteAddr, scheme: Scheme, - ep: Arc>, + ep: Arc>, server_graceful_shutdown_token: CancellationToken, idle_connection_close_timeout: Option, ) { diff --git a/poem/src/session/cookie_session.rs b/poem/src/session/cookie_session.rs index e4aa193935..c769889f40 100644 --- a/poem/src/session/cookie_session.rs +++ b/poem/src/session/cookie_session.rs @@ -42,7 +42,6 @@ pub struct CookieSessionEndpoint { config: Arc, } -#[async_trait::async_trait] impl Endpoint for CookieSessionEndpoint { type Output = E::Output; diff --git a/poem/src/session/memory_storage.rs b/poem/src/session/memory_storage.rs index 13c40a5230..1b07a5b196 100644 --- a/poem/src/session/memory_storage.rs +++ b/poem/src/session/memory_storage.rs @@ -1,6 +1,7 @@ use std::{ cmp::Reverse, collections::{BTreeMap, HashMap}, + future::Future, sync::Arc, time::{Duration, Instant}, }; @@ -68,37 +69,48 @@ impl MemoryStorage { } } -#[async_trait::async_trait] impl SessionStorage for MemoryStorage { - async fn load_session(&self, session_id: &str) -> Result>> { - let inner = self.inner.lock(); - Ok(inner.sessions.get(session_id).cloned()) + fn load_session<'a>( + &'a self, + session_id: &'a str, + ) -> impl Future>>> + Send + 'a { + async move { + let inner = self.inner.lock(); + Ok(inner.sessions.get(session_id).cloned()) + } } - async fn update_session( - &self, - session_id: &str, - entries: &BTreeMap, + fn update_session<'a>( + &'a self, + session_id: &'a str, + entries: &'a BTreeMap, expires: Option, - ) -> Result<()> { - let mut inner = self.inner.lock(); - inner.timeout_queue.remove(session_id); - inner - .sessions - .insert(session_id.to_string(), entries.clone()); - if let Some(expires) = expires { + ) -> impl Future> + Send + 'a { + async move { + let mut inner = self.inner.lock(); + inner.timeout_queue.remove(session_id); inner - .timeout_queue - .push(session_id.to_string(), Reverse(Instant::now() + expires)); + .sessions + .insert(session_id.to_string(), entries.clone()); + if let Some(expires) = expires { + inner + .timeout_queue + .push(session_id.to_string(), Reverse(Instant::now() + expires)); + } + Ok(()) } - Ok(()) } - async fn remove_session(&self, session_id: &str) -> Result<()> { - let mut inner = self.inner.lock(); - inner.sessions.remove(session_id); - inner.timeout_queue.remove(session_id); - Ok(()) + fn remove_session<'a>( + &'a self, + session_id: &'a str, + ) -> impl Future> + Send + 'a { + async move { + let mut inner = self.inner.lock(); + inner.sessions.remove(session_id); + inner.timeout_queue.remove(session_id); + Ok(()) + } } } diff --git a/poem/src/session/redis_storage.rs b/poem/src/session/redis_storage.rs index 148673824d..8f3694fb42 100644 --- a/poem/src/session/redis_storage.rs +++ b/poem/src/session/redis_storage.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeMap, time::Duration}; +use std::{collections::BTreeMap, future::Future, time::Duration}; use redis::{aio::ConnectionLike, Cmd}; use serde_json::Value; @@ -22,46 +22,57 @@ impl RedisStorage { } } -#[async_trait::async_trait] impl SessionStorage for RedisStorage { - async fn load_session(&self, session_id: &str) -> Result>> { - let data: Option = Cmd::get(session_id) - .query_async(&mut self.connection.clone()) - .await - .map_err(RedisSessionError::Redis)?; + fn load_session<'a>( + &'a self, + session_id: &'a str, + ) -> impl Future>>> + Send + 'a { + async move { + let data: Option = Cmd::get(session_id) + .query_async(&mut self.connection.clone()) + .await + .map_err(RedisSessionError::Redis)?; - match data { - Some(data) => match serde_json::from_str::>(&data) { - Ok(entries) => Ok(Some(entries)), - Err(_) => Ok(None), - }, - None => Ok(None), + match data { + Some(data) => match serde_json::from_str::>(&data) { + Ok(entries) => Ok(Some(entries)), + Err(_) => Ok(None), + }, + None => Ok(None), + } } } - async fn update_session( - &self, - session_id: &str, - entries: &BTreeMap, + fn update_session<'a>( + &'a self, + session_id: &'a str, + entries: &'a BTreeMap, expires: Option, - ) -> Result<()> { - let value = serde_json::to_string(entries).unwrap_or_default(); - let cmd = match expires { - Some(expires) => Cmd::set_ex(session_id, value, expires.as_secs()), - None => Cmd::set(session_id, value), - }; - cmd.query_async(&mut self.connection.clone()) - .await - .map_err(RedisSessionError::Redis)?; - Ok(()) + ) -> impl Future> + Send + 'a { + async move { + let value = serde_json::to_string(entries).unwrap_or_default(); + let cmd = match expires { + Some(expires) => Cmd::set_ex(session_id, value, expires.as_secs()), + None => Cmd::set(session_id, value), + }; + cmd.query_async(&mut self.connection.clone()) + .await + .map_err(RedisSessionError::Redis)?; + Ok(()) + } } - async fn remove_session(&self, session_id: &str) -> Result<()> { - Cmd::del(session_id) - .query_async(&mut self.connection.clone()) - .await - .map_err(RedisSessionError::Redis)?; - Ok(()) + fn remove_session<'a>( + &'a self, + session_id: &'a str, + ) -> impl Future> + Send + 'a { + async move { + Cmd::del(session_id) + .query_async(&mut self.connection.clone()) + .await + .map_err(RedisSessionError::Redis)?; + Ok(()) + } } } diff --git a/poem/src/session/server_session.rs b/poem/src/session/server_session.rs index de345c6973..5537dc0471 100644 --- a/poem/src/session/server_session.rs +++ b/poem/src/session/server_session.rs @@ -52,7 +52,6 @@ pub struct ServerSessionEndpoint { storage: Arc, } -#[async_trait::async_trait] impl Endpoint for ServerSessionEndpoint where T: SessionStorage, diff --git a/poem/src/session/session_storage.rs b/poem/src/session/session_storage.rs index f266fd8474..b4d4a5f24c 100644 --- a/poem/src/session/session_storage.rs +++ b/poem/src/session/session_storage.rs @@ -1,23 +1,28 @@ -use std::{collections::BTreeMap, time::Duration}; +use std::{collections::BTreeMap, future::Future, time::Duration}; use serde_json::Value; use crate::Result; /// Represents a back-end session storage. -#[async_trait::async_trait] pub trait SessionStorage: Send + Sync { /// Load session entries. - async fn load_session(&self, session_id: &str) -> Result>>; + fn load_session<'a>( + &'a self, + session_id: &'a str, + ) -> impl Future>>> + Send + 'a; /// Insert or update a session. - async fn update_session( - &self, - session_id: &str, - entries: &BTreeMap, + fn update_session<'a>( + &'a self, + session_id: &'a str, + entries: &'a BTreeMap, expires: Option, - ) -> Result<()>; + ) -> impl Future> + Send + 'a; /// Remove a session by session id. - async fn remove_session(&self, session_id: &str) -> Result<()>; + fn remove_session<'a>( + &'a self, + session_id: &'a str, + ) -> impl Future> + Send + 'a; } From a4ba7d9114609dd91d63aee3953202a21c2028ca Mon Sep 17 00:00:00 2001 From: Sunli Date: Sat, 23 Mar 2024 20:20:01 +0800 Subject: [PATCH 04/24] use AFIT instead of `async_trait` 2 --- poem-grpc/src/client.rs | 8 +- poem/src/endpoint/endpoint.rs | 6 +- poem/src/endpoint/mod.rs | 3 +- poem/src/listener/acme/listener.rs | 1 - poem/src/listener/combined.rs | 1 - poem/src/listener/mod.rs | 173 ++++++++++++++++------------- poem/src/listener/native_tls.rs | 1 - poem/src/listener/openssl_tls.rs | 1 - poem/src/listener/rustls.rs | 1 - poem/src/listener/tcp.rs | 1 - poem/src/listener/unix.rs | 2 - poem/src/middleware/mod.rs | 1 - poem/src/server.rs | 4 +- 13 files changed, 108 insertions(+), 95 deletions(-) diff --git a/poem-grpc/src/client.rs b/poem-grpc/src/client.rs index 3f657ac737..e42db405cc 100644 --- a/poem-grpc/src/client.rs +++ b/poem-grpc/src/client.rs @@ -5,7 +5,7 @@ use futures_util::TryStreamExt; use http_body_util::BodyExt; use hyper_util::{client::legacy::Client, rt::TokioExecutor}; use poem::{ - endpoint::{DynEndpoint, DynEndpointWrapper}, + endpoint::{DynEndpoint, ToDynEndpoint}, http::{ header::{self, InvalidHeaderValue}, uri::InvalidUri, @@ -172,7 +172,7 @@ impl GrpcClient { ::Output: 'static, { Self { - ep: Arc::new(DynEndpointWrapper(ep.map_to_response())), + ep: Arc::new(ToDynEndpoint(ep.map_to_response())), } } @@ -181,7 +181,7 @@ impl GrpcClient { M: Middleware + 'static>>, M::Output: 'static, { - self.ep = Arc::new(DynEndpointWrapper( + self.ep = Arc::new(ToDynEndpoint( middleware.transform(self.ep).map_to_response(), )); self @@ -407,7 +407,7 @@ fn create_client_endpoint( let config = Arc::new(config); - Arc::new(DynEndpointWrapper(poem::endpoint::make(move |request| { + Arc::new(ToDynEndpoint(poem::endpoint::make(move |request| { let config = config.clone(); let cli = cli.clone(); async move { diff --git a/poem/src/endpoint/endpoint.rs b/poem/src/endpoint/endpoint.rs index fbd6ba6e32..cf8f678379 100644 --- a/poem/src/endpoint/endpoint.rs +++ b/poem/src/endpoint/endpoint.rs @@ -209,9 +209,9 @@ pub trait DynEndpoint: Send + Sync { } /// A [`Endpoint`] wrapper used to implement [`DynEndpoint`]. -pub struct DynEndpointWrapper(pub E); +pub struct ToDynEndpoint(pub E); -impl DynEndpoint for DynEndpointWrapper +impl DynEndpoint for ToDynEndpoint where E: Endpoint, { @@ -246,7 +246,7 @@ pub trait EndpointExt: IntoEndpoint { where Self: Sized + 'a, { - Box::new(DynEndpointWrapper(self.into_endpoint())) + Box::new(ToDynEndpoint(self.into_endpoint())) } /// Use middleware to transform this endpoint. diff --git a/poem/src/endpoint/mod.rs b/poem/src/endpoint/mod.rs index 9c3960f456..cd22b9080c 100644 --- a/poem/src/endpoint/mod.rs +++ b/poem/src/endpoint/mod.rs @@ -31,8 +31,7 @@ pub use catch_error::CatchError; #[cfg(feature = "embed")] pub use embed::{EmbeddedFileEndpoint, EmbeddedFilesEndpoint}; pub use endpoint::{ - make, make_sync, BoxEndpoint, DynEndpoint, DynEndpointWrapper, Endpoint, EndpointExt, - IntoEndpoint, + make, make_sync, BoxEndpoint, DynEndpoint, Endpoint, EndpointExt, IntoEndpoint, ToDynEndpoint, }; pub use inspect_all_err::InspectAllError; pub use inspect_err::InspectError; diff --git a/poem/src/listener/acme/listener.rs b/poem/src/listener/acme/listener.rs index e5115fc13f..d62a093e23 100644 --- a/poem/src/listener/acme/listener.rs +++ b/poem/src/listener/acme/listener.rs @@ -214,7 +214,6 @@ pub struct AutoCertAcceptor { acceptor: TlsAcceptor, } -#[async_trait::async_trait] impl Acceptor for AutoCertAcceptor { type Io = HandshakeStream>; diff --git a/poem/src/listener/combined.rs b/poem/src/listener/combined.rs index 1a309f9221..c2eb949a53 100644 --- a/poem/src/listener/combined.rs +++ b/poem/src/listener/combined.rs @@ -35,7 +35,6 @@ impl Listener for Combined { } } -#[async_trait::async_trait] impl Acceptor for Combined { type Io = CombinedStream; diff --git a/poem/src/listener/mod.rs b/poem/src/listener/mod.rs index 6b5bfda60a..e7e5f41c7e 100644 --- a/poem/src/listener/mod.rs +++ b/poem/src/listener/mod.rs @@ -49,8 +49,100 @@ pub use self::{ }; use crate::web::{LocalAddr, RemoteAddr}; +/// An IO type for BoxAcceptor. +pub struct BoxIo { + reader: Box, + writer: Box, +} + +impl BoxIo { + fn new(io: impl AsyncRead + AsyncWrite + Send + Unpin + 'static) -> Self { + let (reader, writer) = tokio::io::split(io); + Self { + reader: Box::new(reader), + writer: Box::new(writer), + } + } +} + +impl AsyncRead for BoxIo { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + let this = &mut *self; + Pin::new(&mut this.reader).poll_read(cx, buf) + } +} + +impl AsyncWrite for BoxIo { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let this = &mut *self; + Pin::new(&mut this.writer).poll_write(cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = &mut *self; + Pin::new(&mut this.writer).poll_flush(cx) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = &mut *self; + Pin::new(&mut this.writer).poll_shutdown(cx) + } +} + +/// A `acceptor` that can be dynamically dispatched. +pub trait DynAcceptor: Send { + /// Returns the local address that this listener is bound to. + fn local_addr(&self) -> Vec; + + /// Accepts a new incoming connection from this listener. + /// + /// This function will yield once a new TCP connection is established. When + /// established, the corresponding IO stream and the remote peer’s + /// address will be returned. + fn accept(&mut self) -> BoxFuture>; +} + +/// A [`Acceptor`] wrapper used to implement [`DynAcceptor`]. +pub struct ToDynAcceptor(pub A); + +impl DynAcceptor for ToDynAcceptor { + #[inline] + fn local_addr(&self) -> Vec { + self.0.local_addr() + } + + #[inline] + fn accept(&mut self) -> BoxFuture> { + async move { + let (io, local_addr, remote_addr, scheme) = self.0.accept().await?; + let io = BoxIo::new(io); + Ok((io, local_addr, remote_addr, scheme)) + } + .boxed() + } +} + +impl Acceptor for dyn DynAcceptor + '_ { + type Io = BoxIo; + + fn local_addr(&self) -> Vec { + todo!() + } + + async fn accept(&mut self) -> IoResult<(BoxIo, LocalAddr, RemoteAddr, Scheme)> { + DynAcceptor::accept(self).await + } +} + /// Represents a acceptor type. -#[async_trait::async_trait] pub trait Acceptor: Send { /// IO stream type. type Io: AsyncRead + AsyncWrite + Send + Unpin + 'static; @@ -63,12 +155,14 @@ pub trait Acceptor: Send { /// This function will yield once a new TCP connection is established. When /// established, the corresponding IO stream and the remote peer’s /// address will be returned. - async fn accept(&mut self) -> IoResult<(Self::Io, LocalAddr, RemoteAddr, Scheme)>; + fn accept( + &mut self, + ) -> impl Future> + Send; } /// An owned dynamically typed Acceptor for use in cases where you can’t /// statically type your result or need to add some indirection. -pub type BoxAcceptor = Box>; +pub type BoxAcceptor = Box; /// Extension trait for [`Acceptor`]. pub trait AcceptorExt: Acceptor { @@ -86,7 +180,7 @@ pub trait AcceptorExt: Acceptor { where Self: Sized + 'static, { - Box::new(WrappedAcceptor(self)) + Box::new(ToDynAcceptor(self)) } /// Consume this acceptor and return a new TLS acceptor with [`rustls`](https://crates.io/crates/rustls). @@ -126,7 +220,6 @@ pub trait AcceptorExt: Acceptor { impl AcceptorExt for T {} /// Represents a listener that can be listens for incoming connections. -#[async_trait::async_trait] pub trait Listener: Send { /// The acceptor type. type Acceptor: Acceptor; @@ -248,7 +341,6 @@ impl Listener for Box { } } -#[async_trait::async_trait] impl Acceptor for Box { type Io = T::Io; @@ -261,7 +353,6 @@ impl Acceptor for Box { } } -#[async_trait::async_trait] impl Acceptor for Infallible { type Io = BoxIo; @@ -274,54 +365,6 @@ impl Acceptor for Infallible { } } -/// An IO type for BoxAcceptor. -pub struct BoxIo { - reader: Box, - writer: Box, -} - -impl BoxIo { - fn new(io: impl AsyncRead + AsyncWrite + Send + Unpin + 'static) -> Self { - let (reader, writer) = tokio::io::split(io); - Self { - reader: Box::new(reader), - writer: Box::new(writer), - } - } -} - -impl AsyncRead for BoxIo { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - let this = &mut *self; - Pin::new(&mut this.reader).poll_read(cx, buf) - } -} - -impl AsyncWrite for BoxIo { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - let this = &mut *self; - Pin::new(&mut this.writer).poll_write(cx, buf) - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let this = &mut *self; - Pin::new(&mut this.writer).poll_flush(cx) - } - - fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let this = &mut *self; - Pin::new(&mut this.writer).poll_shutdown(cx) - } -} - /// An owned dynamically typed Listener for use in cases where you can’t /// statically type your result or need to add some indirection. pub struct BoxListener(BoxFuture<'static, IoResult>); @@ -340,26 +383,6 @@ impl Listener for BoxListener { } } -struct WrappedAcceptor(T); - -#[async_trait::async_trait] -impl Acceptor for WrappedAcceptor { - type Io = BoxIo; - - fn local_addr(&self) -> Vec { - self.0.local_addr() - } - - async fn accept(&mut self) -> IoResult<(Self::Io, LocalAddr, RemoteAddr, Scheme)> { - self.0 - .accept() - .await - .map(|(io, local_addr, remote_addr, scheme)| { - (BoxIo::new(io), local_addr, remote_addr, scheme) - }) - } -} - #[cfg(test)] mod tests { use super::{AcceptorExt, *}; diff --git a/poem/src/listener/native_tls.rs b/poem/src/listener/native_tls.rs index deb2be01b7..4f933c5ff8 100644 --- a/poem/src/listener/native_tls.rs +++ b/poem/src/listener/native_tls.rs @@ -135,7 +135,6 @@ where } } -#[async_trait::async_trait] impl Acceptor for NativeTlsAcceptor where S: Stream + Send + Unpin + 'static, diff --git a/poem/src/listener/openssl_tls.rs b/poem/src/listener/openssl_tls.rs index 935cec6933..5b814d12e8 100644 --- a/poem/src/listener/openssl_tls.rs +++ b/poem/src/listener/openssl_tls.rs @@ -180,7 +180,6 @@ where } } -#[async_trait::async_trait] impl Acceptor for OpensslTlsAcceptor where S: Stream + Send + Unpin + 'static, diff --git a/poem/src/listener/rustls.rs b/poem/src/listener/rustls.rs index 23aa526a05..93277f947c 100644 --- a/poem/src/listener/rustls.rs +++ b/poem/src/listener/rustls.rs @@ -352,7 +352,6 @@ where } } -#[async_trait::async_trait] impl Acceptor for RustlsAcceptor where S: Stream + Send + Unpin + 'static, diff --git a/poem/src/listener/tcp.rs b/poem/src/listener/tcp.rs index a11ca2346a..3bf26b60c8 100644 --- a/poem/src/listener/tcp.rs +++ b/poem/src/listener/tcp.rs @@ -62,7 +62,6 @@ impl TcpAcceptor { } } -#[async_trait::async_trait] impl Acceptor for TcpAcceptor { type Io = TcpStream; diff --git a/poem/src/listener/unix.rs b/poem/src/listener/unix.rs index 5072e6cc22..e0e679c59f 100644 --- a/poem/src/listener/unix.rs +++ b/poem/src/listener/unix.rs @@ -52,7 +52,6 @@ impl UnixListener { } } -#[async_trait::async_trait] impl + Send + Clone> Listener for UnixListener { type Acceptor = UnixAcceptor; @@ -104,7 +103,6 @@ impl UnixAcceptor { } } -#[async_trait::async_trait] impl Acceptor for UnixAcceptor { type Io = UnixStream; diff --git a/poem/src/middleware/mod.rs b/poem/src/middleware/mod.rs index 4b00bd02cb..f4776d1c07 100644 --- a/poem/src/middleware/mod.rs +++ b/poem/src/middleware/mod.rs @@ -84,7 +84,6 @@ use crate::endpoint::Endpoint; /// #[derive(Clone)] /// struct Token(String); /// -/// #[poem::async_trait] /// impl Endpoint for TokenMiddlewareImpl { /// type Output = E::Output; /// diff --git a/poem/src/server.rs b/poem/src/server.rs index 5a2a0e3b0f..6da5be1e8a 100644 --- a/poem/src/server.rs +++ b/poem/src/server.rs @@ -23,7 +23,7 @@ use tokio::{ use tokio_util::sync::CancellationToken; use crate::{ - endpoint::{DynEndpoint, DynEndpointWrapper}, + endpoint::{DynEndpoint, ToDynEndpoint}, listener::{Acceptor, AcceptorExt, Listener}, web::{LocalAddr, RemoteAddr}, Endpoint, EndpointExt, IntoEndpoint, Response, @@ -110,7 +110,7 @@ where E: IntoEndpoint, E::Endpoint: 'static, { - let ep = Arc::new(DynEndpointWrapper(ep.into_endpoint().map_to_response())); + let ep = Arc::new(ToDynEndpoint(ep.into_endpoint().map_to_response())); let Server { listener, name, From ddfa81ea8323f0886a6f7212955d7504d4968083 Mon Sep 17 00:00:00 2001 From: Sunli Date: Sat, 23 Mar 2024 20:24:42 +0800 Subject: [PATCH 05/24] update MSRV to `1.75.0` --- .github/workflows/ci.yml | 4 ++-- .github/workflows/code-coverage.yml | 2 +- README.md | 6 +++--- poem-grpc/README.md | 8 ++++---- poem-lambda/README.md | 8 ++++---- poem-openapi/README.md | 8 ++++---- poem/README.md | 8 ++++---- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 427a838e48..ee6301999e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,7 +55,7 @@ jobs: # Switch to stable Rust - uses: actions-rs/toolchain@v1 with: - toolchain: 1.74.0 + toolchain: 1.75.0 components: rustfmt, clippy override: true - name: Cache Rust @@ -91,7 +91,7 @@ jobs: # Switch to stable Rust - uses: actions-rs/toolchain@v1 with: - toolchain: 1.74.0 + toolchain: 1.75.0 components: rustfmt, clippy - name: Cache Rust uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index da5e6cf62a..f2bba3f2bc 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -21,7 +21,7 @@ jobs: - name: Install Stable Toolchain uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.74.0 + toolchain: 1.75.0 components: rustfmt - name: Cache Rust uses: Swatinem/rust-cache@v2 diff --git a/README.md b/README.md index 09befe5795..a853c09552 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ Unsafe Rust forbidden - - rustc 1.74.0+ + + rustc 1.75.0+ diff --git a/poem-grpc/README.md b/poem-grpc/README.md index 001a1eba4c..54729377c5 100644 --- a/poem-grpc/README.md +++ b/poem-grpc/README.md @@ -20,9 +20,9 @@ Unsafe Rust forbidden - - rustc 1.74.0+ + + rustc 1.75.0+ @@ -63,7 +63,7 @@ This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in ## MSRV -The minimum supported Rust version for this crate is `1.74.0`. +The minimum supported Rust version for this crate is `1.75.0`. ## Contributing diff --git a/poem-lambda/README.md b/poem-lambda/README.md index c96b89c1c2..63f34b2598 100644 --- a/poem-lambda/README.md +++ b/poem-lambda/README.md @@ -20,9 +20,9 @@ Unsafe Rust forbidden - - rustc 1.74.0+ + + rustc 1.75.0+ @@ -49,7 +49,7 @@ This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in ## MSRV -The minimum supported Rust version for this crate is `1.74.0`. +The minimum supported Rust version for this crate is `1.75.0`. ## Contributing diff --git a/poem-openapi/README.md b/poem-openapi/README.md index 96b7b01484..f4b76be306 100644 --- a/poem-openapi/README.md +++ b/poem-openapi/README.md @@ -21,9 +21,9 @@ Unsafe Rust forbidden - - rustc 1.74.0+ + + rustc 1.75.0+ @@ -131,7 +131,7 @@ hello, sunli! ## MSRV -The minimum supported Rust version for this crate is `1.74.0`. +The minimum supported Rust version for this crate is `1.75.0`. ## Contributing diff --git a/poem/README.md b/poem/README.md index 7f3bd6b019..5ba35b784b 100644 --- a/poem/README.md +++ b/poem/README.md @@ -20,9 +20,9 @@ Unsafe Rust forbidden - - rustc 1.74.0+ + + rustc 1.75.0+ @@ -107,7 +107,7 @@ More examples can be found [here][examples]. ## MSRV -The minimum supported Rust version for this crate is `1.74.0`. +The minimum supported Rust version for this crate is `1.75.0`. ## Contributing From e501b2c43ff021379c08f49ee8bd32cddd0b73f5 Mon Sep 17 00:00:00 2001 From: Sunli Date: Sat, 23 Mar 2024 22:07:47 +0800 Subject: [PATCH 06/24] use AFIT instead of `async_trait` 3 --- poem/src/route/router_method.rs | 38 +++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/poem/src/route/router_method.rs b/poem/src/route/router_method.rs index f572c8cb3b..778fadfd8d 100644 --- a/poem/src/route/router_method.rs +++ b/poem/src/route/router_method.rs @@ -1,5 +1,7 @@ use std::future::Future; +use futures_util::{future::Either, FutureExt}; + use crate::{ endpoint::BoxEndpoint, error::MethodNotAllowedError, http::Method, Endpoint, EndpointExt, IntoEndpoint, Request, Response, Result, @@ -169,22 +171,26 @@ impl Endpoint for RouteMethod { type Output = Response; fn call(&self, mut req: Request) -> impl Future> + Send { - async move { - match self - .methods - .iter() - .find(|(method, _)| method == req.method()) - .map(|(_, ep)| ep) - { - Some(ep) => ep.call(req).await, - None => { - if req.method() == Method::HEAD { - req.set_method(Method::GET); - let mut resp = Box::pin(self.call(req)).await?; - resp.set_body(()); - return Ok(resp); - } - Err(MethodNotAllowedError.into()) + match self + .methods + .iter() + .find(|(method, _)| method == req.method()) + .map(|(_, ep)| ep) + { + Some(ep) => Either::Left(ep.call(req)), + None => { + if req.method() == Method::HEAD { + Either::Right(Either::Left( + async move { + req.set_method(Method::GET); + let mut resp = self.call(req).await?; + resp.set_body(()); + Ok(resp) + } + .boxed(), + )) + } else { + Either::Right(Either::Right(async { Err(MethodNotAllowedError.into()) })) } } } From 955d97321c55163e07bae7d486e8566257dd0013 Mon Sep 17 00:00:00 2001 From: Sunli Date: Sat, 23 Mar 2024 22:13:59 +0800 Subject: [PATCH 07/24] use AFIT instead of `async_trait` 4 --- poem/src/listener/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/poem/src/listener/mod.rs b/poem/src/listener/mod.rs index e7e5f41c7e..68c4f169d7 100644 --- a/poem/src/listener/mod.rs +++ b/poem/src/listener/mod.rs @@ -133,10 +133,12 @@ impl DynAcceptor for ToDynAcceptor { impl Acceptor for dyn DynAcceptor + '_ { type Io = BoxIo; + #[inline] fn local_addr(&self) -> Vec { - todo!() + DynAcceptor::local_addr(self) } + #[inline] async fn accept(&mut self) -> IoResult<(BoxIo, LocalAddr, RemoteAddr, Scheme)> { DynAcceptor::accept(self).await } From e24509c2008a3ae86950e59bc9431561113f6f48 Mon Sep 17 00:00:00 2001 From: Sunli Date: Sun, 24 Mar 2024 08:52:35 +0800 Subject: [PATCH 08/24] clippy clean --- poem/src/endpoint/static_files.rs | 4 +- poem/src/listener/acme/listener.rs | 4 +- poem/src/session/memory_storage.rs | 50 +++++++++------------- poem/src/session/redis_storage.rs | 69 +++++++++++++----------------- 4 files changed, 54 insertions(+), 73 deletions(-) diff --git a/poem/src/endpoint/static_files.rs b/poem/src/endpoint/static_files.rs index 79cdb505cf..3ffe7e23e2 100644 --- a/poem/src/endpoint/static_files.rs +++ b/poem/src/endpoint/static_files.rs @@ -210,10 +210,10 @@ impl Endpoint for StaticFilesEndpoint { } if file_path.is_file() { - return Ok(StaticFileRequest::from_request_without_body(&req) + Ok(StaticFileRequest::from_request_without_body(&req) .await? .create_response(&file_path, self.prefer_utf8)? - .into_response()); + .into_response()) } else { if self.redirect_to_slash && !req.original_uri().path().ends_with('/') diff --git a/poem/src/listener/acme/listener.rs b/poem/src/listener/acme/listener.rs index d62a093e23..160b09ffe1 100644 --- a/poem/src/listener/acme/listener.rs +++ b/poem/src/listener/acme/listener.rs @@ -204,7 +204,7 @@ impl Listener for AutoCertListener { tokio::time::sleep(Duration::from_secs(60 * 5)).await; } }); - Ok(auto_cert_acceptor(self.inner, cert_resolver, challenge_type).await?) + auto_cert_acceptor(self.inner, cert_resolver, challenge_type).await } } @@ -224,7 +224,7 @@ impl Acceptor for AutoCertAcceptor { async fn accept(&mut self) -> IoResult<(Self::Io, LocalAddr, RemoteAddr, Scheme)> { let (stream, local_addr, remote_addr, _) = self.inner.accept().await?; let stream = HandshakeStream::new(self.acceptor.accept(stream)); - return Ok((stream, local_addr, remote_addr, Scheme::HTTPS)); + Ok((stream, local_addr, remote_addr, Scheme::HTTPS)) } } diff --git a/poem/src/session/memory_storage.rs b/poem/src/session/memory_storage.rs index 1b07a5b196..bb83813e0f 100644 --- a/poem/src/session/memory_storage.rs +++ b/poem/src/session/memory_storage.rs @@ -1,7 +1,6 @@ use std::{ cmp::Reverse, collections::{BTreeMap, HashMap}, - future::Future, sync::Arc, time::{Duration, Instant}, }; @@ -70,47 +69,38 @@ impl MemoryStorage { } impl SessionStorage for MemoryStorage { - fn load_session<'a>( + async fn load_session<'a>( &'a self, session_id: &'a str, - ) -> impl Future>>> + Send + 'a { - async move { - let inner = self.inner.lock(); - Ok(inner.sessions.get(session_id).cloned()) - } + ) -> Result>> { + let inner = self.inner.lock(); + Ok(inner.sessions.get(session_id).cloned()) } - fn update_session<'a>( + async fn update_session<'a>( &'a self, session_id: &'a str, entries: &'a BTreeMap, expires: Option, - ) -> impl Future> + Send + 'a { - async move { - let mut inner = self.inner.lock(); - inner.timeout_queue.remove(session_id); + ) -> Result<()> { + let mut inner = self.inner.lock(); + inner.timeout_queue.remove(session_id); + inner + .sessions + .insert(session_id.to_string(), entries.clone()); + if let Some(expires) = expires { inner - .sessions - .insert(session_id.to_string(), entries.clone()); - if let Some(expires) = expires { - inner - .timeout_queue - .push(session_id.to_string(), Reverse(Instant::now() + expires)); - } - Ok(()) + .timeout_queue + .push(session_id.to_string(), Reverse(Instant::now() + expires)); } + Ok(()) } - fn remove_session<'a>( - &'a self, - session_id: &'a str, - ) -> impl Future> + Send + 'a { - async move { - let mut inner = self.inner.lock(); - inner.sessions.remove(session_id); - inner.timeout_queue.remove(session_id); - Ok(()) - } + async fn remove_session<'a>(&'a self, session_id: &'a str) -> Result<()> { + let mut inner = self.inner.lock(); + inner.sessions.remove(session_id); + inner.timeout_queue.remove(session_id); + Ok(()) } } diff --git a/poem/src/session/redis_storage.rs b/poem/src/session/redis_storage.rs index 8f3694fb42..ab80962e7c 100644 --- a/poem/src/session/redis_storage.rs +++ b/poem/src/session/redis_storage.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeMap, future::Future, time::Duration}; +use std::{collections::BTreeMap, time::Duration}; use redis::{aio::ConnectionLike, Cmd}; use serde_json::Value; @@ -23,56 +23,47 @@ impl RedisStorage { } impl SessionStorage for RedisStorage { - fn load_session<'a>( + async fn load_session<'a>( &'a self, session_id: &'a str, - ) -> impl Future>>> + Send + 'a { - async move { - let data: Option = Cmd::get(session_id) - .query_async(&mut self.connection.clone()) - .await - .map_err(RedisSessionError::Redis)?; + ) -> Result>> { + let data: Option = Cmd::get(session_id) + .query_async(&mut self.connection.clone()) + .await + .map_err(RedisSessionError::Redis)?; - match data { - Some(data) => match serde_json::from_str::>(&data) { - Ok(entries) => Ok(Some(entries)), - Err(_) => Ok(None), - }, - None => Ok(None), - } + match data { + Some(data) => match serde_json::from_str::>(&data) { + Ok(entries) => Ok(Some(entries)), + Err(_) => Ok(None), + }, + None => Ok(None), } } - fn update_session<'a>( + async fn update_session<'a>( &'a self, session_id: &'a str, entries: &'a BTreeMap, expires: Option, - ) -> impl Future> + Send + 'a { - async move { - let value = serde_json::to_string(entries).unwrap_or_default(); - let cmd = match expires { - Some(expires) => Cmd::set_ex(session_id, value, expires.as_secs()), - None => Cmd::set(session_id, value), - }; - cmd.query_async(&mut self.connection.clone()) - .await - .map_err(RedisSessionError::Redis)?; - Ok(()) - } + ) -> Result<()> { + let value = serde_json::to_string(entries).unwrap_or_default(); + let cmd = match expires { + Some(expires) => Cmd::set_ex(session_id, value, expires.as_secs()), + None => Cmd::set(session_id, value), + }; + cmd.query_async(&mut self.connection.clone()) + .await + .map_err(RedisSessionError::Redis)?; + Ok(()) } - fn remove_session<'a>( - &'a self, - session_id: &'a str, - ) -> impl Future> + Send + 'a { - async move { - Cmd::del(session_id) - .query_async(&mut self.connection.clone()) - .await - .map_err(RedisSessionError::Redis)?; - Ok(()) - } + async fn remove_session<'a>(&'a self, session_id: &'a str) -> Result<()> { + Cmd::del(session_id) + .query_async(&mut self.connection.clone()) + .await + .map_err(RedisSessionError::Redis)?; + Ok(()) } } From 0279506c4a6eeefc88ba051df9a04d54988d0701 Mon Sep 17 00:00:00 2001 From: Sunli Date: Sun, 24 Mar 2024 09:49:36 +0800 Subject: [PATCH 09/24] openapi: add `Upload::size` method --- poem-openapi/src/auth/api_key.rs | 1 + poem-openapi/src/auth/basic.rs | 1 + poem-openapi/src/types/multipart/upload.rs | 12 +++++++++++- poem-openapi/tests/multipart.rs | 1 + poem-openapi/tests/security_scheme.rs | 6 +++++- 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/poem-openapi/src/auth/api_key.rs b/poem-openapi/src/auth/api_key.rs index f2a714bd27..90bcc36231 100644 --- a/poem-openapi/src/auth/api_key.rs +++ b/poem-openapi/src/auth/api_key.rs @@ -5,6 +5,7 @@ use crate::{ }; /// Used to extract the Api Key from the request. +#[derive(Debug)] pub struct ApiKey { /// Api key pub key: String, diff --git a/poem-openapi/src/auth/basic.rs b/poem-openapi/src/auth/basic.rs index a07cfe7c46..4089b5de77 100644 --- a/poem-openapi/src/auth/basic.rs +++ b/poem-openapi/src/auth/basic.rs @@ -6,6 +6,7 @@ use poem::{ use crate::{auth::BasicAuthorization, error::AuthorizationError}; /// Used to extract the username/password from the request. +#[derive(Debug)] pub struct Basic { /// username pub username: String, diff --git a/poem-openapi/src/types/multipart/upload.rs b/poem-openapi/src/types/multipart/upload.rs index 909e7e8bfb..eefa85de3d 100644 --- a/poem-openapi/src/types/multipart/upload.rs +++ b/poem-openapi/src/types/multipart/upload.rs @@ -19,6 +19,7 @@ pub struct Upload { file_name: Option, content_type: Option, file: File, + size: usize, } impl Debug for Upload { @@ -47,6 +48,12 @@ impl Upload { self.file_name.as_deref() } + /// Returns the file size in bytes. + #[inline] + pub fn size(&self) -> usize { + self.size + } + /// Consumes this body object to return a [`Vec`] that contains all /// data. pub async fn into_vec(self) -> Result, IoError> { @@ -104,10 +111,13 @@ impl ParseFromMultipartField for Upload { Some(field) => { let content_type = field.content_type().map(ToString::to_string); let file_name = field.file_name().map(ToString::to_string); + let file = field.tempfile().await.map_err(ParseError::custom)?; + let size = file.metadata().await.map_err(ParseError::custom)?.len() as usize; Ok(Self { content_type, file_name, - file: field.tempfile().await.map_err(ParseError::custom)?, + file, + size, }) } None => Err(ParseError::expected_input()), diff --git a/poem-openapi/tests/multipart.rs b/poem-openapi/tests/multipart.rs index 3066687dbf..5a1e9009cf 100644 --- a/poem-openapi/tests/multipart.rs +++ b/poem-openapi/tests/multipart.rs @@ -235,6 +235,7 @@ async fn upload() { assert_eq!(a.file.file_name(), Some("1.txt")); assert_eq!(a.file.content_type(), None); + assert_eq!(a.file.size(), 3); assert_eq!(a.file.into_vec().await.unwrap(), vec![1, 2, 3]); } diff --git a/poem-openapi/tests/security_scheme.rs b/poem-openapi/tests/security_scheme.rs index 7d3470da97..5e30984e1a 100644 --- a/poem-openapi/tests/security_scheme.rs +++ b/poem-openapi/tests/security_scheme.rs @@ -3,7 +3,7 @@ use poem::{ http::{header, StatusCode}, test::TestClient, web::{cookie::Cookie, headers}, - Request, + Error, Request, }; use poem_openapi::{ auth::{ApiKey, Basic, Bearer}, @@ -18,6 +18,7 @@ use crate::headers::Authorization; fn rename() { #[derive(SecurityScheme)] #[oai(rename = "ABC", ty = "basic")] + #[allow(dead_code)] struct MySecurityScheme(Basic); assert_eq!(MySecurityScheme::security_schemes(), &["ABC"]); @@ -27,6 +28,7 @@ fn rename() { fn default_rename() { #[derive(SecurityScheme)] #[oai(ty = "basic")] + #[allow(dead_code)] struct MySecurityScheme(Basic); assert_eq!(MySecurityScheme::security_schemes(), &["MySecurityScheme"]); @@ -39,6 +41,7 @@ fn desc() { /// D #[derive(SecurityScheme)] #[oai(ty = "basic")] + #[allow(dead_code)] struct MySecurityScheme(Basic); let mut registry = Registry::new(); @@ -382,6 +385,7 @@ async fn oauth2_auth() { ), ) )] + #[allow(dead_code)] struct MySecurityScheme(Bearer); let mut registry = Registry::new(); From 869d7c0e1bd46bf7cd12b4c159911132b8476653 Mon Sep 17 00:00:00 2001 From: Sunli Date: Sun, 24 Mar 2024 10:20:22 +0800 Subject: [PATCH 10/24] openapi: when `Union` uses discriminator, if the members is not an `Object`, an error will be reported at compile time. #755 --- Cargo.toml | 2 ++ poem-openapi-derive/Cargo.toml | 2 +- poem-openapi-derive/src/object.rs | 2 ++ poem-openapi-derive/src/union.rs | 2 ++ poem-openapi/Cargo.toml | 1 + poem-openapi/src/lib.rs | 1 + poem-openapi/src/types/mod.rs | 3 +++ poem-openapi/tests/security_scheme.rs | 2 +- 8 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b57c64a0c2..277a642b56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,8 @@ base64 = "0.21.0" serde_urlencoded = "0.7.1" indexmap = "2.0.0" reqwest = { version = "0.11.23", default-features = false } +darling = "0.20.8" +static_assertions = "1.1.0" # rustls, update together rustls = "0.22.0" diff --git a/poem-openapi-derive/Cargo.toml b/poem-openapi-derive/Cargo.toml index af3dde9597..96c7123c99 100644 --- a/poem-openapi-derive/Cargo.toml +++ b/poem-openapi-derive/Cargo.toml @@ -16,7 +16,7 @@ categories = ["network-programming", "asynchronous"] proc-macro = true [dependencies] -darling = "0.20.1" +darling.workspace = true proc-macro-crate.workspace = true proc-macro2.workspace = true quote.workspace = true diff --git a/poem-openapi-derive/src/object.rs b/poem-openapi-derive/src/object.rs index d3ff889a7c..20f54236fb 100644 --- a/poem-openapi-derive/src/object.rs +++ b/poem-openapi-derive/src/object.rs @@ -412,6 +412,8 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult { } } + impl #impl_generics #crate_name::types::IsObjectType for #ident #ty_generics #where_clause {} + impl #impl_generics #crate_name::types::ParseFromJSON for #ident #ty_generics #where_clause { fn parse_from_json(value: ::std::option::Option<#crate_name::__private::serde_json::Value>) -> ::std::result::Result> { let value = value.unwrap_or_default(); diff --git a/poem-openapi-derive/src/union.rs b/poem-openapi-derive/src/union.rs index 69e04b6492..0b6d74cd7c 100644 --- a/poem-openapi-derive/src/union.rs +++ b/poem-openapi-derive/src/union.rs @@ -145,6 +145,7 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult { if let Some(discriminator_name) = &args.discriminator_name { create_schemas.push(quote! { + #crate_name::__private::static_assertions::assert_impl_all!(#object_ty: #crate_name::types::IsObjectType); let schema = #crate_name::registry::MetaSchema { all_of: ::std::vec![ #crate_name::registry::MetaSchemaRef::Inline(::std::boxed::Box::new(#crate_name::registry::MetaSchema { @@ -268,6 +269,7 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult { } fn register(registry: &mut #crate_name::registry::Registry) { + use ::std::marker::Sized; registry.create_schema::(::name().into_owned(), |registry| { #(<#types as #crate_name::types::Type>::register(registry);)* #(#create_schemas)* diff --git a/poem-openapi/Cargo.toml b/poem-openapi/Cargo.toml index 4443da139c..6c35e78713 100644 --- a/poem-openapi/Cargo.toml +++ b/poem-openapi/Cargo.toml @@ -50,6 +50,7 @@ thiserror.workspace = true bytes.workspace = true futures-util.workspace = true indexmap.workspace = true +static_assertions.workspace = true # Non-feature optional dependencies email_address = { version = "0.2.1", optional = true } diff --git a/poem-openapi/src/lib.rs b/poem-openapi/src/lib.rs index 5ee4707919..5ffe6d3363 100644 --- a/poem-openapi/src/lib.rs +++ b/poem-openapi/src/lib.rs @@ -189,6 +189,7 @@ pub mod __private { pub use poem; pub use serde; pub use serde_json; + pub use static_assertions; pub use crate::{auth::CheckerReturn, base::UrlQuery, path_util::join_path}; } diff --git a/poem-openapi/src/types/mod.rs b/poem-openapi/src/types/mod.rs index a69f894ee2..07e9834866 100644 --- a/poem-openapi/src/types/mod.rs +++ b/poem-openapi/src/types/mod.rs @@ -82,6 +82,9 @@ pub trait Type: Send + Sync { } } +/// Represents a object type. +pub trait IsObjectType: Type {} + /// Represents a type that can parsing from JSON. pub trait ParseFromJSON: Sized + Type { /// Parse from [`serde_json::Value`]. diff --git a/poem-openapi/tests/security_scheme.rs b/poem-openapi/tests/security_scheme.rs index 5e30984e1a..71f6f78d34 100644 --- a/poem-openapi/tests/security_scheme.rs +++ b/poem-openapi/tests/security_scheme.rs @@ -3,7 +3,7 @@ use poem::{ http::{header, StatusCode}, test::TestClient, web::{cookie::Cookie, headers}, - Error, Request, + Request, }; use poem_openapi::{ auth::{ApiKey, Basic, Bearer}, From c33301b0290ddd077a4a04cf1a30c3aeb0ace50c Mon Sep 17 00:00:00 2001 From: Sunli Date: Sun, 24 Mar 2024 10:30:00 +0800 Subject: [PATCH 11/24] update CHANGELOG --- poem-lambda/CHANGELOG.md | 3 ++- poem-openapi/CHANGELOG.md | 6 ++++++ poem/CHANGELOG.md | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/poem-lambda/CHANGELOG.md b/poem-lambda/CHANGELOG.md index e8fe601d99..71db916474 100644 --- a/poem-lambda/CHANGELOG.md +++ b/poem-lambda/CHANGELOG.md @@ -4,8 +4,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -# [4.0.1] unreleased +# [5.0.0] 2024-03-24 +- use AFIT instead of `async_trait` - Bump `lambda_http` from `0.9` to `0.10` # [1.3.47] 2022-10-19 diff --git a/poem-openapi/CHANGELOG.md b/poem-openapi/CHANGELOG.md index ee6d78d41e..54503aebfc 100644 --- a/poem-openapi/CHANGELOG.md +++ b/poem-openapi/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# [5.0.0] 2024-03-24 + +- use AFIT instead of `async_trait` +- add `Upload::size` method +- when `Union` uses discriminator, if the members is not an `Object`, an error will be reported at compile time + # [4.0.1] 2024-03-04 - added example value support for param/schema [#717](https://github.com/poem-web/poem/pull/717) diff --git a/poem/CHANGELOG.md b/poem/CHANGELOG.md index 8fffebcf06..ccc96b278a 100644 --- a/poem/CHANGELOG.md +++ b/poem/CHANGELOG.md @@ -4,8 +4,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -# [unrealsed] +# [3.0.0] 2024-03-24 +- use AFIT instead of `async_trait` - bump `priority-queue` to 2.0 - bump `x509-parser` to 0.16 - bump `nix` to 0.28 From 10a000f6e6bbc8ccf1679ecbd6d999f316d3cc42 Mon Sep 17 00:00:00 2001 From: Yiyu Lin Date: Tue, 26 Mar 2024 14:42:41 +0800 Subject: [PATCH 12/24] build(deps): update redis requirement from 0.24.0 to 0.25.2 (#781) * build(deps): update redis requirement from 0.24.0 to 0.25.2 * changelog --------- Co-authored-by: hzlinyiyu --- examples/poem/redis-session/Cargo.toml | 2 +- poem/CHANGELOG.md | 1 + poem/Cargo.toml | 2 +- poem/src/middleware/set_header.rs | 2 -- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/poem/redis-session/Cargo.toml b/examples/poem/redis-session/Cargo.toml index 0c39f07e1f..a188a4440e 100644 --- a/examples/poem/redis-session/Cargo.toml +++ b/examples/poem/redis-session/Cargo.toml @@ -8,4 +8,4 @@ publish.workspace = true poem = { workspace = true, features = ["redis-session"] } tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } tracing-subscriber.workspace = true -redis = { version = "0.24.0", features = ["aio", "tokio-comp", "connection-manager"] } +redis = { version = "0.25.2", features = ["aio", "tokio-comp", "connection-manager"] } diff --git a/poem/CHANGELOG.md b/poem/CHANGELOG.md index ccc96b278a..4d9f2d2abe 100644 --- a/poem/CHANGELOG.md +++ b/poem/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - bump `priority-queue` to 2.0 - bump `x509-parser` to 0.16 - bump `nix` to 0.28 +- bump `redis` to 0.25 # [2.0.1] 2024-03-04 diff --git a/poem/Cargo.toml b/poem/Cargo.toml index f4684d7707..3868fed3ea 100644 --- a/poem/Cargo.toml +++ b/poem/Cargo.toml @@ -116,7 +116,7 @@ chrono = { workspace = true, optional = true, default-features = false, features time = { version = "0.3", optional = true } mime_guess = { version = "2.0.3", optional = true } rand = { version = "0.8.4", optional = true } -redis = { version = "0.24.0", optional = true, features = [ +redis = { version = "0.25.2", optional = true, features = [ "aio", "tokio-comp", "connection-manager", diff --git a/poem/src/middleware/set_header.rs b/poem/src/middleware/set_header.rs index d6aca5f8d3..31142a5022 100644 --- a/poem/src/middleware/set_header.rs +++ b/poem/src/middleware/set_header.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use crate::{ http::{header::HeaderName, HeaderValue}, Endpoint, IntoResponse, Middleware, Request, Response, Result, From a08032eff4dfd0c1bcfadb78bead5ea5b1e1d7f1 Mon Sep 17 00:00:00 2001 From: Yiyu Lin Date: Tue, 26 Mar 2024 16:03:27 +0800 Subject: [PATCH 13/24] build(deps): update reqwest requirement from 0.11 to 0.12 (#782) * build(deps): update reqwest requirement from 0.11 to 0.12 * fix clippy --------- Co-authored-by: hzlinyiyu --- Cargo.toml | 2 +- examples/disabled/auth-github/Cargo.toml | 2 +- examples/poem/opentelemetry-jaeger/Cargo.toml | 2 +- poem/CHANGELOG.md | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 277a642b56..dc130925a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ quick-xml = { version = "0.31.0", features = ["serialize"] } base64 = "0.21.0" serde_urlencoded = "0.7.1" indexmap = "2.0.0" -reqwest = { version = "0.11.23", default-features = false } +reqwest = { version = "0.12.2", default-features = false } darling = "0.20.8" static_assertions = "1.1.0" diff --git a/examples/disabled/auth-github/Cargo.toml b/examples/disabled/auth-github/Cargo.toml index e4be54c698..a757baf816 100644 --- a/examples/disabled/auth-github/Cargo.toml +++ b/examples/disabled/auth-github/Cargo.toml @@ -8,7 +8,7 @@ publish.workspace = true poem.workspace = true poem-openapi.workspace = true tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } -reqwest = { version = "0.11.4", default-features = false, features = [ +reqwest = { version = "0.12.2", default-features = false, features = [ "rustls-tls", ] } tracing-subscriber.workspace = true diff --git a/examples/poem/opentelemetry-jaeger/Cargo.toml b/examples/poem/opentelemetry-jaeger/Cargo.toml index b40ef52114..5e69c09a8c 100644 --- a/examples/poem/opentelemetry-jaeger/Cargo.toml +++ b/examples/poem/opentelemetry-jaeger/Cargo.toml @@ -14,7 +14,7 @@ opentelemetry-http = { version = "0.10.0" } opentelemetry-otlp = { version = "0.14.0", features = [ "trace", ] } -reqwest = "0.11.6" +reqwest = "0.11" [[bin]] name = "example-opentelemetry-client" diff --git a/poem/CHANGELOG.md b/poem/CHANGELOG.md index 4d9f2d2abe..971ac96d0e 100644 --- a/poem/CHANGELOG.md +++ b/poem/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - bump `x509-parser` to 0.16 - bump `nix` to 0.28 - bump `redis` to 0.25 +- bump `reqwest` to 0.12 # [2.0.1] 2024-03-04 From 6484545b09291b281baafef11b743b224d196515 Mon Sep 17 00:00:00 2001 From: Artem Medvedev Date: Wed, 27 Mar 2024 10:23:24 +0100 Subject: [PATCH 14/24] fix: graceful shutdown after switch to hyper `1.x` (#779) For some reason it was removed after switch to `hyper 1.x` ([commit](https://github.com/poem-web/poem/commit/0b6ca89be9636472b25f3677dc957fe098f72fab))
Patch affected the behavior ```patch - let mut conn = Http::new() - .serve_connection(socket, service) - .with_upgrades(); + let builder = auto::Builder::new(hyper_util::rt::TokioExecutor::new()); + let conn = + builder.serve_connection_with_upgrades(hyper_util::rt::TokioIo::new(socket), service); + futures_util::pin_mut!(conn); tokio::select! { - _ = &mut conn => { + _ = conn => { // Connection completed successfully. return; }, @@ -366,10 +368,4 @@ } _ = server_graceful_shutdown_token.cancelled() => {} } - - // Init graceful shutdown for connection (`GOAWAY` for `HTTP/2` or disabling `keep-alive` for `HTTP/1`) - Pin::new(&mut conn).graceful_shutdown(); - - // Continue awaiting after graceful-shutdown is initiated to handle existed requests. - let _ = conn.await; ```
--- poem/src/server.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/poem/src/server.rs b/poem/src/server.rs index 6da5be1e8a..1460481bfc 100644 --- a/poem/src/server.rs +++ b/poem/src/server.rs @@ -360,7 +360,7 @@ async fn serve_connection( futures_util::pin_mut!(conn); tokio::select! { - _ = conn => { + _ = &mut conn => { // Connection completed successfully. }, _ = connection_shutdown_token.cancelled() => { @@ -368,4 +368,9 @@ async fn serve_connection( } _ = server_graceful_shutdown_token.cancelled() => {} } + + // Init graceful shutdown for connection + conn.as_mut().graceful_shutdown(); + // Continue awaiting after graceful-shutdown is initiated to handle existed requests. + let _ = conn.await; } From ed0b0777d6e58fb4555235b86691dbe64d475511 Mon Sep 17 00:00:00 2001 From: Miikka Koskinen Date: Wed, 27 Mar 2024 11:23:46 +0200 Subject: [PATCH 15/24] update MSRV to 1.75.0 in Cargo.toml (#783) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index dc130925a3..0fe5324bfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ license = "MIT OR Apache-2.0" documentation = "https://docs.rs/poem/" homepage = "https://github.com/poem-web/poem" repository = "https://github.com/poem-web/poem" -rust-version = "1.74" +rust-version = "1.75" [workspace.dependencies] poem = { path = "poem", version = "2.0.1", default-features = false } From accbb3e0678c1e91e0db8b03ac783b7d230c1a6d Mon Sep 17 00:00:00 2001 From: Miikka Koskinen Date: Wed, 27 Mar 2024 12:24:01 +0200 Subject: [PATCH 16/24] update opentelemetry to v0.22.x (#784) * update opentelemetry to v0.22.x * update examples to opentelemetry to v0.22.x * update CHANGELOG.md --- examples/poem/opentelemetry-jaeger/Cargo.toml | 8 +-- poem/CHANGELOG.md | 1 + poem/Cargo.toml | 8 +-- poem/src/middleware/opentelemetry_metrics.rs | 28 +++++++--- poem/src/middleware/opentelemetry_tracing.rs | 56 ++++++++++++------- 5 files changed, 66 insertions(+), 35 deletions(-) diff --git a/examples/poem/opentelemetry-jaeger/Cargo.toml b/examples/poem/opentelemetry-jaeger/Cargo.toml index 5e69c09a8c..b2a79ec3b8 100644 --- a/examples/poem/opentelemetry-jaeger/Cargo.toml +++ b/examples/poem/opentelemetry-jaeger/Cargo.toml @@ -8,10 +8,10 @@ publish.workspace = true poem = { workspace = true, features = ["opentelemetry"] } tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } tracing-subscriber.workspace = true -opentelemetry = { version = "0.21.0", features = ["metrics"] } -opentelemetry_sdk = { version = "0.21.0", features = ["rt-tokio"] } -opentelemetry-http = { version = "0.10.0" } -opentelemetry-otlp = { version = "0.14.0", features = [ +opentelemetry = { version = "0.22.0", features = ["metrics"] } +opentelemetry_sdk = { version = "0.22.0", features = ["rt-tokio"] } +opentelemetry-http = { version = "0.11.0" } +opentelemetry-otlp = { version = "0.15.0", features = [ "trace", ] } reqwest = "0.11" diff --git a/poem/CHANGELOG.md b/poem/CHANGELOG.md index 971ac96d0e..2dd2a2dd23 100644 --- a/poem/CHANGELOG.md +++ b/poem/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - bump `nix` to 0.28 - bump `redis` to 0.25 - bump `reqwest` to 0.12 +- bump `opentelemetry` to 0.22 [#784](https://github.com/poem-web/poem/pull/784) # [2.0.1] 2024-03-04 diff --git a/poem/Cargo.toml b/poem/Cargo.toml index 3868fed3ea..2a4f7b1b31 100644 --- a/poem/Cargo.toml +++ b/poem/Cargo.toml @@ -128,11 +128,11 @@ libcookie = { package = "cookie", version = "0.18", features = [ "key-expansion", "secure", ], optional = true } -opentelemetry-http = { version = "0.10.0", optional = true } -opentelemetry-semantic-conventions = { version = "0.13.0", optional = true } -opentelemetry-prometheus = { version = "0.14.0", optional = true } +opentelemetry-http = { version = "0.11.0", optional = true } +opentelemetry-semantic-conventions = { version = "0.14.0", optional = true } +opentelemetry-prometheus = { version = "0.15.0", optional = true } libprometheus = { package = "prometheus", version = "0.13.0", optional = true } -libopentelemetry = { package = "opentelemetry", version = "0.21.0", features = [ +libopentelemetry = { package = "opentelemetry", version = "0.22.0", features = [ "metrics", ], optional = true } libtempfile = { package = "tempfile", version = "3.2.0", optional = true } diff --git a/poem/src/middleware/opentelemetry_metrics.rs b/poem/src/middleware/opentelemetry_metrics.rs index 02d2cb7d17..d5bbc4635e 100644 --- a/poem/src/middleware/opentelemetry_metrics.rs +++ b/poem/src/middleware/opentelemetry_metrics.rs @@ -3,7 +3,7 @@ use std::time::Instant; use libopentelemetry::{ global, metrics::{Counter, Histogram, Unit}, - Key, + Key, KeyValue, }; use opentelemetry_semantic_conventions::trace; @@ -74,8 +74,14 @@ impl Endpoint for OpenTelemetryMetricsEndpoint { async fn call(&self, req: Request) -> Result { let mut labels = Vec::with_capacity(3); - labels.push(trace::HTTP_REQUEST_METHOD.string(req.method().to_string())); - labels.push(trace::URL_FULL.string(req.original_uri().to_string())); + labels.push(KeyValue::new( + trace::HTTP_REQUEST_METHOD, + req.method().to_string(), + )); + labels.push(KeyValue::new( + trace::URL_FULL, + req.original_uri().to_string(), + )); let s = Instant::now(); let res = self.inner.call(req).await.map(IntoResponse::into_response); @@ -85,20 +91,26 @@ impl Endpoint for OpenTelemetryMetricsEndpoint { Ok(resp) => { if let Some(path_pattern) = resp.data::() { const HTTP_PATH_PATTERN: Key = Key::from_static_str("http.path_pattern"); - labels.push(HTTP_PATH_PATTERN.string(path_pattern.0.to_string())); + labels.push(KeyValue::new(HTTP_PATH_PATTERN, path_pattern.0.to_string())); } - labels.push(trace::HTTP_RESPONSE_STATUS_CODE.i64(resp.status().as_u16() as i64)); + labels.push(KeyValue::new( + trace::HTTP_RESPONSE_STATUS_CODE, + resp.status().as_u16() as i64, + )); } Err(err) => { if let Some(path_pattern) = err.data::() { const HTTP_PATH_PATTERN: Key = Key::from_static_str("http.path_pattern"); - labels.push(HTTP_PATH_PATTERN.string(path_pattern.0.to_string())); + labels.push(KeyValue::new(HTTP_PATH_PATTERN, path_pattern.0.to_string())); } - labels.push(trace::HTTP_RESPONSE_STATUS_CODE.i64(err.status().as_u16() as i64)); + labels.push(KeyValue::new( + trace::HTTP_RESPONSE_STATUS_CODE, + err.status().as_u16() as i64, + )); self.error_count.add(1, &labels); - labels.push(trace::EXCEPTION_MESSAGE.string(err.to_string())); + labels.push(KeyValue::new(trace::EXCEPTION_MESSAGE, err.to_string())); } } diff --git a/poem/src/middleware/opentelemetry_tracing.rs b/poem/src/middleware/opentelemetry_tracing.rs index a171358851..9a17c0ae19 100644 --- a/poem/src/middleware/opentelemetry_tracing.rs +++ b/poem/src/middleware/opentelemetry_tracing.rs @@ -4,7 +4,7 @@ use libopentelemetry::{ global, propagation::Extractor, trace::{FutureExt, Span, SpanKind, TraceContextExt, Tracer}, - Context, Key, + Context, Key, KeyValue, }; use opentelemetry_semantic_conventions::{resource, trace}; @@ -88,17 +88,32 @@ where }); let mut attributes = Vec::new(); - attributes.push(resource::TELEMETRY_SDK_NAME.string(env!("CARGO_CRATE_NAME"))); - attributes.push(resource::TELEMETRY_SDK_VERSION.string(env!("CARGO_PKG_VERSION"))); - attributes.push(resource::TELEMETRY_SDK_LANGUAGE.string("rust")); - attributes.push(trace::HTTP_REQUEST_METHOD.string(req.method().to_string())); - attributes.push(trace::URL_FULL.string(req.original_uri().to_string())); - attributes.push(trace::CLIENT_ADDRESS.string(remote_addr)); - attributes.push(trace::NETWORK_PROTOCOL_VERSION.string(format!("{:?}", req.version()))); + attributes.push(KeyValue::new( + resource::TELEMETRY_SDK_NAME, + env!("CARGO_CRATE_NAME"), + )); + attributes.push(KeyValue::new( + resource::TELEMETRY_SDK_VERSION, + env!("CARGO_PKG_VERSION"), + )); + attributes.push(KeyValue::new(resource::TELEMETRY_SDK_LANGUAGE, "rust")); + attributes.push(KeyValue::new( + trace::HTTP_REQUEST_METHOD, + req.method().to_string(), + )); + attributes.push(KeyValue::new( + trace::URL_FULL, + req.original_uri().to_string(), + )); + attributes.push(KeyValue::new(trace::CLIENT_ADDRESS, remote_addr)); + attributes.push(KeyValue::new( + trace::NETWORK_PROTOCOL_VERSION, + format!("{:?}", req.version()), + )); if let Some(path_pattern) = req.data::() { const HTTP_PATH_PATTERN: Key = Key::from_static_str("http.path_pattern"); - attributes.push(HTTP_PATH_PATTERN.string(path_pattern.0.to_string())); + attributes.push(KeyValue::new(HTTP_PATH_PATTERN, path_pattern.0.to_string())); } let mut span = self @@ -119,25 +134,28 @@ where Ok(resp) => { let resp = resp.into_response(); span.add_event("request.completed".to_string(), vec![]); - span.set_attribute( - trace::HTTP_RESPONSE_STATUS_CODE.i64(resp.status().as_u16() as i64), - ); + span.set_attribute(KeyValue::new( + trace::HTTP_RESPONSE_STATUS_CODE, + resp.status().as_u16() as i64, + )); if let Some(content_length) = resp.headers().typed_get::() { - span.set_attribute( - trace::HTTP_RESPONSE_BODY_SIZE.i64(content_length.0 as i64), - ); + span.set_attribute(KeyValue::new( + trace::HTTP_RESPONSE_BODY_SIZE, + content_length.0 as i64, + )); } Ok(resp) } Err(err) => { - span.set_attribute( - trace::HTTP_RESPONSE_STATUS_CODE.i64(err.status().as_u16() as i64), - ); + span.set_attribute(KeyValue::new( + trace::HTTP_RESPONSE_STATUS_CODE, + err.status().as_u16() as i64, + )); span.add_event( "request.error".to_string(), - vec![trace::EXCEPTION_MESSAGE.string(err.to_string())], + vec![KeyValue::new(trace::EXCEPTION_MESSAGE, err.to_string())], ); Err(err) } From f6611bae64bb9071f6a291855d973ea879e7969f Mon Sep 17 00:00:00 2001 From: Sunli Date: Sat, 30 Mar 2024 10:33:17 +0800 Subject: [PATCH 17/24] use AFIT instead of `async_trait` 5 --- examples/poem/custom-extractor/src/main.rs | 1 - poem-lambda/src/lib.rs | 1 - poem-openapi-derive/src/multipart.rs | 1 - poem-openapi-derive/src/request.rs | 1 - poem-openapi-derive/src/response_content.rs | 1 - poem-openapi-derive/src/security_scheme.rs | 2 - poem-openapi-derive/src/webhook.rs | 7 ---- poem-openapi/src/base.rs | 12 +++--- poem-openapi/src/macros.rs | 1 - poem-openapi/src/param/cookie.rs | 3 -- poem-openapi/src/param/header.rs | 1 - poem-openapi/src/param/path.rs | 1 - poem-openapi/src/param/query.rs | 1 - poem-openapi/src/payload/form.rs | 2 +- poem-openapi/src/payload/json.rs | 2 +- poem-openapi/src/payload/xml.rs | 2 +- poem-openapi/src/payload/yaml.rs | 2 +- poem-openapi/tests/webhook.rs | 30 +++++++-------- poem/Cargo.toml | 1 - poem/src/i18n/locale.rs | 1 - poem/src/lib.rs | 1 - poem/src/session/session.rs | 1 - poem/src/web/accept.rs | 1 - poem/src/web/cookie.rs | 2 - poem/src/web/csrf.rs | 2 - poem/src/web/data.rs | 1 - poem/src/web/form.rs | 4 +- poem/src/web/json.rs | 4 +- poem/src/web/mod.rs | 41 ++++++++++----------- poem/src/web/multipart.rs | 1 - poem/src/web/path/mod.rs | 1 - poem/src/web/query.rs | 1 - poem/src/web/real_ip.rs | 1 - poem/src/web/static_file.rs | 1 - poem/src/web/tempfile.rs | 1 - poem/src/web/typed_header.rs | 1 - poem/src/web/websocket/extractor.rs | 1 - poem/src/web/xml.rs | 4 +- poem/src/web/yaml.rs | 1 - 39 files changed, 52 insertions(+), 91 deletions(-) diff --git a/examples/poem/custom-extractor/src/main.rs b/examples/poem/custom-extractor/src/main.rs index fee3290f87..1fb495af74 100644 --- a/examples/poem/custom-extractor/src/main.rs +++ b/examples/poem/custom-extractor/src/main.rs @@ -6,7 +6,6 @@ use poem::{ struct Token(String); // Implements a token extractor -#[poem::async_trait] impl<'a> FromRequest<'a> for Token { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { let token = req diff --git a/poem-lambda/src/lib.rs b/poem-lambda/src/lib.rs index 4262d78a29..77c52dcba3 100644 --- a/poem-lambda/src/lib.rs +++ b/poem-lambda/src/lib.rs @@ -110,7 +110,6 @@ fn from_lambda_request(req: LambdaRequest) -> Request { req } -#[poem::async_trait] impl<'a> FromRequest<'a> for &'a Context { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { let ctx = match req.extensions().get::() { diff --git a/poem-openapi-derive/src/multipart.rs b/poem-openapi-derive/src/multipart.rs index 112a75cbbd..0c7f6b77c0 100644 --- a/poem-openapi-derive/src/multipart.rs +++ b/poem-openapi-derive/src/multipart.rs @@ -258,7 +258,6 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult { } } - #[#crate_name::__private::poem::async_trait] impl #extractor_impl_generics #crate_name::ApiExtractor<'__request> for #ident #ty_generics #where_clause { const TYPES: &'static [#crate_name::ApiExtractorType] = &[#crate_name::ApiExtractorType::RequestObject]; diff --git a/poem-openapi-derive/src/request.rs b/poem-openapi-derive/src/request.rs index 05168c419e..5c9b91e773 100644 --- a/poem-openapi-derive/src/request.rs +++ b/poem-openapi-derive/src/request.rs @@ -115,7 +115,6 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult { let expanded = { quote! { - #[#crate_name::__private::poem::async_trait] impl #impl_generics #crate_name::ApiExtractor<'__request> for #ident #ty_generics #where_clause { const TYPES: &'static [#crate_name::ApiExtractorType] = &[#crate_name::ApiExtractorType::RequestObject]; diff --git a/poem-openapi-derive/src/response_content.rs b/poem-openapi-derive/src/response_content.rs index 5bac8e1430..712116b274 100644 --- a/poem-openapi-derive/src/response_content.rs +++ b/poem-openapi-derive/src/response_content.rs @@ -122,7 +122,6 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult { let expanded = { quote! { - #[#crate_name::__private::poem::async_trait] impl #impl_generics #crate_name::ResponseContent for #ident #ty_generics #where_clause { fn media_types() -> Vec<#crate_name::registry::MetaMediaType> { ::std::vec![#(#content),*] diff --git a/poem-openapi-derive/src/security_scheme.rs b/poem-openapi-derive/src/security_scheme.rs index f2d095ed3f..e7b4298c4e 100644 --- a/poem-openapi-derive/src/security_scheme.rs +++ b/poem-openapi-derive/src/security_scheme.rs @@ -460,7 +460,6 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult { }; let expanded = quote! { - #[#crate_name::__private::poem::async_trait] impl<'a> #crate_name::ApiExtractor<'a> for #ident { const TYPES: &'static [#crate_name::ApiExtractorType] = &[#crate_name::ApiExtractorType::SecurityScheme]; @@ -545,7 +544,6 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult { }; let expanded = quote! { - #[#crate_name::__private::poem::async_trait] impl<'a> #crate_name::ApiExtractor<'a> for #ident { const TYPES: &'static [#crate_name::ApiExtractorType] = &[#crate_name::ApiExtractorType::SecurityScheme]; diff --git a/poem-openapi-derive/src/webhook.rs b/poem-openapi-derive/src/webhook.rs index 5fe98ccee5..6961ddde01 100644 --- a/poem-openapi-derive/src/webhook.rs +++ b/poem-openapi-derive/src/webhook.rs @@ -78,12 +78,6 @@ pub(crate) fn generate( for item in &mut trait_impl.items { if let TraitItem::Fn(method) = item { if let Some(operation_args) = parse_oai_attrs::(&method.attrs)? { - if method.sig.asyncness.is_none() { - return Err( - Error::new_spanned(&method.sig.ident, "Must be asynchronous").into(), - ); - } - generate_operation(&mut ctx, &crate_name, &args, operation_args, method)?; remove_oai_attrs(&mut method.attrs); } @@ -99,7 +93,6 @@ pub(crate) fn generate( let operations = operations.values(); let expanded = quote! { - #[#crate_name::__private::poem::async_trait] #trait_impl impl #crate_name::Webhook for &dyn #ident { diff --git a/poem-openapi/src/base.rs b/poem-openapi/src/base.rs index 85e225e2ea..b23f8f5749 100644 --- a/poem-openapi/src/base.rs +++ b/poem-openapi/src/base.rs @@ -1,9 +1,11 @@ use std::{ collections::HashMap, fmt::{self, Debug, Display}, + future::Future, ops::Deref, }; +use futures_util::FutureExt; use poem::{endpoint::BoxEndpoint, http::Method, Error, FromRequest, Request, RequestBody, Result}; use crate::{ @@ -152,7 +154,6 @@ impl Default for ExtractParamOptions { /// - **T: poem::FromRequest** /// /// Use Poem's extractor. -#[poem::async_trait] #[allow(unused_variables)] pub trait ApiExtractor<'a>: Sized { /// The type of API extractor. @@ -196,14 +197,13 @@ pub trait ApiExtractor<'a>: Sized { } /// Parse from the HTTP request. - async fn from_request( + fn from_request( request: &'a Request, body: &mut RequestBody, param_opts: ExtractParamOptions, - ) -> Result; + ) -> impl Future> + Send; } -#[poem::async_trait] impl<'a, T: FromRequest<'a>> ApiExtractor<'a> for T { const TYPES: &'static [ApiExtractorType] = &[ApiExtractorType::PoemExtractor]; @@ -215,7 +215,9 @@ impl<'a, T: FromRequest<'a>> ApiExtractor<'a> for T { body: &mut RequestBody, _param_opts: ExtractParamOptions, ) -> Result { - T::from_request(request, body).await + // FIXME: remove the unnecessary boxed + // https://github.com/rust-lang/rust/issues/100013 + T::from_request(request, body).boxed().await } } diff --git a/poem-openapi/src/macros.rs b/poem-openapi/src/macros.rs index 96bb3643cc..abc69c8c9c 100644 --- a/poem-openapi/src/macros.rs +++ b/poem-openapi/src/macros.rs @@ -7,7 +7,6 @@ macro_rules! impl_apirequest_for_payload { }; ($ty:ty, $($bounds:tt)*) => { - #[poem::async_trait] impl<'a, $($bounds)*> $crate::ApiExtractor<'a> for $ty { const TYPES: &'static [$crate::ApiExtractorType] = &[$crate::ApiExtractorType::RequestObject]; diff --git a/poem-openapi/src/param/cookie.rs b/poem-openapi/src/param/cookie.rs index 7b5c2a2211..696b124b99 100644 --- a/poem-openapi/src/param/cookie.rs +++ b/poem-openapi/src/param/cookie.rs @@ -26,7 +26,6 @@ impl DerefMut for Cookie { } } -#[poem::async_trait] impl<'a, T: ParseFromParameter> ApiExtractor<'a> for Cookie { const TYPES: &'static [ApiExtractorType] = &[ApiExtractorType::Parameter]; const PARAM_IS_REQUIRED: bool = T::IS_REQUIRED; @@ -95,7 +94,6 @@ impl DerefMut for CookiePrivate { } } -#[poem::async_trait] impl<'a, T: ParseFromParameter> ApiExtractor<'a> for CookiePrivate { const TYPES: &'static [ApiExtractorType] = &[ApiExtractorType::Parameter]; const PARAM_IS_REQUIRED: bool = T::IS_REQUIRED; @@ -165,7 +163,6 @@ impl DerefMut for CookieSigned { } } -#[poem::async_trait] impl<'a, T: ParseFromParameter> ApiExtractor<'a> for CookieSigned { const TYPES: &'static [ApiExtractorType] = &[ApiExtractorType::Parameter]; const PARAM_IS_REQUIRED: bool = T::IS_REQUIRED; diff --git a/poem-openapi/src/param/header.rs b/poem-openapi/src/param/header.rs index 3e772913f2..dbdeb01888 100644 --- a/poem-openapi/src/param/header.rs +++ b/poem-openapi/src/param/header.rs @@ -26,7 +26,6 @@ impl DerefMut for Header { } } -#[poem::async_trait] impl<'a, T: ParseFromParameter> ApiExtractor<'a> for Header { const TYPES: &'static [ApiExtractorType] = &[ApiExtractorType::Parameter]; const PARAM_IS_REQUIRED: bool = T::IS_REQUIRED; diff --git a/poem-openapi/src/param/path.rs b/poem-openapi/src/param/path.rs index fd06f99208..529b9afe9a 100644 --- a/poem-openapi/src/param/path.rs +++ b/poem-openapi/src/param/path.rs @@ -26,7 +26,6 @@ impl DerefMut for Path { } } -#[poem::async_trait] impl<'a, T: ParseFromParameter> ApiExtractor<'a> for Path { const TYPES: &'static [ApiExtractorType] = &[ApiExtractorType::Parameter]; const PARAM_IS_REQUIRED: bool = T::IS_REQUIRED; diff --git a/poem-openapi/src/param/query.rs b/poem-openapi/src/param/query.rs index e036d63b97..f14252278a 100644 --- a/poem-openapi/src/param/query.rs +++ b/poem-openapi/src/param/query.rs @@ -27,7 +27,6 @@ impl DerefMut for Query { } } -#[poem::async_trait] impl<'a, T: ParseFromParameter> ApiExtractor<'a> for Query { const TYPES: &'static [ApiExtractorType] = &[ApiExtractorType::Parameter]; const PARAM_IS_REQUIRED: bool = T::IS_REQUIRED; diff --git a/poem-openapi/src/payload/form.rs b/poem-openapi/src/payload/form.rs index 42b3c0784c..2464989064 100644 --- a/poem-openapi/src/payload/form.rs +++ b/poem-openapi/src/payload/form.rs @@ -53,7 +53,7 @@ impl ParsePayload for Form { const IS_REQUIRED: bool = true; async fn from_request(req: &Request, body: &mut RequestBody) -> Result { - let data: Vec = FromRequest::from_request(req, body).await?; + let data = Vec::::from_request(req, body).await?; Ok(Self(serde_urlencoded::from_bytes(&data).map_err( |err| ParseRequestPayloadError { reason: err.to_string(), diff --git a/poem-openapi/src/payload/json.rs b/poem-openapi/src/payload/json.rs index f588d93ce2..ce858a9d96 100644 --- a/poem-openapi/src/payload/json.rs +++ b/poem-openapi/src/payload/json.rs @@ -54,7 +54,7 @@ impl ParsePayload for Json { const IS_REQUIRED: bool = true; async fn from_request(request: &Request, body: &mut RequestBody) -> Result { - let data: Vec = FromRequest::from_request(request, body).await?; + let data = Vec::::from_request(request, body).await?; let value = if data.is_empty() { Value::Null } else { diff --git a/poem-openapi/src/payload/xml.rs b/poem-openapi/src/payload/xml.rs index ea7935d87f..36558e4470 100644 --- a/poem-openapi/src/payload/xml.rs +++ b/poem-openapi/src/payload/xml.rs @@ -54,7 +54,7 @@ impl ParsePayload for Xml { const IS_REQUIRED: bool = true; async fn from_request(request: &Request, body: &mut RequestBody) -> Result { - let data: Vec = FromRequest::from_request(request, body).await?; + let data = Vec::::from_request(request, body).await?; let value = if data.is_empty() { Value::Null } else { diff --git a/poem-openapi/src/payload/yaml.rs b/poem-openapi/src/payload/yaml.rs index c39cc236d4..24e4bd5d78 100644 --- a/poem-openapi/src/payload/yaml.rs +++ b/poem-openapi/src/payload/yaml.rs @@ -54,7 +54,7 @@ impl ParsePayload for Yaml { const IS_REQUIRED: bool = true; async fn from_request(request: &Request, body: &mut RequestBody) -> Result { - let data: Vec = FromRequest::from_request(request, body).await?; + let data = Vec::::from_request(request, body).await?; let value = if data.is_empty() { Value::Null } else { diff --git a/poem-openapi/tests/webhook.rs b/poem-openapi/tests/webhook.rs index 297955e4c7..d4f3340ffc 100644 --- a/poem-openapi/tests/webhook.rs +++ b/poem-openapi/tests/webhook.rs @@ -15,10 +15,10 @@ async fn name() { #[Webhook] trait MyWebhooks: Sync { #[oai(name = "a", method = "post")] - async fn test1(&self); + fn test1(&self); #[oai(method = "trace")] - async fn test2(&self); + fn test2(&self); } assert_eq!(<&dyn MyWebhooks>::meta()[0].name, "a"); @@ -30,10 +30,10 @@ async fn method() { #[Webhook] trait MyWebhooks: Sync { #[oai(method = "post")] - async fn test1(&self); + fn test1(&self); #[oai(method = "trace")] - async fn test2(&self); + fn test2(&self); } assert_eq!(<&dyn MyWebhooks>::meta()[0].operation.method, Method::POST); @@ -45,10 +45,10 @@ async fn deprecated() { #[Webhook] trait MyWebhooks: Sync { #[oai(method = "post")] - async fn test1(&self); + fn test1(&self); #[oai(method = "get", deprecated)] - async fn test2(&self); + fn test2(&self); } assert!(!<&dyn MyWebhooks>::meta()[0].operation.deprecated); @@ -67,10 +67,10 @@ async fn tags() { #[Webhook(tag = "MyTags::A")] trait MyWebhooks: Sync { #[oai(method = "post", tag = "MyTags::B", tag = "MyTags::C")] - async fn test1(&self); + fn test1(&self); #[oai(method = "get", tag = "MyTags::B")] - async fn test2(&self); + fn test2(&self); } assert_eq!( @@ -85,10 +85,10 @@ async fn operation_id() { #[Webhook] trait MyWebhooks: Sync { #[oai(method = "post", operation_id = "a")] - async fn test1(&self); + fn test1(&self); #[oai(method = "get", operation_id = "b")] - async fn test2(&self); + fn test2(&self); } assert_eq!( @@ -106,7 +106,7 @@ async fn parameters() { #[Webhook] trait MyWebhooks: Sync { #[oai(method = "post")] - async fn test(&self, a: Query, b: Path); + fn test(&self, a: Query, b: Path); } assert_eq!( @@ -139,7 +139,7 @@ async fn request_body() { #[Webhook] trait MyWebhooks: Sync { #[oai(method = "post")] - async fn test(&self, req: Json); + fn test(&self, req: Json); } assert_eq!( @@ -160,7 +160,7 @@ async fn response() { #[Webhook] trait MyWebhooks: Sync { #[oai(method = "post")] - async fn test(&self) -> Json; + fn test(&self) -> Json; } assert_eq!( @@ -184,7 +184,7 @@ async fn create() { #[Webhook] trait MyWebhooks: Sync { #[oai(method = "post")] - async fn test(&self) -> Json; + fn test(&self) -> Json; } let _ = OpenApiService::new((), "Test", "1.0").webhooks::<&dyn MyWebhooks>(); @@ -198,7 +198,7 @@ async fn external_docs() { method = "post", external_docs = "https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md" )] - async fn test(&self); + fn test(&self); } assert_eq!( diff --git a/poem/Cargo.toml b/poem/Cargo.toml index 2a4f7b1b31..75b4dfd484 100644 --- a/poem/Cargo.toml +++ b/poem/Cargo.toml @@ -70,7 +70,6 @@ yaml = ["serde_yaml"] [dependencies] poem-derive.workspace = true -async-trait = "0.1.51" bytes.workspace = true futures-util = { workspace = true, features = ["sink"] } http = "1.0.0" diff --git a/poem/src/i18n/locale.rs b/poem/src/i18n/locale.rs index 584c26d77a..629b58ff2c 100644 --- a/poem/src/i18n/locale.rs +++ b/poem/src/i18n/locale.rs @@ -84,7 +84,6 @@ impl Locale { } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for Locale { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { let resources = req diff --git a/poem/src/lib.rs b/poem/src/lib.rs index 4a9b4648f2..58c9c6f11f 100644 --- a/poem/src/lib.rs +++ b/poem/src/lib.rs @@ -294,7 +294,6 @@ mod route; mod server; pub use addr::Addr; -pub use async_trait::async_trait; pub use body::Body; pub use endpoint::{Endpoint, EndpointExt, IntoEndpoint}; pub use error::{Error, Result}; diff --git a/poem/src/session/session.rs b/poem/src/session/session.rs index 60611989cd..b884ad333f 100644 --- a/poem/src/session/session.rs +++ b/poem/src/session/session.rs @@ -147,7 +147,6 @@ impl Session { } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for &'a Session { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(req diff --git a/poem/src/web/accept.rs b/poem/src/web/accept.rs index dd866f9cd3..142b9f10ac 100644 --- a/poem/src/web/accept.rs +++ b/poem/src/web/accept.rs @@ -26,7 +26,6 @@ fn parse_accept(headers: &HeaderMap) -> Vec { items.into_iter().map(|(mime, _)| mime).collect() } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for Accept { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(Self(parse_accept(req.headers()))) diff --git a/poem/src/web/cookie.rs b/poem/src/web/cookie.rs index 1222a9e5e1..93f2d67ad7 100644 --- a/poem/src/web/cookie.rs +++ b/poem/src/web/cookie.rs @@ -291,7 +291,6 @@ impl Cookie { } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for Cookie { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { let value = req @@ -501,7 +500,6 @@ impl FromStr for CookieJar { } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for &'a CookieJar { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(req.cookie()) diff --git a/poem/src/web/csrf.rs b/poem/src/web/csrf.rs index 10e618fc78..1d4f32f1b8 100644 --- a/poem/src/web/csrf.rs +++ b/poem/src/web/csrf.rs @@ -20,7 +20,6 @@ impl Deref for CsrfToken { } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for &'a CsrfToken { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(req @@ -49,7 +48,6 @@ impl CsrfVerifier { } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for &'a CsrfVerifier { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(req diff --git a/poem/src/web/data.rs b/poem/src/web/data.rs index 2383a74c94..4267989e59 100644 --- a/poem/src/web/data.rs +++ b/poem/src/web/data.rs @@ -37,7 +37,6 @@ impl Deref for Data { } } -#[async_trait::async_trait] impl<'a, T: Send + Sync + 'static> FromRequest<'a> for Data<&'a T> { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(Data( diff --git a/poem/src/web/form.rs b/poem/src/web/form.rs index 0bfdbf3192..d694fa20e5 100644 --- a/poem/src/web/form.rs +++ b/poem/src/web/form.rs @@ -86,9 +86,9 @@ impl DerefMut for Form { } } -#[async_trait::async_trait] impl<'a, T: DeserializeOwned> FromRequest<'a> for Form { - async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result { + async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result + { if req.method() == Method::GET { Ok( serde_urlencoded::from_str(req.uri().query().unwrap_or_default()) diff --git a/poem/src/web/json.rs b/poem/src/web/json.rs index 98f455e6b9..8fc2090701 100644 --- a/poem/src/web/json.rs +++ b/poem/src/web/json.rs @@ -103,9 +103,9 @@ impl DerefMut for Json { } } -#[async_trait::async_trait] impl<'a, T: DeserializeOwned> FromRequest<'a> for Json { - async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result { + async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result + { let content_type = req .headers() .get(header::CONTENT_TYPE) diff --git a/poem/src/web/mod.rs b/poem/src/web/mod.rs index 8a04cb04fc..d97c9830f0 100644 --- a/poem/src/web/mod.rs +++ b/poem/src/web/mod.rs @@ -36,11 +36,12 @@ mod typed_header; #[cfg_attr(docsrs, doc(cfg(feature = "websocket")))] pub mod websocket; -use std::{convert::Infallible, fmt::Debug}; +use std::{convert::Infallible, fmt::Debug, future::Future}; #[cfg(feature = "compression")] pub use async_compression::Level as CompressionLevel; use bytes::Bytes; +use futures_util::FutureExt; use http::header; #[cfg(feature = "compression")] @@ -281,7 +282,6 @@ impl RequestBody { /// /// struct Token(String); /// -/// #[poem::async_trait] /// impl<'a> FromRequest<'a> for Token { /// async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result { /// let token = req @@ -309,10 +309,12 @@ impl RequestBody { /// .assert_status_is_ok(); /// # }); /// ``` -#[async_trait::async_trait] pub trait FromRequest<'a>: Sized { /// Extract from request head and body. - async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result; + fn from_request( + req: &'a Request, + body: &mut RequestBody, + ) -> impl Future> + Send; /// Extract from request head. /// @@ -323,8 +325,14 @@ pub trait FromRequest<'a>: Sized { /// request head, using this method would be more convenient. /// `String`,`Vec` they extract the body of the request, using this /// method will cause `ReadBodyError` error. - async fn from_request_without_body(req: &'a Request) -> Result { - Self::from_request(req, &mut Default::default()).await + fn from_request_without_body(req: &'a Request) -> impl Future> + Send { + async move { + // FIXME: remove the unnecessary boxed + // https://github.com/rust-lang/rust/issues/100013 + Self::from_request(req, &mut Default::default()) + .boxed() + .await + } } } @@ -728,49 +736,42 @@ impl + Send> IntoResponse for Html { } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for &'a Request { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(req) } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for &'a Uri { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(req.uri()) } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for Method { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(req.method().clone()) } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for Version { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(req.version()) } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for &'a HeaderMap { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(req.headers()) } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for Body { async fn from_request(_req: &'a Request, body: &mut RequestBody) -> Result { Ok(body.take()?) } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for String { async fn from_request(_req: &'a Request, body: &mut RequestBody) -> Result { let data = body.take()?.into_bytes().await?; @@ -778,45 +779,43 @@ impl<'a> FromRequest<'a> for String { } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for Bytes { async fn from_request(_req: &'a Request, body: &mut RequestBody) -> Result { Ok(body.take()?.into_bytes().await?) } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for Vec { async fn from_request(_req: &'a Request, body: &mut RequestBody) -> Result { Ok(body.take()?.into_vec().await?) } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for &'a RemoteAddr { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(&req.state().remote_addr) } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for &'a LocalAddr { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(&req.state().local_addr) } } -#[async_trait::async_trait] impl<'a, T: FromRequest<'a>> FromRequest<'a> for Option { async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result { - Ok(T::from_request(req, body).await.ok()) + // FIXME: remove the unnecessary boxed + // https://github.com/rust-lang/rust/issues/100013 + Ok(T::from_request(req, body).boxed().await.ok()) } } -#[async_trait::async_trait] impl<'a, T: FromRequest<'a>> FromRequest<'a> for Result { async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result { - Ok(T::from_request(req, body).await) + // FIXME: remove the unnecessary boxed + // https://github.com/rust-lang/rust/issues/100013 + Ok(T::from_request(req, body).boxed().await) } } diff --git a/poem/src/web/multipart.rs b/poem/src/web/multipart.rs index 1e7addb09c..fae4502f9a 100644 --- a/poem/src/web/multipart.rs +++ b/poem/src/web/multipart.rs @@ -129,7 +129,6 @@ pub struct Multipart { inner: multer::Multipart<'static>, } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for Multipart { async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result { let content_type = req diff --git a/poem/src/web/path/mod.rs b/poem/src/web/path/mod.rs index 255a2f8932..8c0fd356bc 100644 --- a/poem/src/web/path/mod.rs +++ b/poem/src/web/path/mod.rs @@ -125,7 +125,6 @@ impl Path { } } -#[async_trait::async_trait] impl<'a, T: DeserializeOwned> FromRequest<'a> for Path { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Self::internal_from_request(req).await.map_err(Into::into) diff --git a/poem/src/web/query.rs b/poem/src/web/query.rs index fc4d68578a..9712f424c0 100644 --- a/poem/src/web/query.rs +++ b/poem/src/web/query.rs @@ -70,7 +70,6 @@ impl Query { } } -#[async_trait::async_trait] impl<'a, T: DeserializeOwned> FromRequest<'a> for Query { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Self::internal_from_request(req).await.map_err(Into::into) diff --git a/poem/src/web/real_ip.rs b/poem/src/web/real_ip.rs index 37c891d586..c718ae441f 100644 --- a/poem/src/web/real_ip.rs +++ b/poem/src/web/real_ip.rs @@ -8,7 +8,6 @@ use crate::{Addr, FromRequest, Request, RequestBody, Result}; #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct RealIp(pub Option); -#[async_trait::async_trait] impl<'a> FromRequest<'a> for RealIp { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { if let Some(real_ip) = req diff --git a/poem/src/web/static_file.rs b/poem/src/web/static_file.rs index 047abdee11..0ca188ceb9 100644 --- a/poem/src/web/static_file.rs +++ b/poem/src/web/static_file.rs @@ -101,7 +101,6 @@ pub struct StaticFileRequest { range: Option, } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for StaticFileRequest { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Ok(Self { diff --git a/poem/src/web/tempfile.rs b/poem/src/web/tempfile.rs index 610276ee5a..f18aeebb19 100644 --- a/poem/src/web/tempfile.rs +++ b/poem/src/web/tempfile.rs @@ -30,7 +30,6 @@ impl TempFile { } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for TempFile { async fn from_request(_req: &'a Request, body: &mut RequestBody) -> Result { Self::internal_from_request(body).await.map_err(Into::into) diff --git a/poem/src/web/typed_header.rs b/poem/src/web/typed_header.rs index 4c2f2b1e18..fe0a2ae652 100644 --- a/poem/src/web/typed_header.rs +++ b/poem/src/web/typed_header.rs @@ -65,7 +65,6 @@ impl TypedHeader { } } -#[async_trait::async_trait] impl<'a, T: Header> FromRequest<'a> for TypedHeader { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Self::internal_from_request(req).await.map_err(Into::into) diff --git a/poem/src/web/websocket/extractor.rs b/poem/src/web/websocket/extractor.rs index 13f9ab7fde..43bc109e49 100644 --- a/poem/src/web/websocket/extractor.rs +++ b/poem/src/web/websocket/extractor.rs @@ -66,7 +66,6 @@ impl WebSocket { } } -#[async_trait::async_trait] impl<'a> FromRequest<'a> for WebSocket { async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result { Self::internal_from_request(req).await.map_err(Into::into) diff --git a/poem/src/web/xml.rs b/poem/src/web/xml.rs index 375ef8afc3..9b5bd69c5b 100644 --- a/poem/src/web/xml.rs +++ b/poem/src/web/xml.rs @@ -106,9 +106,9 @@ impl DerefMut for Xml { } } -#[async_trait::async_trait] impl<'a, T: DeserializeOwned> FromRequest<'a> for Xml { - async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result { + async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result + { let content_type = req .headers() .get(header::CONTENT_TYPE) diff --git a/poem/src/web/yaml.rs b/poem/src/web/yaml.rs index 169a5f7c1f..1960ec694b 100644 --- a/poem/src/web/yaml.rs +++ b/poem/src/web/yaml.rs @@ -107,7 +107,6 @@ impl DerefMut for Yaml { } } -#[async_trait::async_trait] impl<'a, T: DeserializeOwned> FromRequest<'a> for Yaml { async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result { let content_type = req From ce49f76b4f65082ef5391252f11b267272451e4c Mon Sep 17 00:00:00 2001 From: Sunli Date: Sat, 30 Mar 2024 10:39:59 +0800 Subject: [PATCH 18/24] rustfmt --- poem/src/web/form.rs | 3 +-- poem/src/web/json.rs | 3 +-- poem/src/web/xml.rs | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/poem/src/web/form.rs b/poem/src/web/form.rs index d694fa20e5..c1f7e48078 100644 --- a/poem/src/web/form.rs +++ b/poem/src/web/form.rs @@ -87,8 +87,7 @@ impl DerefMut for Form { } impl<'a, T: DeserializeOwned> FromRequest<'a> for Form { - async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result - { + async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result { if req.method() == Method::GET { Ok( serde_urlencoded::from_str(req.uri().query().unwrap_or_default()) diff --git a/poem/src/web/json.rs b/poem/src/web/json.rs index 8fc2090701..2ba14ef814 100644 --- a/poem/src/web/json.rs +++ b/poem/src/web/json.rs @@ -104,8 +104,7 @@ impl DerefMut for Json { } impl<'a, T: DeserializeOwned> FromRequest<'a> for Json { - async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result - { + async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result { let content_type = req .headers() .get(header::CONTENT_TYPE) diff --git a/poem/src/web/xml.rs b/poem/src/web/xml.rs index 9b5bd69c5b..28c45eb85c 100644 --- a/poem/src/web/xml.rs +++ b/poem/src/web/xml.rs @@ -107,8 +107,7 @@ impl DerefMut for Xml { } impl<'a, T: DeserializeOwned> FromRequest<'a> for Xml { - async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result - { + async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result { let content_type = req .headers() .get(header::CONTENT_TYPE) From 446c0de453115e9ddcd53d7fd40abcc0de2ec2b5 Mon Sep 17 00:00:00 2001 From: Sunli Date: Sat, 30 Mar 2024 10:46:11 +0800 Subject: [PATCH 19/24] fix examples --- examples/openapi/content-type-accept/src/bcs_payload.rs | 2 +- examples/openapi/custom-payload/src/bcs_payload.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/openapi/content-type-accept/src/bcs_payload.rs b/examples/openapi/content-type-accept/src/bcs_payload.rs index 543e9fe3e5..e725ccf023 100644 --- a/examples/openapi/content-type-accept/src/bcs_payload.rs +++ b/examples/openapi/content-type-accept/src/bcs_payload.rs @@ -54,7 +54,7 @@ impl Deserialize<'b>> ParsePayload for Bcs { const IS_REQUIRED: bool = true; async fn from_request(request: &Request, body: &mut RequestBody) -> Result { - let data: Vec = FromRequest::from_request(request, body).await?; + let data = Vec::::from_request(request, body).await?; let value: T = from_bytes(&data).map_err(|err| ParseRequestPayloadError { reason: err.to_string(), })?; diff --git a/examples/openapi/custom-payload/src/bcs_payload.rs b/examples/openapi/custom-payload/src/bcs_payload.rs index 543e9fe3e5..e725ccf023 100644 --- a/examples/openapi/custom-payload/src/bcs_payload.rs +++ b/examples/openapi/custom-payload/src/bcs_payload.rs @@ -54,7 +54,7 @@ impl Deserialize<'b>> ParsePayload for Bcs { const IS_REQUIRED: bool = true; async fn from_request(request: &Request, body: &mut RequestBody) -> Result { - let data: Vec = FromRequest::from_request(request, body).await?; + let data = Vec::::from_request(request, body).await?; let value: T = from_bytes(&data).map_err(|err| ParseRequestPayloadError { reason: err.to_string(), })?; From c4137eda3103b7bef32a24fc8c90176e05271583 Mon Sep 17 00:00:00 2001 From: Sunli Date: Sat, 30 Mar 2024 11:22:52 +0800 Subject: [PATCH 20/24] Update webhook.md --- poem-openapi/src/docs/webhook.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poem-openapi/src/docs/webhook.md b/poem-openapi/src/docs/webhook.md index b63a1e515b..5191fda61f 100644 --- a/poem-openapi/src/docs/webhook.md +++ b/poem-openapi/src/docs/webhook.md @@ -51,7 +51,7 @@ struct Pet { #[Webhook] trait MyWebhooks: Sync { #[oai(method = "post")] - async fn new_pet(&self, pet: Json); + fn new_pet(&self, pet: Json); } let api = OpenApiService::new((), "Demo", "1.0.0") From dedb994a72b7a8add68d2545dcd61d049f00bd66 Mon Sep 17 00:00:00 2001 From: Artem Medvedev Date: Sat, 30 Mar 2024 04:38:09 +0100 Subject: [PATCH 21/24] fix: possible race condition during graceful shutdown (#780) This PR just makes the behavior more reliable, also should address #661 The possible race condition: - `notify_waiters()` will notify only already registered waiters Thus, if it's called after ```rust drop(acceptor); if alive_connections.load(Ordering::Acquire) > 0 { ``` But before `notify.notified()` - it might be never notified. --- poem/src/server.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/poem/src/server.rs b/poem/src/server.rs index 1460481bfc..77507215b4 100644 --- a/poem/src/server.rs +++ b/poem/src/server.rs @@ -166,7 +166,7 @@ where let server_graceful_shutdown_token = server_graceful_shutdown_token.clone(); tokio::spawn(async move { - let serve_connection = serve_connection(socket, local_addr, remote_addr, scheme, ep, server_graceful_shutdown_token, idle_timeout); + let serve_connection = serve_connection(socket, local_addr, remote_addr, scheme, ep, server_graceful_shutdown_token.clone(), idle_timeout); if timeout.is_some() { tokio::select! { @@ -178,8 +178,11 @@ where } if alive_connections.fetch_sub(1, Ordering::Acquire) == 1 { - // We have to notify only if there is a registered waiter on shutdown - notify.notify_waiters(); + // notify only if shutdown is initiated, to prevent notification when server is active. + // It's a valid state to have 0 alive connections when server is not shutting down. + if server_graceful_shutdown_token.is_cancelled() { + notify.notify_one(); + } } }); } From d7ff091a736e071fa8efad3a0b65f896e7e9df45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?19=E5=B9=B4=E6=A2=A6=E9=86=92?= <3949379+getong@users.noreply.github.com> Date: Sat, 30 Mar 2024 11:38:44 +0800 Subject: [PATCH 22/24] delete imported redundantly (#785) --- poem-grpc/src/reflection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poem-grpc/src/reflection.rs b/poem-grpc/src/reflection.rs index 34e455349d..52c0540133 100644 --- a/poem-grpc/src/reflection.rs +++ b/poem-grpc/src/reflection.rs @@ -8,7 +8,7 @@ use proto::{ server_reflection_request::MessageRequest, server_reflection_response::MessageResponse, }; -use crate::{include_file_descriptor_set, Code, Request, Response, Service, Status, Streaming}; +use crate::{Code, Request, Response, Service, Status, Streaming}; #[allow(unreachable_pub)] #[allow(clippy::enum_variant_names)] From cba2ecb42d21a2023c82d7d7b081af5e86ed73ee Mon Sep 17 00:00:00 2001 From: Sunli Date: Sat, 30 Mar 2024 11:42:38 +0800 Subject: [PATCH 23/24] remove `Sync` bound on WebHook --- poem-openapi/src/docs/webhook.md | 2 +- poem-openapi/tests/webhook.rs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/poem-openapi/src/docs/webhook.md b/poem-openapi/src/docs/webhook.md index 5191fda61f..7fcc86c75a 100644 --- a/poem-openapi/src/docs/webhook.md +++ b/poem-openapi/src/docs/webhook.md @@ -49,7 +49,7 @@ struct Pet { } #[Webhook] -trait MyWebhooks: Sync { +trait MyWebhooks { #[oai(method = "post")] fn new_pet(&self, pet: Json); } diff --git a/poem-openapi/tests/webhook.rs b/poem-openapi/tests/webhook.rs index d4f3340ffc..4897cf13a9 100644 --- a/poem-openapi/tests/webhook.rs +++ b/poem-openapi/tests/webhook.rs @@ -13,7 +13,7 @@ use poem_openapi::{ #[tokio::test] async fn name() { #[Webhook] - trait MyWebhooks: Sync { + trait MyWebhooks { #[oai(name = "a", method = "post")] fn test1(&self); @@ -28,7 +28,7 @@ async fn name() { #[tokio::test] async fn method() { #[Webhook] - trait MyWebhooks: Sync { + trait MyWebhooks { #[oai(method = "post")] fn test1(&self); @@ -43,7 +43,7 @@ async fn method() { #[tokio::test] async fn deprecated() { #[Webhook] - trait MyWebhooks: Sync { + trait MyWebhooks { #[oai(method = "post")] fn test1(&self); @@ -83,7 +83,7 @@ async fn tags() { #[tokio::test] async fn operation_id() { #[Webhook] - trait MyWebhooks: Sync { + trait MyWebhooks { #[oai(method = "post", operation_id = "a")] fn test1(&self); @@ -104,7 +104,7 @@ async fn operation_id() { #[tokio::test] async fn parameters() { #[Webhook] - trait MyWebhooks: Sync { + trait MyWebhooks { #[oai(method = "post")] fn test(&self, a: Query, b: Path); } @@ -137,7 +137,7 @@ async fn parameters() { #[tokio::test] async fn request_body() { #[Webhook] - trait MyWebhooks: Sync { + trait MyWebhooks { #[oai(method = "post")] fn test(&self, req: Json); } @@ -158,7 +158,7 @@ async fn request_body() { #[tokio::test] async fn response() { #[Webhook] - trait MyWebhooks: Sync { + trait MyWebhooks { #[oai(method = "post")] fn test(&self) -> Json; } @@ -182,7 +182,7 @@ async fn response() { #[tokio::test] async fn create() { #[Webhook] - trait MyWebhooks: Sync { + trait MyWebhooks { #[oai(method = "post")] fn test(&self) -> Json; } @@ -193,7 +193,7 @@ async fn create() { #[tokio::test] async fn external_docs() { #[Webhook] - trait MyWebhooks: Sync { + trait MyWebhooks { #[oai( method = "post", external_docs = "https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md" From f6bec886333a60e7f12eab9e0830cf9cae332029 Mon Sep 17 00:00:00 2001 From: Sunli Date: Sat, 30 Mar 2024 11:45:55 +0800 Subject: [PATCH 24/24] poem `3.0.0` Co-Authored-By: hzlinyiyu --- Cargo.toml | 10 ++-- .../auth-github/Cargo.toml | 0 .../auth-github/src/main.rs | 0 poem-derive/Cargo.toml | 2 +- poem-grpc-build/Cargo.toml | 2 +- poem-grpc/Cargo.toml | 4 +- poem-grpc/src/reflection.rs | 2 +- poem-lambda/CHANGELOG.md | 2 +- poem-lambda/Cargo.toml | 4 +- poem-openapi-derive/Cargo.toml | 2 +- poem-openapi/CHANGELOG.md | 2 +- poem-openapi/Cargo.toml | 2 +- poem-openapi/src/openapi.rs | 2 +- poem/CHANGELOG.md | 17 +++--- poem/Cargo.toml | 6 +- poem/src/listener/acme/listener.rs | 58 ++++++++----------- poem/src/listener/mod.rs | 3 +- poem/src/listener/rustls.rs | 10 ++-- poem/src/middleware/cookie_jar_manager.rs | 2 +- poem/src/route/router.rs | 2 +- poem/src/route/router_method.rs | 6 +- poem/src/web/accept.rs | 2 - 22 files changed, 63 insertions(+), 77 deletions(-) rename examples/{disabled => openapi}/auth-github/Cargo.toml (100%) rename examples/{disabled => openapi}/auth-github/src/main.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 0fe5324bfc..6419e75267 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,10 +20,10 @@ repository = "https://github.com/poem-web/poem" rust-version = "1.75" [workspace.dependencies] -poem = { path = "poem", version = "2.0.1", default-features = false } -poem-derive = { path = "poem-derive", version = "2.0.1" } -poem-openapi-derive = { path = "poem-openapi-derive", version = "4.0.1" } -poem-grpc-build = { path = "poem-grpc-build", version = "0.3.0" } +poem = { path = "poem", version = "3.0.0", default-features = false } +poem-derive = { path = "poem-derive", version = "3.0.0" } +poem-openapi-derive = { path = "poem-openapi-derive", version = "5.0.0" } +poem-grpc-build = { path = "poem-grpc-build", version = "0.4.0" } proc-macro-crate = "3.0.0" proc-macro2 = "1.0.29" @@ -42,7 +42,7 @@ futures-util = "0.3.17" tokio-stream = "0.1.8" serde_yaml = "0.9" quick-xml = { version = "0.31.0", features = ["serialize"] } -base64 = "0.21.0" +base64 = "0.22.0" serde_urlencoded = "0.7.1" indexmap = "2.0.0" reqwest = { version = "0.12.2", default-features = false } diff --git a/examples/disabled/auth-github/Cargo.toml b/examples/openapi/auth-github/Cargo.toml similarity index 100% rename from examples/disabled/auth-github/Cargo.toml rename to examples/openapi/auth-github/Cargo.toml diff --git a/examples/disabled/auth-github/src/main.rs b/examples/openapi/auth-github/src/main.rs similarity index 100% rename from examples/disabled/auth-github/src/main.rs rename to examples/openapi/auth-github/src/main.rs diff --git a/poem-derive/Cargo.toml b/poem-derive/Cargo.toml index 63d46376f1..52ea6715db 100644 --- a/poem-derive/Cargo.toml +++ b/poem-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "poem-derive" -version = "2.0.1" +version = "3.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/poem-grpc-build/Cargo.toml b/poem-grpc-build/Cargo.toml index c0f107a382..efb44e6054 100644 --- a/poem-grpc-build/Cargo.toml +++ b/poem-grpc-build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "poem-grpc-build" -version = "0.3.0" +version = "0.4.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/poem-grpc/Cargo.toml b/poem-grpc/Cargo.toml index dab8b02c75..fe2a2dbf96 100644 --- a/poem-grpc/Cargo.toml +++ b/poem-grpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "poem-grpc" -version = "0.3.0" +version = "0.4.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -28,7 +28,7 @@ itoa = "1.0.2" percent-encoding = "2.1.0" bytes.workspace = true prost = "0.12.0" -base64 = "0.21.0" +base64.workspace = true prost-types = "0.12.0" tokio-stream = { workspace = true, features = ["sync"] } serde = { workspace = true, optional = true } diff --git a/poem-grpc/src/reflection.rs b/poem-grpc/src/reflection.rs index 52c0540133..f9e80d2400 100644 --- a/poem-grpc/src/reflection.rs +++ b/poem-grpc/src/reflection.rs @@ -138,7 +138,7 @@ impl Reflection { let fd_iter = std::mem::take(&mut this.file_descriptor_sets) .into_iter() .flat_map(|fds| fds.file.into_iter()); - let mut files = HashMap::new(); + let mut files = HashMap::with_capacity(fd_iter.size_hint().0); for fd in fd_iter { let fd = Arc::new(fd); diff --git a/poem-lambda/CHANGELOG.md b/poem-lambda/CHANGELOG.md index 71db916474..f9c612503b 100644 --- a/poem-lambda/CHANGELOG.md +++ b/poem-lambda/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -# [5.0.0] 2024-03-24 +# [5.0.0] 2024-03-30 - use AFIT instead of `async_trait` - Bump `lambda_http` from `0.9` to `0.10` diff --git a/poem-lambda/Cargo.toml b/poem-lambda/Cargo.toml index 777cc3ff08..2ea3f62aeb 100644 --- a/poem-lambda/Cargo.toml +++ b/poem-lambda/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "poem-lambda" -version = "4.0.0" +version = "5.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -21,7 +21,7 @@ categories = [ [dependencies] poem = { workspace = true, default-features = false } -lambda_http = { version = "0.10.0" } +lambda_http = { version = "0.11.0" } [dev-dependencies] tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } diff --git a/poem-openapi-derive/Cargo.toml b/poem-openapi-derive/Cargo.toml index 96c7123c99..181345ef0b 100644 --- a/poem-openapi-derive/Cargo.toml +++ b/poem-openapi-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "poem-openapi-derive" -version = "4.0.1" +version = "5.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/poem-openapi/CHANGELOG.md b/poem-openapi/CHANGELOG.md index 54503aebfc..bb65e1dd51 100644 --- a/poem-openapi/CHANGELOG.md +++ b/poem-openapi/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -# [5.0.0] 2024-03-24 +# [5.0.0] 2024-03-30 - use AFIT instead of `async_trait` - add `Upload::size` method diff --git a/poem-openapi/Cargo.toml b/poem-openapi/Cargo.toml index 6c35e78713..298a254b5e 100644 --- a/poem-openapi/Cargo.toml +++ b/poem-openapi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "poem-openapi" -version = "4.0.1" +version = "5.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/poem-openapi/src/openapi.rs b/poem-openapi/src/openapi.rs index 2a48bf4e69..77efd0eab1 100644 --- a/poem-openapi/src/openapi.rs +++ b/poem-openapi/src/openapi.rs @@ -651,7 +651,7 @@ impl IntoEndpoint for OpenApiService { #[cfg(test)] mod tests { use super::*; - use crate::{types::Type, OpenApi}; + use crate::OpenApi; #[test] fn extra_response_headers() { diff --git a/poem/CHANGELOG.md b/poem/CHANGELOG.md index 2dd2a2dd23..b89263d240 100644 --- a/poem/CHANGELOG.md +++ b/poem/CHANGELOG.md @@ -4,15 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -# [3.0.0] 2024-03-24 +# [3.0.0] 2024-03-30 - use AFIT instead of `async_trait` -- bump `priority-queue` to 2.0 -- bump `x509-parser` to 0.16 -- bump `nix` to 0.28 -- bump `redis` to 0.25 -- bump `reqwest` to 0.12 -- bump `opentelemetry` to 0.22 [#784](https://github.com/poem-web/poem/pull/784) +- bump `priority-queue` to `2.0` +- bump `x509-parser` to `0.16` +- bump `nix` to `0.28` +- bump `redis` to `0.25` +- bump `reqwest` to `0.12` +- bump `opentelemetry` to `0.22` [#784](https://github.com/poem-web/poem/pull/784) +- bump `sync_wrapper` to `1.0.0` +- bump `rcgen ` to `0.13.0` +- bump `base64 ` to `0.22.0` # [2.0.1] 2024-03-04 diff --git a/poem/Cargo.toml b/poem/Cargo.toml index 75b4dfd484..591a862b47 100644 --- a/poem/Cargo.toml +++ b/poem/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "poem" -version = "2.0.1" +version = "3.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -92,7 +92,7 @@ thiserror.workspace = true rfc7239 = "0.1.0" mime.workspace = true wildmatch = "2" -sync_wrapper = { version = "0.1.2", features = ["futures"] } +sync_wrapper = { version = "1.0.0", features = ["futures"] } # Non-feature optional dependencies multer = { version = "3.0.0", features = ["tokio"], optional = true } @@ -150,7 +150,7 @@ unic-langid = { version = "0.9.0", optional = true, features = ["macros"] } intl-memoizer = { version = "0.5.1", optional = true } ring = { version = "0.17.7", optional = true } reqwest = { workspace = true, features = ["json"], optional = true } -rcgen = { version = "0.12.0", optional = true } +rcgen = { version = "0.13.0", optional = true } x509-parser = { version = "0.16.0", optional = true } tokio-metrics = { version = "0.3.0", optional = true } rust-embed = { version = "8.0", optional = true } diff --git a/poem/src/listener/acme/listener.rs b/poem/src/listener/acme/listener.rs index 160b09ffe1..a9b36b195a 100644 --- a/poem/src/listener/acme/listener.rs +++ b/poem/src/listener/acme/listener.rs @@ -5,9 +5,7 @@ use std::{ }; use http::uri::Scheme; -use rcgen::{ - Certificate, CertificateParams, CustomExtension, DistinguishedName, PKCS_ECDSA_P256_SHA256, -}; +use rcgen::{CertificateParams, CustomExtension, KeyPair, PKCS_ECDSA_P256_SHA256}; use tokio_rustls::{ rustls::{ crypto::ring::sign::any_ecdsa_type, @@ -229,21 +227,21 @@ impl Acceptor for AutoCertAcceptor { } fn gen_acme_cert(domain: &str, acme_hash: &[u8]) -> IoResult { - let mut params = CertificateParams::new(vec![domain.to_string()]); - params.alg = &PKCS_ECDSA_P256_SHA256; - params.custom_extensions = vec![CustomExtension::new_acme_identifier(acme_hash)]; - let cert = Certificate::from_params(params) - .map_err(|_| IoError::new(ErrorKind::Other, "failed to generate acme certificate"))?; - let key = any_ecdsa_type(&PrivateKeyDer::Pkcs8( - cert.serialize_private_key_der().into(), - )) - .unwrap(); - Ok(CertifiedKey::new( - vec![CertificateDer::from(cert.serialize_der().map_err( - |_| IoError::new(ErrorKind::Other, "failed to serialize acme certificate"), - )?)], - key, - )) + let keypair = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256).expect("create key pair"); + let cert = CertificateParams::new(vec![domain.to_string()]) + .and_then(|mut params| { + params.custom_extensions = vec![CustomExtension::new_acme_identifier(acme_hash)]; + params.self_signed(&keypair) + }) + .map_err(|err| { + IoError::new( + ErrorKind::Other, + format!("failed to generate acme certificate: {err}"), + ) + })?; + + let key = any_ecdsa_type(&PrivateKeyDer::Pkcs8(keypair.serialized_der().into())).unwrap(); + Ok(CertifiedKey::new(vec![cert.der().clone()], key)) } /// The result of [`issue_cert`] function. @@ -343,31 +341,23 @@ pub async fn issue_cert>( } // send csr - let mut params = CertificateParams::new( + let keypair = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256).expect("create key pair"); + let request = CertificateParams::new( domains .iter() .map(|domain| domain.as_ref().to_string()) .collect::>(), - ); - params.distinguished_name = DistinguishedName::new(); - params.alg = &PKCS_ECDSA_P256_SHA256; - let cert = Certificate::from_params(params).map_err(|err| { + ) + .and_then(|params| params.serialize_request(&keypair)) + .map_err(|err| { IoError::new( ErrorKind::Other, format!("failed create certificate request: {err}"), ) })?; - let pk = any_ecdsa_type(&PrivateKeyDer::Pkcs8( - cert.serialize_private_key_der().into(), - )) - .unwrap(); - let csr = cert.serialize_request_der().map_err(|err| { - IoError::new( - ErrorKind::Other, - format!("failed to serialize request der {err}"), - ) - })?; + let pk = any_ecdsa_type(&PrivateKeyDer::Pkcs8(keypair.serialized_der().into())).unwrap(); + let csr = request.der().as_ref(); let order_resp = client.send_csr(&order_resp.finalize, &csr).await?; if order_resp.status == "invalid" { @@ -403,7 +393,7 @@ pub async fn issue_cert>( ) })?) .await?; - let pkey_pem = cert.serialize_private_key_pem(); + let pkey_pem = keypair.serialize_pem(); let cert_chain = rustls_pemfile::certs(&mut acme_cert_pem.as_slice()) .collect::>() .map_err(|err| IoError::new(ErrorKind::Other, format!("invalid pem: {err}")))?; diff --git a/poem/src/listener/mod.rs b/poem/src/listener/mod.rs index 68c4f169d7..1c9d0f093b 100644 --- a/poem/src/listener/mod.rs +++ b/poem/src/listener/mod.rs @@ -387,8 +387,7 @@ impl Listener for BoxListener { #[cfg(test)] mod tests { - use super::{AcceptorExt, *}; - use crate::listener::TcpListener; + use super::*; #[tokio::test] async fn combined_listener() { diff --git a/poem/src/listener/rustls.rs b/poem/src/listener/rustls.rs index 93277f947c..5b2be369c2 100644 --- a/poem/src/listener/rustls.rs +++ b/poem/src/listener/rustls.rs @@ -222,10 +222,10 @@ impl RustlsConfig { .map(|fallback| fallback.create_certificate_key()) .transpose()? .map(Arc::new); - let mut certifcate_keys = HashMap::new(); + let mut certificate_keys = HashMap::with_capacity(self.certificates.len()); for (name, certificate) in &self.certificates { - certifcate_keys.insert( + certificate_keys.insert( name.clone(), Arc::new(certificate.create_certificate_key()?), ); @@ -252,7 +252,7 @@ impl RustlsConfig { }; let mut server_config = builder.with_cert_resolver(Arc::new(ResolveServerCert { - certifcate_keys, + certificate_keys, fallback, })); server_config.alpn_protocols = vec!["h2".into(), "http/1.1".into()]; @@ -401,7 +401,7 @@ where #[derive(Debug)] struct ResolveServerCert { - certifcate_keys: HashMap>, + certificate_keys: HashMap>, fallback: Option>, } @@ -409,7 +409,7 @@ impl ResolvesServerCert for ResolveServerCert { fn resolve(&self, client_hello: ClientHello) -> Option> { client_hello .server_name() - .and_then(|name| self.certifcate_keys.get(name).cloned()) + .and_then(|name| self.certificate_keys.get(name).cloned()) .or_else(|| self.fallback.clone()) } } diff --git a/poem/src/middleware/cookie_jar_manager.rs b/poem/src/middleware/cookie_jar_manager.rs index 5a4da25478..24a153a61a 100644 --- a/poem/src/middleware/cookie_jar_manager.rs +++ b/poem/src/middleware/cookie_jar_manager.rs @@ -55,7 +55,7 @@ impl Endpoint for CookieJarManagerEndpoint { async fn call(&self, mut req: Request) -> Result { if req.state().cookie_jar.is_none() { let mut cookie_jar = CookieJar::extract_from_headers(req.headers()); - cookie_jar.key = self.key.clone(); + cookie_jar.key.clone_from(&self.key); req.state_mut().cookie_jar = Some(cookie_jar.clone()); let mut resp = self.inner.call(req).await?.into_response(); cookie_jar.append_delta_to_headers(resp.headers_mut()); diff --git a/poem/src/route/router.rs b/poem/src/route/router.rs index 490f978069..7a6149a468 100644 --- a/poem/src/route/router.rs +++ b/poem/src/route/router.rs @@ -387,7 +387,7 @@ fn normalize_path(path: &str) -> String { #[cfg(test)] mod tests { use futures_util::lock::Mutex; - use http::{StatusCode, Uri}; + use http::StatusCode; use super::*; use crate::{endpoint::make_sync, handler, test::TestClient, Error}; diff --git a/poem/src/route/router_method.rs b/poem/src/route/router_method.rs index 778fadfd8d..566134c5de 100644 --- a/poem/src/route/router_method.rs +++ b/poem/src/route/router_method.rs @@ -281,11 +281,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{ - handler, - http::{Method, StatusCode}, - test::TestClient, - }; + use crate::{handler, http::StatusCode, test::TestClient}; #[tokio::test] async fn method_not_allowed() { diff --git a/poem/src/web/accept.rs b/poem/src/web/accept.rs index 142b9f10ac..ed4cfa3d95 100644 --- a/poem/src/web/accept.rs +++ b/poem/src/web/accept.rs @@ -36,8 +36,6 @@ impl<'a> FromRequest<'a> for Accept { mod tests { use std::str::FromStr; - use http::header; - use super::*; #[tokio::test]