test/jdk/java/net/httpclient/http2/BadHeadersTest.java
branchhttp-client-branch
changeset 56205 f4c9c5920141
parent 56167 96fa4f49a9ff
child 56206 a0cf7477d139
equal deleted inserted replaced
56204:e5d0c20217a3 56205:f4c9c5920141
    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) {
   264 
   289 
   265             os.goodToGo();
   290             os.goodToGo();
   266             System.err.println("Sent response headers " + rCode);
   291             System.err.println("Sent response headers " + rCode);
   267         }
   292         }
   268     }
   293     }
       
   294 
       
   295     private static class OrderedHttpHeaders extends HttpHeadersImpl {
       
   296 
       
   297         private final Map<String, List<String>> map = new LinkedHashMap<>();
       
   298 
       
   299         @Override
       
   300         public void addHeader(String name, String value) {
       
   301             super.addHeader(name.toLowerCase(Locale.ROOT), value);
       
   302         }
       
   303 
       
   304         @Override
       
   305         public void setHeader(String name, String value) {
       
   306             super.setHeader(name.toLowerCase(Locale.ROOT), value);
       
   307         }
       
   308 
       
   309         @Override
       
   310         protected Map<String, List<String>> headersMap() {
       
   311             return map;
       
   312         }
       
   313 
       
   314         @Override
       
   315         protected HttpHeadersImpl newDeepCopy() {
       
   316             return new OrderedHttpHeaders();
       
   317         }
       
   318     }
   269 }
   319 }