Crate tower
Expand description
Tower support for Rama.
See https://github.com/plabayo/rama/blob/main/examples/http_rama_tower.rs for an example on how to use this crate with opt-in tower support for rama.
§Overview
Rama has the concept of a Context
, containing dynamic and static state,
as well as runtime utilities. Tower does not have this. While it is possible
that rama will move also away from the Context
concept all together
(see https://github.com/plabayo/rama/issues/462), it is there for now,
which is an issue for tower::Layer
-tower::Service
s in specific.
+-----------+ +-----------+ +-----------+
| serviceA | | serviceB | | serviceC |
| | | | | |
| serve(ctx, req) | call(req) | | serve(ctx, req)
| | | | | | |
| | | | | | |
| +--> [ctx] | | | | |
| | | | | | |
| +---------->+---+------>+-------->+ |
| passes req | calls C, but only | |
| & ctx held | has req, so must | |
| somewhere | forward ctx | |
+-----------+ +-----------+ +-----------+
That is where the ContextSmuggler
trait comes into play.
Note that this is only a problem for tower::Layer
-tower::Service
s,
and not for (leaf/endpoint) tower::Service
s.
§tower::Service
adapters
Adapters to use a tower::Service
as a rama::Service
.
- either
ServiceAdapter
:Clone
/ call, most commonly used and also what we do for the layer version; - or
SharedServiceAdapter
: shared service across all calls, locked using an async [Mutex
], less commonly done, but there if you really have to.
§tower::Layer
adapters
Adapters to use a tower::Layer
as a rama::Layer
. Adapting layers
is a bit more complicated than service adapters because of the entire
Context
smuggling troubles.
Next to that there is the fact that a layer produces
a service which also has to be wrapped, or in our case we have to wrap 2 times.
Once we have to wrap it to turn the “inner” rama::Service
(“service C” in the above diagram)
into a tower::Service
and produce the resulting tower::Service
produced by the wrapped
tower::Layer
also into a rama::Service
(“service A” in the above diagram).
To make this all happen and possible we have the following components:
LayerAdapter
: use atower::Layer
as arama::Layer
, which ensures that:- the inner
rama::Service
passed to the adaptedtower::Layer
is first wrapped by aTowerAdapterService
to ensure it is atower::Service
; - the produced
tower::Service
by thetower::Layer
is turned into arama::Service
by wrapping it with aLayerAdapterService
.
- the inner
It is the LayerAdapterService
which makes use of ContextSmuggler::inject_ctx
to smuggle the Context
,
such that the nested TowerAdapterService
can pass it on using ContextSmuggler::try_extract_ctx
to the “most” inner rama::Service
from the POV of this (sub)stack.
§Halting
The adapters in this carate assumes that a tower::Service
will always become ready eventually,
as it will call poll_ready
until ready prior to calling
the tower::Service
.
Please ensure that your tower::Service
does not require a side-step to prevent such halting.
§Rama
Crate used by the end-user rama
crate and rama
crate authors alike.
Learn more about rama
:
- Github: https://github.com/plabayo/rama
- Book: https://ramaproxy.org/book/
§Rama Tower Origins
Initially Rama was designed fully around the idea of Tower, directly. The initial design of Rama took many
iterations and was R&D’d over a timespan of about a year, in between other work and parenting.
We switched between tower
,
tower-async
(our own public fork of tower) and back to tower
again…
It became clear however that the version of tower
at the time was incompatible (and still is) with the ideas which we wanted it to have:
- We are not interested in the
poll_ready
code of tower, and in fact it would be harmful if something is used which makes use of it (Axum warns for it, but strictly it is possible…);- This idea is also further elaborated in the FAQ of our tower-async fork: https://github.com/plabayo/tower-async?tab=readme-ov-file#faq
- We want to start to prepare for an
async
-ready future as soon as we can…
All in all, it was clear after several iterations that usage of tower did more harm then it did good. What was supposed to be a stack to help us implement our vision, became a hurdle instead.
This is not the fault of tower, but more a sign that it did not age well, or perhaps… it is actually a very different beast altogether.
As both tower and rama are still in their pre “1.0” days, and we are still evolving together and with the rest of the wider ecosystems, it is possible that we grow closer once again.
Modules§
Structs§
- Layer
Adapter - Adapter to use a
tower::Layer
-tower::Service
as arama::Layer
-rama::Service
. - Layer
Adapter Service - Adapter to use a
tower::Service
as arama::Service
in function oftower::Layer
. - Service
Adapter - Adapter to use a
tower::Service
as arama::Service
, cloning the servicer for each request it has to serve. - Shared
Service Adapter - Adapter to use a
tower::Service
as arama::Service
, sharing the service between each request it has to serve. - Tower
Adapter Service - Adapter to use a
rama::Service
as atower::Service
in functio noftower::Layer
.