test/jdk/java/net/httpclient/ShortRequestBody.java
changeset 48083 b1c1b4ef4be2
parent 47216 71c04702a3d5
child 49765 ee6f7a61f3a5
child 56082 1da51fab3032
equal deleted inserted replaced
48081:89829dd3cc54 48083:b1c1b4ef4be2
     1 /*
     1 /*
     2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.
     7  * published by the Free Software Foundation.
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    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
    20  * or visit www.oracle.com if you need additional information or have any
    21  * questions.
    21  * questions.
    22  */
    22  */
    23 
    23 
    24 import java.io.*;
    24 import java.io.IOException;
    25 import jdk.incubator.http.HttpClient;
    25 import java.io.InputStream;
    26 import jdk.incubator.http.HttpResponse;
    26 import java.io.OutputStream;
    27 import jdk.incubator.http.HttpRequest;
    27 import java.io.UncheckedIOException;
    28 import java.net.ServerSocket;
    28 import java.net.ServerSocket;
    29 import java.net.Socket;
    29 import java.net.Socket;
    30 import java.net.URI;
    30 import java.net.URI;
    31 import java.nio.file.Files;
    31 import java.nio.file.Files;
    32 import java.nio.file.Path;
    32 import java.nio.file.Path;
    33 import java.nio.file.Paths;
    33 import java.nio.file.Paths;
    34 import java.nio.ByteBuffer;
    34 import java.nio.ByteBuffer;
       
    35 import java.util.ArrayList;
       
    36 import java.util.List;
    35 import java.util.concurrent.CompletableFuture;
    37 import java.util.concurrent.CompletableFuture;
    36 import java.util.concurrent.Executor;
       
    37 import java.util.concurrent.ExecutorService;
       
    38 import java.util.concurrent.ExecutionException;
    38 import java.util.concurrent.ExecutionException;
    39 import java.util.concurrent.Flow;
    39 import java.util.concurrent.Flow;
    40 import java.util.concurrent.TimeoutException;
    40 import java.util.concurrent.TimeoutException;
    41 import java.util.concurrent.TimeUnit;
    41 import java.util.concurrent.TimeUnit;
    42 import static java.lang.System.out;
    42 import java.util.function.Supplier;
       
    43 import jdk.incubator.http.HttpClient;
       
    44 import jdk.incubator.http.HttpResponse;
       
    45 import jdk.incubator.http.HttpRequest;
       
    46 import jdk.incubator.http.HttpTimeoutException;
       
    47 
       
    48 import static java.lang.System.err;
    43 import static java.nio.charset.StandardCharsets.US_ASCII;
    49 import static java.nio.charset.StandardCharsets.US_ASCII;
    44 import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
    50 import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
    45 import static java.nio.charset.StandardCharsets.UTF_8;
    51 import static java.nio.charset.StandardCharsets.UTF_8;
    46 
    52 
    47 /**
    53 /**
    48  * @test
    54  * @test
    49  * @bug 8151441
    55  * @bug 8151441
    50  * @summary Request body of incorrect (larger or smaller) sizes than that
    56  * @summary Request body of incorrect (larger or smaller) sizes than that
    51  *          reported by the body processor
    57  *          reported by the body publisher
    52  * @run main/othervm ShortRequestBody
    58  * @run main/othervm ShortRequestBody
    53  */
    59  */
    54 
    60 
    55 public class ShortRequestBody {
    61 public class ShortRequestBody {
    56 
    62 
    57     static final Path testSrc = Paths.get(System.getProperty("test.src", "."));
    63     static final Path testSrc = Paths.get(System.getProperty("test.src", "."));
    58     static volatile HttpClient staticDefaultClient;
       
    59 
       
    60     static HttpClient defaultClient() {
       
    61         if (staticDefaultClient == null) {
       
    62             synchronized (ShortRequestBody.class) {
       
    63                 staticDefaultClient = HttpClient.newHttpClient();
       
    64             }
       
    65         }
       
    66         return staticDefaultClient;
       
    67     }
       
    68 
    64 
    69     // Some body types ( sources ) for testing.
    65     // Some body types ( sources ) for testing.
    70     static final String STRING_BODY = "Hello world";
    66     static final String STRING_BODY = "Hello world";
    71     static final byte[] BYTE_ARRAY_BODY = new byte[] {
    67     static final byte[] BYTE_ARRAY_BODY = new byte[] {
    72         (byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE };
    68         (byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE };
    77     static final int[] BODY_LENGTHS = new int[] { STRING_BODY.length(),
    73     static final int[] BODY_LENGTHS = new int[] { STRING_BODY.length(),
    78                                                   BYTE_ARRAY_BODY.length,
    74                                                   BYTE_ARRAY_BODY.length,
    79                                                   fileSize(FILE_BODY) };
    75                                                   fileSize(FILE_BODY) };
    80     static final int[] BODY_OFFSETS = new int[] { 0, +1, -1, +2, -2, +3, -3 };
    76     static final int[] BODY_OFFSETS = new int[] { 0, +1, -1, +2, -2, +3, -3 };
    81 
    77 
    82     // A delegating body processor. Subtypes will have a concrete body type.
    78     // A delegating Body Publisher. Subtypes will have a concrete body type.
    83     static abstract class AbstractDelegateRequestBody
    79     static abstract class AbstractDelegateRequestBody
    84             implements HttpRequest.BodyProcessor {
    80             implements HttpRequest.BodyPublisher {
    85 
    81 
    86         final HttpRequest.BodyProcessor delegate;
    82         final HttpRequest.BodyPublisher delegate;
    87         final long contentLength;
    83         final long contentLength;
    88 
    84 
    89         AbstractDelegateRequestBody(HttpRequest.BodyProcessor delegate,
    85         AbstractDelegateRequestBody(HttpRequest.BodyPublisher delegate,
    90                                     long contentLength) {
    86                                     long contentLength) {
    91             this.delegate = delegate;
    87             this.delegate = delegate;
    92             this.contentLength = contentLength;
    88             this.contentLength = contentLength;
    93         }
    89         }
    94 
    90 
    99 
    95 
   100         @Override
    96         @Override
   101         public long contentLength() { return contentLength; /* may be wrong! */ }
    97         public long contentLength() { return contentLength; /* may be wrong! */ }
   102     }
    98     }
   103 
    99 
   104     // Request body processors that may generate a different number of actual
   100     // Request body Publishers that may generate a different number of actual
   105     // bytes to that of what is reported through their {@code contentLength}.
   101     // bytes to that of what is reported through their {@code contentLength}.
   106 
   102 
   107     static class StringRequestBody extends AbstractDelegateRequestBody {
   103     static class StringRequestBody extends AbstractDelegateRequestBody {
   108         StringRequestBody(String body, int additionalLength) {
   104         StringRequestBody(String body, int additionalLength) {
   109             super(HttpRequest.BodyProcessor.fromString(body),
   105             super(HttpRequest.BodyPublisher.fromString(body),
   110                   body.getBytes(UTF_8).length + additionalLength);
   106                   body.getBytes(UTF_8).length + additionalLength);
   111         }
   107         }
   112     }
   108     }
   113 
   109 
   114     static class ByteArrayRequestBody extends AbstractDelegateRequestBody {
   110     static class ByteArrayRequestBody extends AbstractDelegateRequestBody {
   115         ByteArrayRequestBody(byte[] body, int additionalLength) {
   111         ByteArrayRequestBody(byte[] body, int additionalLength) {
   116             super(HttpRequest.BodyProcessor.fromByteArray(body),
   112             super(HttpRequest.BodyPublisher.fromByteArray(body),
   117                   body.length + additionalLength);
   113                   body.length + additionalLength);
   118         }
   114         }
   119     }
   115     }
   120 
   116 
   121     static class FileRequestBody extends AbstractDelegateRequestBody {
   117     static class FileRequestBody extends AbstractDelegateRequestBody {
   122         FileRequestBody(Path path, int additionalLength) throws IOException {
   118         FileRequestBody(Path path, int additionalLength) throws IOException {
   123             super(HttpRequest.BodyProcessor.fromFile(path),
   119             super(HttpRequest.BodyPublisher.fromFile(path),
   124                   Files.size(path) + additionalLength);
   120                   Files.size(path) + additionalLength);
   125         }
   121         }
   126     }
   122     }
   127 
   123 
   128     // ---
   124     // ---
   129 
   125 
   130     public static void main(String[] args) throws Exception {
   126     public static void main(String[] args) throws Exception {
       
   127         HttpClient sharedClient = HttpClient.newHttpClient();
       
   128         List<Supplier<HttpClient>> clientSuppliers = new ArrayList<>();
       
   129         clientSuppliers.add(() -> HttpClient.newHttpClient());
       
   130         clientSuppliers.add(() -> sharedClient);
       
   131 
   131         try (Server server = new Server()) {
   132         try (Server server = new Server()) {
   132             URI uri = new URI("http://127.0.0.1:" + server.getPort() + "/");
   133             for (Supplier<HttpClient> cs : clientSuppliers) {
   133 
   134                 err.println("\n---- next supplier ----\n");
   134             // sanity
   135                 URI uri = new URI("http://127.0.0.1:" + server.getPort() + "/");
   135             success(uri, new StringRequestBody(STRING_BODY, 0));
   136 
   136             success(uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, 0));
   137                 // sanity ( 6 requests to keep client and server offsets easy to workout )
   137             success(uri, new FileRequestBody(FILE_BODY, 0));
   138                 success(cs, uri, new StringRequestBody(STRING_BODY, 0));
   138 
   139                 success(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, 0));
   139             for (int i=1; i< BODY_OFFSETS.length; i++) {
   140                 success(cs, uri, new FileRequestBody(FILE_BODY, 0));
   140                 failureBlocking(uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i]));
   141                 success(cs, uri, new StringRequestBody(STRING_BODY, 0));
   141                 failureBlocking(uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i]));
   142                 success(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, 0));
   142                 failureBlocking(uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i]));
   143                 success(cs, uri, new FileRequestBody(FILE_BODY, 0));
   143 
   144 
   144                 failureNonBlocking(uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i]));
   145                 for (int i = 1; i < BODY_OFFSETS.length; i++) {
   145                 failureNonBlocking(uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i]));
   146                     failureBlocking(cs, uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i]));
   146                 failureNonBlocking(uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i]));
   147                     failureBlocking(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i]));
       
   148                     failureBlocking(cs, uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i]));
       
   149 
       
   150                     failureNonBlocking(cs, uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i]));
       
   151                     failureNonBlocking(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i]));
       
   152                     failureNonBlocking(cs, uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i]));
       
   153                 }
   147             }
   154             }
   148         } finally {
   155         }
   149             Executor def = defaultClient().executor();
   156     }
   150             if (def instanceof ExecutorService) {
   157 
   151                ((ExecutorService)def).shutdownNow();
   158     static void success(Supplier<HttpClient> clientSupplier,
   152             }
   159                         URI uri,
   153         }
   160                         HttpRequest.BodyPublisher publisher)
   154     }
       
   155 
       
   156     static void success(URI uri, HttpRequest.BodyProcessor processor)
       
   157         throws Exception
   161         throws Exception
   158     {
   162     {
   159         CompletableFuture<HttpResponse<Void>> cf;
   163         CompletableFuture<HttpResponse<Void>> cf;
   160         HttpRequest request = HttpRequest.newBuilder(uri)
   164         HttpRequest request = HttpRequest.newBuilder(uri)
   161                                          .POST(processor)
   165                                          .POST(publisher)
   162                                          .build();
   166                                          .build();
   163         cf = defaultClient().sendAsync(request, discard(null));
   167         cf = clientSupplier.get().sendAsync(request, discard(null));
   164 
   168 
   165         HttpResponse<Void> resp = cf.get(30, TimeUnit.SECONDS);
   169         HttpResponse<Void> resp = cf.get(30, TimeUnit.SECONDS);
   166         out.println("Response code: " + resp.statusCode());
   170         err.println("Response code: " + resp.statusCode());
   167         check(resp.statusCode() == 200, "Expected 200, got ", resp.statusCode());
   171         check(resp.statusCode() == 200, "Expected 200, got ", resp.statusCode());
   168     }
   172     }
   169 
   173 
   170     static void failureNonBlocking(URI uri, HttpRequest.BodyProcessor processor)
   174     static void failureNonBlocking(Supplier<HttpClient> clientSupplier,
       
   175                                    URI uri,
       
   176                                    HttpRequest.BodyPublisher publisher)
   171         throws Exception
   177         throws Exception
   172     {
   178     {
   173         CompletableFuture<HttpResponse<Void>> cf;
   179         CompletableFuture<HttpResponse<Void>> cf;
   174         HttpRequest request = HttpRequest.newBuilder(uri)
   180         HttpRequest request = HttpRequest.newBuilder(uri)
   175                                          .POST(processor)
   181                                          .POST(publisher)
   176                                          .build();
   182                                          .build();
   177         cf = defaultClient().sendAsync(request, discard(null));
   183         cf = clientSupplier.get().sendAsync(request, discard(null));
   178 
   184 
   179         try {
   185         try {
   180             HttpResponse<Void> r = cf.get(30, TimeUnit.SECONDS);
   186             HttpResponse<Void> r = cf.get(30, TimeUnit.SECONDS);
   181             throw new RuntimeException("Unexpected response: " + r.statusCode());
   187             throw new RuntimeException("Unexpected response: " + r.statusCode());
   182         } catch (TimeoutException x) {
   188         } catch (TimeoutException x) {
   183             throw new RuntimeException("Unexpected timeout", x);
   189             throw new RuntimeException("Unexpected timeout", x);
   184         } catch (ExecutionException expected) {
   190         } catch (ExecutionException expected) {
   185             out.println("Caught expected: " + expected);
   191             err.println("Caught expected: " + expected);
   186             check(expected.getCause() instanceof IOException,
   192             Throwable t = expected.getCause();
       
   193             check(t instanceof IOException,
   187                   "Expected cause IOException, but got: ", expected.getCause());
   194                   "Expected cause IOException, but got: ", expected.getCause());
   188         }
   195             String msg = t.getMessage();
   189     }
   196             check(msg.contains("Too many") || msg.contains("Too few"),
   190 
   197                     "Expected Too many|Too few, got: ", t);
   191     static void failureBlocking(URI uri, HttpRequest.BodyProcessor processor)
   198         }
       
   199     }
       
   200 
       
   201     static void failureBlocking(Supplier<HttpClient> clientSupplier,
       
   202                                 URI uri,
       
   203                                 HttpRequest.BodyPublisher publisher)
   192         throws Exception
   204         throws Exception
   193     {
   205     {
   194         HttpRequest request = HttpRequest.newBuilder(uri)
   206         HttpRequest request = HttpRequest.newBuilder(uri)
   195                                          .POST(processor)
   207                                          .POST(publisher)
   196                                          .build();
   208                                          .build();
   197         try {
   209         try {
   198             HttpResponse<Void> r = defaultClient().send(request, discard(null));
   210             HttpResponse<Void> r = clientSupplier.get().send(request, discard(null));
   199             throw new RuntimeException("Unexpected response: " + r.statusCode());
   211             throw new RuntimeException("Unexpected response: " + r.statusCode());
       
   212         } catch (HttpTimeoutException x) {
       
   213             throw new RuntimeException("Unexpected timeout", x);
   200         } catch (IOException expected) {
   214         } catch (IOException expected) {
   201             out.println("Caught expected: " + expected);
   215             err.println("Caught expected: " + expected);
       
   216             String msg = expected.getMessage();
       
   217             check(msg.contains("Too many") || msg.contains("Too few"),
       
   218                     "Expected Too many|Too few, got: ", expected);
   202         }
   219         }
   203     }
   220     }
   204 
   221 
   205     static class Server extends Thread implements AutoCloseable {
   222     static class Server extends Thread implements AutoCloseable {
   206 
   223 
   223             int count = 0;
   240             int count = 0;
   224             int offset = 0;
   241             int offset = 0;
   225 
   242 
   226             while (!closed) {
   243             while (!closed) {
   227                 try (Socket s = ss.accept()) {
   244                 try (Socket s = ss.accept()) {
       
   245                     err.println("Server: got connection");
   228                     InputStream is = s.getInputStream();
   246                     InputStream is = s.getInputStream();
   229                     readRequestHeaders(is);
   247                     readRequestHeaders(is);
   230                     byte[] ba = new byte[1024];
   248                     byte[] ba = new byte[1024];
   231 
   249 
   232                     int length = BODY_LENGTHS[count % 3];
   250                     int length = BODY_LENGTHS[count % 3];
   233                     length += BODY_OFFSETS[offset];
   251                     length += BODY_OFFSETS[offset];
   234 
   252                     err.println("Server: count=" + count + ", offset=" + offset);
   235                     is.readNBytes(ba, 0, length);
   253                     err.println("Server: expecting " +length+ " bytes");
   236 
   254                     int read = is.readNBytes(ba, 0, length);
   237                     OutputStream os = s.getOutputStream();
   255                     err.println("Server: actually read " + read + " bytes");
   238                     os.write(RESPONSE.getBytes(US_ASCII));
   256 
       
   257                     // Update the counts before replying, to prevent the
       
   258                     // client-side racing reset with this thread.
   239                     count++;
   259                     count++;
   240                     if (count % 6 == 0) // 6 is the number of failure requests per offset
   260                     if (count % 6 == 0) // 6 is the number of failure requests per offset
   241                         offset++;
   261                         offset++;
       
   262                     if (count % 42 == 0) {
       
   263                         count = 0;  // reset, for second iteration
       
   264                         offset = 0;
       
   265                     }
       
   266 
       
   267                     if (read < length) {
       
   268                         // no need to reply, client has already closed
       
   269                         // ensure closed
       
   270                         if (is.read() != -1)
       
   271                             new AssertionError("Unexpected read");
       
   272                     } else {
       
   273                         OutputStream os = s.getOutputStream();
       
   274                         err.println("Server: writing "
       
   275                                 + RESPONSE.getBytes(US_ASCII).length + " bytes");
       
   276                         os.write(RESPONSE.getBytes(US_ASCII));
       
   277                     }
       
   278 
   242                 } catch (IOException e) {
   279                 } catch (IOException e) {
   243                     if (!closed)
   280                     if (!closed)
   244                         System.out.println("Unexpected" + e);
   281                         System.out.println("Unexpected" + e);
   245                 }
   282                 }
   246             }
   283             }