-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
response_writer_test.go
150 lines (129 loc) · 4.72 KB
/
response_writer_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package http3
import (
"bytes"
"io"
"net/http"
mockquic "github.com/lucas-clemente/quic-go/internal/mocks/quic"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/golang/mock/gomock"
"github.com/marten-seemann/qpack"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Response Writer", func() {
var (
rw *responseWriter
strBuf *bytes.Buffer
)
BeforeEach(func() {
strBuf = &bytes.Buffer{}
str := mockquic.NewMockStream(mockCtrl)
str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes()
rw = newResponseWriter(str, nil, utils.DefaultLogger)
})
decodeHeader := func(str io.Reader) map[string][]string {
rw.Flush()
fields := make(map[string][]string)
decoder := qpack.NewDecoder(nil)
frame, err := parseNextFrame(str, nil)
Expect(err).ToNot(HaveOccurred())
Expect(frame).To(BeAssignableToTypeOf(&headersFrame{}))
headersFrame := frame.(*headersFrame)
data := make([]byte, headersFrame.Length)
_, err = io.ReadFull(str, data)
Expect(err).ToNot(HaveOccurred())
hfs, err := decoder.DecodeFull(data)
Expect(err).ToNot(HaveOccurred())
for _, p := range hfs {
fields[p.Name] = append(fields[p.Name], p.Value)
}
return fields
}
getData := func(str io.Reader) []byte {
frame, err := parseNextFrame(str, nil)
Expect(err).ToNot(HaveOccurred())
Expect(frame).To(BeAssignableToTypeOf(&dataFrame{}))
df := frame.(*dataFrame)
data := make([]byte, df.Length)
_, err = io.ReadFull(str, data)
Expect(err).ToNot(HaveOccurred())
return data
}
It("writes status", func() {
rw.WriteHeader(http.StatusTeapot)
fields := decodeHeader(strBuf)
Expect(fields).To(HaveLen(1))
Expect(fields).To(HaveKeyWithValue(":status", []string{"418"}))
})
It("writes headers", func() {
rw.Header().Add("content-length", "42")
rw.WriteHeader(http.StatusTeapot)
fields := decodeHeader(strBuf)
Expect(fields).To(HaveKeyWithValue("content-length", []string{"42"}))
})
It("writes multiple headers with the same name", func() {
const cookie1 = "test1=1; Max-Age=7200; path=/"
const cookie2 = "test2=2; Max-Age=7200; path=/"
rw.Header().Add("set-cookie", cookie1)
rw.Header().Add("set-cookie", cookie2)
rw.WriteHeader(http.StatusTeapot)
fields := decodeHeader(strBuf)
Expect(fields).To(HaveKey("set-cookie"))
cookies := fields["set-cookie"]
Expect(cookies).To(ContainElement(cookie1))
Expect(cookies).To(ContainElement(cookie2))
})
It("writes data", func() {
n, err := rw.Write([]byte("foobar"))
Expect(n).To(Equal(6))
Expect(err).ToNot(HaveOccurred())
// Should have written 200 on the header stream
fields := decodeHeader(strBuf)
Expect(fields).To(HaveKeyWithValue(":status", []string{"200"}))
// And foobar on the data stream
Expect(getData(strBuf)).To(Equal([]byte("foobar")))
})
It("writes data after WriteHeader is called", func() {
rw.WriteHeader(http.StatusTeapot)
n, err := rw.Write([]byte("foobar"))
Expect(n).To(Equal(6))
Expect(err).ToNot(HaveOccurred())
// Should have written 418 on the header stream
fields := decodeHeader(strBuf)
Expect(fields).To(HaveKeyWithValue(":status", []string{"418"}))
// And foobar on the data stream
Expect(getData(strBuf)).To(Equal([]byte("foobar")))
})
It("does not WriteHeader() twice", func() {
rw.WriteHeader(200)
rw.WriteHeader(500)
fields := decodeHeader(strBuf)
Expect(fields).To(HaveLen(1))
Expect(fields).To(HaveKeyWithValue(":status", []string{"200"}))
})
It("allows calling WriteHeader() several times when using the 103 status code", func() {
rw.Header().Add("Link", "</style.css>; rel=preload; as=style")
rw.Header().Add("Link", "</script.js>; rel=preload; as=script")
rw.WriteHeader(http.StatusEarlyHints)
n, err := rw.Write([]byte("foobar"))
Expect(n).To(Equal(6))
Expect(err).ToNot(HaveOccurred())
// Early Hints must have been received
fields := decodeHeader(strBuf)
Expect(fields).To(HaveLen(2))
Expect(fields).To(HaveKeyWithValue(":status", []string{"103"}))
Expect(fields).To(HaveKeyWithValue("link", []string{"</style.css>; rel=preload; as=style", "</script.js>; rel=preload; as=script"}))
// According to the spec, headers sent in the informational response must also be included in the final response
fields = decodeHeader(strBuf)
Expect(fields).To(HaveLen(2))
Expect(fields).To(HaveKeyWithValue(":status", []string{"200"}))
Expect(fields).To(HaveKeyWithValue("link", []string{"</style.css>; rel=preload; as=style", "</script.js>; rel=preload; as=script"}))
Expect(getData(strBuf)).To(Equal([]byte("foobar")))
})
It("doesn't allow writes if the status code doesn't allow a body", func() {
rw.WriteHeader(304)
n, err := rw.Write([]byte("foobar"))
Expect(n).To(BeZero())
Expect(err).To(MatchError(http.ErrBodyNotAllowed))
})
})