1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   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
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.io.IOException;
  25 import java.io.InputStream;
  26 import java.io.OutputStream;
  27 import java.net.InetAddress;
  28 import java.net.InetSocketAddress;
  29 import java.net.ServerSocket;
  30 import java.net.Socket;
  31 import java.net.URI;
  32 import java.net.http.HttpClient;
  33 import java.net.http.HttpHeaders;
  34 import java.net.http.HttpRequest;
  35 import java.net.http.HttpResponse;
  36 import java.nio.ByteBuffer;
  37 import java.nio.charset.StandardCharsets;
  38 import java.util.List;
  39 import java.util.concurrent.CompletableFuture;
  40 import java.util.concurrent.CompletionStage;
  41 import java.util.concurrent.Flow;
  42 
  43 /**
  44  * @test
  45  * @bug 8212926
  46  * @summary Basic tests for response timeouts
  47  * @run main/othervm LargeResponseContent
  48  */
  49 
  50 public class LargeResponseContent {
  51     final ServerSocket server;
  52     final int port;
  53 
  54     public LargeResponseContent() throws Exception {
  55         server = new ServerSocket(0, 10, InetAddress.getLoopbackAddress());
  56         Thread serverThread = new Thread(this::handleConnection);
  57         serverThread.setDaemon(false);
  58         port = server.getLocalPort();
  59         serverThread.start();
  60     }
  61 
  62     void runClient() throws IOException, InterruptedException {
  63         URI uri = URI.create("http://127.0.0.1:" + Integer.toString(port) + "/foo");
  64         HttpClient client = HttpClient.newHttpClient();
  65         HttpRequest request = HttpRequest.newBuilder(uri)
  66                 .GET()
  67                 .build();
  68         HttpResponse<Long> response = client.send(request, new ClientHandler());
  69         System.out.println("Response code = " + response.statusCode());
  70         long blen = response.body();
  71         if (blen != CONTENT_LEN)
  72             throw new RuntimeException("wrong content length");
  73     }
  74 
  75     public static void main(String[] args) throws Exception {
  76         System.out.println ("CONTENT_LEN = " + CONTENT_LEN);
  77         System.out.println ("CLEN_STR = " + CLEN_STR);
  78         LargeResponseContent test = new LargeResponseContent();
  79         test.runClient();
  80     }
  81 
  82     static class ClientHandler implements HttpResponse.BodyHandler<Long> {
  83 
  84         @Override
  85         public HttpResponse.BodySubscriber<Long> apply(HttpResponse.ResponseInfo responseInfo) {
  86             HttpHeaders headers = responseInfo.headers();
  87             headers.firstValue("content-length");
  88             long  clen = headers.firstValueAsLong("content-length").orElse(-1);
  89             if (clen != CONTENT_LEN)
  90                 return new Subscriber(new RuntimeException("Wrong content length received"));
  91             return new Subscriber(null);
  92         }
  93     }
  94 
  95     static class Subscriber implements HttpResponse.BodySubscriber<Long> {
  96         final CompletableFuture<Long> cf = new CompletableFuture<>();
  97         volatile Flow.Subscription subscription;
  98         volatile long counter = 0;
  99 
 100         Subscriber(Throwable t) {
 101             if (t != null)
 102                 cf.completeExceptionally(t);
 103         }
 104 
 105         @Override
 106         public CompletionStage<Long> getBody() {
 107             return cf;
 108         }
 109 
 110         @Override
 111         public void onSubscribe(Flow.Subscription subscription) {
 112             this.subscription = subscription;
 113             subscription.request(Long.MAX_VALUE);
 114         }
 115 
 116         @Override
 117         public void onNext(List<ByteBuffer> item) {
 118             long v = 0;
 119             for (ByteBuffer b : item)
 120                 v+= b.remaining();
 121             counter += v;
 122         }
 123 
 124         @Override
 125         public void onError(Throwable throwable) {
 126             throwable.printStackTrace();
 127         }
 128 
 129         @Override
 130         public void onComplete() {
 131             cf.complete(counter);
 132         }
 133     }
 134 
 135     static final long CONTENT_LEN = Integer.MAX_VALUE + 1000L;
 136     static final String CLEN_STR = Long.valueOf(CONTENT_LEN).toString();
 137 
 138     static String RESPONSE = "HTTP/1.1 200 OK\r\n" +
 139             "Content-length: " + CLEN_STR + "\r\n" +
 140             "\r\n";
 141 
 142 
 143     void readHeaders(InputStream is) throws IOException {
 144         String s = "";
 145         byte[] buf = new byte[128];
 146         while (!s.endsWith("\r\n\r\n")) {
 147             int c = is.read(buf);
 148             String f = new String(buf, 0, c, StandardCharsets.ISO_8859_1);
 149             s = s + f;
 150         }
 151     }
 152 
 153     public void handleConnection() {
 154         long remaining = CONTENT_LEN;
 155         try {
 156             Socket socket = server.accept();
 157             InputStream is = socket.getInputStream();
 158             readHeaders(is); // read first byte
 159             OutputStream os = socket.getOutputStream();
 160             os.write(RESPONSE.getBytes());
 161             byte[] buf = new byte[64 * 1024];
 162             while (remaining > 0) {
 163                 int amount = (int)Math.min(remaining, buf.length);
 164                 os.write(buf, 0, amount);
 165                 remaining -= amount;
 166             }
 167             System.out.println("Server: finished writing");
 168             os.close();
 169 
 170         } catch (IOException e) {
 171             long sent = CONTENT_LEN - remaining;
 172             System.out.println("Sent " + sent);
 173             e.printStackTrace();
 174         }
 175     }
 176 }
 177