From b1e57037052fdfe7d93056e08820e34215be7e12 Mon Sep 17 00:00:00 2001 From: Elena Kolevska Date: Mon, 13 Jan 2025 11:37:46 +0000 Subject: [PATCH] sync from upstream Signed-off-by: Elena Kolevska --- CHANGELOG.md | 5 ++++ README.md | 2 +- durabletask/internal/shared.py | 17 ++++++++++++ tests/test_client.py | 49 +++++++++++++++++++++++++++++++++- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc4b3d2..286312c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `set_custom_status` orchestrator API ([#31](https://github.com/microsoft/durabletask-python/pull/31)) - contributed by [@famarting](https://github.com/famarting) - Added `purge_orchestration` client API ([#34](https://github.com/microsoft/durabletask-python/pull/34)) - contributed by [@famarting](https://github.com/famarting) +### Changes + +- Protos are compiled with gRPC 1.62.3 / protobuf 3.25.X instead of the latest release. This ensures compatibility with a wider range of grpcio versions for better compatibility with other packages / libraries ([#36](https://github.com/microsoft/durabletask-python/pull/36)) - by [@berndverst](https://github.com/berndverst) +- Http and grpc protocols and their secure variants are stripped from the host name parameter if provided. Secure mode is enabled if the protocol provided is https or grpcs ([#38](https://github.com/microsoft/durabletask-python/pull/38) - by [@berndverst)(https://github.com/berndverst) + ### Updates - Updated `durabletask-protobuf` submodule reference to latest diff --git a/README.md b/README.md index 47e7a00..215b0a7 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ Orchestrations can specify retry policies for activities and sub-orchestrations. ### Prerequisites -- Python 3.8 +- Python 3.9 - A Durable Task-compatible sidecar, like [Dapr Workflow](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/) ### Installing the Durable Task Python client SDK diff --git a/durabletask/internal/shared.py b/durabletask/internal/shared.py index 80c3d56..d45252c 100644 --- a/durabletask/internal/shared.py +++ b/durabletask/internal/shared.py @@ -15,6 +15,9 @@ # and should be deserialized as a SimpleNamespace AUTO_SERIALIZED = "__durabletask_autoobject__" +SECURE_PROTOCOLS = ["https://", "grpcs://"] +INSECURE_PROTOCOLS = ["http://", "grpc://"] + def get_default_host_address() -> str: return "localhost:4001" @@ -24,6 +27,20 @@ def get_grpc_channel(host_address: Union[str, None], metadata: Union[List[Tuple[ if host_address is None: host_address = get_default_host_address() + for protocol in SECURE_PROTOCOLS: + if host_address.lower().startswith(protocol): + secure_channel = True + # remove the protocol from the host name + host_address = host_address[len(protocol):] + break + + for protocol in INSECURE_PROTOCOLS: + if host_address.lower().startswith(protocol): + secure_channel = False + # remove the protocol from the host name + host_address = host_address[len(protocol):] + break + if secure_channel: channel = grpc.secure_channel(host_address, grpc.ssl_channel_credentials()) else: diff --git a/tests/test_client.py b/tests/test_client.py index b27f8e3..caacf65 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,4 +1,4 @@ -from unittest.mock import patch +from unittest.mock import patch, ANY from durabletask.internal.shared import (DefaultClientInterceptorImpl, get_default_host_address, @@ -39,3 +39,50 @@ def test_get_grpc_channel_with_metadata(): assert args[0] == mock_channel.return_value assert isinstance(args[1], DefaultClientInterceptorImpl) assert args[1]._metadata == METADATA + + +def test_grpc_channel_with_host_name_protocol_stripping(): + with patch('grpc.insecure_channel') as mock_insecure_channel, patch( + 'grpc.secure_channel') as mock_secure_channel: + + host_name = "myserver.com:1234" + + prefix = "grpc://" + get_grpc_channel(prefix + host_name, METADATA) + mock_insecure_channel.assert_called_with(host_name) + + prefix = "http://" + get_grpc_channel(prefix + host_name, METADATA) + mock_insecure_channel.assert_called_with(host_name) + + prefix = "HTTP://" + get_grpc_channel(prefix + host_name, METADATA) + mock_insecure_channel.assert_called_with(host_name) + + prefix = "GRPC://" + get_grpc_channel(prefix + host_name, METADATA) + mock_insecure_channel.assert_called_with(host_name) + + prefix = "" + get_grpc_channel(prefix + host_name, METADATA) + mock_insecure_channel.assert_called_with(host_name) + + prefix = "grpcs://" + get_grpc_channel(prefix + host_name, METADATA) + mock_secure_channel.assert_called_with(host_name, ANY) + + prefix = "https://" + get_grpc_channel(prefix + host_name, METADATA) + mock_secure_channel.assert_called_with(host_name, ANY) + + prefix = "HTTPS://" + get_grpc_channel(prefix + host_name, METADATA) + mock_secure_channel.assert_called_with(host_name, ANY) + + prefix = "GRPCS://" + get_grpc_channel(prefix + host_name, METADATA) + mock_secure_channel.assert_called_with(host_name, ANY) + + prefix = "" + get_grpc_channel(prefix + host_name, METADATA, True) + mock_secure_channel.assert_called_with(host_name, ANY) \ No newline at end of file