From 3972f97d049edde37936d8b675e3600d10705dbc Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 11 Oct 2019 13:50:01 -0400 Subject: [PATCH] net/http, net/textproto: add Header.Values, MIMEHeader.Values methods Fixes #34799 Change-Id: I134b2717fa90c8955902e7eeaaf8510dcc28340e Reviewed-on: https://go-review.googlesource.com/c/go/+/200760 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/net/http/header.go | 14 +++++++-- src/net/textproto/header.go | 15 +++++++-- src/net/textproto/header_test.go | 54 ++++++++++++++++++++++++++++++++ src/net/textproto/reader_test.go | 35 --------------------- 4 files changed, 78 insertions(+), 40 deletions(-) create mode 100644 src/net/textproto/header_test.go diff --git a/src/net/http/header.go b/src/net/http/header.go index 230ca03d4f..4a4ebbcf2f 100644 --- a/src/net/http/header.go +++ b/src/net/http/header.go @@ -40,13 +40,21 @@ func (h Header) Set(key, value string) { // Get gets the first value associated with the given key. If // there are no values associated with the key, Get returns "". // It is case insensitive; textproto.CanonicalMIMEHeaderKey is -// used to canonicalize the provided key. To access multiple -// values of a key, or to use non-canonical keys, access the -// map directly. +// used to canonicalize the provided key. To use non-canonical keys, +// access the map directly. func (h Header) Get(key string) string { return textproto.MIMEHeader(h).Get(key) } +// Values returns all values associated with the given key. +// It is case insensitive; textproto.CanonicalMIMEHeaderKey is +// used to canonicalize the provided key. To use non-canonical +// keys, access the map directly. +// The returned slice is not a copy. +func (h Header) Values(key string) []string { + return textproto.MIMEHeader(h).Values(key) +} + // get is like Get, but key must already be in CanonicalHeaderKey form. func (h Header) get(key string) string { if v := h[key]; len(v) > 0 { diff --git a/src/net/textproto/header.go b/src/net/textproto/header.go index ed096d9a3c..a58df7aebc 100644 --- a/src/net/textproto/header.go +++ b/src/net/textproto/header.go @@ -26,8 +26,7 @@ func (h MIMEHeader) Set(key, value string) { // It is case insensitive; CanonicalMIMEHeaderKey is used // to canonicalize the provided key. // If there are no values associated with the key, Get returns "". -// To access multiple values of a key, or to use non-canonical keys, -// access the map directly. +// To use non-canonical keys, access the map directly. func (h MIMEHeader) Get(key string) string { if h == nil { return "" @@ -39,6 +38,18 @@ func (h MIMEHeader) Get(key string) string { return v[0] } +// Values returns all values associated with the given key. +// It is case insensitive; CanonicalMIMEHeaderKey is +// used to canonicalize the provided key. To use non-canonical +// keys, access the map directly. +// The returned slice is not a copy. +func (h MIMEHeader) Values(key string) []string { + if h == nil { + return nil + } + return h[CanonicalMIMEHeaderKey(key)] +} + // Del deletes the values associated with key. func (h MIMEHeader) Del(key string) { delete(h, CanonicalMIMEHeaderKey(key)) diff --git a/src/net/textproto/header_test.go b/src/net/textproto/header_test.go new file mode 100644 index 0000000000..de9405ca86 --- /dev/null +++ b/src/net/textproto/header_test.go @@ -0,0 +1,54 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package textproto + +import "testing" + +type canonicalHeaderKeyTest struct { + in, out string +} + +var canonicalHeaderKeyTests = []canonicalHeaderKeyTest{ + {"a-b-c", "A-B-C"}, + {"a-1-c", "A-1-C"}, + {"User-Agent", "User-Agent"}, + {"uSER-aGENT", "User-Agent"}, + {"user-agent", "User-Agent"}, + {"USER-AGENT", "User-Agent"}, + + // Other valid tchar bytes in tokens: + {"foo-bar_baz", "Foo-Bar_baz"}, + {"foo-bar$baz", "Foo-Bar$baz"}, + {"foo-bar~baz", "Foo-Bar~baz"}, + {"foo-bar*baz", "Foo-Bar*baz"}, + + // Non-ASCII or anything with spaces or non-token chars is unchanged: + {"üser-agenT", "üser-agenT"}, + {"a B", "a B"}, + + // This caused a panic due to mishandling of a space: + {"C Ontent-Transfer-Encoding", "C Ontent-Transfer-Encoding"}, + {"foo bar", "foo bar"}, +} + +func TestCanonicalMIMEHeaderKey(t *testing.T) { + for _, tt := range canonicalHeaderKeyTests { + if s := CanonicalMIMEHeaderKey(tt.in); s != tt.out { + t.Errorf("CanonicalMIMEHeaderKey(%q) = %q, want %q", tt.in, s, tt.out) + } + } +} + +// Issue #34799 add a Header method to get multiple values []string, with canonicalized key +func TestMIMEHeaderMultipleValues(t *testing.T) { + testHeader := MIMEHeader{ + "Set-Cookie": {"cookie 1", "cookie 2"}, + } + values := testHeader.Values("set-cookie") + n := len(values) + if n != 2 { + t.Errorf("count: %d; want 2", n) + } +} diff --git a/src/net/textproto/reader_test.go b/src/net/textproto/reader_test.go index 595d94f938..3124d438fa 100644 --- a/src/net/textproto/reader_test.go +++ b/src/net/textproto/reader_test.go @@ -13,41 +13,6 @@ import ( "testing" ) -type canonicalHeaderKeyTest struct { - in, out string -} - -var canonicalHeaderKeyTests = []canonicalHeaderKeyTest{ - {"a-b-c", "A-B-C"}, - {"a-1-c", "A-1-C"}, - {"User-Agent", "User-Agent"}, - {"uSER-aGENT", "User-Agent"}, - {"user-agent", "User-Agent"}, - {"USER-AGENT", "User-Agent"}, - - // Other valid tchar bytes in tokens: - {"foo-bar_baz", "Foo-Bar_baz"}, - {"foo-bar$baz", "Foo-Bar$baz"}, - {"foo-bar~baz", "Foo-Bar~baz"}, - {"foo-bar*baz", "Foo-Bar*baz"}, - - // Non-ASCII or anything with spaces or non-token chars is unchanged: - {"üser-agenT", "üser-agenT"}, - {"a B", "a B"}, - - // This caused a panic due to mishandling of a space: - {"C Ontent-Transfer-Encoding", "C Ontent-Transfer-Encoding"}, - {"foo bar", "foo bar"}, -} - -func TestCanonicalMIMEHeaderKey(t *testing.T) { - for _, tt := range canonicalHeaderKeyTests { - if s := CanonicalMIMEHeaderKey(tt.in); s != tt.out { - t.Errorf("CanonicalMIMEHeaderKey(%q) = %q, want %q", tt.in, s, tt.out) - } - } -} - func reader(s string) *Reader { return NewReader(bufio.NewReader(strings.NewReader(s))) }