From e368317c9ff99db572a0463e9dbd5e77294cfcd4 Mon Sep 17 00:00:00 2001 From: Tim Swast Date: Fri, 26 Oct 2018 11:00:10 -0700 Subject: [PATCH] Don't URL-encode slashes in gRPC request headers. (#6310) Per internal document go/api-url-encoding (approved on 2017-04-20), "the client library will %-encode everything except "/" and unreserved characters, and the server will %-decode everything except "%2F" and %2f" This is currently affecting a private API which passes a resource name (containing slashes) as a query parameter over gRPC. --- api_core/google/api_core/gapic_v1/routing_header.py | 10 +++++++++- api_core/tests/unit/gapic/test_routing_header.py | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/api_core/google/api_core/gapic_v1/routing_header.py b/api_core/google/api_core/gapic_v1/routing_header.py index ca3511611863..fc88bb6a6c2d 100644 --- a/api_core/google/api_core/gapic_v1/routing_header.py +++ b/api_core/google/api_core/gapic_v1/routing_header.py @@ -20,6 +20,8 @@ Generally, these headers are specified as gRPC metadata. """ +import sys + from six.moves.urllib.parse import urlencode ROUTING_METADATA_KEY = 'x-goog-request-params' @@ -35,7 +37,13 @@ def to_routing_header(params): Returns: str: The routing header string. """ - return urlencode(params) + if sys.version_info[0] < 3: + # Python 2 does not have the "safe" parameter for urlencode. + return urlencode(params).replace('%2F', '/') + return urlencode( + params, + # Per Google API policy (go/api-url-encoding), / is not encoded. + safe='/') def to_grpc_metadata(params): diff --git a/api_core/tests/unit/gapic/test_routing_header.py b/api_core/tests/unit/gapic/test_routing_header.py index d3a4bc35f7a2..6bedf292a0be 100644 --- a/api_core/tests/unit/gapic/test_routing_header.py +++ b/api_core/tests/unit/gapic/test_routing_header.py @@ -22,6 +22,12 @@ def test_to_routing_header(): assert value == "name=meep&book.read=1" +def test_to_routing_header_with_slashes(): + params = [('name', 'me/ep'), ('book.read', '1&2')] + value = routing_header.to_routing_header(params) + assert value == "name=me/ep&book.read=1%262" + + def test_to_grpc_metadata(): params = [('name', 'meep'), ('book.read', '1')] metadata = routing_header.to_grpc_metadata(params)