A full-featured and easy-to-use web framework with the Rust programming language.

Overview

Poem Framework

A program is like a poem, you cannot write a poem without writing it. --- Dijkstra

A full-featured and easy-to-use web framework with the Rust programming language.


Features

  • Fast: Both Ease of use and performance.
  • Minimal generalization: Minimizing the use of generics.
  • Open API: Use poem-openapi to write APIs that comply with OAS3 specifications and automatically generate documents.

Example

use poem::{handler, route, web::Path, route::get, Server};

#[handler]
fn hello(Path(name): Path<String>) -> String {
    format!("hello: {}", name)
}

#[tokio::main]
async fn main() {
    let app = route().at("/hello/:name", get(hello));
    let server = Server::bind("127.0.0.1:3000").await.unwrap();
    server.run(app).await.unwrap();
}

More examples can be found here.

Contributing

🎈 Thanks for your help improving the project! We are so happy to have you!

License

Licensed under either of

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Poem by you, shall be licensed as Apache, without any additional terms or conditions.

Comments
  • The generated openapi scheme for union has changed. Is it possible to restore the previous schema?

    The generated openapi scheme for union has changed. Is it possible to restore the previous schema?

    Previously the union scheme was generated like this:

    {
        "type": "object",
        "properties": {
            "type": {
                "type": "string",
                "enum": [
                    "CreateSessionResponseOk",
                    "CreateSessionResponseErrors",
                    "CreateSessionResponseErrorAccess"
                ]
            }
        },
        "oneOf": [{
            "$ref": "#/components/schemas/CreateSessionResponseOk"
        }, {
            "$ref": "#/components/schemas/CreateSessionResponseErrors"
        }, {
            "$ref": "#/components/schemas/CreateSessionResponseErrorAccess"
        }],
        "discriminator": {
            "propertyName": "type"
        }
    }
    

    After recently updating the poem in the project to the latest version, I noticed that it generates like this:

    
    {
        "type": "object",
        "anyOf": [
            {
                "required": [
                    "type"
                ],
                "allOf": [{
                    "$ref": "#/components/schemas/CreateSessionResponseOk"
                }, {
                    "type": "object",
                    "title": "CreateSessionResponseOk",
                    "properties": {
                        "type": {
                            "type": "string",
                            "example": "CreateSessionResponseOk"
                        }
                    }
                }]
            }, {
                "required": [
                    "type"
                ],
                "allOf": [{
                    "$ref": "#/components/schemas/CreateSessionResponseErrors"
                }, {
                    "type": "object",
                    "title": "CreateSessionResponseErrors",
                    "properties": {
                        "type": {
                            "type": "string",
                            "example": "CreateSessionResponseErrors"
                        }
                    }
                }]
            }, {
                "required": [
                "type"
                ],
                "allOf": [{
                    "$ref": "#/components/schemas/CreateSessionResponseErrorAccess"
                }, {
                    "type": "object",
                    "title": "CreateSessionResponseErrorAccess",
                    "properties": {
                        "type": {
                            "type": "string",
                            "example": "CreateSessionResponseErrorAccess"
                        }
                    }
                }]
            }
        ],
        "discriminator": {
            "propertyName": "type"
        }
    }
    

    Is it possible to restore the previous specification format ? The previous version was much better

    question Stale 
    opened by szagi3891 35
  • How to return bytes but generate spec for specific type + encoding

    How to return bytes but generate spec for specific type + encoding

    First, some code demonstrating what I want to do:

    struct Api {
        db_handle: DbHandle
    }
    
    #[OpenApi]
    impl Api {
        #[oai(path = "/get_stuff", method = "get", actual_type = Base64<Stuff>)]
        async fn get_stuff(&self) -> Binary<Vec<u8>> {
            self.db_handle.get_stuff()
        }
    }
    

    In short, I want to be able to read bytes from a DB and return them, but because I know they really represent a type (Stuff) encoded in a particular way (Base64), generate the OpenAPI spec in a way that says it returns Base64 encoded Stuff, not just a bunch of bytes.

    Is there a way to do this today in Poem?

    I want this to save myself having to deserialise Stuff just to reserialize it again.

    Thanks!!

    question Stale 
    opened by banool 15
  • Generate union with disciminator name without making intermediate types

    Generate union with disciminator name without making intermediate types

    First, some code:

    #[derive(Clone, Debug, PartialEq, Union)]
    #[oai(discriminator_name = "type", one_of)]
    pub enum WriteSetChange {
        DeleteModule(DeleteModule),
        DeleteResource(DeleteResource),
    }
    
    #[derive(Clone, Debug, PartialEq, Object)]
    pub struct DeleteModule {
        pub address: Address,
        pub state_key_hash: String,
        pub module: MoveModuleId,
    }
    
    #[derive(Clone, Debug, PartialEq, Object)]
    pub struct DeleteResource {
        pub address: Address,
        pub state_key_hash: String,
        pub resource: MoveStructTag,
    }
    

    If you generate a spec from this, you get:

        WriteSetChange:
          type: object
          oneOf:
            - $ref: "#/components/schemas/WriteSetChange[DeleteModule]"
            - $ref: "#/components/schemas/WriteSetChange[DeleteResource]"
          discriminator:
            propertyName: type
            mapping:
              DeleteModule: "#/components/schemas/WriteSetChange[DeleteModule]"
              DeleteResource: "#/components/schemas/WriteSetChange[DeleteResource]"
        "WriteSetChange[DeleteModule]":
          allOf:
            - type: object
              required:
                - type
              properties:
                type:
                  type: string
                  example: DeleteModule
            - $ref: "#/components/schemas/DeleteModule"
        "WriteSetChange[DeleteResource]":
          allOf:
            - type: object
              required:
                - type
              properties:
                type:
                  type: string
                  example: DeleteResource
            - $ref: "#/components/schemas/DeleteResource"
         DeleteModule:
          type: object
          required:
            - address
            - state_key_hash
            - module
          properties:
            address:
              type: string
              format: Address
            state_key_hash:
              type: string
            module:
              $ref: "#/components/schemas/MoveModuleId"
        DeleteResource:
          type: object
          required:
            - address
            - state_key_hash
            - resource
          properties:
            address:
              type: string
              format: Address
            state_key_hash:
              type: string
            resource:
              $ref: "#/components/schemas/MoveStructTag"
    

    As you can see, this generates an intermediate type that holds both the "data", e.g. DeleteResource, and the type (a string).

    What I really want is something like this:

        WriteSetChange:
          type: object
          oneOf:
            - $ref: "#/components/schemas/DeleteModule"
            - $ref: "#/components/schemas/DeleteResource"
         DeleteModule:
          type: object
          required:
            - address
            - state_key_hash
            - module
          properties:
            address:
              type: string
              format: Address
            state_key_hash:
              type: string
            module:
              $ref: "#/components/schemas/MoveModuleId"
        DeleteResource:
          type: object
          required:
            - address
            - state_key_hash
            - resource
          properties:
            address:
              type: string
              format: Address
            state_key_hash:
              type: string
            resource:
              $ref: "#/components/schemas/MoveStructTag"
    

    As in, no intermediate types. This second output is actually generated from the schema when I don't specify discriminator_type, just one_of. The problem now is there is no discriminator type, since what I really want is this:

        WriteSetChange:
          type: object
          oneOf:
            - $ref: "#/components/schemas/DeleteModule"
            - $ref: "#/components/schemas/DeleteResource"
          discriminator:
            propertyName: type
         DeleteModule:
          type: object
          required:
            - address
            - state_key_hash
            - module
          properties:
            address:
              type: string
              format: Address
            state_key_hash:
              type: string
            module:
              $ref: "#/components/schemas/MoveModuleId"
        DeleteResource:
          type: object
          required:
            - address
            - state_key_hash
            - resource
          properties:
            address:
              type: string
              format: Address
            state_key_hash:
              type: string
            resource:
              $ref: "#/components/schemas/MoveStructTag"
    

    This is necessary so clients know where to find the type of the data. I don't need the intermediate types, it makes the spec more verbose than necessary. The mapping is nice but not required in either case.

    Is there any way to do this with Poem today?

    question Stale 
    opened by banool 14
  • Error in openapi schema for Enum

    Error in openapi schema for Enum

    Expected Behavior

    A different type is expected for the enum:

    "geoRuleType": {
      "type": "string",
      "enum": [
        "DENY",
        "ALLOW"
      ]
    }
    

    Actual Behavior

    "GeoRuleType": {
      "type": "GeoRuleType",
      "enum": [
        "DENY",
        "ALLOW"
      ]
    },
    

    Steps to Reproduce the Problem

    1. You have to clone https://github.com/szagi3891/poem-test
    2. And then run with cargo run

    Specifications

    • Version: poem = "1.0.38", poem-openapi = "1.0.41"
    • Platform: MacOs
    • Subsystem:
    {
      "openapi": "3.0.0",
      "info": {
        "title": "Oneof",
        "version": "1.0"
      },
      "servers": [],
      "tags": [],
      "paths": {
        "/api1/hello": {
          "post": {
            "requestBody": {
              "content": {
                "application/json": {
                  "schema": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "A",
                          "B"
                        ]
                      }
                    },
                    "oneOf": [
                      {
                        "$ref": "#/components/schemas/A"
                      },
                      {
                        "$ref": "#/components/schemas/B"
                      }
                    ],
                    "discriminator": {
                      "propertyName": "type"
                    }
                  }
                }
              },
              "required": true
            },
            "responses": {
              "200": {
                "description": "Created successfully",
                "content": {
                  "application/json": {
                    "schema": {
                      "type": "integer",
                      "format": "uint64"
                    }
                  }
                }
              },
              "403": {
                "description": "Permission denied",
                "content": {
                  "application/json": {
                    "schema": {
                      "$ref": "#/components/schemas/Forb"
                    }
                  }
                }
              },
              "500": {
                "description": "Internal error"
              }
            }
          }
        }
      },
      "components": {
        "schemas": {
          "GeoRuleType": {
            "type": "GeoRuleType",
            "enum": [
              "DENY",
              "ALLOW"
            ]
          },
          "B": {
            "type": "object",
            "required": [
              "v3",
              "list"
            ],
            "properties": {
              "v3": {
                "type": "number",
                "format": "float32"
              },
              "list": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer",
                  "format": "uint64"
                }
              },
              "sport": {
                "$ref": "#/components/schemas/SportModel"
              }
            }
          },
          "Forb": {
            "type": "object",
            "required": [
              "message",
              "age"
            ],
            "properties": {
              "message": {
                "type": "string"
              },
              "age": {
                "type": "integer",
                "format": "int32"
              }
            }
          },
          "A": {
            "type": "object",
            "required": [
              "v1",
              "v2"
            ],
            "properties": {
              "v1": {
                "type": "integer",
                "format": "int32"
              },
              "v2": {
                "type": "string"
              }
            }
          },
          "SportModel": {
            "type": "object",
            "required": [
              "id",
              "name",
              "displayOrder",
              "geoRuleType"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "displayOrder": {
                "type": "integer",
                "format": "uint64"
              },
              "geoRuleType": {
                "$ref": "#/components/schemas/GeoRuleType"
              }
            }
          }
        }
      }
    }
    
    bug 
    opened by szagi3891 14
  • Problem generating specification when Arc field is in structure

    Problem generating specification when Arc field is in structure

    Expected Behavior

    Possibility to use Object macro on a structure where one of the fields is Arc

    Actual Behavior

    Error message appears: no function or associated item named register found for struct Arc<std::string::String> in the current scope

    Steps to Reproduce the Problem

    1. You have to clone https://github.com/szagi3891/poem-test
    2. And then run with cargo run

    Specifications

    • Version: poem = "1.0.37", poem-openapi = "1.0.37"
    • Platform: MacOs
    • Subsystem:

    Structure affected by the compilation error:

    #[derive(Object, Debug, PartialEq)]
    struct A {
        v1: i32,
        v2: Arc<String>,
    }
    
    bug 
    opened by szagi3891 14
  • CORS Example

    CORS Example

    Hello, I'm really enjoying using Poem, but I've got to the point with my project that I need CORS and I can't figure out how it works? Is there an example somewhere for using the CORS middleware?

    I'm doing it like this:

    .with( Cors::new() .allow_origin("http://localhost:3000") .allow_method(Method::POST), );

    But only the last call seems to take effect?

    question 
    opened by shnads 14
  • Need to input all data of every field?

    Need to input all data of every field?

    #[crud_table] #[derive(Clone, Debug)] pub struct BizActivity { pub id: Option<String>, pub name: Option<String>, pub sort: Option<String>, pub status: Option<i32>, pub version: Option<i32>, }

    #[handler] pub async fn biz_activity_create(state: Data<&AppState>, form: Form<BizActivity>,) -> Result { println!("form: {:?}", &form.0); let rb = &state.rb; let r = rb.save(&form.0, &[]).await; if r.is_err() { println!("{}", r.err().unwrap().to_string()); } Ok(StatusCode::FOUND.with_header("location", "/biz_activity")) }

    When only name field is entered, an error is returned: url decode: cannot parse integer from empty string

    question 
    opened by easykoala 13
  • Add support for `remote` on fields

    Add support for `remote` on fields

    Hey, I just took a look at modifying poem-openapi-derive/src/object.rs but I couldn't quite figure out what to do.

    What I'd love to be able to do is something like this:

    #[derive(Object)]
    struct MyStruct {
        a: u32,
        #[oai(remote = "u32")]
        b: u64,
    }
    

    This is just an example, in reality the type I use in remote would be a local version of a remote type, so I can impl the Poem stuff on it.

    I want this because today if I wanted to do this, I would have to add remote to the whole struct instead and define a whole new wrapper struct, even though I only want this for one field.

    I suppose in some ways this is similar to https://github.com/poem-web/poem/issues/322. My ultimate goal is I have a newtype in a remote crate that I don't want to have to wrap, since I use it in many places.

    enhancement Stale 
    opened by banool 12
  • Is it possible to define a generic enum ?

    Is it possible to define a generic enum ?

    I would like to define a generic enum that I will use in the openapi specification.

    I tried to define it this way:

    use poem_openapi::{OneOf, Object};
    use poem_openapi::types::ToJSON;
    
    #[derive(Debug, Object)]
    pub struct ProxyResponseOk<R: Send + ToJSON> {
        pub response: R
    }
    
    #[derive(Debug, Object)]
    pub struct ProxyResponseMessage {
        pub code: String,
        pub message: String,
    }
    
    #[derive(Debug, Object)]
    pub struct ProxyResponseUnknown {
        pub code: u32,
        pub body: String,
    }
    
    #[derive(Debug, OneOf)]
    #[oai(property_name="type")]
    pub enum ProxyResponse<R: Send + ToJSON> {
        Ok(ProxyResponseOk<R>),
        Error(ProxyResponseMessage),
        Unknown(ProxyResponseUnknown),
    }
    

    When trying to compile I get errors:

    error[E0412]: cannot find type `R` in this scope
      --> src/proxy/handler/selection_errors.rs:25:24
       |
    22 | #[derive(Debug, OneOf)]
       |                      - help: you might be missing a type parameter: `<R>`
    ...
    25 |     Ok(ProxyResponseOk<R>),
       |                        ^ not found in this scope
    
    error[E0107]: missing generics for enum `ProxyResponse`
      --> src/proxy/handler/selection_errors.rs:24:10
       |
    24 | pub enum ProxyResponse<R: Send + ToJSON> {
       |          ^^^^^^^^^^^^^ expected 1 generic argument
       |
    note: enum defined here, with 1 generic parameter: `R`
      --> src/proxy/handler/selection_errors.rs:24:10
       |
    24 | pub enum ProxyResponse<R: Send + ToJSON> {
       |          ^^^^^^^^^^^^^ -
    help: add missing generic argument
       |
    24 | pub enum ProxyResponse<R><R: Send + ToJSON> {
       |          ~~~~~~~~~~~~~~~~
    

    Can I ask for help?

    question 
    opened by szagi3891 12
  • Provide some way to alter only framework errors

    Provide some way to alter only framework errors

    As it is now, if you downcast an error, the error disappears and the error message becomes "response". This is a start to trying to fix that, though I'm not sure yet if it fixes it completely. The use case is a function like this:

    // Copyright (c) Aptos
    // SPDX-License-Identifier: UNLICENSED
    
    use crate::endpoints::{AptosTapError, AptosTapErrorCode};
    use poem::{IntoResponse, Response};
    
    use super::errors::AptosTapErrorResponse;
    
    /// In the OpenAPI spec for this API, we say that every response we return will
    /// be a JSON representation of AptosTapError. For our own errors, this is exactly
    /// what we do. The problem is the Poem framework does not conform to this
    /// format, it can return errors in a different format. The purpose of this
    /// function is to catch those errors and convert them to the correct format.
    pub async fn convert_error(error: poem::Error) -> impl poem::IntoResponse {
        // In the tap the only error we ever return ourselves is AptosTapError.
        // Therefore, if the error is a different type, it must be an error from
        // the Poem framework.
        match error.downcast::<AptosTapError>() {
            Ok(aptos_tap_error) => {
                println!("aptos_tap_error: {:?}", aptos_tap_error);
                AptosTapErrorResponse::from(aptos_tap_error).into_response()
            }
            Err(poem_error) => {
                let error_string = poem_error.to_string();
                build_error_response(error_string)
            }
        }
    }
    
    fn build_error_response(error_string: String) -> Response {
        AptosTapErrorResponse::from(AptosTapError::new_with_error_code(
            error_string,
            AptosTapErrorCode::WebFrameworkError,
        ))
        .into_response()
    }
    
    opened by banool 11
  • [poem-openapi] `Attachment` type doesn't reflect the `Content-Disposition` header in OpenAPI spec

    [poem-openapi] `Attachment` type doesn't reflect the `Content-Disposition` header in OpenAPI spec

    Expected Behavior

    I want to see Content-Disposition in OpenAPI spec when I'm using poem_openapi::payload::attachment::Attachment

    I had to add response_header manually:

    image

    Actual Behavior

    There is no header in OpenAPI spec

    Steps to Reproduce the Problem

    1. create endpoint with response type(impl ApiResponse) and include Attachment
    2. Take a look at OpenAPI spec

    Specifications

    • Version: 2.0.3
    opened by DDtKey 11
  • tokio-runtime-worker panic at src/route/router.rs:240:66

    tokio-runtime-worker panic at src/route/router.rs:240:66

    I have been running a simple poem openAPI server. Today I checked my server log and found that there were some panics in tokio runtime due to poem.

    Sample server code

    mod client;
    mod datacenter;
    mod oauth;
    mod refresher;
    use crate::client::ConfigGatewayClient;
    use crate::refresher::TokenRefresher;
    use std::sync::Arc;
    
    use crate::datacenter::Datacenter;
    use crate::oauth::service_account_login;
    use client::{QueueInformation, QueueQuery};
    use dotenv::dotenv;
    use poem::{
        http::StatusCode, listener::TcpListener, middleware::Cors, web::Data, EndpointExt, Error,
        Route, Server,
    };
    use poem_openapi::{
        auth::Basic, param::Path, payload::Json, OpenApi, OpenApiService, SecurityScheme,
    };
    use regex::Regex;
    use tokio::sync::RwLock;
    
    #[derive(SecurityScheme)]
    #[oai(type = "basic")]
    struct QABasicAuth(Basic);
    
    struct QueueFinderApi;
    
    #[OpenApi]
    impl QueueFinderApi {
        fn check_auth(&self, auth: QABasicAuth) -> poem::Result<()> {
            if auth.0.username != std::env::var("AUTHENTICATION_USERNAME").unwrap()
                && auth.0.password != std::env::var("AUTHENTICATION_PASSWORD").unwrap()
            {
                return Err(Error::from_status(StatusCode::UNAUTHORIZED));
            }
            Ok(())
        }
    
        #[oai(path = "/invalidate/:org", method = "post")]
        async fn invalidate(
            &self,
            auth: QABasicAuth,
            org: Path<String>,
            client: Data<&Arc<ConfigGatewayClient>>,
        ) -> poem::Result<()> {
            self.check_auth(auth)?;
            // Some Implementation
            Ok(())
        }
    
        #[oai(path = "/find/:id/one", method = "post")]
        async fn findone(
            &self,
            auth: QABasicAuth,
            id: Path<String>,
            query: Json<QueueQuery>,
            client: Data<&Arc<ConfigGatewayClient>>,
        ) -> poem::Result<Json<QueueInformation>> {
            self.check_auth(auth)?;
            // Some implementation
            Ok(Json(queue))
        }
    
        #[oai(path = "/find/:id/all", method = "post")]
        async fn findall(
            &self,
            auth: QABasicAuth,
            id: Path<String>,
            query: Json<QueueQuery>,
            client: Data<&Arc<ConfigGatewayClient>>,
        ) -> poem::Result<Json<Vec<QueueInformation>>> {
            self.check_auth(auth)?;
            if id.0.is_empty() {
                return Err(Error::from_status(StatusCode::BAD_REQUEST));
            }
            let Ok(regx) = Regex::new(&query.0.queue_regex) else {
                return Err(Error::from_status(StatusCode::BAD_REQUEST));
            };
            Ok(Json(client.fetch_all(&id.0, regx).await.ok_or_else(
                || Error::from_status(StatusCode::INTERNAL_SERVER_ERROR),
            )?))
        }
    }
    
    #[tokio::main]
    async fn main() -> anyhow::Result<()> {
        dotenv().ok();
        let dc: Datacenter = std::env::var("DATACENTER")?.parse()?;
        let name = std::env::var("MACHINE_USERNAME")?;
        let password = std::env::var("MACHINE_PASSWORD")?;
        let auth = Arc::new(RwLock::new(
            service_account_login(&name, &password, &dc).await?,
        ));
        let client = Arc::new(ConfigGatewayClient::new(auth.clone(), &dc));
        let mut refresher = TokenRefresher::new(auth.clone(), &dc);
    
        refresher.start().await;
    
        let api_service =
            OpenApiService::new(QueueFinderApi, "Queue Finder", env!("CARGO_PKG_VERSION"))
                .server("https://foo.example.com");
        let redoc = api_service.redoc();
        let rapidoc = api_service.rapidoc();
        let app = Route::new()
            .nest("/", api_service)
            .nest("/redoc", redoc)
            .nest("/docs", rapidoc)
            .with(Cors::new())
            .data(client);
    
        Server::new(TcpListener::bind("0.0.0.0:8080"))
            .run(app)
            .await?;
        Ok(())
    }
    

    Specifications

    Dec 28 22:59:07 ip-172-31-33-157 queue-finder[2578]: [2022-12-28T22:59:07Z INFO  queue_finder::refresher] Token refresh to be performed after 64749 seconds
    Dec 29 00:51:47 ip-172-31-33-157 queue-finder[2578]: thread 'tokio-runtime-worker' panicked at 'removal index (is 18446744073709551615) should be < len (is 0)', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/poem-1.3.50/src/route/router.rs:240:66
    Dec 29 00:51:47 ip-172-31-33-157 queue-finder[2578]: note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    Dec 29 00:51:47 ip-172-31-33-157 queue-finder[2578]: thread 'tokio-runtime-worker' panicked at 'removal index (is 18446744073709551615) should be < len (is 0)', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/poem-1.3.50/src/route/router.rs:240:66
    Dec 29 00:51:47 ip-172-31-33-157 queue-finder[2578]: thread 'tokio-runtime-worker' panicked at 'removal index (is 18446744073709551615) should be < len (is 0)', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/poem-1.3.50/src/route/router.rs:240:66
    Dec 29 00:51:47 ip-172-31-33-157 queue-finder[2578]: thread 'tokio-runtime-worker' panicked at 'removal index (is 18446744073709551615) should be < len (is 0)', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/poem-1.3.50/src/route/router.rs:240:66
    Dec 29 00:51:47 ip-172-31-33-157 queue-finder[2578]: thread 'tokio-runtime-worker' panicked at 'removal index (is 18446744073709551615) should be < len (is 0)', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/poem-1.3.50/src/route/router.rs:240:66
    Dec 29 00:51:47 ip-172-31-33-157 queue-finder[2578]: thread 'tokio-runtime-worker' panicked at 'removal index (is 18446744073709551615) should be < len (is 0)', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/poem-1.3.50/src/route/router.rs:240:66
    Dec 29 16:58:16 ip-172-31-33-157 queue-finder[2578]: [2022-12-29T16:58:16Z INFO  queue_finder::refresher] Performing token refresh using refresh token
    Dec 29 16:58:17 ip-172-31-33-157 queue-finder[2578]: [2022-12-29T16:58:17Z INFO  queue_finder::refresher] Token refresh completed, releasing token lock for read access
    Dec 29 16:58:17 ip-172-31-33-157 queue-finder[2578]: [2022-12-29T16:58:17Z INFO  queue_finder::refresher] Token refresh to be performed after 64749 seconds
    Dec 30 01:00:32 ip-172-31-33-157 queue-finder[2578]: thread 'tokio-runtime-worker' panicked at 'removal index (is 18446744073709551615) should be < len (is 0)', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/poem-1.3.50/src/route/router.rs:240:66
    Dec 30 01:00:33 ip-172-31-33-157 queue-finder[2578]: thread 'tokio-runtime-worker' panicked at 'removal index (is 18446744073709551615) should be < len (is 0)', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/poem-1.3.50/src/route/router.rs:240:66
    Dec 30 01:00:33 ip-172-31-33-157 queue-finder[2578]: thread 'tokio-runtime-worker' panicked at 'removal index (is 18446744073709551615) should be < len (is 0)', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/poem-1.3.50/src/route/router.rs:240:66
    Dec 30 01:00:33 ip-172-31-33-157 queue-finder[2578]: thread 'tokio-runtime-worker' panicked at 'removal index (is 18446744073709551615) should be < len (is 0)', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/poem-1.3.50/src/route/router.rs:240:66
    Dec 30 01:00:33 ip-172-31-33-157 queue-finder[2578]: thread 'tokio-runtime-worker' panicked at 'removal index (is 18446744073709551615) should be < len (is 0)', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/poem-1.3.50/src/route/router.rs:240:66
    Dec 30 01:00:33 ip-172-31-33-157 queue-finder[2578]: thread 'tokio-runtime-worker' panicked at 'removal index (is 18446744073709551615) should be < len (is 0)', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/poem-1.3.50/src/route/router.rs:240:66
    Dec 30 10:36:21 ip-172-31-33-157 queue-finder[2578]: [2022-12-30T10:36:21Z INFO  queue_finder::client] Cache miss for org 89b474a3-17d9-41ff-8fb0-534bebdfb160
    

    My service has been running fine the API is reachable but I am opening this issue anyways since it looks some error at poem router.

    • Version: poem-1.3.50
    • Platform: Ubuntu 22.04LTS
    • Subsystem: Linux EC2
    bug 
    opened by coder3101 2
  • Provide optional URL prefix to generated spec json

    Provide optional URL prefix to generated spec json

    Description of the feature

    Would you consider adding an option to pass in url prefix into the generated spec?

    "paths" : {
        "/my_prefix/rest/of/my/path" : {
          "get" : {
            "parameters" : [
              {
                "name" : "X-Customheader",
                "in" : "header",
                "required" : true,
                "schema" : {
                  "type" : "string"
                 }
               }
             ]
          }
        ... snip ...
     
    

    Code example (if possible)

    Maybe something like that:

    let spec_options = SpecOptions::default().with_url_prefix("/my_prefix");
    let my_api_schema = my_api.spec_endpoint_with_options(options);
    
    enhancement 
    opened by VersBinarii 3
  • Create a derive for custom errors

    Create a derive for custom errors

    Description of the feature

    You can create request params by deriving from Object but you can not create custom errors yet, which are shown as errors in OpenApi and can be returned directly.

    enhancement 
    opened by TheCataliasTNT2k 2
  • Access to client's peer certificate in a handler

    Access to client's peer certificate in a handler

    Hi

    Background: I want to authenticate a client using the X509 certificate. For security reasons I need to have access to some fields in the cert and process them.

    I am using poem with rustls and I am struggling with a way to access certficate in the handler.

    I would really appreciate a working example of accessing the cert in a handler (or passing some data from the cert to the handler) if it's possible.

    Thanks in advance!

    question 
    opened by anatse 1
  • Enforce redirect from http to https after provisioning HTTPS certificates

    Enforce redirect from http to https after provisioning HTTPS certificates

    Description of the feature

    It's common for pure HTTP servers to enforce redirect from HTTP to HTTPS after provisioning valid SSL certificates

    Code example (if possible)

    I believe that can extra logic can be added into acme auto_cert Example already exists at acme_http,

        let app = RouteScheme::new()
            .https(Route::new().at("/hello/:name", get(hello)))
            .http(auto_cert.http_01_endpoint())
            .with(Tracing);
    

    Expected behaviour - after valid SSL certificates are obtained, HTTP requests are redirected using hard redirect using the Location header.

    enhancement 
    opened by AlexMikhalev 1
Releases(v2.0.0)
Owner
Poem Web
Poem Web
A highly customizable, full scale web backend for web-rwkv, built on axum with websocket protocol.

web-rwkv-axum A axum web backend for web-rwkv, built on websocket. Supports BNF-constrained grammar, CFG sampling, etc., all streamed over network. St

Li Junyu 12 Sep 25, 2023
example of a full stack web app (backend and frontend) wrtiten in Rust

rust-fullstack-example An example of creating a full stack web application (backend and frontend) using Rust. Backend Go to ./backend and start the se

mario 41 Dec 16, 2022
A super-easy, composable, web server framework for warp speeds.

warp A super-easy, composable, web server framework for warp speeds. The fundamental building block of warp is the Filter: they can be combined and co

Sean McArthur 7.5k Jan 2, 2023
Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.

Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.

Actix 16.2k Jan 2, 2023
Starter template for use with the Leptos web framework and Axum.

Leptos Axum Starter Template This is a template for use with the Leptos web framework and the cargo-leptos tool using Axum. Creating your template rep

Leptos 10 Mar 4, 2023
Sauron is an html web framework for building web-apps. It is heavily inspired by elm.

sauron Guide Sauron is an web framework for creating fast and interactive client side web application, as well as server-side rendering for back-end w

Jovansonlee Cesar 1.7k Dec 26, 2022
A (flash) message framework for actix-web. A port to Rust of Django's message framework.

actix-web-flash-messages Flash messages for actix-web Web applications sometimes need to show a one-time notification to the user - e.g. an error mess

Luca Palmieri 31 Dec 29, 2022
A web framework for Rust programing language

kalgan A web framework for Rust programing language. Getting Started Create your project with cargo: cargo new project Add the dependency in Cargo.tom

Eduardo Casas 4 Jun 9, 2022
A customizable, simple and easy to use json REST API consumer

JACK is a generic JSON API client. It is useful to interact with APIs from multiple services such as Google and Twitter

Mente Binária 6 May 22, 2022
Ergonomic and modular web framework built with Tokio, Tower, and Hyper

axum axum is a web application framework that focuses on ergonomics and modularity. More information about this crate can be found in the crate docume

Tokio 7.9k Dec 31, 2022
A Rust GraphQL system with full support for subscriptions and authentication that works out of the box.

Diana is a GraphQL system for Rust that's designed to work as simply as possible out of the box, without sacrificing configuration ability.

arctic_hen7 36 Dec 19, 2022
A rust web framework with safety and speed in mind.

darpi A web api framework with speed and safety in mind. One of the big goals is to catch all errors at compile time, if possible. The framework uses

null 32 Apr 11, 2022
Thruster - An fast and intuitive rust web framework

A fast, middleware based, web framework written in Rust

null 913 Dec 27, 2022
Seed is a Rust front-end framework for creating fast and reliable web apps with an Elm-like architecture.

Seed is a Rust front-end framework for creating fast and reliable web apps with an Elm-like architecture.

null 3.6k Jan 6, 2023
Implementation of the RealWorld backend API spec in Actix, Rust's powerful actor system and most fun web framework.

Actix codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the RealWorld spec and API. ❗ (2021/05/13) This cod

Allen 475 Jan 2, 2023
Demo of Rust and axum web framework

Demo of Rust and axum web framework Demonstration of: Rust: programming language that focuses on reliability and stability. axum: web framework that f

Joel Parker Henderson 115 Dec 29, 2022
A pure Rust implementation of the Web Local Storage API, for use in non-browser contexts

Rust Web Local Storage API A Rust implementation of the Web LocalStorage API, for use in non-browser contexts About the Web Local Storage API MDN docs

RICHΛRD ΛNΛYΛ 10 Nov 28, 2022
The simplest build-time framework for writing web apps with html templates and typescript

Encoped A build-time fast af tool to write static apps with html and TypeScript Features Template-based ESLint, Prettier and Rollup integration No ext

null 1 Dec 11, 2021
Hirola is an opinionated web framework for that is focused on simplicity and predictability.

Hirola Hirola is an opinionated web framework for that is focused on simplicity and predictability. Goals Keep it simple. Most Rust web frameworks hav

null 27 Nov 3, 2022