[HN Gopher] A small reivew of the Poem Rust web framework
       ___________________________________________________________________
        
       A small reivew of the Poem Rust web framework
        
       Author : rendaw
       Score  : 36 points
       Date   : 2022-09-11 16:06 UTC (6 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | rendaw wrote:
       | After wasting a couple days trying to wrangle Axum, Hyper, Rocket
       | and a couple others I tried Poem and it solves every problem I
       | had with the other frameworks. Waiting for the other shoe to
       | drop.
       | 
       | 1. The response object type isn't generic (as per project goals).
       | In other frameworks, if I had branches returning different
       | response types the types would conflict - no such issue here.
       | 
       | 2. The cookie middleware is easy to use. In axum you had to
       | return the cookie jar from your handler and trust the response
       | tuple magic to do something with it to set the cookie in the
       | response. I wasn't using tuples though, and due to the above it
       | was non-obvious (and awkward) how to set the cookie in the
       | response using response objects.
       | 
       | 3. When you enable TLS via rustls it automatically makes non-
       | default config changes to negotiate http2 (this is boilerplate in
       | other frameworks).
       | 
       | 4. Endpoint handlers are non-closures, and state must be shared
       | via extensions/middleware (not captures). Rust closures are a
       | hornet's nest so I'm plenty happy just being able to avoid them,
       | but I also like that there's a single way to do things (and that
       | way has good DX). In other frameworks you need to use async
       | closures in order to get the compiler to intuit the future type
       | since they're complex and you'd have to define it manually with a
       | non-closure.
       | 
       | 5. TLS configs are a stream to handle live cert refreshes.
       | 
       | It feels like a batteries included/one size fits all solution,
       | but it's very well integrated and there are a lot of batteries.
       | It's pretty locked down, where you can't easily tweak the
       | internals. I haven't hit limitations from that yet, despite
       | having some somewhat weird use cases.
       | 
       | There are also a lot of examples in the repo.
       | 
       | I was fighting with other frameworks for the last 3 days (a good
       | 20+ hours), and when I hit another future-handler-generic-
       | incompatibility error I threw my hands up and tried Poem, and got
       | my code compiling in 30m.
        
         | 0x457 wrote:
         | Can't talk about hyper and rocket, but I use Axum extensively.
         | 
         | 1. This one is annoying, but easily solvable by making your own
         | return type that implements IntoResponse
         | 
         | 2. I'm not sure what you're talking about? You just add
         | `Cookies` extractor to your handler and use that. You don't
         | need to add it to the return, middleware takes care of it.
         | 
         | Middleware in axum split into two: Tower middleware and Axum
         | middleware. Tower middleware is...hard to write, but it's
         | faster, axum middleware is easier to write, but it's slower.
         | 
         | 4. axum doesn't force using closures, and state is shared via
         | extensions?
         | 
         | I'm currently working on a service where I have tonic (gRPC)
         | and axum on the same port, and they share a lot of middleware.
        
           | davidpdrsn wrote:
           | > This one is annoying, but easily solvable by making your
           | own return type that implements IntoResponse
           | 
           | You can just call `.into_response()` like documented here htt
           | ps://docs.rs/axum/latest/axum/response/index.html#returni...
           | 
           | > I'm not sure what you're talking about? You just add
           | `Cookies` extractor to your handler and use that. You don't
           | need to add it to the return, middleware takes care of it.
           | 
           | I think you're thinking of tower-cookies which works the same
           | way poem's CookieJarManager does.
           | 
           | axum-extra's cookies work differently in that you have to
           | return the updated jar from handlers. We chose this design
           | because it composes better when you have multiple libraries
           | that all wanna set cookies. tower-cookies doesn't work if you
           | accidentally add multiple cookie middleware, whereas axum-
           | extra's approach does. So its a trade-off.
        
             | 0x457 wrote:
             | > I think you're thinking of tower-cookies which works the
             | same way poem's CookieJarManager does.
             | 
             | Yes, that's what I'm using.
             | 
             | > You can just call `.into_response()` like documented here
             | https://docs.rs/axum/latest/axum/response/index.html#return
             | i...
             | 
             | Oh yeah, totally forgot about that one.
        
         | pdimitar wrote:
         | Pretty useful to know, thanks for sharing. <3
         | 
         | Shame you didn't try ActixWeb. That's the one I'd start with --
         | I heard a lot of praise for it but haven't tried it yet.
         | 
         | I'll make sure to check Poem next time I want to write a web
         | service in Rust, thanks to you.
        
           | capableweb wrote:
           | > Shame you didn't try ActixWeb. That's the one I'd start
           | with -- I heard a lot of praise for it but haven't tried it
           | yet.
           | 
           | actix_web is what I go for as well, really simple to setup
           | and haven't hit any major blockers for as long as I've been
           | using it (~7 months, 3-4 projects)
        
         | electromech wrote:
         | > The cookie middleware is easy to use. In axum...
         | 
         | Been there, fought that! By the end, I had thoroughly abused
         | the Axum API to get it working.
        
         | electromech wrote:
         | Ah shoot, poem's websocket implementation uses
         | tokio_tungstenite like most of the other hyper-adjacent Rust
         | web frameworks. It's unfortunate that library has become the
         | default for websockets because the performance is rather
         | lacking. (new allocations for each incoming message IIRC)
        
           | paulgb wrote:
           | Is there a good alternative in the tokio world currently?
           | Actix-web does WebSockets but it expects to run on an actix
           | executor AFAIK.
        
         | the__alchemist wrote:
         | Does anyone else think generics detract from the usefulness of
         | Rust docs? I often hit walls on learning libs when trying to
         | figure out what type to use when the API uses a generic. A non-
         | generic flow might be like this: fn accepts param y. Param y is
         | of type z. Docs for z shows it constructed with z::new(), or z
         | { (fields }. When it's a generic instead, there's no link to
         | click, and you're on your own to figure it out, possibly by
         | browsing source code.
         | 
         | This is exacerbated by the same libs using minimal examples
         | that don't show the code used in a function, struct etc where
         | explicit types are required.
        
       | the__alchemist wrote:
       | "unsafe forbidden" (GH tag)
       | 
       | Is this practical for web programming? In the domains I'm used to
       | (embedded and graphics programming), this isn't feasible. I
       | recall some Actix drama re `unsafe` that this tag presumably
       | alludes to. Perhaps web programming is abstract enough where this
       | is a reasonable goal (?)
       | 
       | Unrelated: What have y'all been using these minimal Rust web
       | frameworks for? I've found them too sparse for websites, eg
       | missing automatic DB migrations, auth, admin, email etc. On the
       | plus side, Serde and Chrono are both things that apply to web
       | programming and are (IMO) best-in-class.
       | 
       | > Minimizing the use of generics.
       | 
       | From the readme. Love this!
        
         | lovingCranberry wrote:
         | > "unsafe forbidden" (GH tag)
         | 
         | Unrelated to this project, but I dislike the obsession of
         | "unsafe" within the rust community.
         | 
         | Sometimes I _need_ to dereference a raw pointer (rare!).
         | 
         | Sometimes I actually _know_ what I 'm doing (very rare!!).
         | 
         | Sometimes I rigorously tested my code (exceptionally rare!!!).
         | 
         | When I see people making PRs (to e.g. Actix) to change unsafe
         | code to safe code in an API the user *never* sees, which
         | results in a performance penalty, just for the sake of not
         | using the word "unsafe" in the code, I get mad. I totally
         | understood Nikolay's reaction back then. Random people opened
         | PRs and flamed him without knowing anything about the internals
         | and the consequences.
         | 
         | The unsafe keyword means that I know what I'm doing. Just trust
         | me for once, please.
         | 
         | Edit: if you actually want to know what you're doing too, I
         | recommend you writing some linked lists. I hate linked lists
         | with passion, I think they are a bad data structure and you
         | should use Vectors 90% of the time and VecDeque the other 10%
         | of cases. But they help you to understand what you're spending
         | your electricity on.
        
           | huimang wrote:
           | What does it matter if a user never interacts with that API
           | or not?
           | 
           | Rust is focused around -safety- and performance. I would
           | rather have a slight performance hit and safe code, rather
           | than trusting some random person to 100% correctly write
           | unsafe code. Which is why tools like cargo-audit and cargo-
           | geiger exist. IIRC Nikolay didn't communicate well about
           | -why- unsafe was used, and just closed PRs that converted
           | unsafe code to safe code.
           | 
           | > The unsafe keyword means that I know what I'm doing. Just
           | trust me for once, please.
           | 
           | No, it means you think you know what you're doing.
           | 
           | It's more likely that you don't know what you're doing and/or
           | are unnecessarily invoking unsafe for convenience, than the
           | opposite. Theoretically I can look at your code and see if
           | it's correct... or I could just use projects that don't use
           | unsafe at all and save the time/headache.
           | 
           | When it comes to web server frameworks and security, I would
           | like to see as little unsafe usage as possible, and
           | documentation as to exactly why it's needed. Which is why
           | people switched to Warp/Tower and now Axum which forbids
           | unsafe code entirely.
           | 
           | If all I cared about were eking out all performance at the
           | cost of safety, I wouldn't be using Rust in the first place.
        
             | the__alchemist wrote:
             | I think the different philosophies you see re `unsafe` may
             | be due to 2 related use-case pairs that both come up here:
             | 
             | #1: Low level vice applications programming. In the former,
             | unsafe is a regular part of (at least certain layers) of
             | code; ie you're working with memory (MMIO etc) as core
             | operations, so will need `unsafe`. The situation gets
             | ambiguous for things like peripheral typestates and owned
             | singletons for register blocks etc; the line is blurred
             | about what you're using the ownership model for, and what
             | APIs should be marked as `unsafe`. For higher level uses
             | like desktop programs and web servers, you may not need any
             | `unsafe`.
             | 
             | #2: Libraries vice programs This is directly related to
             | your main point: If using someone else's code as a
             | dependency, unsafe can be a liability if you don't know why
             | it's is used. This is one aspect of the broader topic of
             | whether you can/should trust any given dependency, and
             | balancing not re-inventing wheels with learning library
             | quirks, edge-cases, subtle bugs, complexity etc. A spin on
             | this is making infrastructure specifically; I think Actix's
             | creators and users may have had different opinions on this.
        
           | CJefferson wrote:
           | The problem is, do people know what they are doing?
           | 
           | I didn't follow the whole Actix situation carefully, but here
           | is a discussion where someone found of 15 ways to trigger
           | undefined behaviour in safe code, caused by the unsafes in
           | Actix:
           | 
           | https://github.com/actix/actix-
           | web/issues/289#issuecomment-3...
           | 
           | Personally, I'd take halving the speed of my project to
           | reduce the possibility of remote security holes. We live in a
           | dangerous world nowadays, and we should take every chance to
           | minimise the risk of serious security issues.
        
           | brundolf wrote:
           | It's all context-dependent. You're right that people
           | shouldn't just drop into a project they don't understand and
           | demand that all unsafes be factored out, but just because an
           | unsafe block is internal and carefully vetted that doesn't
           | mean it's totally fine and chill either.
           | 
           | Here's a recent example where an unsafe led to a memory
           | corruption vulnerability in a thoroughly battle-tested
           | codebase: https://www.graplsecurity.com/post/attacking-
           | firecracker
        
         | 0x457 wrote:
         | Most frameworks in rust a like Sinatra and Flask. There aren't
         | any Rails level frameworks. I don't think there are any Rails
         | level frameworks besides Django and a few JVM frameworks.
         | 
         | Rust web frameworks just give you tools to get from request to
         | response, the rest is up to you. I think that is fine, yeah it
         | leads to some boilerplate to wire up a few libraries together,
         | but you did it once, and you're done.
        
       | brundolf wrote:
       | Is the URL wrong? This says "a small review of" but it just links
       | to Poem's GitHub repo
        
         | homarp wrote:
         | Another comment is the review:
         | https://news.ycombinator.com/item?id=32800797
        
       | bryanlarsen wrote:
       | The author of poem is not a native English speaker so the
       | documentation is not extensive. However, the examples in the repo
       | are superb, so treat those as the primary documentation.
       | 
       | He's also responsive to GitHub issues.
       | 
       | I love the native openapi integration. Its also convenient to
       | exercise your API ad hoc.
       | 
       | Two thumbs up from me.
        
       ___________________________________________________________________
       (page generated 2022-09-11 23:01 UTC)