From 41993649c5469b8e926f09e7c940b260632d222a Mon Sep 17 00:00:00 2001 From: yndu13 Date: Wed, 26 Nov 2025 19:38:36 +0800 Subject: [PATCH] [POP gateway]fix SignedHeaders --- .github/workflows/pop_python.yml | 12 ++-- .../csharp/core/Client.cs | 22 ++++-- .../csharp/core/Properties/AssemblyInfo.cs | 4 +- .../csharp/tests/UnitTest.cs | 68 ++++++++++++++++++- .../golang/client/client.go | 19 ++++-- .../golang/client/client_test.go | 47 +++++++++++++ .../java/com/aliyun/gateway/pop/Client.java | 19 ++++-- .../java/com/aliyun/gateway/pop/UnitTest.java | 60 ++++++++++++++++ alibabacloud-gateway-pop/main.tea | 20 ++++-- alibabacloud-gateway-pop/php/src/Client.php | 18 +++-- .../php/tests/UnitTest.php | 61 ++++++++++++++++- .../python/alibabacloud_gateway_pop/client.py | 16 +++-- alibabacloud-gateway-pop/python/setup.py | 2 +- .../python/tests/test_client.py | 65 ++++++++++++++++++ alibabacloud-gateway-pop/ts/package.json | 3 +- alibabacloud-gateway-pop/ts/src/client.ts | 20 ++++-- .../ts/test/client.spec.ts | 61 ++++++++++++++++- 17 files changed, 461 insertions(+), 56 deletions(-) diff --git a/.github/workflows/pop_python.yml b/.github/workflows/pop_python.yml index 9cda599d..9da64713 100644 --- a/.github/workflows/pop_python.yml +++ b/.github/workflows/pop_python.yml @@ -11,22 +11,22 @@ defaults: working-directory: alibabacloud-gateway-pop/python jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies - run: pip install setuptools urllib3==1.26.20 alibabacloud-tea coverage pytest alibabacloud-credentials==0.3.6 && python setup.py install + run: pip install setuptools urllib3==1.26.20 'cryptography>=3.0.0, <45.0.0' alibabacloud-tea alibabacloud_darabonba_encode_util alibabacloud_darabonba_signature_util alibabacloud_gateway_spi alibabacloud_tea_util alibabacloud_openapi_util alibabacloud_endpoint_util alibabacloud_darabonba_string alibabacloud_darabonba_map alibabacloud_darabonba_array alibabacloud_tea_xml coverage pytest alibabacloud-credentials && python setup.py install - name: Test with unittest run: | - coverage run --source="./alibabacloud_tea_util" -m pytest tests/test_* + coverage run -m unittest discover - name: CodeCov run: bash <(curl -s https://codecov.io/bash) -cF python \ No newline at end of file diff --git a/alibabacloud-gateway-pop/csharp/core/Client.cs b/alibabacloud-gateway-pop/csharp/core/Client.cs index 052c3eef..289594c9 100644 --- a/alibabacloud-gateway-pop/csharp/core/Client.cs +++ b/alibabacloud-gateway-pop/csharp/core/Client.cs @@ -605,18 +605,28 @@ public string BuildCanonicalizedHeaders(Dictionary headers) public List GetSignedHeaders(Dictionary headers) { List headersArray = AlibabaCloud.DarabonbaMap.MapUtil.KeySet(headers); - List sortedHeadersArray = AlibabaCloud.DarabonbaArray.ArrayUtil.AscSort(headersArray); + List newHeadersArray = new List + { + }; + + foreach (var key in headersArray) { + string lowerKey = AlibabaCloud.DarabonbaString.StringUtil.ToLower(key); + string value = headers.Get(key); + if (!AlibabaCloud.TeaUtil.Common.IsUnset(value)) + { + AlibabaCloud.DarabonbaArray.ArrayUtil.Append(newHeadersArray, lowerKey); + } + } + List sortedHeadersArray = AlibabaCloud.DarabonbaArray.ArrayUtil.AscSort(newHeadersArray); string tmp = ""; string separator = ""; foreach (var key in sortedHeadersArray) { - string lowerKey = AlibabaCloud.DarabonbaString.StringUtil.ToLower(key); - if (AlibabaCloud.DarabonbaString.StringUtil.HasPrefix(lowerKey, "x-acs-") || AlibabaCloud.DarabonbaString.StringUtil.Equals(lowerKey, "host") || AlibabaCloud.DarabonbaString.StringUtil.Equals(lowerKey, "content-type")) + if (AlibabaCloud.DarabonbaString.StringUtil.HasPrefix(key, "x-acs-") || AlibabaCloud.DarabonbaString.StringUtil.Equals(key, "host") || AlibabaCloud.DarabonbaString.StringUtil.Equals(key, "content-type")) { - string value = headers.Get(key); - if (!AlibabaCloud.TeaUtil.Common.IsUnset(value) && !AlibabaCloud.DarabonbaString.StringUtil.Contains(tmp, lowerKey)) + if (!AlibabaCloud.DarabonbaString.StringUtil.Contains(tmp, key)) { - tmp = "" + tmp + separator + lowerKey; + tmp = "" + tmp + separator + key; separator = ";"; } } diff --git a/alibabacloud-gateway-pop/csharp/core/Properties/AssemblyInfo.cs b/alibabacloud-gateway-pop/csharp/core/Properties/AssemblyInfo.cs index 7c2cb59b..bdfab620 100644 --- a/alibabacloud-gateway-pop/csharp/core/Properties/AssemblyInfo.cs +++ b/alibabacloud-gateway-pop/csharp/core/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ // Build Number // Revision // -[assembly: AssemblyVersion("0.1.1.0")] -[assembly: AssemblyFileVersion("0.1.1.0")] +[assembly: AssemblyVersion("0.1.2.0")] +[assembly: AssemblyFileVersion("0.1.2.0")] diff --git a/alibabacloud-gateway-pop/csharp/tests/UnitTest.cs b/alibabacloud-gateway-pop/csharp/tests/UnitTest.cs index d494ef8f..7e3a690e 100644 --- a/alibabacloud-gateway-pop/csharp/tests/UnitTest.cs +++ b/alibabacloud-gateway-pop/csharp/tests/UnitTest.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using AlibabaCloud.GatewayPop; using Xunit; @@ -31,5 +32,70 @@ public void Test_GetRegion() Assert.Equal("cn-hangzhou-acdr-ut-1", client.GetRegion("test", "test-inner.cn-hangzhou-acdr-ut-1.aliyuncs.com", null)); Assert.Equal("cn-edge-1", client.GetRegion("test", "test-inner.cn-edge-1.aliyuncs.com", null)); } + + [Fact] + public void Test_GetSignedHeaders() + { + Client client = new Client(); + + // 测试空headers + var emptyHeaders = new Dictionary(); + var result = client.GetSignedHeaders(emptyHeaders); + Assert.Equal(1, result.Count); + + // 测试只包含需要签名的headers + var headers = new Dictionary + { + { "host", "example.com" }, + { "Host", "example.com" }, + { "content-type", "application/json" }, + { "x-acs-action", "TestAction" } + }; + result = client.GetSignedHeaders(headers); + Assert.Equal(3, result.Count); + Assert.Equal("content-type", result[0]); + Assert.Equal("host", result[1]); + Assert.Equal("x-acs-action", result[2]); + + // 测试包含不需要签名的headers + var headersWithIgnore = new Dictionary + { + { "host", "example.com" }, + { "content-type", "application/json" }, + { "x-acs-action", "TestAction" }, + { "authorization", "Bearer token" }, + { "user-agent", "C#-client" } + }; + result = client.GetSignedHeaders(headersWithIgnore); + Assert.Equal(3, result.Count); + Assert.Equal("content-type", result[0]); + Assert.Equal("host", result[1]); + Assert.Equal("x-acs-action", result[2]); + + // 测试空值header + var headersWithEmpty = new Dictionary + { + { "host", "example.com" }, + { "content-type", null }, + { "x-acs-action", "TestAction" } + }; + result = client.GetSignedHeaders(headersWithEmpty); + Assert.Equal(2, result.Count); + Assert.Equal("host", result[0]); + Assert.Equal("x-acs-action", result[1]); + + // 测试大小写不敏感 + var headersWithCase = new Dictionary + { + { "HOST", "example.com" }, + { "Content-Type", "application/json" }, + { "X-Acs-Action", "TestAction" } + }; + result = client.GetSignedHeaders(headersWithCase); + Assert.Equal(3, result.Count); + Assert.Equal("content-type", result[0]); + Assert.Equal("host", result[1]); + Assert.Equal("x-acs-action", result[2]); + } } -} +} \ No newline at end of file diff --git a/alibabacloud-gateway-pop/golang/client/client.go b/alibabacloud-gateway-pop/golang/client/client.go index 89cb0a49..a84cdbda 100644 --- a/alibabacloud-gateway-pop/golang/client/client.go +++ b/alibabacloud-gateway-pop/golang/client/client.go @@ -446,15 +446,22 @@ func (client *Client) BuildCanonicalizedHeaders(headers map[string]*string) (_re func (client *Client) GetSignedHeaders(headers map[string]*string) (_result []*string) { headersArray := map_.KeySet(headers) - sortedHeadersArray := array.AscSort(headersArray) + newHeadersArray := []*string{} + for _, key := range headersArray { + lowerKey := string_.ToLower(key) + value := headers[tea.StringValue(key)] + if !tea.BoolValue(util.IsUnset(value)) { + newHeadersArray = append(newHeadersArray, lowerKey) + } + + } + sortedHeadersArray := array.AscSort(newHeadersArray) tmp := tea.String("") separator := tea.String("") for _, key := range sortedHeadersArray { - lowerKey := string_.ToLower(key) - if tea.BoolValue(string_.HasPrefix(lowerKey, tea.String("x-acs-"))) || tea.BoolValue(string_.Equals(lowerKey, tea.String("host"))) || tea.BoolValue(string_.Equals(lowerKey, tea.String("content-type"))) { - value := headers[tea.StringValue(key)] - if !tea.BoolValue(util.IsUnset(value)) && !tea.BoolValue(string_.Contains(tmp, lowerKey)) { - tmp = tea.String(tea.StringValue(tmp) + tea.StringValue(separator) + tea.StringValue(lowerKey)) + if tea.BoolValue(string_.HasPrefix(key, tea.String("x-acs-"))) || tea.BoolValue(string_.Equals(key, tea.String("host"))) || tea.BoolValue(string_.Equals(key, tea.String("content-type"))) { + if !tea.BoolValue(string_.Contains(tmp, key)) { + tmp = tea.String(tea.StringValue(tmp) + tea.StringValue(separator) + tea.StringValue(key)) separator = tea.String(";") } diff --git a/alibabacloud-gateway-pop/golang/client/client_test.go b/alibabacloud-gateway-pop/golang/client/client_test.go index 0cacc9bd..20aa711d 100644 --- a/alibabacloud-gateway-pop/golang/client/client_test.go +++ b/alibabacloud-gateway-pop/golang/client/client_test.go @@ -30,3 +30,50 @@ func Test_GetRegion(t *testing.T) { utils.AssertEqual(t, "cn-hangzhou-acdr-ut-1", tea.StringValue(client.GetRegion(tea.String("test"), tea.String("test-inner.cn-hangzhou-acdr-ut-1.aliyuncs.com"), nil))) utils.AssertEqual(t, "cn-edge-1", tea.StringValue(client.GetRegion(tea.String("test"), tea.String("test-inner.cn-edge-1.aliyuncs.com"), nil))) } + +func Test_GetSignedHeaders(t *testing.T) { + client, err := NewClient() + utils.AssertNil(t, err) + + // 测试空headers + result := client.GetSignedHeaders(make(map[string]*string)) + utils.AssertEqual(t, 1, len(result)) + + // 测试只包含需要签名的headers + headers := map[string]*string{ + "host": tea.String("example.com"), + "Host": tea.String("example.com"), + "content-type": tea.String("application/json"), + "x-acs-action": tea.String("TestAction"), + } + result = client.GetSignedHeaders(headers) + utils.AssertEqual(t, 3, len(result)) + utils.AssertEqual(t, "content-type", tea.StringValue(result[0])) + utils.AssertEqual(t, "host", tea.StringValue(result[1])) + utils.AssertEqual(t, "x-acs-action", tea.StringValue(result[2])) + + // 测试包含不需要签名的headers + headersWithIgnore := map[string]*string{ + "host": tea.String("example.com"), + "content-type": tea.String("application/json"), + "x-acs-action": tea.String("TestAction"), + "authorization": tea.String("Bearer token"), + "user-agent": tea.String("Go-client"), + } + result = client.GetSignedHeaders(headersWithIgnore) + utils.AssertEqual(t, 3, len(result)) + utils.AssertEqual(t, "content-type", tea.StringValue(result[0])) + utils.AssertEqual(t, "host", tea.StringValue(result[1])) + utils.AssertEqual(t, "x-acs-action", tea.StringValue(result[2])) + + // 测试空值header + headersWithEmpty := map[string]*string{ + "host": tea.String("example.com"), + "content-type": nil, + "x-acs-action": tea.String("TestAction"), + } + result = client.GetSignedHeaders(headersWithEmpty) + utils.AssertEqual(t, 2, len(result)) + utils.AssertEqual(t, "host", tea.StringValue(result[0])) + utils.AssertEqual(t, "x-acs-action", tea.StringValue(result[1])) +} diff --git a/alibabacloud-gateway-pop/java/src/main/java/com/aliyun/gateway/pop/Client.java b/alibabacloud-gateway-pop/java/src/main/java/com/aliyun/gateway/pop/Client.java index 2b49dd78..83dac049 100644 --- a/alibabacloud-gateway-pop/java/src/main/java/com/aliyun/gateway/pop/Client.java +++ b/alibabacloud-gateway-pop/java/src/main/java/com/aliyun/gateway/pop/Client.java @@ -340,15 +340,22 @@ public String buildCanonicalizedHeaders(java.util.Map headers) t public java.util.List getSignedHeaders(java.util.Map headers) throws Exception { java.util.List headersArray = com.aliyun.darabonba.map.Client.keySet(headers); - java.util.List sortedHeadersArray = com.aliyun.darabonba.array.Client.ascSort(headersArray); + java.util.List newHeadersArray = new java.util.ArrayList<>(); + for (String key : headersArray) { + String lowerKey = com.aliyun.darabonbastring.Client.toLower(key); + String value = headers.get(key); + if (!com.aliyun.teautil.Common.isUnset(value)) { + com.aliyun.darabonba.array.Client.append(newHeadersArray, lowerKey); + } + + } + java.util.List sortedHeadersArray = com.aliyun.darabonba.array.Client.ascSort(newHeadersArray); String tmp = ""; String separator = ""; for (String key : sortedHeadersArray) { - String lowerKey = com.aliyun.darabonbastring.Client.toLower(key); - if (com.aliyun.darabonbastring.Client.hasPrefix(lowerKey, "x-acs-") || com.aliyun.darabonbastring.Client.equals(lowerKey, "host") || com.aliyun.darabonbastring.Client.equals(lowerKey, "content-type")) { - String value = headers.get(key); - if (!com.aliyun.teautil.Common.isUnset(value) && !com.aliyun.darabonbastring.Client.contains(tmp, lowerKey)) { - tmp = "" + tmp + "" + separator + "" + lowerKey + ""; + if (com.aliyun.darabonbastring.Client.hasPrefix(key, "x-acs-") || com.aliyun.darabonbastring.Client.equals(key, "host") || com.aliyun.darabonbastring.Client.equals(key, "content-type")) { + if (!com.aliyun.darabonbastring.Client.contains(tmp, key)) { + tmp = "" + tmp + "" + separator + "" + key + ""; separator = ";"; } diff --git a/alibabacloud-gateway-pop/java/src/test/java/com/aliyun/gateway/pop/UnitTest.java b/alibabacloud-gateway-pop/java/src/test/java/com/aliyun/gateway/pop/UnitTest.java index 34b86e50..2d29823d 100644 --- a/alibabacloud-gateway-pop/java/src/test/java/com/aliyun/gateway/pop/UnitTest.java +++ b/alibabacloud-gateway-pop/java/src/test/java/com/aliyun/gateway/pop/UnitTest.java @@ -1,5 +1,9 @@ package com.aliyun.gateway.pop; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.junit.Assert; import org.junit.Test; @@ -27,4 +31,60 @@ public void getRegionTest() throws Exception { Assert.assertEquals("cn-hangzhou-acdr-ut-1", client.getRegion("test", "test-inner.cn-hangzhou-acdr-ut-1.aliyuncs.com", null)); Assert.assertEquals("cn-edge-1", client.getRegion("test", "test-inner.cn-edge-1.aliyuncs.com", null)); } + + @Test + public void getSignedHeadersTest() throws Exception { + Client client = new Client(); + + // 测试空headers + Map emptyHeaders = new HashMap<>(); + List result = client.getSignedHeaders(emptyHeaders); + Assert.assertEquals(1, result.size()); + + // 测试只包含需要签名的headers + Map headers = new HashMap<>(); + headers.put("host", "example.com"); + headers.put("Host", "example.com"); + headers.put("content-type", "application/json"); + headers.put("x-acs-action", "TestAction"); + result = client.getSignedHeaders(headers); + Assert.assertEquals(3, result.size()); + Assert.assertEquals("content-type", result.get(0)); + Assert.assertEquals("host", result.get(1)); + Assert.assertEquals("x-acs-action", result.get(2)); + + // 测试包含不需要签名的headers + Map headersWithIgnore = new HashMap<>(); + headersWithIgnore.put("host", "example.com"); + headersWithIgnore.put("content-type", "application/json"); + headersWithIgnore.put("x-acs-action", "TestAction"); + headersWithIgnore.put("authorization", "Bearer token"); + headersWithIgnore.put("user-agent", "Java-client"); + result = client.getSignedHeaders(headersWithIgnore); + Assert.assertEquals(3, result.size()); + Assert.assertEquals("content-type", result.get(0)); + Assert.assertEquals("host", result.get(1)); + Assert.assertEquals("x-acs-action", result.get(2)); + + // 测试空值header + Map headersWithEmpty = new HashMap<>(); + headersWithEmpty.put("host", "example.com"); + headersWithEmpty.put("content-type", null); + headersWithEmpty.put("x-acs-action", "TestAction"); + result = client.getSignedHeaders(headersWithEmpty); + Assert.assertEquals(2, result.size()); + Assert.assertEquals("host", result.get(0)); + Assert.assertEquals("x-acs-action", result.get(1)); + + // 测试大小写不敏感 + Map headersWithCase = new HashMap<>(); + headersWithCase.put("HOST", "example.com"); + headersWithCase.put("Content-Type", "application/json"); + headersWithCase.put("X-Acs-Action", "TestAction"); + result = client.getSignedHeaders(headersWithCase); + Assert.assertEquals(3, result.size()); + Assert.assertEquals("content-type", result.get(0)); + Assert.assertEquals("host", result.get(1)); + Assert.assertEquals("x-acs-action", result.get(2)); + } } \ No newline at end of file diff --git a/alibabacloud-gateway-pop/main.tea b/alibabacloud-gateway-pop/main.tea index e08de33e..d66e6024 100644 --- a/alibabacloud-gateway-pop/main.tea +++ b/alibabacloud-gateway-pop/main.tea @@ -324,16 +324,22 @@ function buildCanonicalizedHeaders(headers: map[string]string): string { function getSignedHeaders(headers: map[string]string): [string] { var headersArray : [string] = Map.keySet(headers); - var sortedHeadersArray = Array.ascSort(headersArray); + var newHeadersArray : [string] = []; + for(var key : headersArray) { + var lowerKey = String.toLower(key); + var value = headers[key]; + if (!Util.isUnset(value)) { + Array.append(newHeadersArray, lowerKey); + } + } + var sortedHeadersArray = Array.ascSort(newHeadersArray); var tmp : string = ''; var separator : string = ''; for(var key : sortedHeadersArray) { - var lowerKey = String.toLower(key); - if (String.hasPrefix(lowerKey, 'x-acs-') || String.equals(lowerKey, 'host') - || String.equals(lowerKey, 'content-type')) { - var value = headers[key]; - if (!Util.isUnset(value) && !String.contains(tmp, lowerKey)) { - tmp = `${tmp}${separator}${lowerKey}`; + if (String.hasPrefix(key, 'x-acs-') || String.equals(key, 'host') + || String.equals(key, 'content-type')) { + if (!String.contains(tmp, key)) { + tmp = `${tmp}${separator}${key}`; separator = ';'; } } diff --git a/alibabacloud-gateway-pop/php/src/Client.php b/alibabacloud-gateway-pop/php/src/Client.php index 23cfbca5..f206efe4 100644 --- a/alibabacloud-gateway-pop/php/src/Client.php +++ b/alibabacloud-gateway-pop/php/src/Client.php @@ -421,15 +421,21 @@ public function buildCanonicalizedHeaders($headers) public function getSignedHeaders($headers) { $headersArray = MapUtil::keySet($headers); - $sortedHeadersArray = ArrayUtil::ascSort($headersArray); + $newHeadersArray = []; + foreach ($headersArray as $key) { + $lowerKey = StringUtil::toLower($key); + $value = @$headers[$key]; + if (!Utils::isUnset($value)) { + ArrayUtil::append($newHeadersArray, $lowerKey); + } + } + $sortedHeadersArray = ArrayUtil::ascSort($newHeadersArray); $tmp = ""; $separator = ""; foreach ($sortedHeadersArray as $key) { - $lowerKey = StringUtil::toLower($key); - if (StringUtil::hasPrefix($lowerKey, "x-acs-") || StringUtil::equals($lowerKey, "host") || StringUtil::equals($lowerKey, "content-type")) { - $value = @$headers[$key]; - if (!Utils::isUnset($value) && !StringUtil::contains($tmp, $lowerKey)) { - $tmp = "" . $tmp . "" . $separator . "" . $lowerKey . ""; + if (StringUtil::hasPrefix($key, "x-acs-") || StringUtil::equals($key, "host") || StringUtil::equals($key, "content-type")) { + if (!StringUtil::contains($tmp, $key)) { + $tmp = "" . $tmp . "" . $separator . "" . $key . ""; $separator = ";"; } } diff --git a/alibabacloud-gateway-pop/php/tests/UnitTest.php b/alibabacloud-gateway-pop/php/tests/UnitTest.php index 9df543f4..1c746f9d 100644 --- a/alibabacloud-gateway-pop/php/tests/UnitTest.php +++ b/alibabacloud-gateway-pop/php/tests/UnitTest.php @@ -36,4 +36,63 @@ public function testGetRegion() $this->assertEquals("cn-edge-1", $client->getRegion("test", "test-inner.cn-edge-1.aliyuncs.com", null)); } -} + public function testGetSignedHeaders() + { + $client = new Client(); + + // 测试空headers + $emptyHeaders = []; + $result = $client->getSignedHeaders($emptyHeaders); + $this->assertEmpty($result); + + // 测试只包含需要签名的headers + $headers = [ + "host" => "example.com", + "content-type" => "application/json", + "x-acs-action" => "TestAction" + ]; + $result = $client->getSignedHeaders($headers); + $this->assertCount(3, $result); + $this->assertEquals("content-type", $result[0]); + $this->assertEquals("host", $result[1]); + $this->assertEquals("x-acs-action", $result[2]); + + // 测试包含不需要签名的headers + $headersWithIgnore = [ + "host" => "example.com", + "content-type" => "application/json", + "x-acs-action" => "TestAction", + "authorization" => "Bearer token", + "user-agent" => "PHP-client" + ]; + $result = $client->getSignedHeaders($headersWithIgnore); + $this->assertCount(3, $result); + $this->assertEquals("content-type", $result[0]); + $this->assertEquals("host", $result[1]); + $this->assertEquals("x-acs-action", $result[2]); + + // 测试空值header + $headersWithEmpty = [ + "host" => "example.com", + "content-type" => null, + "x-acs-action" => "TestAction" + ]; + $result = $client->getSignedHeaders($headersWithEmpty); + $this->assertCount(2, $result); + $this->assertEquals("host", $result[0]); + $this->assertEquals("x-acs-action", $result[1]); + + // 测试大小写不敏感 + $headersWithCase = [ + "HOST" => "example.com", + "Content-Type" => "application/json", + "X-Acs-Action" => "TestAction" + ]; + $result = $client->getSignedHeaders($headersWithCase); + $this->assertCount(3, $result); + $this->assertEquals("content-type", $result[0]); + $this->assertEquals("host", $result[1]); + $this->assertEquals("x-acs-action", $result[2]); + } + +} \ No newline at end of file diff --git a/alibabacloud-gateway-pop/python/alibabacloud_gateway_pop/client.py b/alibabacloud-gateway-pop/python/alibabacloud_gateway_pop/client.py index c102edbd..37d8da22 100644 --- a/alibabacloud-gateway-pop/python/alibabacloud_gateway_pop/client.py +++ b/alibabacloud-gateway-pop/python/alibabacloud_gateway_pop/client.py @@ -482,14 +482,18 @@ def get_signed_headers( headers: Dict[str, str], ) -> List[str]: headers_array = MapClient.key_set(headers) - sorted_headers_array = ArrayClient.asc_sort(headers_array) + new_headers_array = [] + for key in headers_array: + lower_key = StringClient.to_lower(key) + value = headers.get(key) + if not UtilClient.is_unset(value): + new_headers_array.append(lower_key) + sorted_headers_array = ArrayClient.asc_sort(new_headers_array) tmp = '' separator = '' for key in sorted_headers_array: - lower_key = StringClient.to_lower(key) - if StringClient.has_prefix(lower_key, 'x-acs-') or StringClient.equals(lower_key, 'host') or StringClient.equals(lower_key, 'content-type'): - value = headers.get(key) - if not UtilClient.is_unset(value) and not StringClient.contains(tmp, lower_key): - tmp = f'{tmp}{separator}{lower_key}' + if StringClient.has_prefix(key, 'x-acs-') or StringClient.equals(key, 'host') or StringClient.equals(key, 'content-type'): + if not StringClient.contains(tmp, key): + tmp = f'{tmp}{separator}{key}' separator = ';' return StringClient.split(tmp, ';', None) diff --git a/alibabacloud-gateway-pop/python/setup.py b/alibabacloud-gateway-pop/python/setup.py index e141c768..f4b4175f 100644 --- a/alibabacloud-gateway-pop/python/setup.py +++ b/alibabacloud-gateway-pop/python/setup.py @@ -24,7 +24,7 @@ """ setup module for alibabacloud_gateway_pop. -Created on 25/11/2025 +Created on 26/11/2025 @author: Alibaba Cloud SDK """ diff --git a/alibabacloud-gateway-pop/python/tests/test_client.py b/alibabacloud-gateway-pop/python/tests/test_client.py index 66a92198..2ee1cc96 100644 --- a/alibabacloud-gateway-pop/python/tests/test_client.py +++ b/alibabacloud-gateway-pop/python/tests/test_client.py @@ -26,3 +26,68 @@ def test_get_region(self): self.assertEqual('cn-hangzhou', client.get_region('test', 'test-proxy.cn-hangzhou.aliyuncs.com', None)) self.assertEqual('cn-hangzhou-acdr-ut-1', client.get_region('test', 'test-inner.cn-hangzhou-acdr-ut-1.aliyuncs.com', None)) self.assertEqual('cn-edge-1', client.get_region('test', 'test-inner.cn-edge-1.aliyuncs.com', None)) + + def test_get_signed_headers(self): + client = Client() + + # 测试基本场景:只有必须的headers + headers1 = { + 'host': 'example.com', + 'Host': 'example.com', + 'content-type': 'application/json' + } + result1 = client.get_signed_headers(headers1) + self.assertEqual(['content-type', 'host'], result1) + + # 测试包含acs相关headers的情况 + headers2 = { + 'host': 'example.com', + 'content-type': 'application/json', + 'x-acs-action': 'TestAction', + 'x-acs-version': '2019-01-01' + } + result2 = client.get_signed_headers(headers2) + self.assertEqual(['content-type', 'host', 'x-acs-action', 'x-acs-version'], result2) + + # 测试包含非acs headers的情况 + headers3 = { + 'host': 'example.com', + 'content-type': 'application/json', + 'user-agent': 'TestAgent', + 'x-custom-header': 'CustomValue' + } + result3 = client.get_signed_headers(headers3) + self.assertEqual(['content-type', 'host'], result3) # 应该只包含规定的headers + + # 测试空headers情况 + headers4 = {} + result4 = client.get_signed_headers(headers4) + self.assertEqual([''], result4) + + # 测试包含空值的headers情况 + headers5 = { + 'host': 'example.com', + 'content-type': 'application/json', + 'x-acs-action': None + } + result5 = client.get_signed_headers(headers5) + self.assertEqual(['content-type', 'host'], result5) # x-acs-action因为值为空被过滤掉 + + # 测试大小写不敏感的情况 + headers6 = { + 'Host': 'example.com', + 'Content-Type': 'application/json', + 'X-Acs-Action': 'TestAction' + } + result6 = client.get_signed_headers(headers6) + self.assertEqual(['content-type', 'host', 'x-acs-action'], result6) # 应该都被转为小写 + + # 测试排序情况 + headers7 = { + 'x-acs-zzz': 'zzz', + 'host': 'example.com', + 'x-acs-aaa': 'aaa', + 'content-type': 'application/json' + } + result7 = client.get_signed_headers(headers7) + self.assertEqual(['content-type', 'host', 'x-acs-aaa', 'x-acs-zzz'], result7) # 应该按字典序排列 \ No newline at end of file diff --git a/alibabacloud-gateway-pop/ts/package.json b/alibabacloud-gateway-pop/ts/package.json index 2e99127f..d9bbca7a 100644 --- a/alibabacloud-gateway-pop/ts/package.json +++ b/alibabacloud-gateway-pop/ts/package.json @@ -37,5 +37,6 @@ "files": [ "dist", "src" - ] + ], + "repository": "git@github.com:aliyun/alibabacloud-gateway.git" } \ No newline at end of file diff --git a/alibabacloud-gateway-pop/ts/src/client.ts b/alibabacloud-gateway-pop/ts/src/client.ts index 6a2a871c..dc01442d 100644 --- a/alibabacloud-gateway-pop/ts/src/client.ts +++ b/alibabacloud-gateway-pop/ts/src/client.ts @@ -351,16 +351,24 @@ export default class Client extends SPI { getSignedHeaders(headers: {[key: string ]: string}): string[] { let headersArray : string[] = Map.keySet(headers); - let sortedHeadersArray = Array.ascSort(headersArray); + let newHeadersArray : string[] = [ ]; + + for (let key of headersArray) { + let lowerKey = String.toLower(key); + let value = headers[key]; + if (!Util.isUnset(value)) { + Array.append(newHeadersArray, lowerKey); + } + + } + let sortedHeadersArray = Array.ascSort(newHeadersArray); let tmp : string = ""; let separator : string = ""; for (let key of sortedHeadersArray) { - let lowerKey = String.toLower(key); - if (String.hasPrefix(lowerKey, "x-acs-") || String.equals(lowerKey, "host") || String.equals(lowerKey, "content-type")) { - let value = headers[key]; - if (!Util.isUnset(value) && !String.contains(tmp, lowerKey)) { - tmp = `${tmp}${separator}${lowerKey}`; + if (String.hasPrefix(key, "x-acs-") || String.equals(key, "host") || String.equals(key, "content-type")) { + if (!String.contains(tmp, key)) { + tmp = `${tmp}${separator}${key}`; separator = ";"; } diff --git a/alibabacloud-gateway-pop/ts/test/client.spec.ts b/alibabacloud-gateway-pop/ts/test/client.spec.ts index 83c3b232..117c126e 100644 --- a/alibabacloud-gateway-pop/ts/test/client.spec.ts +++ b/alibabacloud-gateway-pop/ts/test/client.spec.ts @@ -26,4 +26,63 @@ describe('Client', function () { assert.deepStrictEqual('cn-edge-1', client.getRegion('test', 'test-inner.cn-edge-1.aliyuncs.com', '')); }); -}); + it('getSignedHeaders should ok', function () { + var client = new Client(); + + // 测试基本场景:只有必须的headers + const headers1 = { + 'host': 'example.com', + 'Host': 'example.com', + 'content-type': 'application/json' + }; + assert.deepStrictEqual(['content-type', 'host'], client.getSignedHeaders(headers1)); + + // 测试包含acs相关headers的情况 + const headers2 = { + 'host': 'example.com', + 'content-type': 'application/json', + 'x-acs-action': 'TestAction', + 'x-acs-version': '2019-01-01' + }; + assert.deepStrictEqual(['content-type', 'host', 'x-acs-action', 'x-acs-version'], client.getSignedHeaders(headers2)); + + // 测试包含非acs headers的情况 + const headers3 = { + 'host': 'example.com', + 'content-type': 'application/json', + 'user-agent': 'TestAgent', + 'x-custom-header': 'CustomValue' + }; + assert.deepStrictEqual(['content-type', 'host'], client.getSignedHeaders(headers3)); // 应该只包含规定的headers + + // 测试空headers情况 + const headers4 = {}; + assert.deepStrictEqual([''], client.getSignedHeaders(headers4)); + + // 测试包含空值的headers情况 + const headers5 = { + 'host': 'example.com', + 'content-type': 'application/json', + 'x-acs-action': null + }; + assert.deepStrictEqual(['content-type', 'host'], client.getSignedHeaders(headers5)); // x-acs-action因为值为空被过滤掉 + + // 测试大小写不敏感的情况 + const headers6 = { + 'Host': 'example.com', + 'Content-Type': 'application/json', + 'X-Acs-Action': 'TestAction' + }; + assert.deepStrictEqual(['content-type', 'host', 'x-acs-action'], client.getSignedHeaders(headers6)); // 应该都被转为小写 + + // 测试排序情况 + const headers7 = { + 'x-acs-zzz': 'zzz', + 'host': 'example.com', + 'x-acs-aaa': 'aaa', + 'content-type': 'application/json' + }; + assert.deepStrictEqual(['content-type', 'host', 'x-acs-aaa', 'x-acs-zzz'], client.getSignedHeaders(headers7)); // 应该按字典序排列 + }); + +}); \ No newline at end of file