Skip to content

Commit 491137f

Browse files
rsbradfitz
authored andcommitted
http2: split cookie pair into separate hpack header fields
As per 8.1.2.5, To allow for better compression efficiency, the Cookie header field MAY be split into separate header fields, each with one or more cookie-pairs. Fixes golang/go#29386 Change-Id: Ia73aea00b76350c822544f180b5da19d50e51f68 Reviewed-on: https://go-review.googlesource.com/c/net/+/155657 Reviewed-by: Katie Hockman <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> Reviewed-by: Emmanuel Odeke <[email protected]> Run-TryBot: Katie Hockman <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent d66e710 commit 491137f

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

http2/transport.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1478,7 +1478,29 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
14781478
if vv[0] == "" {
14791479
continue
14801480
}
1481-
1481+
} else if strings.EqualFold(k, "cookie") {
1482+
// Per 8.1.2.5 To allow for better compression efficiency, the
1483+
// Cookie header field MAY be split into separate header fields,
1484+
// each with one or more cookie-pairs.
1485+
for _, v := range vv {
1486+
for {
1487+
p := strings.IndexByte(v, ';')
1488+
if p < 0 {
1489+
break
1490+
}
1491+
f("cookie", v[:p])
1492+
p++
1493+
// strip space after semicolon if any.
1494+
for p+1 <= len(v) && v[p] == ' ' {
1495+
p++
1496+
}
1497+
v = v[p:]
1498+
}
1499+
if len(v) > 0 {
1500+
f("cookie", v)
1501+
}
1502+
}
1503+
continue
14821504
}
14831505

14841506
for _, v := range vv {

http2/transport_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,57 @@ func TestTransportChecksResponseHeaderListSize(t *testing.T) {
17951795
ct.run()
17961796
}
17971797

1798+
func TestTransportCookieHeaderSplit(t *testing.T) {
1799+
ct := newClientTester(t)
1800+
ct.client = func() error {
1801+
req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
1802+
req.Header.Add("Cookie", "a=b;c=d; e=f;")
1803+
req.Header.Add("Cookie", "e=f;g=h; ")
1804+
req.Header.Add("Cookie", "i=j")
1805+
_, err := ct.tr.RoundTrip(req)
1806+
return err
1807+
}
1808+
ct.server = func() error {
1809+
ct.greet()
1810+
for {
1811+
f, err := ct.fr.ReadFrame()
1812+
if err != nil {
1813+
return err
1814+
}
1815+
switch f := f.(type) {
1816+
case *HeadersFrame:
1817+
dec := hpack.NewDecoder(initialHeaderTableSize, nil)
1818+
hfs, err := dec.DecodeFull(f.HeaderBlockFragment())
1819+
if err != nil {
1820+
return err
1821+
}
1822+
got := []string{}
1823+
want := []string{"a=b", "c=d", "e=f", "e=f", "g=h", "i=j"}
1824+
for _, hf := range hfs {
1825+
if hf.Name == "cookie" {
1826+
got = append(got, hf.Value)
1827+
}
1828+
}
1829+
if !reflect.DeepEqual(got, want) {
1830+
t.Errorf("Cookies = %#v, want %#v", got, want)
1831+
}
1832+
1833+
var buf bytes.Buffer
1834+
enc := hpack.NewEncoder(&buf)
1835+
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
1836+
ct.fr.WriteHeaders(HeadersFrameParam{
1837+
StreamID: f.StreamID,
1838+
EndHeaders: true,
1839+
EndStream: true,
1840+
BlockFragment: buf.Bytes(),
1841+
})
1842+
return nil
1843+
}
1844+
}
1845+
}
1846+
ct.run()
1847+
}
1848+
17981849
// Test that the Transport returns a typed error from Response.Body.Read calls
17991850
// when the server sends an error. (here we use a panic, since that should generate
18001851
// a stream error, but others like cancel should be similar)

0 commit comments

Comments
 (0)