48 import javax.net.ssl.SSLContext; |
48 import javax.net.ssl.SSLContext; |
49 import javax.net.ssl.SSLSession; |
49 import javax.net.ssl.SSLSession; |
50 import java.io.IOException; |
50 import java.io.IOException; |
51 import java.io.InputStream; |
51 import java.io.InputStream; |
52 import java.io.OutputStream; |
52 import java.io.OutputStream; |
|
53 import java.net.Socket; |
53 import java.net.URI; |
54 import java.net.URI; |
54 import java.net.http.HttpClient; |
55 import java.net.http.HttpClient; |
55 import java.net.http.HttpRequest; |
56 import java.net.http.HttpRequest; |
56 import java.net.http.HttpRequest.BodyPublishers; |
57 import java.net.http.HttpRequest.BodyPublishers; |
57 import java.net.http.HttpResponse.BodyHandlers; |
58 import java.net.http.HttpResponse.BodyHandlers; |
58 import java.nio.ByteBuffer; |
59 import java.nio.ByteBuffer; |
59 import java.util.ArrayList; |
60 import java.util.ArrayList; |
|
61 import java.util.LinkedHashMap; |
60 import java.util.List; |
62 import java.util.List; |
|
63 import java.util.Locale; |
|
64 import java.util.Map; |
61 import java.util.concurrent.CompletionException; |
65 import java.util.concurrent.CompletionException; |
62 import java.util.concurrent.atomic.AtomicInteger; |
66 import java.util.concurrent.atomic.AtomicInteger; |
63 import java.util.function.BiFunction; |
67 import java.util.function.BiFunction; |
64 |
68 |
|
69 import static java.util.List.of; |
65 import static jdk.internal.net.http.common.Pair.pair; |
70 import static jdk.internal.net.http.common.Pair.pair; |
66 import static org.testng.Assert.assertThrows; |
71 import static org.testng.Assert.assertThrows; |
67 |
72 |
68 // Code copied from ContinuationFrameTest |
73 // Code copied from ContinuationFrameTest |
69 public class BadHeadersTest { |
74 public class BadHeadersTest { |
70 |
75 |
71 private static final List<Pair<String, String>> BAD_HEADERS = List.of( |
76 private static final List<List<Pair<String, String>>> BAD_HEADERS = of( |
72 pair(":hello", "GET"), // Unknown pseudo-header |
77 of(pair(":status", "200"), pair(":hello", "GET")), // Unknown pseudo-header |
73 pair("hell o", "value"), // Space in the name |
78 of(pair(":status", "200"), pair("hell o", "value")), // Space in the name |
74 pair("hello", "line1\r\n line2\r\n"), // Multiline value |
79 of(pair(":status", "200"), pair("hello", "line1\r\n line2\r\n")), // Multiline value |
75 pair("hello", "DE" + ((char) 0x7F) + "L") // Bad byte in value |
80 of(pair(":status", "200"), pair("hello", "DE" + ((char) 0x7F) + "L")), // Bad byte in value |
|
81 of(pair("hello", "world!"), pair(":status", "200")) // Pseudo header is not the first one |
76 ); |
82 ); |
77 |
83 |
78 SSLContext sslContext; |
84 SSLContext sslContext; |
79 Http2TestServer http2TestServer; // HTTP/2 ( h2c ) |
85 Http2TestServer http2TestServer; // HTTP/2 ( h2c ) |
80 Http2TestServer https2TestServer; // HTTP/2 ( h2 ) |
86 Http2TestServer https2TestServer; // HTTP/2 ( h2 ) |
85 * A function that returns a list of 1) a HEADERS frame ( with an empty |
91 * A function that returns a list of 1) a HEADERS frame ( with an empty |
86 * payload ), and 2) a CONTINUATION frame with the actual headers. |
92 * payload ), and 2) a CONTINUATION frame with the actual headers. |
87 */ |
93 */ |
88 static BiFunction<Integer,List<ByteBuffer>,List<Http2Frame>> oneContinuation = |
94 static BiFunction<Integer,List<ByteBuffer>,List<Http2Frame>> oneContinuation = |
89 (Integer streamid, List<ByteBuffer> encodedHeaders) -> { |
95 (Integer streamid, List<ByteBuffer> encodedHeaders) -> { |
90 List<ByteBuffer> empty = List.of(ByteBuffer.wrap(new byte[0])); |
96 List<ByteBuffer> empty = of(ByteBuffer.wrap(new byte[0])); |
91 HeadersFrame hf = new HeadersFrame(streamid, 0, empty); |
97 HeadersFrame hf = new HeadersFrame(streamid, 0, empty); |
92 ContinuationFrame cf = new ContinuationFrame(streamid, |
98 ContinuationFrame cf = new ContinuationFrame(streamid, |
93 HeaderFrame.END_HEADERS, |
99 HeaderFrame.END_HEADERS, |
94 encodedHeaders); |
100 encodedHeaders); |
95 return List.of(hf, cf); |
101 return of(hf, cf); |
96 }; |
102 }; |
97 |
103 |
98 /** |
104 /** |
99 * A function that returns a list of a HEADERS frame followed by a number of |
105 * A function that returns a list of a HEADERS frame followed by a number of |
100 * CONTINUATION frames. Each frame contains just a single byte of payload. |
106 * CONTINUATION frames. Each frame contains just a single byte of payload. |
106 ByteBuffer hb = ByteBuffer.wrap(new byte[] {encodedHeaders.get(0).get()}); |
112 ByteBuffer hb = ByteBuffer.wrap(new byte[] {encodedHeaders.get(0).get()}); |
107 HeadersFrame hf = new HeadersFrame(streamid, 0, hb); |
113 HeadersFrame hf = new HeadersFrame(streamid, 0, hb); |
108 frames.add(hf); |
114 frames.add(hf); |
109 for (ByteBuffer bb : encodedHeaders) { |
115 for (ByteBuffer bb : encodedHeaders) { |
110 while (bb.hasRemaining()) { |
116 while (bb.hasRemaining()) { |
111 List<ByteBuffer> data = List.of(ByteBuffer.wrap(new byte[] {bb.get()})); |
117 List<ByteBuffer> data = of(ByteBuffer.wrap(new byte[] {bb.get()})); |
112 ContinuationFrame cf = new ContinuationFrame(streamid, 0, data); |
118 ContinuationFrame cf = new ContinuationFrame(streamid, 0, data); |
113 frames.add(cf); |
119 frames.add(cf); |
114 } |
120 } |
115 } |
121 } |
116 frames.get(frames.size() - 1).setFlag(HeaderFrame.END_HEADERS); |
122 frames.get(frames.size() - 1).setFlag(HeaderFrame.END_HEADERS); |
178 public void setup() throws Exception { |
184 public void setup() throws Exception { |
179 sslContext = new SimpleSSLContext().get(); |
185 sslContext = new SimpleSSLContext().get(); |
180 if (sslContext == null) |
186 if (sslContext == null) |
181 throw new AssertionError("Unexpected null sslContext"); |
187 throw new AssertionError("Unexpected null sslContext"); |
182 |
188 |
183 http2TestServer = new Http2TestServer("127.0.0.1", false, 0); |
189 http2TestServer = new Http2TestServer("127.0.0.1", false, 0) { |
|
190 @Override |
|
191 protected Http2TestServerConnection createConnection(Http2TestServer http2TestServer, |
|
192 Socket socket, |
|
193 Http2TestExchangeSupplier exchangeSupplier) |
|
194 throws IOException { |
|
195 return new Http2TestServerConnection(http2TestServer, socket, exchangeSupplier) { |
|
196 @Override |
|
197 protected HttpHeadersImpl createNewResponseHeaders() { |
|
198 return new OrderedHttpHeaders(); |
|
199 } |
|
200 }; |
|
201 } |
|
202 }; |
184 http2TestServer.addHandler(new Http2EchoHandler(), "/http2/echo"); |
203 http2TestServer.addHandler(new Http2EchoHandler(), "/http2/echo"); |
185 int port = http2TestServer.getAddress().getPort(); |
204 int port = http2TestServer.getAddress().getPort(); |
186 http2URI = "http://127.0.0.1:" + port + "/http2/echo"; |
205 http2URI = "http://127.0.0.1:" + port + "/http2/echo"; |
187 |
206 |
188 https2TestServer = new Http2TestServer("127.0.0.1", true, 0); |
207 https2TestServer = new Http2TestServer("127.0.0.1", true, 0){ |
|
208 @Override |
|
209 protected Http2TestServerConnection createConnection(Http2TestServer http2TestServer, |
|
210 Socket socket, |
|
211 Http2TestExchangeSupplier exchangeSupplier) |
|
212 throws IOException { |
|
213 return new Http2TestServerConnection(http2TestServer, socket, exchangeSupplier) { |
|
214 @Override |
|
215 protected HttpHeadersImpl createNewResponseHeaders() { |
|
216 return new OrderedHttpHeaders(); |
|
217 } |
|
218 }; |
|
219 } |
|
220 }; |
189 https2TestServer.addHandler(new Http2EchoHandler(), "/https2/echo"); |
221 https2TestServer.addHandler(new Http2EchoHandler(), "/https2/echo"); |
190 port = https2TestServer.getAddress().getPort(); |
222 port = https2TestServer.getAddress().getPort(); |
191 https2URI = "https://127.0.0.1:" + port + "/https2/echo"; |
223 https2URI = "https://127.0.0.1:" + port + "/https2/echo"; |
192 |
224 |
193 // Override the default exchange supplier with a custom one to enable |
225 // Override the default exchange supplier with a custom one to enable |
213 public void handle(Http2TestExchange t) throws IOException { |
245 public void handle(Http2TestExchange t) throws IOException { |
214 try (InputStream is = t.getRequestBody(); |
246 try (InputStream is = t.getRequestBody(); |
215 OutputStream os = t.getResponseBody()) { |
247 OutputStream os = t.getResponseBody()) { |
216 byte[] bytes = is.readAllBytes(); |
248 byte[] bytes = is.readAllBytes(); |
217 int i = requestNo.incrementAndGet(); |
249 int i = requestNo.incrementAndGet(); |
218 Pair<String, String> p = BAD_HEADERS.get(i % BAD_HEADERS.size()); |
250 List<Pair<String, String>> p = BAD_HEADERS.get(i % BAD_HEADERS.size()); |
219 t.getResponseHeaders().addHeader(p.first, p.second); |
251 p.forEach(h -> t.getResponseHeaders().addHeader(h.first, h.second)); |
220 t.sendResponseHeaders(200, bytes.length); |
252 t.sendResponseHeaders(200, bytes.length); |
221 os.write(bytes); |
253 os.write(bytes); |
222 } |
254 } |
223 } |
255 } |
224 } |
256 } |
241 |
273 |
242 } |
274 } |
243 |
275 |
244 @Override |
276 @Override |
245 public void sendResponseHeaders(int rCode, long responseLength) throws IOException { |
277 public void sendResponseHeaders(int rCode, long responseLength) throws IOException { |
246 this.responseLength = responseLength; |
|
247 if (responseLength > 0 || responseLength < 0) { |
|
248 long clen = responseLength > 0 ? responseLength : 0; |
|
249 rspheaders.setHeader("Content-length", Long.toString(clen)); |
|
250 } |
|
251 rspheaders.setHeader(":status", Integer.toString(rCode)); |
|
252 |
|
253 List<ByteBuffer> encodeHeaders = conn.encodeHeaders(rspheaders); |
278 List<ByteBuffer> encodeHeaders = conn.encodeHeaders(rspheaders); |
254 List<Http2Frame> headerFrames = headerFrameSupplier.apply(streamid, encodeHeaders); |
279 List<Http2Frame> headerFrames = headerFrameSupplier.apply(streamid, encodeHeaders); |
255 assert headerFrames.size() > 0; // there must always be at least 1 |
280 assert headerFrames.size() > 0; // there must always be at least 1 |
256 |
281 |
257 if (responseLength < 0) { |
282 if (responseLength < 0) { |