blob: e4938c1f91a29949c86dcb9d180709b86194f0f5 [file] [log] [blame]
Neil Fuller3c938a32014-02-19 09:40:26 +00001/*
2 * Copyright (C) 2012 Square, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.squareup.okhttp.internal.http;
17
18import com.squareup.okhttp.Headers;
19import com.squareup.okhttp.Protocol;
20import com.squareup.okhttp.Request;
21import com.squareup.okhttp.Response;
Neil Fuller71b9f472015-09-16 17:39:32 +010022import com.squareup.okhttp.internal.framed.Header;
Neil Fuller3c938a32014-02-19 09:40:26 +000023import java.io.IOException;
Neil Fullere78f1172015-01-20 09:39:41 +000024import java.util.Arrays;
25import java.util.Collections;
26import java.util.HashMap;
Neil Fuller3c938a32014-02-19 09:40:26 +000027import java.util.List;
Neil Fullere78f1172015-01-20 09:39:41 +000028import java.util.Map;
29
Neil Fuller3c938a32014-02-19 09:40:26 +000030import org.junit.Test;
31
Neil Fullere78f1172015-01-20 09:39:41 +000032import static com.squareup.okhttp.TestUtil.headerEntries;
Neil Fuller3c938a32014-02-19 09:40:26 +000033import static org.junit.Assert.assertEquals;
34import static org.junit.Assert.assertNull;
Neil Fullere78f1172015-01-20 09:39:41 +000035import static org.junit.Assert.fail;
Neil Fuller3c938a32014-02-19 09:40:26 +000036
37public final class HeadersTest {
38 @Test public void parseNameValueBlock() throws IOException {
39 List<Header> headerBlock = headerEntries(
40 "cache-control", "no-cache, no-store",
41 "set-cookie", "Cookie1\u0000Cookie2",
42 ":status", "200 OK",
43 ":version", "HTTP/1.1");
44 Request request = new Request.Builder().url("http://square.com/").build();
45 Response response =
Neil Fuller71b9f472015-09-16 17:39:32 +010046 FramedTransport.readNameValueBlock(headerBlock, Protocol.SPDY_3).request(request).build();
Neil Fuller3c938a32014-02-19 09:40:26 +000047 Headers headers = response.headers();
48 assertEquals(4, headers.size());
Neil Fullere78f1172015-01-20 09:39:41 +000049 assertEquals(Protocol.SPDY_3, response.protocol());
50 assertEquals(200, response.code());
51 assertEquals("OK", response.message());
Neil Fuller3c938a32014-02-19 09:40:26 +000052 assertEquals("no-cache, no-store", headers.get("cache-control"));
53 assertEquals("Cookie2", headers.get("set-cookie"));
Neil Fullere78f1172015-01-20 09:39:41 +000054 assertEquals(Protocol.SPDY_3.toString(), headers.get(OkHeaders.SELECTED_PROTOCOL));
Neil Fuller3c938a32014-02-19 09:40:26 +000055 assertEquals(OkHeaders.SELECTED_PROTOCOL, headers.name(0));
Neil Fullere78f1172015-01-20 09:39:41 +000056 assertEquals(Protocol.SPDY_3.toString(), headers.value(0));
Neil Fuller3c938a32014-02-19 09:40:26 +000057 assertEquals("cache-control", headers.name(1));
58 assertEquals("no-cache, no-store", headers.value(1));
59 assertEquals("set-cookie", headers.name(2));
60 assertEquals("Cookie1", headers.value(2));
61 assertEquals("set-cookie", headers.name(3));
62 assertEquals("Cookie2", headers.value(3));
63 assertNull(headers.get(":status"));
64 assertNull(headers.get(":version"));
65 }
66
67 @Test public void readNameValueBlockDropsForbiddenHeadersSpdy3() throws IOException {
68 List<Header> headerBlock = headerEntries(
69 ":status", "200 OK",
70 ":version", "HTTP/1.1",
71 "connection", "close");
72 Request request = new Request.Builder().url("http://square.com/").build();
73 Response response =
Neil Fuller71b9f472015-09-16 17:39:32 +010074 FramedTransport.readNameValueBlock(headerBlock, Protocol.SPDY_3).request(request).build();
Neil Fuller3c938a32014-02-19 09:40:26 +000075 Headers headers = response.headers();
76 assertEquals(1, headers.size());
77 assertEquals(OkHeaders.SELECTED_PROTOCOL, headers.name(0));
Neil Fullere78f1172015-01-20 09:39:41 +000078 assertEquals(Protocol.SPDY_3.toString(), headers.value(0));
Neil Fuller3c938a32014-02-19 09:40:26 +000079 }
80
81 @Test public void readNameValueBlockDropsForbiddenHeadersHttp2() throws IOException {
82 List<Header> headerBlock = headerEntries(
83 ":status", "200 OK",
84 ":version", "HTTP/1.1",
85 "connection", "close");
86 Request request = new Request.Builder().url("http://square.com/").build();
Neil Fuller71b9f472015-09-16 17:39:32 +010087 Response response = FramedTransport.readNameValueBlock(headerBlock, Protocol.HTTP_2)
Neil Fuller3c938a32014-02-19 09:40:26 +000088 .request(request).build();
89 Headers headers = response.headers();
90 assertEquals(1, headers.size());
91 assertEquals(OkHeaders.SELECTED_PROTOCOL, headers.name(0));
Neil Fullere78f1172015-01-20 09:39:41 +000092 assertEquals(Protocol.HTTP_2.toString(), headers.value(0));
Neil Fuller3c938a32014-02-19 09:40:26 +000093 }
94
95 @Test public void toNameValueBlock() {
96 Request request = new Request.Builder()
97 .url("http://square.com/")
98 .header("cache-control", "no-cache, no-store")
99 .addHeader("set-cookie", "Cookie1")
100 .addHeader("set-cookie", "Cookie2")
101 .header(":status", "200 OK")
102 .build();
103 List<Header> headerBlock =
Neil Fuller71b9f472015-09-16 17:39:32 +0100104 FramedTransport.writeNameValueBlock(request, Protocol.SPDY_3, "HTTP/1.1");
Neil Fuller3c938a32014-02-19 09:40:26 +0000105 List<Header> expected = headerEntries(
106 ":method", "GET",
107 ":path", "/",
108 ":version", "HTTP/1.1",
109 ":host", "square.com",
110 ":scheme", "http",
111 "cache-control", "no-cache, no-store",
112 "set-cookie", "Cookie1\u0000Cookie2",
113 ":status", "200 OK");
114 assertEquals(expected, headerBlock);
115 }
116
117 @Test public void toNameValueBlockDropsForbiddenHeadersSpdy3() {
118 Request request = new Request.Builder()
119 .url("http://square.com/")
120 .header("Connection", "close")
121 .header("Transfer-Encoding", "chunked")
122 .build();
123 List<Header> expected = headerEntries(
124 ":method", "GET",
125 ":path", "/",
126 ":version", "HTTP/1.1",
127 ":host", "square.com",
128 ":scheme", "http");
Neil Fuller71b9f472015-09-16 17:39:32 +0100129 assertEquals(expected, FramedTransport.writeNameValueBlock(request, Protocol.SPDY_3, "HTTP/1.1"));
Neil Fuller3c938a32014-02-19 09:40:26 +0000130 }
131
132 @Test public void toNameValueBlockDropsForbiddenHeadersHttp2() {
133 Request request = new Request.Builder()
134 .url("http://square.com/")
135 .header("Connection", "upgrade")
136 .header("Upgrade", "websocket")
137 .build();
138 List<Header> expected = headerEntries(
139 ":method", "GET",
140 ":path", "/",
141 ":authority", "square.com",
142 ":scheme", "http");
143 assertEquals(expected,
Neil Fuller71b9f472015-09-16 17:39:32 +0100144 FramedTransport.writeNameValueBlock(request, Protocol.HTTP_2, "HTTP/1.1"));
Neil Fuller3c938a32014-02-19 09:40:26 +0000145 }
Neil Fullere78f1172015-01-20 09:39:41 +0000146
147 @Test public void ofTrims() {
148 Headers headers = Headers.of("\t User-Agent \n", " \r OkHttp ");
149 assertEquals("User-Agent", headers.name(0));
150 assertEquals("OkHttp", headers.value(0));
151 }
152
153 @Test public void addParsing() {
154 Headers headers = new Headers.Builder()
155 .add("foo: bar")
156 .add(" foo: baz") // Name leading whitespace is trimmed.
157 .add("foo : bak") // Name trailing whitespace is trimmed.
Tobias Thiererf38272f2016-09-09 13:09:25 +0100158 .add("\tkey\t:\tvalue\t") // '\t' also counts as whitespace
Neil Fullere78f1172015-01-20 09:39:41 +0000159 .add("ping: pong ") // Value whitespace is trimmed.
160 .add("kit:kat") // Space after colon is not required.
161 .build();
162 assertEquals(Arrays.asList("bar", "baz", "bak"), headers.values("foo"));
Tobias Thiererf38272f2016-09-09 13:09:25 +0100163 assertEquals(Arrays.asList("value"), headers.values("key"));
Neil Fullere78f1172015-01-20 09:39:41 +0000164 assertEquals(Arrays.asList("pong"), headers.values("ping"));
165 assertEquals(Arrays.asList("kat"), headers.values("kit"));
166 }
167
168 @Test public void addThrowsOnEmptyName() {
169 try {
170 new Headers.Builder().add(": bar");
171 fail();
172 } catch (IllegalArgumentException expected) {
173 }
174 try {
175 new Headers.Builder().add(" : bar");
176 fail();
177 } catch (IllegalArgumentException expected) {
178 }
179 }
180
181 @Test public void addThrowsOnNoColon() {
182 try {
183 new Headers.Builder().add("foo bar");
184 fail();
185 } catch (IllegalArgumentException expected) {
186 }
187 }
188
189 @Test public void addThrowsOnMultiColon() {
190 try {
191 new Headers.Builder().add(":status: 200 OK");
192 fail();
193 } catch (IllegalArgumentException expected) {
194 }
195 }
196
197 @Test public void ofThrowsOddNumberOfHeaders() {
198 try {
199 Headers.of("User-Agent", "OkHttp", "Content-Length");
200 fail();
201 } catch (IllegalArgumentException expected) {
202 }
203 }
204
205 @Test public void ofThrowsOnNull() {
206 try {
207 Headers.of("User-Agent", null);
208 fail();
209 } catch (IllegalArgumentException expected) {
210 }
211 }
212
213 @Test public void ofThrowsOnEmptyName() {
214 try {
215 Headers.of("", "OkHttp");
216 fail();
217 } catch (IllegalArgumentException expected) {
218 }
219 }
220
221 @Test public void ofAcceptsEmptyValue() {
222 Headers headers = Headers.of("User-Agent", "");
223 assertEquals("", headers.value(0));
224 }
225
226 @Test public void ofMakesDefensiveCopy() {
227 String[] namesAndValues = {
228 "User-Agent",
229 "OkHttp"
230 };
231 Headers headers = Headers.of(namesAndValues);
232 namesAndValues[1] = "Chrome";
233 assertEquals("OkHttp", headers.value(0));
234 }
235
236 @Test public void ofRejectsNulChar() {
237 try {
238 Headers.of("User-Agent", "Square\u0000OkHttp");
239 fail();
240 } catch (IllegalArgumentException expected) {
241 }
242 }
243
244 @Test public void ofMapThrowsOnNull() {
245 try {
246 Headers.of(Collections.<String, String>singletonMap("User-Agent", null));
247 fail();
248 } catch (IllegalArgumentException expected) {
249 }
250 }
251
252 @Test public void ofMapThrowsOnEmptyName() {
253 try {
254 Headers.of(Collections.singletonMap("", "OkHttp"));
255 fail();
256 } catch (IllegalArgumentException expected) {
257 }
258 }
259
260 @Test public void ofMapThrowsOnBlankName() {
261 try {
262 Headers.of(Collections.singletonMap(" ", "OkHttp"));
263 fail();
264 } catch (IllegalArgumentException expected) {
265 }
266 }
267
268 @Test public void ofMapAcceptsEmptyValue() {
269 Headers headers = Headers.of(Collections.singletonMap("User-Agent", ""));
270 assertEquals("", headers.value(0));
271 }
272
273 @Test public void ofMapTrimsKey() {
274 Headers headers = Headers.of(Collections.singletonMap(" User-Agent ", "OkHttp"));
275 assertEquals("User-Agent", headers.name(0));
276 }
277
278 @Test public void ofMapTrimsValue() {
279 Headers headers = Headers.of(Collections.singletonMap("User-Agent", " OkHttp "));
280 assertEquals("OkHttp", headers.value(0));
281 }
282
283 @Test public void ofMapMakesDefensiveCopy() {
284 Map<String, String> namesAndValues = new HashMap<>();
285 namesAndValues.put("User-Agent", "OkHttp");
286
287 Headers headers = Headers.of(namesAndValues);
288 namesAndValues.put("User-Agent", "Chrome");
289 assertEquals("OkHttp", headers.value(0));
290 }
291
292 @Test public void ofMapRejectsNulCharInName() {
293 try {
294 Headers.of(Collections.singletonMap("User-Agent", "Square\u0000OkHttp"));
295 fail();
296 } catch (IllegalArgumentException expected) {
297 }
298 }
299
300 @Test public void ofMapRejectsNulCharInValue() {
301 try {
302 Headers.of(Collections.singletonMap("User-\u0000Agent", "OkHttp"));
303 fail();
304 } catch (IllegalArgumentException expected) {
305 }
306 }
Neil Fuller71b9f472015-09-16 17:39:32 +0100307
308 @Test public void toMultimapGroupsHeaders() {
309 Headers headers = Headers.of(
310 "cache-control", "no-cache",
311 "cache-control", "no-store",
312 "user-agent", "OkHttp");
313 Map<String, List<String>> headerMap = headers.toMultimap();
314 assertEquals(2, headerMap.get("cache-control").size());
315 assertEquals(1, headerMap.get("user-agent").size());
316 }
Neil Fuller3c938a32014-02-19 09:40:26 +0000317}