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::Services 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::Services, and not for (leaf/endpoint) tower::Services.

§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:

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:

§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…);
  • 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§

core
re-exported tower-rs crates
layer

Structs§

LayerAdapter
Adapter to use a tower::Layer-tower::Service as a rama::Layer-rama::Service.
LayerAdapterService
Adapter to use a tower::Service as a rama::Service in function of tower::Layer.
ServiceAdapter
Adapter to use a tower::Service as a rama::Service, cloning the servicer for each request it has to serve.
SharedServiceAdapter
Adapter to use a tower::Service as a rama::Service, sharing the service between each request it has to serve.
TowerAdapterService
Adapter to use a rama::Service as a tower::Service in functio nof tower::Layer.