Skip to content

Commit 80eda21

Browse files
authored
private/protocol/rest: Trim space in header key and value (aws#291)
Updates the REST protocol marshaler to trip leading and trailing space from header keys and values before setting the HTTP request header. Fixes a bug when using S3 metadata where metadata values with leading spaces would trigger request signature validation errors when the request is received by the service. V2 SDK port of: aws/aws-sdk-go#2460
1 parent 5ac8f26 commit 80eda21

File tree

9 files changed

+587
-41
lines changed

9 files changed

+587
-41
lines changed

models/protocol_tests/generate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ func Test{{ .OpName }}(t *testing.T) {
186186
// assert headers
187187
{{ range $k, $v := .TestCase.InputTest.Headers -}}
188188
if e, a := "{{ $v }}", r.Header.Get("{{ $k }}"); e != a {
189-
t.Errorf("expect %v to be %v", e, a)
189+
t.Errorf("expect %v, got %v", e, a)
190190
}
191191
{{ end }}
192192
}

models/protocol_tests/input/rest-json.json

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1586,5 +1586,76 @@
15861586
}
15871587
}
15881588
]
1589+
},
1590+
{
1591+
"description": "Header whitespace",
1592+
"metadata": {
1593+
"protocol": "rest-json",
1594+
"apiVersion": "2014-01-01"
1595+
},
1596+
"shapes": {
1597+
"InputShape": {
1598+
"type": "structure",
1599+
"members": {
1600+
"Header1": {
1601+
"shape": "StringType",
1602+
"location": "header"
1603+
},
1604+
"HeaderMap": {
1605+
"shape": "StringMap",
1606+
"location": "headers",
1607+
"locationName": "header-map-"
1608+
}
1609+
}
1610+
},
1611+
"StringType": {
1612+
"type": "string"
1613+
},
1614+
"StringMap": {
1615+
"type": "map",
1616+
"key": {
1617+
"shape": "StringType"
1618+
},
1619+
"value": {
1620+
"shape": "StringType"
1621+
}
1622+
}
1623+
},
1624+
"cases": [
1625+
{
1626+
"given": {
1627+
"http": {
1628+
"method": "GET",
1629+
"requestUri": "/"
1630+
},
1631+
"input": {
1632+
"shape": "InputShape"
1633+
},
1634+
"name": "OperationName"
1635+
},
1636+
"params": {
1637+
"Header1": " headerValue",
1638+
"HeaderMap": {
1639+
"leading-space": " value",
1640+
"with-space": " value ",
1641+
"leading-tab": " value",
1642+
" key-leading-space": "value",
1643+
" key-with-space ": "value"
1644+
}
1645+
},
1646+
"serialized": {
1647+
"body": "",
1648+
"uri": "/",
1649+
"headers": {
1650+
"header1": "headerValue",
1651+
"header-map-leading-space": "value",
1652+
"header-map-with-space": "value",
1653+
"header-map-leading-tab": "value",
1654+
"header-map-key-leading-space": "value",
1655+
"header-map-key-with-space": "value"
1656+
}
1657+
}
1658+
}
1659+
]
15891660
}
15901661
]

models/protocol_tests/input/rest-xml.json

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,5 +1864,76 @@
18641864
}
18651865
}
18661866
]
1867+
},
1868+
{
1869+
"description": "Header whitespace",
1870+
"metadata": {
1871+
"protocol": "rest-xml",
1872+
"apiVersion": "2014-01-01"
1873+
},
1874+
"shapes": {
1875+
"InputShape": {
1876+
"type": "structure",
1877+
"members": {
1878+
"Header1": {
1879+
"shape": "StringType",
1880+
"location": "header"
1881+
},
1882+
"HeaderMap": {
1883+
"shape": "StringMap",
1884+
"location": "headers",
1885+
"locationName": "header-map-"
1886+
}
1887+
}
1888+
},
1889+
"StringType": {
1890+
"type": "string"
1891+
},
1892+
"StringMap": {
1893+
"type": "map",
1894+
"key": {
1895+
"shape": "StringType"
1896+
},
1897+
"value": {
1898+
"shape": "StringType"
1899+
}
1900+
}
1901+
},
1902+
"cases": [
1903+
{
1904+
"given": {
1905+
"http": {
1906+
"method": "GET",
1907+
"requestUri": "/"
1908+
},
1909+
"input": {
1910+
"shape": "InputShape"
1911+
},
1912+
"name": "OperationName"
1913+
},
1914+
"params": {
1915+
"Header1": " headerValue",
1916+
"HeaderMap": {
1917+
"leading-space": " value",
1918+
"with-space": " value ",
1919+
"leading-tab": " value",
1920+
" key-leading-space": "value",
1921+
" key-with-space ": "value"
1922+
}
1923+
},
1924+
"serialized": {
1925+
"body": "",
1926+
"uri": "/",
1927+
"headers": {
1928+
"header1": "headerValue",
1929+
"header-map-leading-space": "value",
1930+
"header-map-with-space": "value",
1931+
"header-map-leading-tab": "value",
1932+
"header-map-key-leading-space": "value",
1933+
"header-map-key-with-space": "value"
1934+
}
1935+
}
1936+
}
1937+
]
18671938
}
18681939
]

private/protocol/header_encoder.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package protocol
33
import (
44
"fmt"
55
"net/http"
6+
"strings"
67
)
78

89
// HeaderMapEncoder builds a map valu
@@ -24,6 +25,9 @@ func (e *HeaderMapEncoder) MapSetValue(k string, v ValueMarshaler) {
2425
return
2526
}
2627

28+
k = strings.TrimSpace(k)
29+
str = strings.TrimSpace(str)
30+
2731
if len(e.Prefix) > 0 {
2832
k = e.Prefix + k
2933
}

private/protocol/jsonrpc/build_test.go

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1733,10 +1733,10 @@ func TestInputService1ProtocolTestScalarMembersCase1(t *testing.T) {
17331733

17341734
// assert headers
17351735
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
1736-
t.Errorf("expect %v to be %v", e, a)
1736+
t.Errorf("expect %v, got %v", e, a)
17371737
}
17381738
if e, a := "com.amazonaws.foo.OperationName", r.Header.Get("X-Amz-Target"); e != a {
1739-
t.Errorf("expect %v to be %v", e, a)
1739+
t.Errorf("expect %v, got %v", e, a)
17401740
}
17411741

17421742
}
@@ -1771,10 +1771,10 @@ func TestInputService2ProtocolTestTimestampValuesCase1(t *testing.T) {
17711771

17721772
// assert headers
17731773
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
1774-
t.Errorf("expect %v to be %v", e, a)
1774+
t.Errorf("expect %v, got %v", e, a)
17751775
}
17761776
if e, a := "com.amazonaws.foo.OperationName", r.Header.Get("X-Amz-Target"); e != a {
1777-
t.Errorf("expect %v to be %v", e, a)
1777+
t.Errorf("expect %v, got %v", e, a)
17781778
}
17791779

17801780
}
@@ -1809,10 +1809,10 @@ func TestInputService3ProtocolTestBase64EncodedBlobsCase1(t *testing.T) {
18091809

18101810
// assert headers
18111811
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
1812-
t.Errorf("expect %v to be %v", e, a)
1812+
t.Errorf("expect %v, got %v", e, a)
18131813
}
18141814
if e, a := "com.amazonaws.foo.OperationName", r.Header.Get("X-Amz-Target"); e != a {
1815-
t.Errorf("expect %v to be %v", e, a)
1815+
t.Errorf("expect %v, got %v", e, a)
18161816
}
18171817

18181818
}
@@ -1850,10 +1850,10 @@ func TestInputService3ProtocolTestBase64EncodedBlobsCase2(t *testing.T) {
18501850

18511851
// assert headers
18521852
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
1853-
t.Errorf("expect %v to be %v", e, a)
1853+
t.Errorf("expect %v, got %v", e, a)
18541854
}
18551855
if e, a := "com.amazonaws.foo.OperationName", r.Header.Get("X-Amz-Target"); e != a {
1856-
t.Errorf("expect %v to be %v", e, a)
1856+
t.Errorf("expect %v, got %v", e, a)
18571857
}
18581858

18591859
}
@@ -1891,10 +1891,10 @@ func TestInputService4ProtocolTestNestedBlobsCase1(t *testing.T) {
18911891

18921892
// assert headers
18931893
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
1894-
t.Errorf("expect %v to be %v", e, a)
1894+
t.Errorf("expect %v, got %v", e, a)
18951895
}
18961896
if e, a := "com.amazonaws.foo.OperationName", r.Header.Get("X-Amz-Target"); e != a {
1897-
t.Errorf("expect %v to be %v", e, a)
1897+
t.Errorf("expect %v, got %v", e, a)
18981898
}
18991899

19001900
}
@@ -1931,10 +1931,10 @@ func TestInputService5ProtocolTestRecursiveShapesCase1(t *testing.T) {
19311931

19321932
// assert headers
19331933
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
1934-
t.Errorf("expect %v to be %v", e, a)
1934+
t.Errorf("expect %v, got %v", e, a)
19351935
}
19361936
if e, a := "com.amazonaws.foo.OperationName", r.Header.Get("X-Amz-Target"); e != a {
1937-
t.Errorf("expect %v to be %v", e, a)
1937+
t.Errorf("expect %v, got %v", e, a)
19381938
}
19391939

19401940
}
@@ -1973,10 +1973,10 @@ func TestInputService5ProtocolTestRecursiveShapesCase2(t *testing.T) {
19731973

19741974
// assert headers
19751975
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
1976-
t.Errorf("expect %v to be %v", e, a)
1976+
t.Errorf("expect %v, got %v", e, a)
19771977
}
19781978
if e, a := "com.amazonaws.foo.OperationName", r.Header.Get("X-Amz-Target"); e != a {
1979-
t.Errorf("expect %v to be %v", e, a)
1979+
t.Errorf("expect %v, got %v", e, a)
19801980
}
19811981

19821982
}
@@ -2019,10 +2019,10 @@ func TestInputService5ProtocolTestRecursiveShapesCase3(t *testing.T) {
20192019

20202020
// assert headers
20212021
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
2022-
t.Errorf("expect %v to be %v", e, a)
2022+
t.Errorf("expect %v, got %v", e, a)
20232023
}
20242024
if e, a := "com.amazonaws.foo.OperationName", r.Header.Get("X-Amz-Target"); e != a {
2025-
t.Errorf("expect %v to be %v", e, a)
2025+
t.Errorf("expect %v, got %v", e, a)
20262026
}
20272027

20282028
}
@@ -2066,10 +2066,10 @@ func TestInputService5ProtocolTestRecursiveShapesCase4(t *testing.T) {
20662066

20672067
// assert headers
20682068
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
2069-
t.Errorf("expect %v to be %v", e, a)
2069+
t.Errorf("expect %v, got %v", e, a)
20702070
}
20712071
if e, a := "com.amazonaws.foo.OperationName", r.Header.Get("X-Amz-Target"); e != a {
2072-
t.Errorf("expect %v to be %v", e, a)
2072+
t.Errorf("expect %v, got %v", e, a)
20732073
}
20742074

20752075
}
@@ -2115,10 +2115,10 @@ func TestInputService5ProtocolTestRecursiveShapesCase5(t *testing.T) {
21152115

21162116
// assert headers
21172117
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
2118-
t.Errorf("expect %v to be %v", e, a)
2118+
t.Errorf("expect %v, got %v", e, a)
21192119
}
21202120
if e, a := "com.amazonaws.foo.OperationName", r.Header.Get("X-Amz-Target"); e != a {
2121-
t.Errorf("expect %v to be %v", e, a)
2121+
t.Errorf("expect %v, got %v", e, a)
21222122
}
21232123

21242124
}
@@ -2162,10 +2162,10 @@ func TestInputService5ProtocolTestRecursiveShapesCase6(t *testing.T) {
21622162

21632163
// assert headers
21642164
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
2165-
t.Errorf("expect %v to be %v", e, a)
2165+
t.Errorf("expect %v, got %v", e, a)
21662166
}
21672167
if e, a := "com.amazonaws.foo.OperationName", r.Header.Get("X-Amz-Target"); e != a {
2168-
t.Errorf("expect %v to be %v", e, a)
2168+
t.Errorf("expect %v, got %v", e, a)
21692169
}
21702170

21712171
}
@@ -2200,10 +2200,10 @@ func TestInputService6ProtocolTestEmptyMapsCase1(t *testing.T) {
22002200

22012201
// assert headers
22022202
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
2203-
t.Errorf("expect %v to be %v", e, a)
2203+
t.Errorf("expect %v, got %v", e, a)
22042204
}
22052205
if e, a := "com.amazonaws.foo.OperationName", r.Header.Get("X-Amz-Target"); e != a {
2206-
t.Errorf("expect %v to be %v", e, a)
2206+
t.Errorf("expect %v, got %v", e, a)
22072207
}
22082208

22092209
}
@@ -2360,10 +2360,10 @@ func TestInputService9ProtocolTestEndpointHostTraitStaticPrefixCase1(t *testing.
23602360

23612361
// assert headers
23622362
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
2363-
t.Errorf("expect %v to be %v", e, a)
2363+
t.Errorf("expect %v, got %v", e, a)
23642364
}
23652365
if e, a := "com.amazonaws.foo.StaticOp", r.Header.Get("X-Amz-Target"); e != a {
2366-
t.Errorf("expect %v to be %v", e, a)
2366+
t.Errorf("expect %v, got %v", e, a)
23672367
}
23682368

23692369
}
@@ -2398,10 +2398,10 @@ func TestInputService9ProtocolTestEndpointHostTraitStaticPrefixCase2(t *testing.
23982398

23992399
// assert headers
24002400
if e, a := "application/x-amz-json-1.1", r.Header.Get("Content-Type"); e != a {
2401-
t.Errorf("expect %v to be %v", e, a)
2401+
t.Errorf("expect %v, got %v", e, a)
24022402
}
24032403
if e, a := "com.amazonaws.foo.MemberRefOp", r.Header.Get("X-Amz-Target"); e != a {
2404-
t.Errorf("expect %v to be %v", e, a)
2404+
t.Errorf("expect %v, got %v", e, a)
24052405
}
24062406

24072407
}

private/protocol/rest/build.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ func buildHeader(header *http.Header, v reflect.Value, name string, tag reflect.
166166
return awserr.New("SerializationError", "failed to encode REST request", err)
167167
}
168168

169+
name = strings.TrimSpace(name)
170+
str = strings.TrimSpace(str)
171+
169172
header.Add(name, str)
170173

171174
return nil
@@ -181,8 +184,10 @@ func buildHeaderMap(header *http.Header, v reflect.Value, tag reflect.StructTag)
181184
return awserr.New("SerializationError", "failed to encode REST request", err)
182185

183186
}
187+
keyStr := strings.TrimSpace(key.String())
188+
str = strings.TrimSpace(str)
184189

185-
header.Add(prefix+key.String(), str)
190+
header.Add(prefix+keyStr, str)
186191
}
187192
return nil
188193
}

0 commit comments

Comments
 (0)