diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a0b6eab72..4b537553f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: - name: Cache Rust uses: Swatinem/rust-cache@v2 - name: Install Protoc - if: matrix.package.name == 'poem-grpc' + if: matrix.package.name == 'poem-grpc' || matrix.package.name == 'poem-openapi' uses: arduino/setup-protoc@v1 # Do tests diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6c0af92ce2..b51f54564b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,7 +43,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Install Protoc - if: matrix.package.name == 'poem-grpc' + if: matrix.package.name == 'poem-grpc' || matrix.package.name == 'poem-openapi' uses: arduino/setup-protoc@v1 - name: Get Version working-directory: ${{ matrix.package.path }} diff --git a/Cargo.toml b/Cargo.toml index 95881c8316..f67aa9bda2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ thiserror = "1.0.30" regex = "1.5.5" mime = "0.3.16" tracing = "0.1.36" -chrono = { version = "0.4.19", default-features = false } +chrono = { version = "0.4.31", default-features = false } bytes = "1.1.0" futures-util = "0.3.17" tokio-stream = "0.1.8" diff --git a/examples/Cargo.toml b/examples/Cargo.toml index d07edfa165..662163f802 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = ["poem/*", "openapi/*", "grpc/*"] [workspace.package] @@ -14,7 +15,7 @@ poem-lambda = { path = "../poem-lambda" } poem-grpc-build = { path = "../poem-grpc-build" } tokio = "1.17.0" -tracing-subscriber = { version ="0.3.9", features = ["env-filter"] } +tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } serde_json = "1.0.68" serde = { version = "1.0.140", features = ["derive"] } mime = "0.3.16" diff --git a/examples/grpc/helloworld/src/main.rs b/examples/grpc/helloworld/src/main.rs index c127c6746d..f147740868 100644 --- a/examples/grpc/helloworld/src/main.rs +++ b/examples/grpc/helloworld/src/main.rs @@ -21,7 +21,7 @@ impl Greeter for GreeterService { #[tokio::main] async fn main() -> Result<(), std::io::Error> { let route = RouteGrpc::new().add_service(GreeterServer::new(GreeterService)); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(route) .await } diff --git a/examples/grpc/jsoncodec/src/main.rs b/examples/grpc/jsoncodec/src/main.rs index 017fac56a9..d1359216a0 100644 --- a/examples/grpc/jsoncodec/src/main.rs +++ b/examples/grpc/jsoncodec/src/main.rs @@ -25,7 +25,7 @@ async fn main() -> Result<(), std::io::Error> { } tracing_subscriber::fmt::init(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run( RouteGrpc::new() .add_service(GreeterServer::new(GreeterService)) diff --git a/examples/grpc/middleware/src/main.rs b/examples/grpc/middleware/src/main.rs index fceb973c6f..ad437bfa9b 100644 --- a/examples/grpc/middleware/src/main.rs +++ b/examples/grpc/middleware/src/main.rs @@ -22,7 +22,7 @@ impl Greeter for GreeterService { #[tokio::main] async fn main() -> Result<(), std::io::Error> { - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(RouteGrpc::new().add_service(GreeterServer::new(GreeterService))) .await } diff --git a/examples/grpc/reflection/src/main.rs b/examples/grpc/reflection/src/main.rs index 83f801c15e..504d3fb033 100644 --- a/examples/grpc/reflection/src/main.rs +++ b/examples/grpc/reflection/src/main.rs @@ -26,7 +26,7 @@ async fn main() -> Result<(), std::io::Error> { } tracing_subscriber::fmt::init(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run( RouteGrpc::new() .add_service( diff --git a/examples/grpc/routeguide/src/main.rs b/examples/grpc/routeguide/src/main.rs index cc49e5ff31..b6a3494079 100644 --- a/examples/grpc/routeguide/src/main.rs +++ b/examples/grpc/routeguide/src/main.rs @@ -148,7 +148,7 @@ async fn main() -> Result<(), std::io::Error> { } tracing_subscriber::fmt::init(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run( RouteGrpc::new() .add_service(RouteGuideServer::new(RouteGuideService { diff --git a/examples/openapi/auth-apikey/src/main.rs b/examples/openapi/auth-apikey/src/main.rs index 1909ff15ba..590ed327dc 100644 --- a/examples/openapi/auth-apikey/src/main.rs +++ b/examples/openapi/auth-apikey/src/main.rs @@ -84,7 +84,7 @@ async fn main() -> Result<(), std::io::Error> { .nest("/", ui) .data(server_key); - poem::Server::new(TcpListener::bind("127.0.0.1:3000")) + poem::Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/openapi/auth-basic/src/main.rs b/examples/openapi/auth-basic/src/main.rs index 6d8ec12956..1c5a7b37a1 100644 --- a/examples/openapi/auth-basic/src/main.rs +++ b/examples/openapi/auth-basic/src/main.rs @@ -33,7 +33,7 @@ async fn main() -> Result<(), std::io::Error> { OpenApiService::new(Api, "Authorization Demo", "1.0").server("http://localhost:3000/api"); let ui = api_service.swagger_ui(); - poem::Server::new(TcpListener::bind("127.0.0.1:3000")) + poem::Server::new(TcpListener::bind("0.0.0.0:3000")) .run(Route::new().nest("/api", api_service).nest("/", ui)) .await } diff --git a/examples/openapi/auth-github/src/main.rs b/examples/openapi/auth-github/src/main.rs index 531e5f2fa4..797c377a46 100644 --- a/examples/openapi/auth-github/src/main.rs +++ b/examples/openapi/auth-github/src/main.rs @@ -91,7 +91,7 @@ async fn main() -> Result<(), std::io::Error> { OpenApiService::new(Api, "Authorization Demo", "1.0").server("http://localhost:3000/api"); let ui = api_service.swagger_ui(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run( Route::new() .at("/proxy", oauth_token_url_proxy) diff --git a/examples/openapi/auth-multiple/src/main.rs b/examples/openapi/auth-multiple/src/main.rs index 9512a2ee1a..04b057c376 100644 --- a/examples/openapi/auth-multiple/src/main.rs +++ b/examples/openapi/auth-multiple/src/main.rs @@ -99,7 +99,7 @@ async fn main() -> Result<(), std::io::Error> { .nest("/", ui) .data(server_key); - poem::Server::new(TcpListener::bind("127.0.0.1:3000")) + poem::Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/openapi/combined-apis/src/main.rs b/examples/openapi/combined-apis/src/main.rs index e81900d807..3eaa7dfcea 100644 --- a/examples/openapi/combined-apis/src/main.rs +++ b/examples/openapi/combined-apis/src/main.rs @@ -42,7 +42,7 @@ async fn main() -> Result<(), std::io::Error> { .server("http://localhost:3000/api"); let ui = api_service.swagger_ui(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(Route::new().nest("/api", api_service).nest("/", ui)) .await } diff --git a/examples/openapi/content-type-accept/src/main.rs b/examples/openapi/content-type-accept/src/main.rs index b077d78bed..564995136c 100644 --- a/examples/openapi/content-type-accept/src/main.rs +++ b/examples/openapi/content-type-accept/src/main.rs @@ -142,7 +142,7 @@ async fn main() -> Result<(), std::io::Error> { let ui = api_service.swagger_ui(); let yaml = api_service.spec_endpoint(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run( Route::new() .nest("/api", api_service) diff --git a/examples/openapi/custom-payload/src/main.rs b/examples/openapi/custom-payload/src/main.rs index bfcc0a826e..487d0a589c 100644 --- a/examples/openapi/custom-payload/src/main.rs +++ b/examples/openapi/custom-payload/src/main.rs @@ -32,7 +32,7 @@ async fn main() -> Result<(), std::io::Error> { OpenApiService::new(Api, "Hello World", "1.0").server("http://localhost:3000/api"); let ui = api_service.swagger_ui(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(Route::new().nest("/api", api_service).nest("/", ui)) .await } diff --git a/examples/openapi/generics/src/main.rs b/examples/openapi/generics/src/main.rs index 9be68c62cc..3e36140670 100644 --- a/examples/openapi/generics/src/main.rs +++ b/examples/openapi/generics/src/main.rs @@ -36,7 +36,7 @@ async fn main() -> Result<(), std::io::Error> { OpenApiService::new(Api, "Hello World", "1.0").server("http://localhost:3000/api"); let ui = api_service.swagger_ui(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(Route::new().nest("/api", api_service).nest("/", ui)) .await } diff --git a/examples/openapi/hello-world/src/main.rs b/examples/openapi/hello-world/src/main.rs index deb1fdc8b0..ea2525c2f3 100644 --- a/examples/openapi/hello-world/src/main.rs +++ b/examples/openapi/hello-world/src/main.rs @@ -25,7 +25,7 @@ async fn main() -> Result<(), std::io::Error> { OpenApiService::new(Api, "Hello World", "1.0").server("http://localhost:3000/api"); let ui = api_service.swagger_ui(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(Route::new().nest("/api", api_service).nest("/", ui)) .await } diff --git a/examples/openapi/log-with-operation-id/src/main.rs b/examples/openapi/log-with-operation-id/src/main.rs index a6c6894809..77c5394c7b 100644 --- a/examples/openapi/log-with-operation-id/src/main.rs +++ b/examples/openapi/log-with-operation-id/src/main.rs @@ -35,7 +35,7 @@ async fn main() -> Result<(), std::io::Error> { Ok(resp) }); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/openapi/poem-extractor/src/main.rs b/examples/openapi/poem-extractor/src/main.rs index 9d4a757229..af6ddf92c9 100644 --- a/examples/openapi/poem-extractor/src/main.rs +++ b/examples/openapi/poem-extractor/src/main.rs @@ -22,7 +22,7 @@ async fn main() -> Result<(), std::io::Error> { OpenApiService::new(Api, "Poem Extractor", "1.0").server("http://localhost:3000/api"); let ui = api_service.swagger_ui(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run( Route::new() .nest("/api", api_service.data(100i32)) diff --git a/examples/openapi/poem-middleware/src/main.rs b/examples/openapi/poem-middleware/src/main.rs index b97ad6a61c..8f26200f20 100644 --- a/examples/openapi/poem-middleware/src/main.rs +++ b/examples/openapi/poem-middleware/src/main.rs @@ -26,7 +26,7 @@ async fn main() -> Result<(), std::io::Error> { OpenApiService::new(Api, "Poem Middleware", "1.0").server("http://localhost:3000/api"); let ui = api_service.swagger_ui(); - poem::Server::new(TcpListener::bind("127.0.0.1:3000")) + poem::Server::new(TcpListener::bind("0.0.0.0:3000")) .run(Route::new().nest("/api", api_service).nest("/", ui)) .await } diff --git a/examples/openapi/sse/src/main.rs b/examples/openapi/sse/src/main.rs index 51444b0b4c..6690093d11 100644 --- a/examples/openapi/sse/src/main.rs +++ b/examples/openapi/sse/src/main.rs @@ -37,7 +37,7 @@ async fn main() -> Result<(), std::io::Error> { OpenApiService::new(Api, "Hello World", "1.0").server("http://localhost:3000/api"); let ui = api_service.swagger_ui(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(Route::new().nest("/api", api_service).nest("/", ui)) .await } diff --git a/examples/openapi/todos/src/main.rs b/examples/openapi/todos/src/main.rs index 4158f885fe..00026c06ea 100644 --- a/examples/openapi/todos/src/main.rs +++ b/examples/openapi/todos/src/main.rs @@ -158,7 +158,7 @@ async fn main() -> Result<(), Box> { .with(Cors::new()) .data(pool); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(route) .await?; Ok(()) diff --git a/examples/openapi/uniform-response/src/main.rs b/examples/openapi/uniform-response/src/main.rs index bcea502232..6c723eb200 100644 --- a/examples/openapi/uniform-response/src/main.rs +++ b/examples/openapi/uniform-response/src/main.rs @@ -104,7 +104,7 @@ async fn main() -> Result<(), std::io::Error> { .server("http://localhost:3000/api"); let ui = api_service.swagger_ui(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(Route::new().nest("/api", api_service).nest("/", ui)) .await } diff --git a/examples/openapi/union/src/main.rs b/examples/openapi/union/src/main.rs index f151c6f38c..d21ae13ca4 100644 --- a/examples/openapi/union/src/main.rs +++ b/examples/openapi/union/src/main.rs @@ -41,7 +41,7 @@ async fn main() -> Result<(), std::io::Error> { let spec = api_service.spec_endpoint(); let spec_yaml = api_service.spec_endpoint_yaml(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run( Route::new() .nest("/api", api_service) diff --git a/examples/openapi/upload/src/main.rs b/examples/openapi/upload/src/main.rs index 21f1fd88aa..606be9a439 100644 --- a/examples/openapi/upload/src/main.rs +++ b/examples/openapi/upload/src/main.rs @@ -101,7 +101,7 @@ async fn main() -> Result<(), std::io::Error> { .server("http://localhost:3000/api"); let ui = api_service.swagger_ui(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(Route::new().nest("/api", api_service).nest("/", ui)) .await } diff --git a/examples/openapi/users-crud/src/main.rs b/examples/openapi/users-crud/src/main.rs index 45ddd8a4e3..84503d4a88 100644 --- a/examples/openapi/users-crud/src/main.rs +++ b/examples/openapi/users-crud/src/main.rs @@ -143,7 +143,7 @@ async fn main() -> Result<(), std::io::Error> { OpenApiService::new(Api::default(), "Users", "1.0").server("http://localhost:3000/api"); let ui = api_service.swagger_ui(); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(Route::new().nest("/api", api_service).nest("/", ui)) .await } diff --git a/examples/poem/async-graphql/src/main.rs b/examples/poem/async-graphql/src/main.rs index b3b4f9ed4a..3548800313 100644 --- a/examples/poem/async-graphql/src/main.rs +++ b/examples/poem/async-graphql/src/main.rs @@ -39,7 +39,7 @@ async fn main() -> Result<(), std::io::Error> { println!("Playground: http://localhost:3000"); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/auth/src/main.rs b/examples/poem/auth/src/main.rs index b7a9cc2bf2..3cf09af856 100644 --- a/examples/poem/auth/src/main.rs +++ b/examples/poem/auth/src/main.rs @@ -105,7 +105,7 @@ async fn main() -> Result<(), std::io::Error> { .at("/signin", get(signin_ui).post(signin)) .at("/logout", get(logout)) .with(CookieSession::new(CookieConfig::new())); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/basic-auth/src/main.rs b/examples/poem/basic-auth/src/main.rs index cf59468132..1d7a7ed5b4 100644 --- a/examples/poem/basic-auth/src/main.rs +++ b/examples/poem/basic-auth/src/main.rs @@ -62,7 +62,7 @@ async fn main() -> Result<(), std::io::Error> { username: "test".to_string(), password: "123456".to_string(), }); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/catch-panic/src/main.rs b/examples/poem/catch-panic/src/main.rs index 9ff252f414..3d8c087768 100644 --- a/examples/poem/catch-panic/src/main.rs +++ b/examples/poem/catch-panic/src/main.rs @@ -21,7 +21,7 @@ async fn main() -> Result<(), std::io::Error> { .at("/", index) .with(Tracing) .with(CatchPanic::new()); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .name("hello-world") .run(app) .await diff --git a/examples/poem/combined-listeners/src/main.rs b/examples/poem/combined-listeners/src/main.rs index 519fd959ce..883563748e 100644 --- a/examples/poem/combined-listeners/src/main.rs +++ b/examples/poem/combined-listeners/src/main.rs @@ -17,8 +17,8 @@ async fn main() -> Result<(), std::io::Error> { tracing_subscriber::fmt::init(); let app = Route::new().at("/", get(hello)); - let listener = TcpListener::bind("127.0.0.1:3000") - .combine(TcpListener::bind("127.0.0.1:3001")) - .combine(TcpListener::bind("127.0.0.1:3002")); + let listener = TcpListener::bind("0.0.0.0:3000") + .combine(TcpListener::bind("0.0.0.0:3001")) + .combine(TcpListener::bind("0.0.0.0:3002")); Server::new(listener).run(app).await } diff --git a/examples/poem/cookie-session/src/main.rs b/examples/poem/cookie-session/src/main.rs index 31f248a82c..d13ec86456 100644 --- a/examples/poem/cookie-session/src/main.rs +++ b/examples/poem/cookie-session/src/main.rs @@ -22,7 +22,7 @@ async fn main() -> Result<(), std::io::Error> { let app = Route::new() .at("/", get(count)) .with(CookieSession::new(CookieConfig::default().secure(false))); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/csrf/src/main.rs b/examples/poem/csrf/src/main.rs index 342d09f547..450122acb5 100644 --- a/examples/poem/csrf/src/main.rs +++ b/examples/poem/csrf/src/main.rs @@ -60,7 +60,7 @@ async fn main() -> Result<(), std::io::Error> { let app = Route::new() .at("/", get(login_ui).post(login)) .with(Csrf::new()); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/custom-error/src/main.rs b/examples/poem/custom-error/src/main.rs index 269c1366e8..7f7f0d754d 100644 --- a/examples/poem/custom-error/src/main.rs +++ b/examples/poem/custom-error/src/main.rs @@ -31,7 +31,7 @@ async fn main() -> Result<(), std::io::Error> { tracing_subscriber::fmt::init(); let app = Route::new().at("/", get(hello)); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/custom-extractor/src/main.rs b/examples/poem/custom-extractor/src/main.rs index 713a14c4a4..fee3290f87 100644 --- a/examples/poem/custom-extractor/src/main.rs +++ b/examples/poem/custom-extractor/src/main.rs @@ -31,7 +31,7 @@ async fn main() -> Result<(), std::io::Error> { tracing_subscriber::fmt::init(); let app = Route::new().at("/", get(index)); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/embed-files/src/main.rs b/examples/poem/embed-files/src/main.rs index 11db4ab382..a4dd065ed2 100644 --- a/examples/poem/embed-files/src/main.rs +++ b/examples/poem/embed-files/src/main.rs @@ -19,7 +19,7 @@ async fn main() -> Result<(), std::io::Error> { let app = Route::new() .at("/", EmbeddedFileEndpoint::::new("index.html")) .nest("/files", EmbeddedFilesEndpoint::::new()); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/graceful-shutdown/src/main.rs b/examples/poem/graceful-shutdown/src/main.rs index a4afbb863d..835af9e176 100644 --- a/examples/poem/graceful-shutdown/src/main.rs +++ b/examples/poem/graceful-shutdown/src/main.rs @@ -45,7 +45,7 @@ async fn main() -> Result<(), std::io::Error> { let app = Route::new().at("/", get(index)).at("/event", get(event)); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run_with_graceful_shutdown( app, async move { diff --git a/examples/poem/handling-404/src/main.rs b/examples/poem/handling-404/src/main.rs index 2188af875c..b93315be4c 100644 --- a/examples/poem/handling-404/src/main.rs +++ b/examples/poem/handling-404/src/main.rs @@ -24,7 +24,7 @@ async fn main() -> Result<(), std::io::Error> { .body("haha") }); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/hello-world/src/main.rs b/examples/poem/hello-world/src/main.rs index 2cf6013a5e..59808f1c8e 100644 --- a/examples/poem/hello-world/src/main.rs +++ b/examples/poem/hello-world/src/main.rs @@ -15,7 +15,7 @@ async fn main() -> Result<(), std::io::Error> { tracing_subscriber::fmt::init(); let app = Route::new().at("/hello/:name", get(hello)).with(Tracing); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .name("hello-world") .run(app) .await diff --git a/examples/poem/i18n/src/main.rs b/examples/poem/i18n/src/main.rs index f766514c1d..f00928659e 100644 --- a/examples/poem/i18n/src/main.rs +++ b/examples/poem/i18n/src/main.rs @@ -51,7 +51,7 @@ async fn main() -> Result<(), std::io::Error> { .at("/welcome_hashmap/:name", get(welcome_hashmap)) .with(Tracing) .data(resources); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .name("hello-world") .run(app) .await diff --git a/examples/poem/json/src/main.rs b/examples/poem/json/src/main.rs index 7830aef222..724bb42cfa 100644 --- a/examples/poem/json/src/main.rs +++ b/examples/poem/json/src/main.rs @@ -22,7 +22,7 @@ async fn main() -> Result<(), std::io::Error> { tracing_subscriber::fmt::init(); let app = Route::new().at("/hello", post(hello)); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/middleware/src/main.rs b/examples/poem/middleware/src/main.rs index b9327413da..9d840ee52b 100644 --- a/examples/poem/middleware/src/main.rs +++ b/examples/poem/middleware/src/main.rs @@ -50,7 +50,7 @@ async fn main() -> Result<(), std::io::Error> { tracing_subscriber::fmt::init(); let app = Route::new().at("/", get(index)).with(Log); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/middleware_fn/src/main.rs b/examples/poem/middleware_fn/src/main.rs index 15aeacec2c..ceca503ed1 100644 --- a/examples/poem/middleware_fn/src/main.rs +++ b/examples/poem/middleware_fn/src/main.rs @@ -33,7 +33,7 @@ async fn main() -> Result<(), std::io::Error> { tracing_subscriber::fmt::init(); let app = Route::new().at("/", get(index)).around(log); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/mongodb/src/main.rs b/examples/poem/mongodb/src/main.rs index 066485aff4..15c21a1d2f 100644 --- a/examples/poem/mongodb/src/main.rs +++ b/examples/poem/mongodb/src/main.rs @@ -66,7 +66,7 @@ async fn main() -> io::Result<()> { .database("test"); let collection = mongodb.collection::("user"); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run( Route::new() .at("/user", get(get_users).post(create_user)) diff --git a/examples/poem/nested-routing/src/main.rs b/examples/poem/nested-routing/src/main.rs index e160fa3c57..2e9c059b0b 100644 --- a/examples/poem/nested-routing/src/main.rs +++ b/examples/poem/nested-routing/src/main.rs @@ -17,7 +17,7 @@ async fn main() -> Result<(), std::io::Error> { tracing_subscriber::fmt::init(); let app = Route::new().nest("/api", api()); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/opentelemetry-jaeger/Cargo.toml b/examples/poem/opentelemetry-jaeger/Cargo.toml index 74fcb3cdfc..ddd7832515 100644 --- a/examples/poem/opentelemetry-jaeger/Cargo.toml +++ b/examples/poem/opentelemetry-jaeger/Cargo.toml @@ -8,9 +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.20.0", features = ["metrics"] } -opentelemetry-http = { version = "0.9.0" } -opentelemetry-jaeger = { version = "0.19.0", features = [ +opentelemetry = { version = "0.21.0", features = ["metrics"] } +opentelemetry_sdk = "0.21.0" +opentelemetry-http = { version = "0.10.0" } +opentelemetry-jaeger = { version = "0.20.0", features = [ "rt-tokio", "collector_client", "hyper_collector_client", diff --git a/examples/poem/opentelemetry-jaeger/src/client.rs b/examples/poem/opentelemetry-jaeger/src/client.rs index 87db60f578..e3a49a72e2 100644 --- a/examples/poem/opentelemetry-jaeger/src/client.rs +++ b/examples/poem/opentelemetry-jaeger/src/client.rs @@ -2,11 +2,11 @@ use std::str::FromStr; use opentelemetry::{ global, - sdk::{propagation::TraceContextPropagator, trace::Tracer}, trace::{FutureExt, TraceContextExt, Tracer as _}, Context, KeyValue, }; use opentelemetry_http::HeaderInjector; +use opentelemetry_sdk::{propagation::TraceContextPropagator, trace::Tracer}; use reqwest::{Client, Method, Url}; fn init_tracer() -> Tracer { @@ -15,7 +15,7 @@ fn init_tracer() -> Tracer { .with_service_name("poem") .with_endpoint("http://localhost:14268/api/traces") .with_hyper() - .install_batch(opentelemetry::runtime::Tokio) + .install_batch(opentelemetry_sdk::runtime::Tokio) .unwrap() } diff --git a/examples/poem/opentelemetry-jaeger/src/server1.rs b/examples/poem/opentelemetry-jaeger/src/server1.rs index bde8849e4d..c04d04ae98 100644 --- a/examples/poem/opentelemetry-jaeger/src/server1.rs +++ b/examples/poem/opentelemetry-jaeger/src/server1.rs @@ -2,11 +2,11 @@ use std::str::FromStr; use opentelemetry::{ global, - sdk::{propagation::TraceContextPropagator, trace::Tracer}, trace::{FutureExt, SpanKind, TraceContextExt, Tracer as _}, Context, KeyValue, }; use opentelemetry_http::HeaderInjector; +use opentelemetry_sdk::{propagation::TraceContextPropagator, trace::Tracer}; use poem::{ get, handler, http::Method, @@ -23,7 +23,7 @@ fn init_tracer() -> Tracer { .with_service_name("poem") .with_endpoint("http://localhost:14268/api/traces") .with_hyper() - .install_batch(opentelemetry::runtime::Tokio) + .install_batch(opentelemetry_sdk::runtime::Tokio) .unwrap() } @@ -80,7 +80,7 @@ async fn main() -> Result<(), std::io::Error> { .with(OpenTelemetryMetrics::new()) .with(OpenTelemetryTracing::new(tracer)); - Server::new(TcpListener::bind("127.0.0.1:3001")) + Server::new(TcpListener::bind("0.0.0.0:3001")) .run(app) .await } diff --git a/examples/poem/opentelemetry-jaeger/src/server2.rs b/examples/poem/opentelemetry-jaeger/src/server2.rs index 701cc85a99..20a27a4063 100644 --- a/examples/poem/opentelemetry-jaeger/src/server2.rs +++ b/examples/poem/opentelemetry-jaeger/src/server2.rs @@ -1,7 +1,5 @@ -use opentelemetry::{ - global, - sdk::{propagation::TraceContextPropagator, trace::Tracer}, -}; +use opentelemetry::global; +use opentelemetry_sdk::{propagation::TraceContextPropagator, trace::Tracer}; use poem::{ get, handler, listener::TcpListener, @@ -15,7 +13,7 @@ fn init_tracer() -> Tracer { .with_service_name("poem") .with_endpoint("http://localhost:14268/api/traces") .with_hyper() - .install_batch(opentelemetry::runtime::Tokio) + .install_batch(opentelemetry_sdk::runtime::Tokio) .unwrap() } @@ -39,7 +37,7 @@ async fn main() -> Result<(), std::io::Error> { .with(OpenTelemetryMetrics::new()) .with(OpenTelemetryTracing::new(tracer)); - Server::new(TcpListener::bind("127.0.0.1:3002")) + Server::new(TcpListener::bind("0.0.0.0:3002")) .run(app) .await } diff --git a/examples/poem/redis-session/src/main.rs b/examples/poem/redis-session/src/main.rs index d053443dc5..0ae7237b03 100644 --- a/examples/poem/redis-session/src/main.rs +++ b/examples/poem/redis-session/src/main.rs @@ -26,7 +26,7 @@ async fn main() -> Result<(), std::io::Error> { CookieConfig::default().secure(false), RedisStorage::new(ConnectionManager::new(client).await.unwrap()), )); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/sse/src/main.rs b/examples/poem/sse/src/main.rs index e9fab7ac9d..a70c9e47e1 100644 --- a/examples/poem/sse/src/main.rs +++ b/examples/poem/sse/src/main.rs @@ -44,7 +44,7 @@ async fn main() -> Result<(), std::io::Error> { tracing_subscriber::fmt::init(); let app = Route::new().at("/", get(index)).at("/event", get(event)); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/static-files/src/main.rs b/examples/poem/static-files/src/main.rs index 12f5d67aab..4c0e466a36 100644 --- a/examples/poem/static-files/src/main.rs +++ b/examples/poem/static-files/src/main.rs @@ -11,7 +11,7 @@ async fn main() -> Result<(), std::io::Error> { "/", StaticFilesEndpoint::new("./poem/static-files/files").show_files_listing(), ); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/tera-templating/src/main.rs b/examples/poem/tera-templating/src/main.rs index b5ce0e3ac6..782ef910da 100644 --- a/examples/poem/tera-templating/src/main.rs +++ b/examples/poem/tera-templating/src/main.rs @@ -33,7 +33,7 @@ fn hello(Path(name): Path) -> Result, poem::Error> { #[tokio::main] async fn main() -> Result<(), std::io::Error> { let app = Route::new().at("/hello/:name", get(hello)); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/tls-reload/src/main.rs b/examples/poem/tls-reload/src/main.rs index 5c30548023..31b5acdf48 100644 --- a/examples/poem/tls-reload/src/main.rs +++ b/examples/poem/tls-reload/src/main.rs @@ -19,7 +19,7 @@ async fn main() -> Result<(), std::io::Error> { let app = Route::new().at("/", get(index)); - let listener = TcpListener::bind("127.0.0.1:3000").rustls(async_stream::stream! { + let listener = TcpListener::bind("0.0.0.0:3000").rustls(async_stream::stream! { loop { if let Ok(tls_config) = load_tls_config() { yield tls_config; diff --git a/examples/poem/tls/src/main.rs b/examples/poem/tls/src/main.rs index 1323caffb6..eefcd5c6e4 100644 --- a/examples/poem/tls/src/main.rs +++ b/examples/poem/tls/src/main.rs @@ -75,7 +75,7 @@ async fn main() -> Result<(), std::io::Error> { let app = Route::new().at("/", get(index)); - let listener = TcpListener::bind("127.0.0.1:3000") + let listener = TcpListener::bind("0.0.0.0:3000") .rustls(RustlsConfig::new().fallback(RustlsCertificate::new().key(KEY).cert(CERT))); Server::new(listener).run(app).await } diff --git a/examples/poem/tokio-metrics/src/main.rs b/examples/poem/tokio-metrics/src/main.rs index 976cc02219..bed226332b 100644 --- a/examples/poem/tokio-metrics/src/main.rs +++ b/examples/poem/tokio-metrics/src/main.rs @@ -34,7 +34,7 @@ async fn main() -> Result<(), std::io::Error> { .at("/a", get(a).with(metrics_a)) .at("/b", get(b).with(metrics_b)) .with(Tracing); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/tonic/src/main.rs b/examples/poem/tonic/src/main.rs index cc6e37a875..08195bad33 100644 --- a/examples/poem/tonic/src/main.rs +++ b/examples/poem/tonic/src/main.rs @@ -39,7 +39,7 @@ async fn main() -> Result<(), std::io::Error> { .compat(), ); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/tower-layer/src/main.rs b/examples/poem/tower-layer/src/main.rs index 519203f98f..cc6c759748 100644 --- a/examples/poem/tower-layer/src/main.rs +++ b/examples/poem/tower-layer/src/main.rs @@ -21,7 +21,7 @@ async fn main() -> Result<(), std::io::Error> { "/", get(hello).with(RateLimitLayer::new(5, Duration::from_secs(30)).compat()), ); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/upload/src/main.rs b/examples/poem/upload/src/main.rs index 9e0405d1f4..0069c76e0d 100644 --- a/examples/poem/upload/src/main.rs +++ b/examples/poem/upload/src/main.rs @@ -50,7 +50,7 @@ async fn main() -> Result<(), std::io::Error> { tracing_subscriber::fmt::init(); let app = Route::new().at("/", get(index).post(upload)); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/examples/poem/websocket-chat/src/main.rs b/examples/poem/websocket-chat/src/main.rs index 4a86f90744..cbb058fc61 100644 --- a/examples/poem/websocket-chat/src/main.rs +++ b/examples/poem/websocket-chat/src/main.rs @@ -102,7 +102,7 @@ async fn main() -> Result<(), std::io::Error> { get(ws.data(tokio::sync::broadcast::channel::(32).0)), ); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/poem-derive/src/lib.rs b/poem-derive/src/lib.rs index 771a960382..47a730194d 100644 --- a/poem-derive/src/lib.rs +++ b/poem-derive/src/lib.rs @@ -3,7 +3,7 @@ #![doc(html_favicon_url = "https://raw.githubusercontent.com/poem-web/poem/master/favicon.ico")] #![doc(html_logo_url = "https://raw.githubusercontent.com/poem-web/poem/master/logo.png")] #![forbid(unsafe_code)] -#![deny(private_in_public, unreachable_pub)] +#![deny(unreachable_pub)] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] diff --git a/poem-grpc-build/src/lib.rs b/poem-grpc-build/src/lib.rs index 5a502120fe..c2dea0882f 100644 --- a/poem-grpc-build/src/lib.rs +++ b/poem-grpc-build/src/lib.rs @@ -3,7 +3,7 @@ #![doc(html_favicon_url = "https://raw.githubusercontent.com/poem-web/poem/master/favicon.ico")] #![doc(html_logo_url = "https://raw.githubusercontent.com/poem-web/poem/master/logo.png")] #![forbid(unsafe_code)] -#![deny(private_in_public, unreachable_pub)] +#![deny(unreachable_pub)] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] diff --git a/poem-grpc/README.md b/poem-grpc/README.md index da351dbeae..56caecd9d4 100644 --- a/poem-grpc/README.md +++ b/poem-grpc/README.md @@ -52,7 +52,7 @@ impl Greeter for GreeterService { #[tokio::main] async fn main() -> Result<(), std::io::Error> { let route = RouteGrpc::new().add_service(GreeterServer::new(GreeterService)); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(route) .await } diff --git a/poem-grpc/src/health.rs b/poem-grpc/src/health.rs index 5f40635161..21cd7a5f81 100644 --- a/poem-grpc/src/health.rs +++ b/poem-grpc/src/health.rs @@ -6,7 +6,7 @@ use tokio::sync::watch::{Receiver, Sender}; use crate::{Code, Request, Response, Service, Status, Streaming}; -#[allow(private_in_public, unreachable_pub)] +#[allow(unreachable_pub)] #[allow(clippy::derive_partial_eq_without_eq)] mod proto { include!(concat!(env!("OUT_DIR"), "/grpc.health.v1.rs")); diff --git a/poem-grpc/src/lib.rs b/poem-grpc/src/lib.rs index 75b299f654..75ba9ce9cc 100644 --- a/poem-grpc/src/lib.rs +++ b/poem-grpc/src/lib.rs @@ -3,7 +3,7 @@ #![doc(html_favicon_url = "https://raw.githubusercontent.com/poem-web/poem/master/favicon.ico")] #![doc(html_logo_url = "https://raw.githubusercontent.com/poem-web/poem/master/logo.png")] #![forbid(unsafe_code)] -#![deny(private_in_public, unreachable_pub)] +#![deny(unreachable_pub)] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] diff --git a/poem-grpc/src/reflection.rs b/poem-grpc/src/reflection.rs index 9b8eababc5..dc28e3b77c 100644 --- a/poem-grpc/src/reflection.rs +++ b/poem-grpc/src/reflection.rs @@ -10,7 +10,7 @@ use proto::{ use crate::{include_file_descriptor_set, Code, Request, Response, Service, Status, Streaming}; -#[allow(private_in_public, unreachable_pub)] +#[allow(unreachable_pub)] #[allow(clippy::enum_variant_names)] #[allow(clippy::derive_partial_eq_without_eq)] mod proto { diff --git a/poem-grpc/src/test_harness.rs b/poem-grpc/src/test_harness.rs index d6049a1c16..ca49b1cdef 100644 --- a/poem-grpc/src/test_harness.rs +++ b/poem-grpc/src/test_harness.rs @@ -1,4 +1,4 @@ -#[allow(private_in_public, unreachable_pub)] +#[allow(unreachable_pub)] mod proto { include!(concat!(env!("OUT_DIR"), "/test_harness.rs")); } diff --git a/poem-lambda/src/lib.rs b/poem-lambda/src/lib.rs index 63cb2bc06c..2b0478766b 100644 --- a/poem-lambda/src/lib.rs +++ b/poem-lambda/src/lib.rs @@ -3,7 +3,7 @@ #![doc(html_favicon_url = "https://raw.githubusercontent.com/poem-web/poem/master/favicon.ico")] #![doc(html_logo_url = "https://raw.githubusercontent.com/poem-web/poem/master/logo.png")] #![forbid(unsafe_code)] -#![deny(private_in_public, unreachable_pub)] +#![deny(unreachable_pub)] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] diff --git a/poem-openapi-derive/src/lib.rs b/poem-openapi-derive/src/lib.rs index 0ef70d8dcb..acfadf85a6 100644 --- a/poem-openapi-derive/src/lib.rs +++ b/poem-openapi-derive/src/lib.rs @@ -3,7 +3,7 @@ #![doc(html_favicon_url = "https://raw.githubusercontent.com/poem-web/poem/master/favicon.ico")] #![doc(html_logo_url = "https://raw.githubusercontent.com/poem-web/poem/master/logo.png")] #![forbid(unsafe_code)] -#![deny(private_in_public, unreachable_pub)] +#![deny(unreachable_pub)] #[macro_use] mod validators; diff --git a/poem-openapi/CHANGELOG.md b/poem-openapi/CHANGELOG.md index 49dcaf5770..1c1bfdc00e 100644 --- a/poem-openapi/CHANGELOG.md +++ b/poem-openapi/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # [3.0.6] 2023-09-07 +- add [`prost-wkt-types` crate](https://crates.io/crates/prost-wkt-types) support [#689](https://github.com/poem-web/poem/pull/689) +- add [`geo-types` crate](https://crates.io/crates/geo-types) support [#693](https://github.com/poem-web/poem/pull/693) +- count string length correctly in OpenAPI validators [#666](https://github.com/poem-web/poem/pull/666) +- Support for custom hash functions for HashMap/HashSet [#654](https://github.com/poem-web/poem/pull/654) +- Misplaced ``` in swagger_ui HTML template [#660](https://github.com/poem-web/poem/issues/660) - for `read-only` properties, can use `default` to specify a function for creating a default value. [#647](https://github.com/poem-web/poem/issues/647) ```rust diff --git a/poem-openapi/Cargo.toml b/poem-openapi/Cargo.toml index d1f8b7d417..66f3d6ef31 100644 --- a/poem-openapi/Cargo.toml +++ b/poem-openapi/Cargo.toml @@ -22,6 +22,7 @@ email = ["email_address"] hostname = ["hostname-validator"] static-files = ["poem/static-files"] websocket = ["poem/websocket"] +geo = ["dep:geo-types", "dep:geojson"] [dependencies] poem-openapi-derive.workspace = true @@ -68,6 +69,9 @@ bson = { version = "2.0.0", optional = true } rust_decimal = { version = "1.22.0", optional = true } humantime = { version = "2.1.0", optional = true } ipnet = { version = "2.7.1", optional = true } +prost-wkt-types = { version = "0.5.0", optional = true} +geo-types = { version = "0.7.12", optional = true } +geojson = { version = "0.24.1", features = ["geo-types"], optional = true } [dev-dependencies] tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/poem-openapi/README.md b/poem-openapi/README.md index 0fe19027d8..294df521c8 100644 --- a/poem-openapi/README.md +++ b/poem-openapi/README.md @@ -50,23 +50,25 @@ important business implementations. To avoid compiling unused dependencies, Poem gates certain features, some of which are disabled by default: -| Feature | Description | -|------------------|----------------------------------------------------------------------------------| -| chrono | Integrate with the [`chrono` crate](https://crates.io/crates/chrono). | -| time | Integrate with the [`time` crate](https://crates.io/crates/time). | -| humantime | Integrate with the [`humantime` crate](https://crates.io/crates/humantime) | -| openapi-explorer | Add OpenAPI Explorer support | -| swagger-ui | Add swagger UI support | -| rapidoc | Add RapiDoc UI support | -| redoc | Add Redoc UI support | -| email | Support for email address string | -| hostname | Support for hostname string | -| uuid | Integrate with the [`uuid` crate](https://crates.io/crates/uuid) | -| url | Integrate with the [`url` crate](https://crates.io/crates/url) | -| bson | Integrate with the [`bson` crate](https://crates.io/crates/bson) | -| rust_decimal | Integrate with the [`rust_decimal` crate](https://crates.io/crates/rust_decimal) | -| static-files | Support for static file response | -| websocket | Support for websocket | +| Feature | Description | +|------------------|----------------------------------------------------------------------------------------| +| chrono | Integrate with the [`chrono` crate](https://crates.io/crates/chrono). | +| time | Integrate with the [`time` crate](https://crates.io/crates/time). | +| humantime | Integrate with the [`humantime` crate](https://crates.io/crates/humantime) | +| openapi-explorer | Add OpenAPI Explorer support | +| swagger-ui | Add swagger UI support | +| rapidoc | Add RapiDoc UI support | +| redoc | Add Redoc UI support | +| email | Support for email address string | +| hostname | Support for hostname string | +| uuid | Integrate with the [`uuid` crate](https://crates.io/crates/uuid) | +| url | Integrate with the [`url` crate](https://crates.io/crates/url) | +| geo | Integrate with the [`geo-types` crate](https://crates.io/crates/geo-types) | +| bson | Integrate with the [`bson` crate](https://crates.io/crates/bson) | +| rust_decimal | Integrate with the [`rust_decimal` crate](https://crates.io/crates/rust_decimal) | +| prost-wkt-types | Integrate with the [`prost-wkt-types` crate](https://crates.io/crates/prost-wkt-types) | +| static-files | Support for static file response | +| websocket | Support for websocket | ## Safety @@ -98,7 +100,7 @@ async fn main() -> Result<(), std::io::Error> { let ui = api_service.swagger_ui(); let app = Route::new().nest("/api", api_service).nest("/", ui); - poem::Server::new(TcpListener::bind("127.0.0.1:3000")) + poem::Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/poem-openapi/src/lib.rs b/poem-openapi/src/lib.rs index 9e450234e0..b2bd86e6c5 100644 --- a/poem-openapi/src/lib.rs +++ b/poem-openapi/src/lib.rs @@ -92,27 +92,30 @@ //! To avoid compiling unused dependencies, Poem gates certain features, some of //! which are disabled by default: //! -//! | Feature | Description | -//! |------------|-----------------------------------------------------------------------| -//! | chrono | Integrate with the [`chrono` crate](https://crates.io/crates/chrono) | -//! | time | Integrate with the [`time` crate](https://crates.io/crates/time). | -//! | humantime | Integrate with the [`humantime` crate](https://crates.io/crates/humantime) | -//! | openapi-explorer | Add OpenAPI Explorer support | -//! | swagger-ui | Add swagger UI support | -//! | rapidoc | Add RapiDoc UI support | -//! | redoc | Add Redoc UI support | -//! | email | Support for email address string | -//! | hostname | Support for hostname string | -//! | uuid | Integrate with the [`uuid` crate](https://crates.io/crates/uuid)| -//! | url | Integrate with the [`url` crate](https://crates.io/crates/url) | -//! | bson | Integrate with the [`bson` crate](https://crates.io/crates/bson) | -//! | rust_decimal | Integrate with the [`rust_decimal` crate](https://crates.io/crates/rust_decimal) | -//! | static-files | Support for static file response | +//! | Feature | Description | +//! |------------------|----------------------------------------------------------------------------------------| +//! | chrono | Integrate with the [`chrono` crate](https://crates.io/crates/chrono). | +//! | time | Integrate with the [`time` crate](https://crates.io/crates/time). | +//! | humantime | Integrate with the [`humantime` crate](https://crates.io/crates/humantime) | +//! | openapi-explorer | Add OpenAPI Explorer support | +//! | swagger-ui | Add swagger UI support | +//! | rapidoc | Add RapiDoc UI support | +//! | redoc | Add Redoc UI support | +//! | email | Support for email address string | +//! | hostname | Support for hostname string | +//! | uuid | Integrate with the [`uuid` crate](https://crates.io/crates/uuid) | +//! | url | Integrate with the [`url` crate](https://crates.io/crates/url) | +//! | geo | Integrate with the [`geo-types` crate](https://crates.io/crates/geo-types) | +//! | bson | Integrate with the [`bson` crate](https://crates.io/crates/bson) | +//! | rust_decimal | Integrate with the [`rust_decimal` crate](https://crates.io/crates/rust_decimal) | +//! | prost-wkt-types | Integrate with the [`prost-wkt-types` crate](https://crates.io/crates/prost-wkt-types) | +//! | static-files | Support for static file response | +//! | websocket | Support for websocket | #![doc(html_favicon_url = "https://raw.githubusercontent.com/poem-web/poem/master/favicon.ico")] #![doc(html_logo_url = "https://raw.githubusercontent.com/poem-web/poem/master/logo.png")] #![forbid(unsafe_code)] -#![deny(private_in_public, unreachable_pub)] +#![deny(unreachable_pub)] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(rustdoc::broken_intra_doc_links)] #![warn(missing_docs)] diff --git a/poem-openapi/src/types/external/geo.rs b/poem-openapi/src/types/external/geo.rs new file mode 100644 index 0000000000..cbab0afc41 --- /dev/null +++ b/poem-openapi/src/types/external/geo.rs @@ -0,0 +1,115 @@ +use geo_types::*; + +use crate::types::Type; + +trait GeoJson { + type Coordinates: Type; +} + +macro_rules! impl_geojson_types { + ($geometry:tt, $name:literal, $coordinates:ty) => { + impl GeoJson for $geometry { + type Coordinates = $coordinates; + } + + impl crate::types::Type for $geometry { + const IS_REQUIRED: bool = true; + type RawValueType = Self; + type RawElementValueType = Self; + + fn name() -> ::std::borrow::Cow<'static, str> { + concat!("GeoJSON<", $name, ">").into() + } + + fn schema_ref() -> crate::registry::MetaSchemaRef { + crate::registry::MetaSchemaRef::Reference(Self::name().into_owned()) + } + + fn register(registry: &mut crate::registry::Registry) { + registry.create_schema::(Self::name().into_owned(), |registry| { + String::register(registry); + <::Coordinates>::register(registry); + crate::registry::MetaSchema { + required: vec!["type", "coordinates"], + properties: vec![ + ("type", String::schema_ref()), + ( + "coordinates", + <::Coordinates>::schema_ref(), + ), + ], + ..crate::registry::MetaSchema::new("object") + } + }) + } + + fn as_raw_value(&self) -> Option<&Self::RawValueType> { + Some(self) + } + + fn raw_element_iter<'a>( + &'a self, + ) -> Box + 'a> { + Box::new(IntoIterator::into_iter(self.as_raw_value())) + } + } + + impl crate::types::ParseFromJSON for $geometry { + fn parse_from_json( + value: Option<::serde_json::Value>, + ) -> Result> { + let value = value.ok_or(crate::types::ParseError::expected_input())?; + Self::try_from(geojson::Geometry::try_from(value)?).map_err(Into::into) + } + } + + impl crate::types::ToJSON for $geometry { + fn to_json(&self) -> Option<::serde_json::Value> { + Some( + ::serde_json::Map::::from( + &geojson::Geometry::from(self), + ) + .into(), + ) + } + } + }; +} + +impl_geojson_types!(Point, "Point", [T; 2]); +impl_geojson_types!(MultiPoint, "MultiPoint", Vec<[T; 2]>); +impl_geojson_types!(LineString, "LineString", Vec<[T; 2]>); +impl_geojson_types!(MultiLineString, "MultiLineString", Vec>); +impl_geojson_types!(Polygon, "Polygon", Vec>); +impl_geojson_types!(MultiPolygon, "MultiPolygon", Vec>>); + +#[cfg(test)] +mod tests { + use geo_types::Point; + + use crate::types::{ParseFromJSON, ToJSON}; + + fn point_geo() -> Point { + Point::new(1.0, 2.0) + } + + fn point_json() -> serde_json::Value { + serde_json::json!({ + "type": "Point", + "coordinates": [1.0, 2.0] + }) + } + + #[test] + fn serializes_geo_to_json() { + assert_eq!(point_json(), point_geo().to_json().unwrap()) + } + + #[test] + fn deserializes_json_to_geo() { + assert_eq!( + Point::parse_from_json(Some(point_json())).unwrap(), + point_geo() + ) + } +} diff --git a/poem-openapi/src/types/external/mod.rs b/poem-openapi/src/types/external/mod.rs index 101ee448c2..ce4af05f60 100644 --- a/poem-openapi/src/types/external/mod.rs +++ b/poem-openapi/src/types/external/mod.rs @@ -10,6 +10,8 @@ mod chrono; #[cfg(feature = "rust_decimal")] mod decimal; mod floats; +#[cfg(feature = "geo")] +mod geo; mod hashmap; mod hashset; #[cfg(feature = "humantime")] @@ -17,6 +19,8 @@ mod humantime; mod integers; mod ip; mod optional; +#[cfg(feature = "prost-wkt-types")] +mod prost_wkt_types; mod regex; mod slice; mod string; diff --git a/poem-openapi/src/types/external/prost_wkt_types/mod.rs b/poem-openapi/src/types/external/prost_wkt_types/mod.rs new file mode 100644 index 0000000000..15be7c48a9 --- /dev/null +++ b/poem-openapi/src/types/external/prost_wkt_types/mod.rs @@ -0,0 +1,2 @@ +mod r#struct; +mod value; diff --git a/poem-openapi/src/types/external/prost_wkt_types/struct.rs b/poem-openapi/src/types/external/prost_wkt_types/struct.rs new file mode 100644 index 0000000000..29c7461265 --- /dev/null +++ b/poem-openapi/src/types/external/prost_wkt_types/struct.rs @@ -0,0 +1,110 @@ +use std::borrow::Cow; + +use prost_wkt_types::Value; + +use crate::{ + registry::{MetaSchema, MetaSchemaRef}, + types::{ParseError, ParseFromJSON, ParseResult, ToJSON, Type}, +}; + +impl Type for prost_wkt_types::Struct { + const IS_REQUIRED: bool = true; + + type RawValueType = Self; + + type RawElementValueType = ::RawValueType; + + fn name() -> Cow<'static, str> { + "Protobuf Struct".into() + } + + fn schema_ref() -> MetaSchemaRef { + MetaSchemaRef::Inline(Box::new(MetaSchema { + additional_properties: Some(Box::new(Value::schema_ref())), + ..MetaSchema::new("object") + })) + } + + fn as_raw_value(&self) -> Option<&Self::RawValueType> { + Some(self) + } + + fn raw_element_iter<'a>( + &'a self, + ) -> Box + 'a> { + self.fields.raw_element_iter() + } + + fn is_empty(&self) -> bool { + self.fields.is_empty() + } +} + +impl ParseFromJSON for prost_wkt_types::Struct { + fn parse_from_json(value: Option) -> ParseResult { + let value = value.unwrap_or_default(); + if let serde_json::Value::Object(_) = &value { + serde_json::from_value::(value) + .map_err(|e| ParseError::custom(e.to_string())) + } else { + Err(ParseError::expected_type(value)) + } + } +} + +impl ToJSON for prost_wkt_types::Struct { + fn to_json(&self) -> Option { + serde_json::to_value(self).ok() + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use prost_wkt_types::Value; + use serde_json::json; + + use super::*; + + #[test] + fn parse_from_parameters() { + let prost_struct = prost_wkt_types::Struct::parse_from_json(Some(json!( + { + "f1":10_f64, + "f2":"Hi", + "f3":true, + "f4":null, + "f5": {"fa": "Hello"}, + "f6": [1,2,3] + } + ))) + .unwrap(); + + assert_eq!( + prost_struct.fields.get("f1").unwrap(), + &Value::number(10_f64) + ); + assert_eq!( + prost_struct.fields.get("f2").unwrap(), + &Value::string("Hi".to_string()) + ); + assert_eq!(prost_struct.fields.get("f3").unwrap(), &Value::bool(true)); + assert_eq!(prost_struct.fields.get("f4").unwrap(), &Value::null()); + assert_eq!( + prost_struct.fields.get("f5").unwrap(), + &Value::pb_struct(HashMap::from([( + "fa".into(), + Value::string("Hello".into()) + )])) + ); + assert_eq!( + prost_struct.fields.get("f6").unwrap(), + &Value::pb_list(vec![ + Value::number(1_f64), + Value::number(2_f64), + Value::number(3_f64) + ]) + ); + } +} diff --git a/poem-openapi/src/types/external/prost_wkt_types/value.rs b/poem-openapi/src/types/external/prost_wkt_types/value.rs new file mode 100644 index 0000000000..7bd4972433 --- /dev/null +++ b/poem-openapi/src/types/external/prost_wkt_types/value.rs @@ -0,0 +1,109 @@ +use std::borrow::Cow; + +use prost_wkt_types::value::Kind; + +use crate::{ + registry::{MetaSchema, MetaSchemaRef}, + types::{ParseError, ParseFromJSON, ParseResult, ToJSON, Type}, +}; + +impl Type for prost_wkt_types::Value { + const IS_REQUIRED: bool = true; + + type RawValueType = prost_wkt_types::Value; + + type RawElementValueType = prost_wkt_types::Value; + + fn name() -> Cow<'static, str> { + "Protobuf Value".into() + } + + fn schema_ref() -> MetaSchemaRef { + MetaSchemaRef::Inline(Box::new(MetaSchema::ANY)) + } + + fn as_raw_value(&self) -> Option<&Self::RawValueType> { + Some(self) + } + + fn raw_element_iter<'a>( + &'a self, + ) -> Box + 'a> { + Box::new(self.as_raw_value().into_iter()) + } + fn is_empty(&self) -> bool { + matches!(self.kind, Some(Kind::NullValue(_)) | None) + } +} + +impl ParseFromJSON for prost_wkt_types::Value { + fn parse_from_json(value: Option) -> ParseResult { + let value = value.unwrap_or_default(); + serde_json::from_value(value).map_err(|e| ParseError::custom(e.to_string())) + } +} + +impl ToJSON for prost_wkt_types::Value { + fn to_json(&self) -> Option { + serde_json::to_value(self).ok() + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use prost_wkt_types::Value; + use serde_json::json; + + use super::*; + + #[test] + fn parse_from_number() { + let value = Value::parse_from_json(Some(json!(10_f64))).unwrap(); + assert_eq!(value, Value::number(10_f64)); + } + + #[test] + fn parse_from_string() { + let value = Value::parse_from_json(Some(json!("Hi"))).unwrap(); + assert_eq!(value, Value::string("Hi".into())); + } + + #[test] + fn parse_from_bool() { + let value = Value::parse_from_json(Some(json!(true))).unwrap(); + assert_eq!(value, Value::bool(true)); + } + + #[test] + fn parse_from_null() { + let value = Value::parse_from_json(Some(json!(null))).unwrap(); + assert_eq!(value, Value::null()); + } + + #[test] + fn parse_from_struct() { + let value = Value::parse_from_json(Some(json!({"f1": "Hello"}))).unwrap(); + assert_eq!( + value, + Value::pb_struct(HashMap::from([( + "f1".into(), + Value::string("Hello".into()) + )])) + ); + } + + #[test] + fn parse_from_list() { + let value = Value::parse_from_json(Some(json!([1, 2, 3]))).unwrap(); + assert_eq!( + value, + Value::pb_list(vec![ + Value::number(1_f64), + Value::number(2_f64), + Value::number(3_f64) + ]) + ); + } +} diff --git a/poem-openapi/src/validation/max_length.rs b/poem-openapi/src/validation/max_length.rs index 57cf287e58..8bd0b9a10a 100644 --- a/poem-openapi/src/validation/max_length.rs +++ b/poem-openapi/src/validation/max_length.rs @@ -21,7 +21,7 @@ impl MaxLength { impl> Validator for MaxLength { #[inline] fn check(&self, value: &T) -> bool { - value.as_ref().len() <= self.len + value.as_ref().chars().nth(self.len).is_none() } } diff --git a/poem-openapi/src/validation/min_length.rs b/poem-openapi/src/validation/min_length.rs index e51a8184de..2bc7fed9e8 100644 --- a/poem-openapi/src/validation/min_length.rs +++ b/poem-openapi/src/validation/min_length.rs @@ -21,7 +21,7 @@ impl MinLength { impl> Validator for MinLength { #[inline] fn check(&self, value: &T) -> bool { - value.as_ref().len() >= self.len + self.len == 0 || value.as_ref().chars().nth(self.len - 1).is_some() } } diff --git a/poem-openapi/tests/validation.rs b/poem-openapi/tests/validation.rs index 358bbad137..9ab1282ac5 100644 --- a/poem-openapi/tests/validation.rs +++ b/poem-openapi/tests/validation.rs @@ -127,6 +127,18 @@ fn test_max_length() { value: "abcd".to_string() } ); + assert_eq!( + A::parse_from_json(Some(json!({ "value": "abcde" }))).unwrap(), + A { + value: "abcde".to_string() + } + ); + assert_eq!( + A::parse_from_json(Some(json!({ "value": "שלום!" }))).unwrap(), + A { + value: "שלום!".to_string() + } + ); assert_eq!( A::parse_from_json(Some(json!({ "value": "abcdef" }))) .unwrap_err() @@ -147,6 +159,12 @@ fn test_min_length() { value: String, } + assert_eq!( + A::parse_from_json(Some(json!({ "value": "abcde" }))).unwrap(), + A { + value: "abcde".to_string() + } + ); assert_eq!( A::parse_from_json(Some(json!({ "value": "abcdef" }))).unwrap(), A { @@ -159,6 +177,12 @@ fn test_min_length() { .into_message(), "failed to parse \"A\": field `value` verification failed. minLength(5)" ); + assert_eq!( + A::parse_from_json(Some(json!({ "value": "שלום" }))) + .unwrap_err() + .into_message(), + "failed to parse \"A\": field `value` verification failed. minLength(5)" + ); let mut schema = MetaSchema::new("string"); validation::MinLength::new(10).update_meta(&mut schema); diff --git a/poem/CHANGELOG.md b/poem/CHANGELOG.md index 8d2f01e19f..fcd19eb38c 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). +# [1.3.59] 2023-11-18 + +- added permissions and owner to UnixListener [#668](https://github.com/poem-web/poem/pull/668) +- In the examples, listen on `0.0.0.0`` instead of `127.0.0.1``[#672](https://github.com/poem-web/poem/pull/672) + + # [1.3.58] 2023-09-02 - bump `rust-embed` from `6.3` to `8.0` diff --git a/poem/Cargo.toml b/poem/Cargo.toml index ce167a87cf..08e940691d 100644 --- a/poem/Cargo.toml +++ b/poem/Cargo.toml @@ -125,11 +125,11 @@ libcookie = { package = "cookie", version = "0.17", features = [ "key-expansion", "secure", ], optional = true } -opentelemetry-http = { version = "0.9.0", optional = true } -opentelemetry-semantic-conventions = { version = "0.12.0", optional = true } -opentelemetry-prometheus = { version = "0.13.0", 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 } libprometheus = { package = "prometheus", version = "0.13.0", optional = true } -libopentelemetry = { package = "opentelemetry", version = "0.20.0", features = [ +libopentelemetry = { package = "opentelemetry", version = "0.21.0", features = [ "metrics", ], optional = true } libtempfile = { package = "tempfile", version = "3.2.0", optional = true } @@ -166,6 +166,9 @@ tokio-stream = { workspace = true, optional = true } 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"] } + [dev-dependencies] async-stream = "0.3.2" tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } diff --git a/poem/README.md b/poem/README.md index 42dacf821a..ffd27a88e7 100644 --- a/poem/README.md +++ b/poem/README.md @@ -95,7 +95,7 @@ fn hello(Path(name): Path) -> String { #[tokio::main] async fn main() -> Result<(), std::io::Error> { let app = Route::new().at("/hello/:name", get(hello)); - Server::new(TcpListener::bind("127.0.0.1:3000")) + Server::new(TcpListener::bind("0.0.0.0:3000")) .run(app) .await } diff --git a/poem/src/lib.rs b/poem/src/lib.rs index d2eda3ade9..4a9b4648f2 100644 --- a/poem/src/lib.rs +++ b/poem/src/lib.rs @@ -25,7 +25,7 @@ //! #[tokio::main] //! async fn main() -> Result<(), std::io::Error> { //! let app = Route::new().at("/hello/:name", get(hello)); -//! Server::new(TcpListener::bind("127.0.0.1:3000")) +//! Server::new(TcpListener::bind("0.0.0.0:3000")) //! .run(app) //! .await //! } @@ -260,7 +260,7 @@ #![doc(html_favicon_url = "https://raw.githubusercontent.com/poem-web/poem/master/favicon.ico")] #![doc(html_logo_url = "https://raw.githubusercontent.com/poem-web/poem/master/logo.png")] #![forbid(unsafe_code)] -#![deny(private_in_public, unreachable_pub)] +#![deny(unreachable_pub)] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(rustdoc::broken_intra_doc_links)] #![warn(missing_docs)] diff --git a/poem/src/listener/mod.rs b/poem/src/listener/mod.rs index 06552939da..2e59598ca4 100644 --- a/poem/src/listener/mod.rs +++ b/poem/src/listener/mod.rs @@ -143,7 +143,7 @@ pub trait Listener: Send { /// ``` /// use poem::listener::{Listener, TcpListener}; /// - /// let listener = TcpListener::bind("127.0.0.1:80").combine(TcpListener::bind("127.0.0.1:81")); + /// let listener = TcpListener::bind("0.0.0.0:80").combine(TcpListener::bind("0.0.0.0:81")); /// ``` #[must_use] fn combine(self, other: T) -> Combined diff --git a/poem/src/listener/rustls.rs b/poem/src/listener/rustls.rs index 88d08c4cd0..7eeba2a3de 100644 --- a/poem/src/listener/rustls.rs +++ b/poem/src/listener/rustls.rs @@ -196,7 +196,7 @@ impl RustlsConfig { /// /// let config = /// RustlsConfig::new().fallback(RustlsCertificate::new().cert(cert_bytes).key(key_bytes)); - /// let listener = TcpListener::bind("127.0.0.1:3000").rustls(config); + /// let listener = TcpListener::bind("0.0.0.0:3000").rustls(config); /// ``` pub fn fallback(mut self, certificate: RustlsCertificate) -> Self { self.fallback = Some(certificate); diff --git a/poem/src/listener/unix.rs b/poem/src/listener/unix.rs index b89bb781f9..8e678f088e 100644 --- a/poem/src/listener/unix.rs +++ b/poem/src/listener/unix.rs @@ -1,6 +1,11 @@ -use std::{io::Result, path::Path}; +use std::{ + fs::{set_permissions, Permissions}, + io::Result, + path::Path, +}; use http::uri::Scheme; +use nix::unistd::{chown, Gid, Uid}; use tokio::{ io::Result as IoResult, net::{UnixListener as TokioUnixListener, UnixStream}, @@ -15,21 +20,63 @@ use crate::{ #[cfg_attr(docsrs, doc(cfg(unix)))] pub struct UnixListener { path: T, + permissions: Option, + owner: Option<(Option, Option)>, } impl UnixListener { /// Binds to the provided address, and returns a [`UnixListener`]. pub fn bind(path: T) -> Self { - Self { path } + Self { + path, + permissions: None, + owner: None, + } + } + + /// Provides permissions to be set on actual bind + pub fn with_permissions(self, permissions: Permissions) -> Self { + Self { + permissions: Some(permissions), + ..self + } + } + + #[cfg(unix)] + /// Provides owner to be set on actual bind + pub fn with_owner(self, uid: Option, gid: Option) -> Self { + Self { + owner: Some((uid.map(|v| Uid::from_raw(v)), gid.map(|v| Gid::from_raw(v)))), + ..self + } } } #[async_trait::async_trait] -impl + Send> Listener for UnixListener { +impl + Send + Clone> Listener for UnixListener { type Acceptor = UnixAcceptor; async fn into_acceptor(self) -> IoResult { - let listener = TokioUnixListener::bind(self.path)?; + let listener = match (self.permissions, self.owner) { + (Some(permissions), Some((uid, gid))) => { + let listener = TokioUnixListener::bind(self.path.clone())?; + set_permissions(self.path.clone(), permissions)?; + chown(self.path.as_ref().as_os_str().into(), uid, gid)?; + listener + } + (Some(permissions), None) => { + let listener = TokioUnixListener::bind(self.path.clone())?; + set_permissions(self.path.clone(), permissions)?; + listener + } + (None, Some((uid, gid))) => { + let listener = TokioUnixListener::bind(self.path.clone())?; + chown(self.path.as_ref().as_os_str().into(), uid, gid)?; + listener + } + (None, None) => TokioUnixListener::bind(self.path)?, + }; + let local_addr = listener.local_addr().map(|addr| LocalAddr(addr.into()))?; Ok(UnixAcceptor { local_addr,