jdk/test/java/net/httpclient/ShortRequestBody.java
changeset 42460 7133f144981a
parent 36433 dcbc230cfa4f
equal deleted inserted replaced
42459:1ad58e0cbf16 42460:7133f144981a
    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.*;
    25 import java.net.http.*;
    25 import jdk.incubator.http.HttpClient;
    26 import java.net.*;
    26 import jdk.incubator.http.HttpResponse;
    27 import java.util.concurrent.*;
    27 import jdk.incubator.http.HttpRequest;
       
    28 import java.net.ServerSocket;
       
    29 import java.net.Socket;
       
    30 import java.net.URI;
       
    31 import java.nio.file.Files;
       
    32 import java.nio.file.Path;
       
    33 import java.nio.file.Paths;
    28 import java.nio.ByteBuffer;
    34 import java.nio.ByteBuffer;
    29 import java.util.function.LongConsumer;
    35 import java.util.concurrent.CompletableFuture;
       
    36 import java.util.concurrent.Executor;
       
    37 import java.util.concurrent.ExecutorService;
       
    38 import java.util.concurrent.ExecutionException;
       
    39 import java.util.concurrent.Flow;
       
    40 import java.util.concurrent.TimeoutException;
       
    41 import java.util.concurrent.TimeUnit;
       
    42 import static java.lang.System.out;
       
    43 import static java.nio.charset.StandardCharsets.US_ASCII;
       
    44 import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
       
    45 import static java.nio.charset.StandardCharsets.UTF_8;
    30 
    46 
    31 /**
    47 /**
    32  * @test
    48  * @test
    33  * @bug 8151441
    49  * @bug 8151441
    34  * @run main/othervm/timeout=10 ShortRequestBody
    50  * @summary Request body of incorrect (larger or smaller) sizes than that
       
    51  *          reported by the body processor
       
    52  * @run main/othervm ShortRequestBody
    35  */
    53  */
    36 
    54 
    37 /**
       
    38  * Exception was not being thrown
       
    39  */
       
    40 public class ShortRequestBody {
    55 public class ShortRequestBody {
    41 
    56 
    42     static Server server;
    57     static final Path testSrc = Paths.get(System.getProperty("test.src", "."));
    43     static String reqbody = "Hello world";
    58     static volatile HttpClient staticDefaultClient;
    44 
    59 
    45     static String response = "HTTP/1.1 200 OK\r\nContent-length: 0\r\n\r\n";
    60     static HttpClient defaultClient() {
    46 
    61         if (staticDefaultClient == null) {
    47     static class RequestBody implements HttpRequest.BodyProcessor {
    62             synchronized (ShortRequestBody.class) {
    48         public long onRequestStart(HttpRequest hr, LongConsumer flowController) {
    63                 staticDefaultClient = HttpClient.newHttpClient();
    49             return reqbody.length() + 1; // wrong!
    64             }
    50         }
    65         }
    51 
    66         return staticDefaultClient;
    52         public boolean onRequestBodyChunk(ByteBuffer buf) throws IOException {
    67     }
    53             byte[] b = reqbody.getBytes();
    68 
    54             buf.put(b);
    69     // Some body types ( sources ) for testing.
       
    70     static final String STRING_BODY = "Hello world";
       
    71     static final byte[] BYTE_ARRAY_BODY = new byte[] {
       
    72         (byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE };
       
    73     static final Path FILE_BODY = testSrc.resolve("docs").resolve("files").resolve("foo.txt");
       
    74 
       
    75     // Body lengths and offsets ( amount to be wrong by ), to make coordination
       
    76     // between client and server easier.
       
    77     static final int[] BODY_LENGTHS = new int[] { STRING_BODY.length(),
       
    78                                                   BYTE_ARRAY_BODY.length,
       
    79                                                   fileSize(FILE_BODY) };
       
    80     static final int[] BODY_OFFSETS = new int[] { 0, +1, -1, +2, -2, +3, -3 };
       
    81 
       
    82     // A delegating body processor. Subtypes will have a concrete body type.
       
    83     static abstract class AbstractDelegateRequestBody
       
    84             implements HttpRequest.BodyProcessor {
       
    85 
       
    86         final HttpRequest.BodyProcessor delegate;
       
    87         final long contentLength;
       
    88 
       
    89         AbstractDelegateRequestBody(HttpRequest.BodyProcessor delegate,
       
    90                                     long contentLength) {
       
    91             this.delegate = delegate;
       
    92             this.contentLength = contentLength;
       
    93         }
       
    94 
       
    95         @Override
       
    96         public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) {
       
    97             delegate.subscribe(subscriber);
       
    98         }
       
    99 
       
   100         @Override
       
   101         public long contentLength() { return contentLength; /* may be wrong! */ }
       
   102     }
       
   103 
       
   104     // Request body processors that may generate a different number of actual
       
   105     // bytes to that of what is reported through their {@code contentLength}.
       
   106 
       
   107     static class StringRequestBody extends AbstractDelegateRequestBody {
       
   108         StringRequestBody(String body, int additionalLength) {
       
   109             super(HttpRequest.BodyProcessor.fromString(body),
       
   110                   body.getBytes(UTF_8).length + additionalLength);
       
   111         }
       
   112     }
       
   113 
       
   114     static class ByteArrayRequestBody extends AbstractDelegateRequestBody {
       
   115         ByteArrayRequestBody(byte[] body, int additionalLength) {
       
   116             super(HttpRequest.BodyProcessor.fromByteArray(body),
       
   117                   body.length + additionalLength);
       
   118         }
       
   119     }
       
   120 
       
   121     static class FileRequestBody extends AbstractDelegateRequestBody {
       
   122         FileRequestBody(Path path, int additionalLength) throws IOException {
       
   123             super(HttpRequest.BodyProcessor.fromFile(path),
       
   124                   Files.size(path) + additionalLength);
       
   125         }
       
   126     }
       
   127 
       
   128     // ---
       
   129 
       
   130     public static void main(String[] args) throws Exception {
       
   131         try (Server server = new Server()) {
       
   132             URI uri = new URI("http://127.0.0.1:" + server.getPort() + "/");
       
   133 
       
   134             // sanity
       
   135             success(uri, new StringRequestBody(STRING_BODY, 0));
       
   136             success(uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, 0));
       
   137             success(uri, new FileRequestBody(FILE_BODY, 0));
       
   138 
       
   139             for (int i=1; i< BODY_OFFSETS.length; i++) {
       
   140                 failureBlocking(uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i]));
       
   141                 failureBlocking(uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i]));
       
   142                 failureBlocking(uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i]));
       
   143 
       
   144                 failureNonBlocking(uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i]));
       
   145                 failureNonBlocking(uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i]));
       
   146                 failureNonBlocking(uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i]));
       
   147             }
       
   148         } finally {
       
   149             Executor def = defaultClient().executor();
       
   150             if (def instanceof ExecutorService) {
       
   151                ((ExecutorService)def).shutdownNow();
       
   152             }
       
   153         }
       
   154     }
       
   155 
       
   156     static void success(URI uri, HttpRequest.BodyProcessor processor)
       
   157         throws Exception
       
   158     {
       
   159         CompletableFuture<HttpResponse<Void>> cf;
       
   160         HttpRequest request = HttpRequest.newBuilder(uri)
       
   161                                          .POST(processor)
       
   162                                          .build();
       
   163         cf = defaultClient().sendAsync(request, discard(null));
       
   164 
       
   165         HttpResponse<Void> resp = cf.get(30, TimeUnit.SECONDS);
       
   166         out.println("Response code: " + resp.statusCode());
       
   167         check(resp.statusCode() == 200, "Expected 200, got ", resp.statusCode());
       
   168     }
       
   169 
       
   170     static void failureNonBlocking(URI uri, HttpRequest.BodyProcessor processor)
       
   171         throws Exception
       
   172     {
       
   173         CompletableFuture<HttpResponse<Void>> cf;
       
   174         HttpRequest request = HttpRequest.newBuilder(uri)
       
   175                                          .POST(processor)
       
   176                                          .build();
       
   177         cf = defaultClient().sendAsync(request, discard(null));
       
   178 
       
   179         try {
       
   180             HttpResponse<Void> r = cf.get(30, TimeUnit.SECONDS);
       
   181             throw new RuntimeException("Unexpected response: " + r.statusCode());
       
   182         } catch (TimeoutException x) {
       
   183             throw new RuntimeException("Unexpected timeout", x);
       
   184         } catch (ExecutionException expected) {
       
   185             out.println("Caught expected: " + expected);
       
   186             check(expected.getCause() instanceof IOException,
       
   187                   "Expected cause IOException, but got: ", expected.getCause());
       
   188         }
       
   189     }
       
   190 
       
   191     static void failureBlocking(URI uri, HttpRequest.BodyProcessor processor)
       
   192         throws Exception
       
   193     {
       
   194         HttpRequest request = HttpRequest.newBuilder(uri)
       
   195                                          .POST(processor)
       
   196                                          .build();
       
   197         try {
       
   198             HttpResponse<Void> r = defaultClient().send(request, discard(null));
       
   199             throw new RuntimeException("Unexpected response: " + r.statusCode());
       
   200         } catch (IOException expected) {
       
   201             out.println("Caught expected: " + expected);
       
   202         }
       
   203     }
       
   204 
       
   205     static class Server extends Thread implements AutoCloseable {
       
   206 
       
   207         static String RESPONSE = "HTTP/1.1 200 OK\r\n" +
       
   208                                  "Connection: close\r\n"+
       
   209                                  "Content-length: 0\r\n\r\n";
       
   210 
       
   211         private final ServerSocket ss;
       
   212         private volatile boolean closed;
       
   213 
       
   214         Server() throws IOException {
       
   215             super("Test-Server");
       
   216             ss = new ServerSocket(0); this.start();
       
   217         }
       
   218 
       
   219         int getPort() { return ss.getLocalPort(); }
       
   220 
       
   221         @Override
       
   222         public void run() {
       
   223             int count = 0;
       
   224             int offset = 0;
       
   225 
       
   226             while (!closed) {
       
   227                 try (Socket s = ss.accept()) {
       
   228                     InputStream is = s.getInputStream();
       
   229                     readRequestHeaders(is);
       
   230                     byte[] ba = new byte[1024];
       
   231 
       
   232                     int length = BODY_LENGTHS[count % 3];
       
   233                     length += BODY_OFFSETS[offset];
       
   234 
       
   235                     is.readNBytes(ba, 0, length);
       
   236 
       
   237                     OutputStream os = s.getOutputStream();
       
   238                     os.write(RESPONSE.getBytes(US_ASCII));
       
   239                     count++;
       
   240                     if (count % 6 == 0) // 6 is the number of failure requests per offset
       
   241                         offset++;
       
   242                 } catch (IOException e) {
       
   243                     if (!closed)
       
   244                         System.out.println("Unexpected" + e);
       
   245                 }
       
   246             }
       
   247         }
       
   248 
       
   249         @Override
       
   250         public void close() {
       
   251             if (closed)
       
   252                 return;
       
   253             closed = true;
       
   254             try {
       
   255                 ss.close();
       
   256             } catch (IOException e) {
       
   257                 throw new UncheckedIOException("Unexpected", e);
       
   258             }
       
   259         }
       
   260     }
       
   261 
       
   262     static final byte[] requestEnd = new byte[] {'\r', '\n', '\r', '\n' };
       
   263 
       
   264     // Read until the end of a HTTP request headers
       
   265     static void readRequestHeaders(InputStream is) throws IOException {
       
   266         int requestEndCount = 0, r;
       
   267         while ((r = is.read()) != -1) {
       
   268             if (r == requestEnd[requestEndCount]) {
       
   269                 requestEndCount++;
       
   270                 if (requestEndCount == 4) {
       
   271                     break;
       
   272                 }
       
   273             } else {
       
   274                 requestEndCount = 0;
       
   275             }
       
   276         }
       
   277     }
       
   278 
       
   279     static int fileSize(Path p) {
       
   280         try { return (int) Files.size(p); }
       
   281         catch (IOException x) { throw new UncheckedIOException(x); }
       
   282     }
       
   283 
       
   284     static boolean check(boolean cond, Object... failedArgs) {
       
   285         if (cond)
    55             return true;
   286             return true;
    56         }
   287         // We are going to fail...
    57     }
   288         StringBuilder sb = new StringBuilder();
    58 
   289         for (Object o : failedArgs)
    59     static void close(Closeable c) {
   290                 sb.append(o);
    60         try {
   291         throw new RuntimeException(sb.toString());
    61             if (c == null)
       
    62                 return;
       
    63             c.close();
       
    64         } catch (IOException e) {}
       
    65     }
       
    66 
       
    67     public static void main(String[] args) throws Exception {
       
    68         ServerSocket server = new ServerSocket(0);
       
    69         int port = server.getLocalPort();
       
    70         URI uri = new URI("http://127.0.0.1:" + port + "/");
       
    71 
       
    72         HttpRequest request;
       
    73         HttpResponse r;
       
    74         Socket s = null;
       
    75         CompletableFuture<HttpResponse> cf1;
       
    76         try {
       
    77             cf1 = HttpRequest.create(uri)
       
    78                     .body(new RequestBody())
       
    79                     .GET()
       
    80                     .responseAsync();
       
    81 
       
    82             s = server.accept();
       
    83             s.getInputStream().readAllBytes();
       
    84             try (OutputStream os = s.getOutputStream()) {
       
    85                 os.write(response.getBytes());
       
    86             } catch (IOException ee) {
       
    87             }
       
    88 
       
    89             try {
       
    90                 r = cf1.get(3, TimeUnit.SECONDS);
       
    91                 throw new RuntimeException("Failed");
       
    92             } catch (TimeoutException e0) {
       
    93                 throw new RuntimeException("Failed timeout");
       
    94             } catch (ExecutionException e) {
       
    95                 System.err.println("OK");
       
    96             }
       
    97         } finally {
       
    98             HttpClient.getDefault().executorService().shutdownNow();
       
    99             close(s);
       
   100             close(server);
       
   101         }
       
   102     }
   292     }
   103 }
   293 }