/* * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /** * @test * @bug 8314978 * @summary Multiple server call from connection failing with expect100 in * getOutputStream * @library /test/lib * @run junit/othervm HttpURLConnectionExpect100Test */ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.URL; import java.net.HttpURLConnection; import jdk.test.lib.net.URIBuilder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import static org.junit.jupiter.api.Assertions.assertEquals; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class HttpURLConnectionExpect100Test { private HttpServer server; private int port; static final String RESPONSE = "This is default response."; @BeforeAll void setup() throws Exception { server = HttpServer.create(); port = server.getPort(); } @AfterAll void teardown() throws Exception { server.close(); } @Test public void expect100ContinueHitCountTest() throws Exception { server.resetHitCount(); URL url = URIBuilder.newBuilder() .scheme("http") .loopback() .port(port) .toURL(); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("PUT"); //send expect continue conn.setRequestProperty("Expect", "100-continue"); sendRequest(conn); getHeaderField(conn); // Server rejects the expect 100-continue request with 417 response assertEquals(417, conn.getResponseCode()); assertEquals(1, server.getServerHitCount()); } @Test public void defaultRequestHitCountTest() throws Exception { server.resetHitCount(); URL url = URIBuilder.newBuilder() .scheme("http") .loopback() .port(port) .toURL(); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("PUT"); sendRequest(conn); getHeaderField(conn); assertEquals(200, conn.getResponseCode()); try ( InputStream in = conn.getInputStream()) { byte[] data = in.readAllBytes(); assertEquals(RESPONSE.length(), data.length); } assertEquals(1, server.getServerHitCount()); } private void sendRequest(final HttpURLConnection conn) throws Exception { conn.setDoOutput(true); conn.setFixedLengthStreamingMode(10); byte[] payload = new byte[10]; try ( OutputStream os = conn.getOutputStream()) { os.write(payload); os.flush(); } catch (IOException e) { // intentional, server will reject the expect 100 System.err.println("Got expected exception: " + e); } } private void getHeaderField(final HttpURLConnection conn) { // Call getHeaderFiels in loop, this should not hit server. for (int i = 0; i < 5; i++) { System.out.println("Getting: field" + i); conn.getHeaderField("field" + i); } } static class HttpServer extends Thread { private final ServerSocket ss; private static HttpServer inst; private volatile int hitCount; private volatile boolean isRunning; private final int port; private HttpServer() throws IOException { InetAddress loopback = InetAddress.getLoopbackAddress(); ss = new ServerSocket(); ss.bind(new InetSocketAddress(loopback, 0)); port = ss.getLocalPort(); isRunning = true; } static HttpServer create() throws IOException { if (inst != null) { return inst; } else { inst = new HttpServer(); inst.setDaemon(true); inst.start(); return inst; } } int getServerHitCount() { return hitCount; } void resetHitCount() { hitCount = 0; } int getPort() { return port; } void close() { isRunning = false; if (ss != null && !ss.isClosed()) { try { ss.close(); } catch (IOException ex) { } } } @Override public void run() { Socket client; try (ss) { while (isRunning) { client = ss.accept(); System.out.println(client.getRemoteSocketAddress().toString()); hitCount++; handleConnection(client); } } catch (IOException ex) { // throw exception only if isRunning is true if (isRunning) { throw new RuntimeException(ex); } } } private void handleConnection(Socket client) throws IOException { try (client; BufferedReader in = new BufferedReader( new InputStreamReader(client.getInputStream())); PrintStream out = new PrintStream(client.getOutputStream())) { handle_connection(in, out); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } private void handle_connection(BufferedReader in, PrintStream out) throws IOException, InterruptedException { StringBuilder clientRequest = new StringBuilder(); String line = null; do { line = in.readLine(); clientRequest.append(line); } while (line != null && line.length() != 0); if (clientRequest.toString().contains("100-continue")) { rejectExpect100Continue(out); } else { defaultResponse(out); } // wait until the client closes the socket while (line != null) { line = in.readLine(); } } private void rejectExpect100Continue(PrintStream out) { out.print("HTTP/1.1 417 Expectation Failed\r\n"); out.print("Server: Test-Server\r\n"); out.print("Connection: close\r\n"); out.print("Content-Length: 0\r\n"); out.print("\r\n"); out.flush(); } private void defaultResponse(PrintStream out) { // send the 200 OK out.print("HTTP/1.1 200 OK\r\n"); out.print("Server: Test-Server\r\n"); out.print("Connection: close\r\n"); out.print("Content-Length: " + RESPONSE.length() + "\r\n\r\n"); out.print(RESPONSE); out.flush(); } } }