Skip to content

Conversation

@bentsku
Copy link
Contributor

@bentsku bentsku commented Sep 16, 2025

Motivation

This PR is part of a bigger initiative (see #13028) to implement support for the new Smithy Protocol smithy-rpc-v2-cbor, as well as the new Botocore multi-protocol support for single services (before, a service could only define one protocol).

This PR implements a new protocol detection for services who support multiple protocols. It sets the protocol as a first class citizen in our handler chain, allowing us to use the detected protocol to create our parser and serializer.

The protocol detection is done by following the different specs as defined at https://smithy.io/2.0/aws/protocols/ and https://smithy.io/2.0/additional-specs/protocols

We have extensive testing of the protocol determination by upgrading our current automated service detection tests, by running them for all protocols a service supports and verifying that the protocol is the one we selected when serializing the request.

This also implements the retrieval of Service Indicators when trying to match a service based on the request, to find indicator if the request is done via smithy-rpc-v2-cbor, as it defines a special way to encode its service and operation in the path.

We also implement a priority detection on the protocol, as some services are pretty greedy (rest-based services).

One important change is of the signature of ServiceModelIdentifier, as we now need to know all protocols a service support for the query and ec2 service detection (we need it to match it with services.by_operation(values["Action"]))

You can see how it is used for CloudWatch with the follow-up PR: #13161

Changes

  • make protocol part of the RequestContext
  • implement protocol detection for services who support multiple protocols
  • use the detected protocol to create the parser and serializer
  • implement RPC v2 service indicators to properly route requests
  • add protocols to ServiceModelIdentifier in aws/spec.py
  • update test_service_router_works_for_every_service to also verify protocol

@bentsku bentsku added this to the 4.9 milestone Sep 16, 2025
@bentsku bentsku self-assigned this Sep 16, 2025
@bentsku bentsku added area: asf semver: minor Non-breaking changes which can be included in minor releases, but not in patch releases docs: skip Pull request does not require documentation changes labels Sep 16, 2025
@github-actions
Copy link

github-actions bot commented Sep 16, 2025

Test Results - Preflight, Unit

22 195 tests  +23   20 457 ✅ +23   15m 30s ⏱️ -27s
     1 suites ± 0    1 738 💤 ± 0 
     1 files   ± 0        0 ❌ ± 0 

Results for commit 61416ea. ± Comparison against base commit 4012f01.

♻️ This comment has been updated with latest results.

@bentsku bentsku force-pushed the rpcv2-cbor-parser-serializer branch from aef8ffa to 646b04c Compare September 17, 2025 10:13
Base automatically changed from rpcv2-cbor-parser-serializer to main September 17, 2025 11:52
@bentsku bentsku force-pushed the asf-multi-protocol branch 2 times, most recently from 6db9a1d to f803320 Compare September 17, 2025 16:42
@github-actions
Copy link

github-actions bot commented Sep 17, 2025

S3 Image Test Results (AMD64 / ARM64)

    2 files    2 suites   7m 56s ⏱️
  532 tests 480 ✅  52 💤 0 ❌
1 064 runs  960 ✅ 104 💤 0 ❌

Results for commit 61416ea.

♻️ This comment has been updated with latest results.

@github-actions
Copy link

github-actions bot commented Sep 17, 2025

Test Results (amd64) - Acceptance

7 tests  ±0   5 ✅ ±0   3m 19s ⏱️ -7s
1 suites ±0   2 💤 ±0 
1 files   ±0   0 ❌ ±0 

Results for commit 61416ea. ± Comparison against base commit 4012f01.

♻️ This comment has been updated with latest results.

@github-actions
Copy link

github-actions bot commented Sep 17, 2025

Test Results (amd64) - Integration, Bootstrap

    5 files      5 suites   2h 36m 52s ⏱️
5 037 tests 4 556 ✅ 481 💤 0 ❌
5 043 runs  4 556 ✅ 487 💤 0 ❌

Results for commit 61416ea.

♻️ This comment has been updated with latest results.

@github-actions
Copy link

github-actions bot commented Sep 17, 2025

LocalStack Community integration with Pro

    2 files  ±0      2 suites  ±0   1h 58m 35s ⏱️ +38s
4 663 tests ±0  4 342 ✅ ±0  321 💤 ±0  0 ❌ ±0 
4 665 runs  ±0  4 342 ✅ ±0  323 💤 ±0  0 ❌ ±0 

Results for commit 61416ea. ± Comparison against base commit 4012f01.

♻️ This comment has been updated with latest results.

raise ProtocolError(
f"Could not determine the protocol for the request: "
f"{request.method} {request.path} for the service '{service_model.service_name}' "
f"(available protocols: {protocols}"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: missing closing parenthesis, will fix before merging

Suggested change
f"(available protocols: {protocols}"
f"(available protocols: {protocols})"

@bentsku bentsku removed the request for review from cloutierMat September 18, 2025 02:28
Copy link
Member

@alexrashed alexrashed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! Really nice to see this final step be actually pretty smooth and clean after the nice preparations with the previous PRs! 💯
This is a huge step towards native multi-protocol support for basically all services out there based on a single service spec file! Really looking forward to #13151! 🤩

# TODO: even though our Service Name Parser will only use a protocol that is available for the service, we might
# want to verify if the given protocol here is available for that service, in case we are manually calling
# this factory. Revisit once we implement multi-protocol support
service_protocol = protocol or service.protocol
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Coming back to this comment in a previous PR: Would it make sense here to fail hard here if the protocol is not in the list of protocols for the service? Should we be more permissive or more strict here? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I initially decided against that, because this check is done in determine_aws_protocol and the only place we really manually use the serializer and parser are in our unit tests.

If a new code path needs to use the serializer/parser manually, it will need to use the protocol from the RequestContext, or hardcode to a specific protocol (like sqs-query Query API routes)

So I'm not sure adding an additional check is worth it? As it will be executed in every request (when not cached)
But if you have other cases I haven't though about, happy to hear and update 👍

Comment on lines +35 to +38
# TODO: we should come up with a better way to create a protocol-aware client
protocol_serializer = create_serializer(protocol_name=protocol, include_validation=False)
monkeypatch.setattr(client, "_serializer", protocol_serializer)
return client
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: While this is a bit hacky, this is really creating an incredibly thorough test! 🤩

:return: the protocol matched, if any
"""
for protocol in _PROTOCOL_DETECTION_PRIORITY:
if protocol in available_protocols and _matches_protocol(request, protocol):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Is it necessary to check if the request matches the protocol if there is only a single protocol supported by a service (which is the absolute vast majority of services)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is done in determine_aws_protocol:

    if not (protocols := service_model.metadata.get("protocols")):
        # if the service does not define multiple protocols, return the `protocol` defined for the service
        return service_model.protocol

    if len(protocols) == 1:
        return protocols[0]

    if protocol := get_protocol_from_request(request, available_protocols=protocols):
        return protocol

We only call this get_protocol_from_request, but I think the naming is wrong which is why this question came up 😄

maybe match_protocol or something like that? What would be a good word for picking a protocol out of a list 🤔

@bentsku bentsku merged commit f188c62 into main Sep 19, 2025
48 checks passed
@bentsku bentsku deleted the asf-multi-protocol branch September 19, 2025 09:34
@alexrashed alexrashed added the notes: skip Pull request does not have to be mentioned in the release notes label Sep 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: asf docs: skip Pull request does not require documentation changes notes: skip Pull request does not have to be mentioned in the release notes semver: minor Non-breaking changes which can be included in minor releases, but not in patch releases

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants