Smithy is a protocol-agnostic interface definition language and set of tools for generating clients, servers, and documentation for any programming language.
Smithy service shapes can be annotated with multiple protocol traits.
$version: "2"
namespace smithy.example
use aws.protocols#restJson1
use aws.protocols#awsJson1_1
@restJson1
@awsJson1_1
service MyService {
version: "2023-09-12"
}
In theory this indicates that clients invoking operations of MyService can use any of the two restJson1, @awsJson1_1 protocols, and MyService should accept incoming requests in any of the two protocols.
However, the spec has no provisions on how to implement multi-protocol support, either for clients or for servers.
Currently smithy-rs server SDKs are generated such that they only support the first protocol the service shape is annotated with; the rest of the protocols are ignored.
Multi-protocol support is important for services that want to begin supporting a better protocol and migrate their existing clients to it. Without a period during which the service understands both protocols, the migration becomes challenging or even impossible.
Server-side
Implementation-wise, one possiblity is that when receiving a request, the server SDK iterates over a priority-ordered list of supported protocols and attempt to "claim" it: if claiming succeeds, the request is handed over to that protocol's router. If routing or any other step thereafter fails, the request is rejected: we do not attempt to process the request with the next protocol.
When tackling this issue I'd like the algorithm to be detailedly documented, and I'd like as much of it to be contributed to the Smithy specification so that other Smithy-based server SDK generators abide by the same rules. In particular, we should answer these questions:
In what order do servers claim a request for a protocol?
Perhaps this should not be publicly documented and left implementation-defined, so that users don't attempt to rely on it in some way. The order should be deterministic though.
How does each of our supported protocols attempt to claim a request? Ideally, this check should be, simple, inexpensive, and not modify the request or attempt to deserialize it.
For example, for the awsJson1_1 protocol, checking first the HTTP version against the http and eventStreamHttp properties and then checking for Content-Type equalling application/x-amz-json-1.1 should suffice to unambiguously (relative to all other currently documented AWS protocols) detect that the request is indeed meant to be processed by said protocol (one could additionally and inexpensively check for the URI being /, method being POST, and the presence of X-Amz-Target, so it's important that a decision is made and the concrete steps be documented, ideally in the spec). For other protocols, like restJson1, the checks to perform are less apparent.
Could Smithy help the user by failing at build time in not defining operations where a request could be claimed by multiple protocols i.e. "ambiguous requests"?
What does a server respond with if the request cannot be claimed by any annotated protocol? Most likely with a 404 and an UnknownOperationException, but serializing said exception is kinda unsolvable when you haven't agreed on a protocol.
What happens with operations that are only available in specific protocols, because they rely on features that are not available in all the annotated protocols (e.g. event streams)?
Client-side
Things are easier I think. A client should probably simply send a request picking the first protocol that the server would consider when processing one. So clients should always be code-generated with a single protocol and discard the rest?
I think it makes sense to add guidance to the specification around how to support multiple protocols simultaneously. We'll want to avoid getting too much into implementation detail, but I think we can do it.
Smithy
service
shapes can be annotated with multiple protocol traits.In theory this indicates that clients invoking operations of
MyService
can use any of the tworestJson1
,@awsJson1_1
protocols, andMyService
should accept incoming requests in any of the two protocols.However, the spec has no provisions on how to implement multi-protocol support, either for clients or for servers.
Currently smithy-rs server SDKs are generated such that they only support the first protocol the
service
shape is annotated with; the rest of the protocols are ignored.Multi-protocol support is important for services that want to begin supporting a better protocol and migrate their existing clients to it. Without a period during which the service understands both protocols, the migration becomes challenging or even impossible.
Server-side
Implementation-wise, one possiblity is that when receiving a request, the server SDK iterates over a priority-ordered list of supported protocols and attempt to "claim" it: if claiming succeeds, the request is handed over to that protocol's router. If routing or any other step thereafter fails, the request is rejected: we do not attempt to process the request with the next protocol.
When tackling this issue I'd like the algorithm to be detailedly documented, and I'd like as much of it to be contributed to the Smithy specification so that other Smithy-based server SDK generators abide by the same rules. In particular, we should answer these questions:
awsJson1_1
protocol, checking first the HTTP version against thehttp
andeventStreamHttp
properties and then checking forContent-Type
equallingapplication/x-amz-json-1.1
should suffice to unambiguously (relative to all other currently documented AWS protocols) detect that the request is indeed meant to be processed by said protocol (one could additionally and inexpensively check for the URI being/
, method beingPOST
, and the presence ofX-Amz-Target
, so it's important that a decision is made and the concrete steps be documented, ideally in the spec). For other protocols, likerestJson1
, the checks to perform are less apparent.UnknownOperationException
, but serializing said exception is kinda unsolvable when you haven't agreed on a protocol.Client-side
Things are easier I think. A client should probably simply send a request picking the first protocol that the server would consider when processing one. So clients should always be code-generated with a single protocol and discard the rest?