1 /*
   2  * Copyright (c) 2018, 2019, 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     final String serverAuthority() {
  63         return InetAddress.getLoopbackAddress().getHostName() + ":"
  64                 + server.getLocalPort();
  65     }
  66 
  67     void runClient() throws IOException, InterruptedException {
  68         URI uri = URI.create("http://" + serverAuthority() + "/foo");
  69         HttpClient client = HttpClient.newHttpClient();
  70         HttpRequest request = HttpRequest.newBuilder(uri)
  71                 .GET()
  72                 .build();
  73         HttpResponse<Long> response = client.send(request, new ClientHandler());
  74         System.out.println("Response code = " + response.statusCode());
  75         long blen = response.body();
  76         if (blen != CONTENT_LEN)
  77             throw new RuntimeException("wrong content length");
  78     }
  79 
  80     public static void main(String[] args) throws Exception {
  81         System.out.println ("CONTENT_LEN = " + CONTENT_LEN);
  82         System.out.println ("CLEN_STR = " + CLEN_STR);
  83         LargeResponseContent test = new LargeResponseContent();
  84         test.runClient();
  85     }
  86 
  87     static class ClientHandler implements HttpResponse.BodyHandler<Long> {
  88 
  89         @Override
  90         public HttpResponse.BodySubscriber<Long> apply(HttpResponse.ResponseInfo responseInfo) {
  91             HttpHeaders headers = responseInfo.headers();
  92             headers.firstValue("content-length");
  93             long  clen = headers.firstValueAsLong("content-length").orElse(-1);
  94             if (clen != CONTENT_LEN)
  95                 return new Subscriber(new RuntimeException("Wrong content length received"));
  96             return new Subscriber(null);
  97         }
  98     }
  99 
 100     static class Subscriber implements HttpResponse.BodySubscriber<Long> {
 101         final CompletableFuture<Long> cf = new CompletableFuture<>();
 102         volatile Flow.Subscription subscription;
 103         volatile long counter = 0;
 104 
 105         Subscriber(Throwable t) {
 106             if (t != null)
 107                 cf.completeExceptionally(t);
 108         }
 109 
 110         @Override
 111         public CompletionStage<Long> getBody() {
 112             return cf;
 113         }
 114 
 115         @Override
 116         public void onSubscribe(Flow.Subscription subscription) {
 117             this.subscription = subscription;
 118             subscription.request(Long.MAX_VALUE);
 119         }
 120 
 121         @Override
 122         public void onNext(List<ByteBuffer> item) {
 123             long v = 0;
 124             for (ByteBuffer b : item)
 125                 v+= b.remaining();
 126             counter += v;
 127         }
 128 
 129         @Override
 130         public void onError(Throwable throwable) {
 131             throwable.printStackTrace();
 132         }
 133 
 134         @Override
 135         public void onComplete() {
 136             cf.complete(counter);
 137         }
 138     }
 139 
 140     static final long CONTENT_LEN = Integer.MAX_VALUE + 1000L;
 141     static final String CLEN_STR = Long.valueOf(CONTENT_LEN).toString();
 142 
 143     static String RESPONSE = "HTTP/1.1 200 OK\r\n" +
 144             "Content-length: " + CLEN_STR + "\r\n" +
 145             "\r\n";
 146 
 147 
 148     void readHeaders(InputStream is) throws IOException {
 149         String s = "";
 150         byte[] buf = new byte[128];
 151         while (!s.endsWith("\r\n\r\n")) {
 152             int c = is.read(buf);
 153             String f = new String(buf, 0, c, StandardCharsets.ISO_8859_1);
 154             s = s + f;
 155         }
 156     }
 157 
 158     public void handleConnection() {
 159         long remaining = CONTENT_LEN;
 160         try {
 161             Socket socket = server.accept();
 162             InputStream is = socket.getInputStream();
 163             readHeaders(is); // read first byte
 164             OutputStream os = socket.getOutputStream();
 165             os.write(RESPONSE.getBytes());
 166             byte[] buf = new byte[64 * 1024];
 167             while (remaining > 0) {
 168                 int amount = (int)Math.min(remaining, buf.length);
 169                 os.write(buf, 0, amount);
 170                 remaining -= amount;
 171             }
 172             System.out.println("Server: finished writing");
 173             os.close();
 174 
 175         } catch (IOException e) {
 176             long sent = CONTENT_LEN - remaining;
 177             System.out.println("Sent " + sent);
 178             e.printStackTrace();
 179         }
 180     }
 181 }
 182