test/jdk/java/net/httpclient/http2/server/Http2TestServerConnection.java
branchhttp-client-branch
changeset 56619 57f17e890a40
parent 56598 4c502e3991bf
child 56795 03ece2518428
equal deleted inserted replaced
56618:e4022357f852 56619:57f17e890a40
    31 import java.net.Socket;
    31 import java.net.Socket;
    32 import java.net.URI;
    32 import java.net.URI;
    33 import java.net.InetAddress;
    33 import java.net.InetAddress;
    34 import javax.net.ssl.*;
    34 import javax.net.ssl.*;
    35 import java.net.URISyntaxException;
    35 import java.net.URISyntaxException;
       
    36 import java.net.http.HttpHeaders;
    36 import java.nio.ByteBuffer;
    37 import java.nio.ByteBuffer;
    37 import java.nio.charset.StandardCharsets;
       
    38 import java.util.*;
    38 import java.util.*;
    39 import java.util.concurrent.CompletableFuture;
    39 import java.util.concurrent.CompletableFuture;
    40 import java.util.concurrent.ExecutorService;
    40 import java.util.concurrent.ExecutorService;
    41 import java.util.concurrent.ConcurrentLinkedQueue;
    41 import java.util.concurrent.ConcurrentLinkedQueue;
    42 import java.util.function.Consumer;
    42 import java.util.function.Consumer;
    43 import jdk.internal.net.http.common.HttpHeadersImpl;
    43 import jdk.internal.net.http.common.HttpHeadersBuilder;
    44 import jdk.internal.net.http.frame.*;
    44 import jdk.internal.net.http.frame.*;
    45 import jdk.internal.net.http.hpack.Decoder;
    45 import jdk.internal.net.http.hpack.Decoder;
    46 import jdk.internal.net.http.hpack.DecodingCallback;
    46 import jdk.internal.net.http.hpack.DecodingCallback;
    47 import jdk.internal.net.http.hpack.Encoder;
    47 import jdk.internal.net.http.hpack.Encoder;
    48 import sun.net.www.http.ChunkedInputStream;
    48 import sun.net.www.http.ChunkedInputStream;
   453         outputQ.put(wup);
   453         outputQ.put(wup);
   454         wup = new WindowUpdateFrame(0 , len);
   454         wup = new WindowUpdateFrame(0 , len);
   455         outputQ.put(wup);
   455         outputQ.put(wup);
   456     }
   456     }
   457 
   457 
   458     HttpHeadersImpl decodeHeaders(List<HeaderFrame> frames) throws IOException {
   458     HttpHeaders decodeHeaders(List<HeaderFrame> frames) throws IOException {
   459         HttpHeadersImpl headers = createNewResponseHeaders();
   459         HttpHeadersBuilder headersBuilder = createNewHeadersBuilder();
   460 
   460 
   461         DecodingCallback cb = (name, value) -> {
   461         DecodingCallback cb = (name, value) -> {
   462             headers.addHeader(name.toString(), value.toString());
   462             headersBuilder.addHeader(name.toString(), value.toString());
   463         };
   463         };
   464 
   464 
   465         for (HeaderFrame frame : frames) {
   465         for (HeaderFrame frame : frames) {
   466             List<ByteBuffer> buffers = frame.getHeaderBlock();
   466             List<ByteBuffer> buffers = frame.getHeaderBlock();
   467             for (ByteBuffer buffer : buffers) {
   467             for (ByteBuffer buffer : buffers) {
   468                 hpackIn.decode(buffer, false, cb);
   468                 hpackIn.decode(buffer, false, cb);
   469             }
   469             }
   470         }
   470         }
   471         hpackIn.decode(EMPTY_BUFFER, true, cb);
   471         hpackIn.decode(EMPTY_BUFFER, true, cb);
   472         return headers;
   472         return headersBuilder.build();
   473     }
   473     }
   474 
   474 
   475     String getRequestLine(String request) {
   475     String getRequestLine(String request) {
   476         int eol = request.indexOf(CRLF);
   476         int eol = request.indexOf(CRLF);
   477         return request.substring(0, eol);
   477         return request.substring(0, eol);
   484             throw new RuntimeException("Malformed request");
   484             throw new RuntimeException("Malformed request");
   485         }
   485         }
   486         return request.substring(start,end);
   486         return request.substring(start,end);
   487     }
   487     }
   488 
   488 
   489     void addHeaders(String headers, HttpHeadersImpl hdrs) {
   489     static void addHeaders(String headersString, HttpHeadersBuilder headersBuilder) {
   490         String[] hh = headers.split(CRLF);
   490         String[] hh = headersString.split(CRLF);
   491         for (String header : hh) {
   491         for (String header : hh) {
   492             int colon = header.indexOf(':');
   492             int colon = header.indexOf(':');
   493             if (colon == -1)
   493             if (colon == -1)
   494                 continue;
   494                 continue;
   495             String name = header.substring(0, colon);
   495             String name = header.substring(0, colon);
   496             String value = header.substring(colon+1);
   496             String value = header.substring(colon+1);
   497             while (value.startsWith(" "))
   497             while (value.startsWith(" "))
   498                 value = value.substring(1);
   498                 value = value.substring(1);
   499             hdrs.addHeader(name, value);
   499             headersBuilder.addHeader(name, value);
   500         }
   500         }
   501     }
   501     }
   502 
   502 
   503     // First stream (1) comes from a plaintext HTTP/1.1 request
   503     // First stream (1) comes from a plaintext HTTP/1.1 request
   504     @SuppressWarnings({"rawtypes","unchecked"})
   504     @SuppressWarnings({"rawtypes","unchecked"})
   505     void createPrimordialStream(Http1InitialRequest request) throws IOException {
   505     void createPrimordialStream(Http1InitialRequest request) throws IOException {
   506         HttpHeadersImpl headers = createNewResponseHeaders();
   506         HttpHeadersBuilder headersBuilder = createNewHeadersBuilder();
   507         String requestLine = getRequestLine(request.headers);
   507         String requestLine = getRequestLine(request.headers);
   508         String[] tokens = requestLine.split(" ");
   508         String[] tokens = requestLine.split(" ");
   509         if (!tokens[2].equals("HTTP/1.1")) {
   509         if (!tokens[2].equals("HTTP/1.1")) {
   510             throw new IOException("bad request line");
   510             throw new IOException("bad request line");
   511         }
   511         }
   518         String host = getHeader(request.headers, "Host");
   518         String host = getHeader(request.headers, "Host");
   519         if (host == null) {
   519         if (host == null) {
   520             throw new IOException("missing Host");
   520             throw new IOException("missing Host");
   521         }
   521         }
   522 
   522 
   523         headers.setHeader(":method", tokens[0]);
   523         headersBuilder.setHeader(":method", tokens[0]);
   524         headers.setHeader(":scheme", "http"); // always in this case
   524         headersBuilder.setHeader(":scheme", "http"); // always in this case
   525         headers.setHeader(":authority", host);
   525         headersBuilder.setHeader(":authority", host);
   526         String path = uri.getRawPath();
   526         String path = uri.getRawPath();
   527         if (uri.getRawQuery() != null)
   527         if (uri.getRawQuery() != null)
   528             path = path + "?" + uri.getRawQuery();
   528             path = path + "?" + uri.getRawQuery();
   529         headers.setHeader(":path", path);
   529         headersBuilder.setHeader(":path", path);
   530 
   530 
   531         Queue q = new Queue(sentinel);
   531         Queue q = new Queue(sentinel);
   532         byte[] body = getRequestBody(request);
   532         byte[] body = getRequestBody(request);
   533         addHeaders(getHeaders(request.headers), headers);
   533         addHeaders(getHeaders(request.headers), headersBuilder);
   534         headers.setHeader("Content-length", Integer.toString(body.length));
   534         headersBuilder.setHeader("Content-length", Integer.toString(body.length));
       
   535         HttpHeaders headers = headersBuilder.build();
   535 
   536 
   536         addRequestBodyToQueue(body, q);
   537         addRequestBodyToQueue(body, q);
   537         streams.put(1, q);
   538         streams.put(1, q);
   538         exec.submit(() -> {
   539         exec.submit(() -> {
   539             handleRequest(headers, q, 1, true /*complete request has been read*/);
   540             handleRequest(headers, q, 1, true /*complete request has been read*/);
   567                 }
   568                 }
   568                 frames.add(frame);
   569                 frames.add(frame);
   569             }
   570             }
   570         }
   571         }
   571         boolean endStreamReceived = endStream;
   572         boolean endStreamReceived = endStream;
   572         HttpHeadersImpl headers = decodeHeaders(frames);
   573         HttpHeaders headers = decodeHeaders(frames);
   573 
   574 
   574         // Strict to assert Client correctness. Not all servers are as strict,
   575         // Strict to assert Client correctness. Not all servers are as strict,
   575         // but some are known to be.
   576         // but some are known to be.
   576         Optional<?> disallowedHeader = headers.firstValue("Upgrade");
   577         Optional<?> disallowedHeader = headers.firstValue("Upgrade");
   577         if (disallowedHeader.isPresent()) {
   578         if (disallowedHeader.isPresent()) {
   591 
   592 
   592     // runs in own thread. Handles request from start to finish. Incoming frames
   593     // runs in own thread. Handles request from start to finish. Incoming frames
   593     // for this stream/request delivered on Q
   594     // for this stream/request delivered on Q
   594 
   595 
   595     @SuppressWarnings({"rawtypes","unchecked"})
   596     @SuppressWarnings({"rawtypes","unchecked"})
   596     void handleRequest(HttpHeadersImpl headers,
   597     void handleRequest(HttpHeaders headers,
   597                        Queue queue,
   598                        Queue queue,
   598                        int streamid,
   599                        int streamid,
   599                        boolean endStreamReceived)
   600                        boolean endStreamReceived)
   600     {
   601     {
   601         String method = headers.firstValue(":method").orElse("");
   602         String method = headers.firstValue(":method").orElse("");
   605         String scheme = headers.firstValue(":scheme").orElse("");
   606         String scheme = headers.firstValue(":scheme").orElse("");
   606         //System.out.println("scheme = " + scheme);
   607         //System.out.println("scheme = " + scheme);
   607         String authority = headers.firstValue(":authority").orElse("");
   608         String authority = headers.firstValue(":authority").orElse("");
   608         //System.out.println("authority = " + authority);
   609         //System.out.println("authority = " + authority);
   609         System.err.printf("TestServer: %s %s\n", method, path);
   610         System.err.printf("TestServer: %s %s\n", method, path);
   610         HttpHeadersImpl rspheaders = createNewResponseHeaders();
       
   611         int winsize = clientSettings.getParameter(
   611         int winsize = clientSettings.getParameter(
   612                 SettingsFrame.INITIAL_WINDOW_SIZE);
   612                 SettingsFrame.INITIAL_WINDOW_SIZE);
   613         //System.err.println ("Stream window size = " + winsize);
   613         //System.err.println ("Stream window size = " + winsize);
   614 
   614 
   615         final InputStream bis;
   615         final InputStream bis;
   625         {
   625         {
   626             outStreams.put(streamid, bos);
   626             outStreams.put(streamid, bos);
   627             String us = scheme + "://" + authority + path;
   627             String us = scheme + "://" + authority + path;
   628             URI uri = new URI(us);
   628             URI uri = new URI(us);
   629             boolean pushAllowed = clientSettings.getParameter(SettingsFrame.ENABLE_PUSH) == 1;
   629             boolean pushAllowed = clientSettings.getParameter(SettingsFrame.ENABLE_PUSH) == 1;
       
   630             HttpHeadersBuilder rspheadersBuilder = createNewHeadersBuilder();
   630             Http2TestExchange exchange = exchangeSupplier.get(streamid, method,
   631             Http2TestExchange exchange = exchangeSupplier.get(streamid, method,
   631                     headers, rspheaders, uri, bis, getSSLSession(),
   632                     headers, rspheadersBuilder, uri, bis, getSSLSession(),
   632                     bos, this, pushAllowed);
   633                     bos, this, pushAllowed);
   633 
   634 
   634             // give to user
   635             // give to user
   635             Http2Handler handler = server.getHandlerFor(uri.getPath());
   636             Http2Handler handler = server.getHandlerFor(uri.getPath());
   636             try {
   637             try {
   653             e.printStackTrace();
   654             e.printStackTrace();
   654             close(-1);
   655             close(-1);
   655         }
   656         }
   656     }
   657     }
   657 
   658 
   658     protected HttpHeadersImpl createNewResponseHeaders() {
   659     protected HttpHeadersBuilder createNewHeadersBuilder() {
   659         return new HttpHeadersImpl();
   660         return new HttpHeadersBuilder();
   660     }
   661     }
   661 
   662 
   662     private SSLSession getSSLSession() {
   663     private SSLSession getSSLSession() {
   663         if (! (socket instanceof SSLSocket))
   664         if (! (socket instanceof SSLSocket))
   664             return null;
   665             return null;
   743             }
   744             }
   744             close(ErrorFrame.PROTOCOL_ERROR);
   745             close(ErrorFrame.PROTOCOL_ERROR);
   745         }
   746         }
   746     }
   747     }
   747 
   748 
   748     List<ByteBuffer> encodeHeaders(HttpHeadersImpl headers) {
   749     /** Encodes an group of headers, without any ordering guarantees. */
       
   750     List<ByteBuffer> encodeHeaders(HttpHeaders headers) {
   749         List<ByteBuffer> buffers = new LinkedList<>();
   751         List<ByteBuffer> buffers = new LinkedList<>();
   750 
   752 
   751         ByteBuffer buf = getBuffer();
   753         ByteBuffer buf = getBuffer();
   752         boolean encoded;
   754         boolean encoded;
   753         for (Map.Entry<String, List<String>> entry : headers.map().entrySet()) {
   755         for (Map.Entry<String, List<String>> entry : headers.map().entrySet()) {
   762                         buffers.add(buf);
   764                         buffers.add(buf);
   763                         buf = getBuffer();
   765                         buf = getBuffer();
   764                     }
   766                     }
   765                 } while (!encoded);
   767                 } while (!encoded);
   766             }
   768             }
       
   769         }
       
   770         buf.flip();
       
   771         buffers.add(buf);
       
   772         return buffers;
       
   773     }
       
   774 
       
   775     /** Encodes an ordered list of headers. */
       
   776     List<ByteBuffer> encodeHeadersOrdered(List<Map.Entry<String,String>> headers) {
       
   777         List<ByteBuffer> buffers = new LinkedList<>();
       
   778 
       
   779         ByteBuffer buf = getBuffer();
       
   780         boolean encoded;
       
   781         for (Map.Entry<String, String> entry : headers) {
       
   782             String value = entry.getValue();
       
   783             String key = entry.getKey().toLowerCase();
       
   784             do {
       
   785                 hpackOut.header(key, value);
       
   786                 encoded = hpackOut.encode(buf);
       
   787                 if (!encoded) {
       
   788                     buf.flip();
       
   789                     buffers.add(buf);
       
   790                     buf = getBuffer();
       
   791                 }
       
   792             } while (!encoded);
   767         }
   793         }
   768         buf.flip();
   794         buf.flip();
   769         buffers.add(buf);
   795         buffers.add(buf);
   770         return buffers;
   796         return buffers;
   771     }
   797     }
   845     }
   871     }
   846 
   872 
   847     // returns a minimal response with status 200
   873     // returns a minimal response with status 200
   848     // that is the response to the push promise just sent
   874     // that is the response to the push promise just sent
   849     private ResponseHeaders getPushResponse(int streamid) {
   875     private ResponseHeaders getPushResponse(int streamid) {
   850         HttpHeadersImpl h = createNewResponseHeaders();
   876         HttpHeadersBuilder hb = createNewHeadersBuilder();
   851         h.addHeader(":status", "200");
   877         hb.addHeader(":status", "200");
   852         ResponseHeaders oh = new ResponseHeaders(h);
   878         ResponseHeaders oh = new ResponseHeaders(hb.build());
   853         oh.streamid(streamid);
   879         oh.streamid(streamid);
   854         oh.setFlag(HeaderFrame.END_HEADERS);
   880         oh.setFlag(HeaderFrame.END_HEADERS);
   855         return oh;
   881         return oh;
   856     }
   882     }
   857 
   883 
  1080 
  1106 
  1081     // simplified output headers class. really just a type safe container
  1107     // simplified output headers class. really just a type safe container
  1082     // for the hashmap.
  1108     // for the hashmap.
  1083 
  1109 
  1084     static class ResponseHeaders extends Http2Frame {
  1110     static class ResponseHeaders extends Http2Frame {
  1085         HttpHeadersImpl headers;
  1111         HttpHeaders headers;
  1086 
  1112 
  1087         ResponseHeaders(HttpHeadersImpl headers) {
  1113         ResponseHeaders(HttpHeaders headers) {
  1088             super(0, 0);
  1114             super(0, 0);
  1089             this.headers = headers;
  1115             this.headers = headers;
  1090         }
  1116         }
  1091 
  1117 
  1092     }
  1118     }