1 /* |
1 /* |
2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. |
2 * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * |
4 * |
5 * This code is free software; you can redistribute it and/or modify it |
5 * This code is free software; you can redistribute it and/or modify it |
6 * under the terms of the GNU General Public License version 2 only, as |
6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. |
7 * published by the Free Software Foundation. |
23 |
23 |
24 /* |
24 /* |
25 * @test |
25 * @test |
26 * @summary Verify that some special headers - such as User-Agent |
26 * @summary Verify that some special headers - such as User-Agent |
27 * can be specified by the caller. |
27 * can be specified by the caller. |
28 * @bug 8203771 |
28 * @bug 8203771 8218546 |
29 * @modules java.base/sun.net.www.http |
29 * @modules java.base/sun.net.www.http |
30 * java.net.http/jdk.internal.net.http.common |
30 * java.net.http/jdk.internal.net.http.common |
31 * java.net.http/jdk.internal.net.http.frame |
31 * java.net.http/jdk.internal.net.http.frame |
32 * java.net.http/jdk.internal.net.http.hpack |
32 * java.net.http/jdk.internal.net.http.hpack |
33 * java.logging |
33 * java.logging |
62 import java.net.http.HttpClient; |
62 import java.net.http.HttpClient; |
63 import java.net.http.HttpHeaders; |
63 import java.net.http.HttpHeaders; |
64 import java.net.http.HttpRequest; |
64 import java.net.http.HttpRequest; |
65 import java.net.http.HttpResponse; |
65 import java.net.http.HttpResponse; |
66 import java.net.http.HttpResponse.BodyHandlers; |
66 import java.net.http.HttpResponse.BodyHandlers; |
67 import java.security.AccessController; |
|
68 import java.security.PrivilegedAction; |
|
69 import java.time.Duration; |
67 import java.time.Duration; |
70 import java.util.ArrayList; |
68 import java.util.ArrayList; |
71 import java.util.Arrays; |
69 import java.util.Arrays; |
72 import java.util.List; |
70 import java.util.List; |
73 import java.util.Locale; |
71 import java.util.Locale; |
74 import java.util.Map; |
72 import java.util.Map; |
75 import java.util.Optional; |
73 import java.util.Optional; |
76 import java.util.function.Function; |
74 import java.util.function.Function; |
77 |
|
78 import static java.lang.System.err; |
|
79 import static java.lang.System.out; |
75 import static java.lang.System.out; |
80 import static java.net.http.HttpClient.Builder.NO_PROXY; |
76 import static java.net.http.HttpClient.Builder.NO_PROXY; |
|
77 import static java.net.http.HttpClient.Version.HTTP_2; |
81 import static java.nio.charset.StandardCharsets.US_ASCII; |
78 import static java.nio.charset.StandardCharsets.US_ASCII; |
82 import org.testng.Assert; |
79 import org.testng.Assert; |
83 import static org.testng.Assert.assertEquals; |
80 import static org.testng.Assert.assertEquals; |
|
81 import static org.testng.Assert.assertTrue; |
84 |
82 |
85 public class SpecialHeadersTest implements HttpServerAdapters { |
83 public class SpecialHeadersTest implements HttpServerAdapters { |
86 |
84 |
87 SSLContext sslContext; |
85 SSLContext sslContext; |
88 HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] |
86 HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] |
149 |
147 |
150 static final Map<String, Function<URI,String>> DEFAULTS = Map.of( |
148 static final Map<String, Function<URI,String>> DEFAULTS = Map.of( |
151 "USER-AGENT", u -> userAgent(), "HOST", u -> u.getRawAuthority()); |
149 "USER-AGENT", u -> userAgent(), "HOST", u -> u.getRawAuthority()); |
152 |
150 |
153 @Test(dataProvider = "variants") |
151 @Test(dataProvider = "variants") |
154 void test(String uriString, String headerNameAndValue, boolean sameClient) throws Exception { |
152 void test(String uriString, |
|
153 String headerNameAndValue, |
|
154 boolean sameClient) |
|
155 throws Exception |
|
156 { |
155 out.println("\n--- Starting "); |
157 out.println("\n--- Starting "); |
156 |
158 |
157 int index = headerNameAndValue.indexOf(":"); |
159 int index = headerNameAndValue.indexOf(":"); |
158 String name = headerNameAndValue.substring(0, index); |
160 String name = headerNameAndValue.substring(0, index); |
159 String v = headerNameAndValue.substring(index+1).trim(); |
161 String v = headerNameAndValue.substring(index+1).trim(); |
181 out.println("Got response: " + resp); |
183 out.println("Got response: " + resp); |
182 out.println("Got body: " + resp.body()); |
184 out.println("Got body: " + resp.body()); |
183 assertEquals(resp.statusCode(), 200, |
185 assertEquals(resp.statusCode(), 200, |
184 "Expected 200, got:" + resp.statusCode()); |
186 "Expected 200, got:" + resp.statusCode()); |
185 |
187 |
186 String receivedHeaderString = value == null ? null |
188 boolean isInitialRequest = i == 0; |
187 : resp.headers().firstValue("X-"+key).get(); |
189 boolean isSecure = uri.getScheme().equalsIgnoreCase("https"); |
188 out.println("Got X-" + key + ": " + resp.headers().allValues("X-"+key)); |
190 boolean isHTTP2 = resp.version() == HTTP_2; |
189 if (value != null) { |
191 boolean isNotH2CUpgrade = isSecure || (sameClient == true && !isInitialRequest); |
190 assertEquals(receivedHeaderString, value); |
192 boolean isDefaultHostHeader = name.equalsIgnoreCase("host") && useDefault; |
191 assertEquals(resp.headers().allValues("X-"+key), List.of(value)); |
193 |
|
194 // By default, HTTP/2 sets the `:authority:` pseudo-header, instead |
|
195 // of the `Host` header. Therefore, there should be no "X-Host" |
|
196 // header in the response, except the response to the h2c Upgrade |
|
197 // request which will have been sent through HTTP/1.1. |
|
198 |
|
199 if (isDefaultHostHeader && isHTTP2 && isNotH2CUpgrade) { |
|
200 assertTrue(resp.headers().firstValue("X-" + key).isEmpty()); |
|
201 assertTrue(resp.headers().allValues("X-" + key).isEmpty()); |
|
202 out.println("No X-" + key + " header received, as expected"); |
192 } else { |
203 } else { |
193 assertEquals(resp.headers().allValues("X-"+key).size(), 0); |
204 String receivedHeaderString = value == null ? null |
194 } |
205 : resp.headers().firstValue("X-"+key).get(); |
195 |
206 out.println("Got X-" + key + ": " + resp.headers().allValues("X-"+key)); |
|
207 if (value != null) { |
|
208 assertEquals(receivedHeaderString, value); |
|
209 assertEquals(resp.headers().allValues("X-"+key), List.of(value)); |
|
210 } else { |
|
211 assertEquals(resp.headers().allValues("X-"+key).size(), 0); |
|
212 } |
|
213 } |
196 } |
214 } |
197 } |
215 } |
198 |
216 |
199 @Test(dataProvider = "variants") |
217 @Test(dataProvider = "variants") |
200 void testHomeMadeIllegalHeader(String uriString, String headerNameAndValue, boolean sameClient) throws Exception { |
218 void testHomeMadeIllegalHeader(String uriString, |
|
219 String headerNameAndValue, |
|
220 boolean sameClient) |
|
221 throws Exception |
|
222 { |
201 out.println("\n--- Starting "); |
223 out.println("\n--- Starting "); |
202 final URI uri = URI.create(uriString); |
224 final URI uri = URI.create(uriString); |
203 |
225 |
204 HttpClient client = HttpClient.newBuilder() |
226 HttpClient client = HttpClient.newBuilder() |
205 .proxy(NO_PROXY) |
227 .proxy(NO_PROXY) |
264 if (!useDefault) { |
286 if (!useDefault) { |
265 requestBuilder.header(name, value); |
287 requestBuilder.header(name, value); |
266 } |
288 } |
267 HttpRequest request = requestBuilder.build(); |
289 HttpRequest request = requestBuilder.build(); |
268 |
290 |
|
291 boolean isInitialRequest = i == 0; |
|
292 boolean isSecure = uri.getScheme().equalsIgnoreCase("https"); |
|
293 boolean isNotH2CUpgrade = isSecure || (sameClient == true && !isInitialRequest); |
|
294 boolean isDefaultHostHeader = name.equalsIgnoreCase("host") && useDefault; |
|
295 |
269 client.sendAsync(request, BodyHandlers.ofString()) |
296 client.sendAsync(request, BodyHandlers.ofString()) |
270 .thenApply(response -> { |
297 .thenApply(response -> { |
271 out.println("Got response: " + response); |
298 out.println("Got response: " + response); |
272 out.println("Got body: " + response.body()); |
299 out.println("Got body: " + response.body()); |
273 assertEquals(response.statusCode(), 200); |
300 assertEquals(response.statusCode(), 200); |
274 return response;}) |
301 return response;}) |
275 .thenAccept(resp -> { |
302 .thenAccept(resp -> { |
276 String receivedHeaderString = value == null ? null |
303 // By default, HTTP/2 sets the `:authority:` pseudo-header, instead |
277 : resp.headers().firstValue("X-"+key).get(); |
304 // of the `Host` header. Therefore, there should be no "X-Host" |
278 out.println("Got X-" + key + ": " + resp.headers().allValues("X-"+key)); |
305 // header in the response, except the response to the h2c Upgrade |
279 if (value != null) { |
306 // request which will have been sent through HTTP/1.1. |
280 assertEquals(receivedHeaderString, value); |
307 |
281 assertEquals(resp.headers().allValues("X-" + key), List.of(value)); |
308 if (isDefaultHostHeader && resp.version() == HTTP_2 && isNotH2CUpgrade) { |
|
309 assertTrue(resp.headers().firstValue("X-" + key).isEmpty()); |
|
310 assertTrue(resp.headers().allValues("X-" + key).isEmpty()); |
|
311 out.println("No X-" + key + " header received, as expected"); |
282 } else { |
312 } else { |
283 assertEquals(resp.headers().allValues("X-" + key).size(), 1); |
313 String receivedHeaderString = value == null ? null |
284 } }) |
314 : resp.headers().firstValue("X-"+key).get(); |
|
315 out.println("Got X-" + key + ": " + resp.headers().allValues("X-"+key)); |
|
316 if (value != null) { |
|
317 assertEquals(receivedHeaderString, value); |
|
318 assertEquals(resp.headers().allValues("X-" + key), List.of(value)); |
|
319 } else { |
|
320 assertEquals(resp.headers().allValues("X-" + key).size(), 1); |
|
321 } |
|
322 } |
|
323 }) |
285 .join(); |
324 .join(); |
286 } |
325 } |
287 } |
326 } |
288 |
327 |
289 static String serverAuthority(HttpTestServer server) { |
328 static String serverAuthority(HttpTestServer server) { |