@@ -11,8 +11,15 @@ import 'response_headers_server_vm.dart'
11
11
if (dart.library.js_interop) 'response_headers_server_web.dart' ;
12
12
13
13
/// Tests that the [Client] correctly processes response headers.
14
+ ///
15
+ /// If [supportsFoldedHeaders] is `false` then the tests that assume that the
16
+ /// [Client] can parse folded headers will be skipped.
17
+ ///
18
+ /// If [correctlyHandlesNullHeaderValues] is `false` then the tests that assume
19
+ /// that the [Client] correctly deals with NUL in header values are skipped.
14
20
void testResponseHeaders (Client client,
15
- {bool supportsFoldedHeaders = true }) async {
21
+ {bool supportsFoldedHeaders = true ,
22
+ bool correctlyHandlesNullHeaderValues = true }) async {
16
23
group ('server headers' , () {
17
24
late String host;
18
25
late StreamChannel <Object ?> httpServerChannel;
@@ -123,6 +130,77 @@ void testResponseHeaders(Client client,
123
130
matches (r'apple[ \t]*,[ \t]*orange[ \t]*,[ \t]*banana' ));
124
131
});
125
132
133
+ group ('invalid headers values' , () {
134
+ // From RFC-9110:
135
+ // Field values containing CR, LF, or NUL characters are invalid and
136
+ // dangerous, due to the varying ways that implementations might parse and
137
+ // interpret those characters; a recipient of CR, LF, or NUL within a
138
+ // field value MUST either reject the message or replace each of those
139
+ // characters with SP before further processing or forwarding of that
140
+ // message.
141
+ test ('NUL' , () async {
142
+ httpServerChannel.sink.add ('invalid: 1\x 002\r\n ' );
143
+
144
+ try {
145
+ final response = await client.get (Uri .http (host, '' ));
146
+ expect (response.headers['invalid' ], '1 2' );
147
+ } on ClientException {
148
+ // The client rejected the response, which is allowed per RFC-9110.
149
+ }
150
+ },
151
+ skip: ! correctlyHandlesNullHeaderValues
152
+ ? 'does not correctly handle NUL in header values'
153
+ : false );
154
+
155
+ // Bare CR/LF seem to be interpreted the same as CR + LF by most clients
156
+ // so allow that behavior.
157
+ test ('LF' , () async {
158
+ httpServerChannel.sink.add ('foo: 1\n 2\r\n ' );
159
+
160
+ try {
161
+ final response = await client.get (Uri .http (host, '' ));
162
+ expect (
163
+ response.headers['foo' ],
164
+ anyOf (
165
+ '1 2' , // RFC-specified behavior
166
+ '1' // Common client behavior.
167
+ ));
168
+ } on ClientException {
169
+ // The client rejected the response, which is allowed per RFC-9110.
170
+ }
171
+ });
172
+
173
+ test ('CR' , () async {
174
+ httpServerChannel.sink.add ('foo: 1\r 2\r\n ' );
175
+
176
+ try {
177
+ final response = await client.get (Uri .http (host, '' ));
178
+ expect (
179
+ response.headers['foo' ],
180
+ anyOf (
181
+ '1 2' , // RFC-specified behavior
182
+ '1' // Common client behavior.
183
+ ));
184
+ } on ClientException {
185
+ // The client rejected the response, which is allowed per RFC-9110.
186
+ }
187
+ });
188
+ });
189
+
190
+ test ('quotes' , () async {
191
+ httpServerChannel.sink.add ('FOO: "1, 2, 3"\r\n ' );
192
+
193
+ final response = await client.get (Uri .http (host, '' ));
194
+ expect (response.headers['foo' ], '"1, 2, 3"' );
195
+ });
196
+
197
+ test ('nested quotes' , () async {
198
+ httpServerChannel.sink.add ('FOO: "\\ "1, 2, 3\\ ""\r\n ' );
199
+
200
+ final response = await client.get (Uri .http (host, '' ));
201
+ expect (response.headers['foo' ], '"\\ "1, 2, 3\\ ""' );
202
+ });
203
+
126
204
group ('content length' , () {
127
205
test ('surrounded in spaces' , () async {
128
206
// RFC-2616 4.2 says:
0 commit comments