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 { |
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 } |