undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
277
test/jdk/com/sun/net/httpserver/bugs/6725892/Test.java
Normal file
277
test/jdk/com/sun/net/httpserver/bugs/6725892/Test.java
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2022, 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 6725892
|
||||
* @library /test/lib
|
||||
* @run main/othervm -Dsun.net.httpserver.maxReqTime=2 -Dsun.net.httpserver.clockTick=2000 Test
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true -Dsun.net.httpserver.maxReqTime=2 -Dsun.net.httpserver.clockTick=2000 Test
|
||||
* @summary
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import javax.net.ssl.*;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
public class Test {
|
||||
|
||||
static HttpServer s1;
|
||||
static int port;
|
||||
static URL url;
|
||||
static final String RESPONSE_BODY = "response";
|
||||
static boolean failed = false;
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
|
||||
public void handle(HttpExchange t)
|
||||
throws IOException
|
||||
{
|
||||
InputStream is = t.getRequestBody();
|
||||
InetSocketAddress rem = t.getRemoteAddress();
|
||||
System.out.println("Request from: " + rem);
|
||||
while (is.read () != -1) ;
|
||||
is.close();
|
||||
String requrl = t.getRequestURI().toString();
|
||||
OutputStream os = t.getResponseBody();
|
||||
t.sendResponseHeaders(200, RESPONSE_BODY.length());
|
||||
os.write(RESPONSE_BODY.getBytes());
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
ExecutorService exec = Executors.newCachedThreadPool();
|
||||
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
try {
|
||||
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
|
||||
s1 = HttpServer.create(addr, 100);
|
||||
HttpHandler h = new Handler();
|
||||
HttpContext c1 = s1.createContext("/", h);
|
||||
s1.setExecutor(exec);
|
||||
s1.start();
|
||||
|
||||
port = s1.getAddress().getPort();
|
||||
System.out.println("Server on port " + port);
|
||||
url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.path("/foo")
|
||||
.toURLUnchecked();
|
||||
System.out.println("URL: " + url);
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
Thread.sleep(2000);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("FAIL");
|
||||
throw new RuntimeException();
|
||||
} finally {
|
||||
s1.stop(0);
|
||||
System.out.println("After Shutdown");
|
||||
exec.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
// open TCP connection without sending anything. Check server closes it.
|
||||
|
||||
static void test1() throws IOException {
|
||||
failed = false;
|
||||
Socket s = new Socket(InetAddress.getLoopbackAddress(), port);
|
||||
InputStream is = s.getInputStream();
|
||||
// server should close connection after 2 seconds. We wait up to 10
|
||||
s.setSoTimeout(10000);
|
||||
try {
|
||||
is.read();
|
||||
} catch (SocketTimeoutException e) {
|
||||
failed = true;
|
||||
}
|
||||
s.close();
|
||||
if (failed) {
|
||||
System.out.println("test1: FAIL");
|
||||
throw new RuntimeException();
|
||||
} else {
|
||||
System.out.println("test1: OK");
|
||||
}
|
||||
}
|
||||
|
||||
// send request and don't read response. Check server closes connection
|
||||
|
||||
static void test2() throws IOException {
|
||||
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
|
||||
urlc.setReadTimeout(20 * 1000);
|
||||
InputStream is = urlc.getInputStream();
|
||||
// we won't read response and check if it times out
|
||||
// on server. If it timesout at client then there is a problem
|
||||
try {
|
||||
Thread.sleep(10 * 1000);
|
||||
while (is.read() != -1) ;
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println(e);
|
||||
System.out.println("test2: FAIL");
|
||||
throw new RuntimeException("unexpected error");
|
||||
} catch (SocketTimeoutException e1) {
|
||||
System.out.println(e1);
|
||||
System.out.println("test2: FAIL");
|
||||
throw new RuntimeException("client timedout");
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
System.out.println("test2: OK");
|
||||
}
|
||||
|
||||
// same as test2, but repeated with multiple connections
|
||||
// including a number of valid request/responses
|
||||
|
||||
// Worker: a thread opens a connection to the server in one of three modes.
|
||||
// NORMAL - sends a request, waits for response, and checks valid response
|
||||
// REQUEST - sends a partial request, and blocks, to see if
|
||||
// server closes the connection.
|
||||
// RESPONSE - sends a request, partially reads response and blocks,
|
||||
// to see if server closes the connection.
|
||||
|
||||
static class Worker extends Thread {
|
||||
CountDownLatch latch;
|
||||
Mode mode;
|
||||
|
||||
enum Mode {
|
||||
REQUEST, // block during sending of request
|
||||
RESPONSE, // block during reading of response
|
||||
NORMAL // don't block
|
||||
};
|
||||
|
||||
Worker (CountDownLatch latch, Mode mode) {
|
||||
this.latch = latch;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
void fail(String msg) {
|
||||
System.out.println(msg);
|
||||
failed = true;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
HttpURLConnection urlc;
|
||||
InputStream is = null;
|
||||
|
||||
try {
|
||||
urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
|
||||
urlc.setReadTimeout(20 * 1000);
|
||||
urlc.setDoOutput(true);
|
||||
} catch (IOException e) {
|
||||
fail("Worker: failed to connect to server");
|
||||
latch.countDown();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
OutputStream os = urlc.getOutputStream();
|
||||
os.write("foo".getBytes());
|
||||
if (mode == Mode.REQUEST) {
|
||||
Thread.sleep(3000);
|
||||
}
|
||||
os.close();
|
||||
is = urlc.getInputStream();
|
||||
if (mode == Mode.RESPONSE) {
|
||||
Thread.sleep(3000);
|
||||
}
|
||||
if (!checkResponse(is, RESPONSE_BODY)) {
|
||||
fail("Worker: response");
|
||||
}
|
||||
is.close();
|
||||
return;
|
||||
} catch (InterruptedException e0) {
|
||||
fail("Worker: timedout");
|
||||
} catch (SocketTimeoutException e1) {
|
||||
fail("Worker: timedout");
|
||||
} catch (IOException e2) {
|
||||
switch (mode) {
|
||||
case NORMAL:
|
||||
fail("Worker: " + e2.getMessage());
|
||||
break;
|
||||
case RESPONSE:
|
||||
if (is == null) {
|
||||
fail("Worker: " + e2.getMessage());
|
||||
break;
|
||||
}
|
||||
// default: is ok
|
||||
}
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final int NUM = 20;
|
||||
|
||||
static void test3() throws Exception {
|
||||
failed = false;
|
||||
CountDownLatch l = new CountDownLatch(NUM*3);
|
||||
Worker[] workers = new Worker[NUM*3];
|
||||
for (int i=0; i<NUM; i++) {
|
||||
workers[i*3] = new Worker(l, Worker.Mode.NORMAL);
|
||||
workers[i*3+1] = new Worker(l, Worker.Mode.REQUEST);
|
||||
workers[i*3+2] = new Worker(l, Worker.Mode.RESPONSE);
|
||||
workers[i*3].start();
|
||||
workers[i*3+1].start();
|
||||
workers[i*3+2].start();
|
||||
}
|
||||
l.await();
|
||||
for (int i=0; i<NUM*3; i++) {
|
||||
workers[i].join();
|
||||
}
|
||||
if (failed) {
|
||||
throw new RuntimeException("test3: failed");
|
||||
}
|
||||
System.out.println("test3: OK");
|
||||
}
|
||||
|
||||
static boolean checkResponse(InputStream is, String resp) {
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
byte[] buf = new byte[64];
|
||||
int c;
|
||||
while ((c=is.read(buf)) != -1) {
|
||||
bos.write(buf, 0, c);
|
||||
}
|
||||
bos.close();
|
||||
if (!bos.toString().equals(resp)) {
|
||||
System.out.println("Wrong response: " + bos.toString());
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println(e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2025, 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 8199849
|
||||
* @library /test/lib
|
||||
* @run main/othervm/timeout=6000 BasicAuthenticatorCharset
|
||||
* @summary Check for correct use of character sets with BasicAuthenticator() authentication
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.*;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.nio.charset.StandardCharsets.ISO_8859_1;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
/**
|
||||
* Test authentication
|
||||
*/
|
||||
|
||||
public class BasicAuthenticatorCharset {
|
||||
|
||||
public static volatile int failCount = 0;
|
||||
|
||||
static final String TEST_USER = "test";
|
||||
static final String UNICODE_PW = "Selam D\u00fcnya. Ho\u015f\u00e7akal D\u00fcnya";
|
||||
|
||||
static Handler testHandler;
|
||||
static HttpServer testHttpServer;
|
||||
static java.net.Authenticator clientAuth;
|
||||
static HttpClient client;
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
InputStream is = t.getRequestBody();
|
||||
while (is.read() != -1) ;
|
||||
is.close();
|
||||
t.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
|
||||
static class ClientAuthenticator extends java.net.Authenticator {
|
||||
public PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(TEST_USER, UNICODE_PW.toCharArray());
|
||||
}
|
||||
}
|
||||
|
||||
static void setAuthenticationPW(String path, String realm, String testPW, Charset charset) {
|
||||
HttpContext ctx = testHttpServer.createContext(path, testHandler);
|
||||
BasicAuthenticator auth;
|
||||
if (charset != null) {
|
||||
auth = new BasicAuthenticator(realm, charset) {
|
||||
public boolean checkCredentials(String username, String pw) {
|
||||
return username.equals(TEST_USER) && pw.equals(testPW);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
auth = new BasicAuthenticator(realm) {
|
||||
public boolean checkCredentials(String username, String pw) {
|
||||
return username.equals(TEST_USER) && pw.equals(testPW);
|
||||
}
|
||||
};
|
||||
}
|
||||
ctx.setAuthenticator(auth);
|
||||
}
|
||||
|
||||
static void connectAndAuth(String path, int expectedStatus) throws Exception {
|
||||
// path is prepended with /old or /new for old and new http client
|
||||
URL oldurl = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(testHttpServer.getAddress().getPort())
|
||||
.path("/old" + path)
|
||||
.toURL();
|
||||
|
||||
URI newuri = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(testHttpServer.getAddress().getPort())
|
||||
.path("/new" + path)
|
||||
.build();
|
||||
|
||||
// check old client
|
||||
|
||||
HttpURLConnection testConnection = (HttpURLConnection) oldurl.openConnection(Proxy.NO_PROXY);
|
||||
|
||||
// Check for successful authentication
|
||||
int status = testConnection.getResponseCode();
|
||||
if (status != 401) {
|
||||
InputStream is = testConnection.getInputStream();
|
||||
while (is.read() != -1) ;
|
||||
is.close();
|
||||
}
|
||||
if (status != expectedStatus) {
|
||||
System.err.println("Error (old): " + path);
|
||||
failCount++;
|
||||
}
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(newuri)
|
||||
.GET()
|
||||
.build();
|
||||
|
||||
status = -1;
|
||||
try {
|
||||
HttpResponse<Void> response = client.send(request, HttpResponse.BodyHandlers.discarding());
|
||||
status = response.statusCode();
|
||||
} catch (IOException e) {
|
||||
System.out.println("NEW: " + e);
|
||||
status = 401; // limitation in new API.
|
||||
}
|
||||
if (status != expectedStatus) {
|
||||
System.err.println("Error (new): " + path);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
clientAuth = new ClientAuthenticator();
|
||||
client = HttpClient.newBuilder()
|
||||
.authenticator(clientAuth)
|
||||
.build();
|
||||
|
||||
String defaultCharset = System.getProperty("file.encoding");
|
||||
boolean isUTF8 = defaultCharset.equalsIgnoreCase("UTF-8");
|
||||
testHandler = new Handler();
|
||||
InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
|
||||
testHttpServer = HttpServer.create(addr, 0);
|
||||
|
||||
// Set the passing credentials OLD client
|
||||
setAuthenticationPW("/old/test1/", "passingCharset@test.realm", UNICODE_PW, UTF_8);
|
||||
setAuthenticationPW("/old/test2/", "failingCharset@test.realm", UNICODE_PW, ISO_8859_1);
|
||||
setAuthenticationPW("/old/test3/", "defaultCharset@test.realm", UNICODE_PW, null);
|
||||
|
||||
// Set the passing credentials NEW client
|
||||
setAuthenticationPW("/new/test1/", "passingCharset@test.realm", UNICODE_PW, UTF_8);
|
||||
setAuthenticationPW("/new/test2/", "failingCharset@test.realm", UNICODE_PW, ISO_8859_1);
|
||||
setAuthenticationPW("/new/test3/", "defaultCharset@test.realm", UNICODE_PW, null);
|
||||
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
testHttpServer.setExecutor(executor);
|
||||
testHttpServer.start();
|
||||
java.net.Authenticator.setDefault(clientAuth);
|
||||
|
||||
connectAndAuth("/test1/passingCharset.html", 200);
|
||||
connectAndAuth("/test2/failingCharset.html", 401);
|
||||
if (isUTF8) {
|
||||
connectAndAuth("/test3/defaultCharset.html", 200);
|
||||
}
|
||||
|
||||
testHttpServer.stop(0);
|
||||
executor.shutdown();
|
||||
|
||||
// should fail once with UNICODE_PW and unsupporting character set
|
||||
if (failCount > 0)
|
||||
throw new RuntimeException("Fail count : " + failCount);
|
||||
}
|
||||
}
|
||||
230
test/jdk/com/sun/net/httpserver/bugs/8199849/ParamTest.java
Normal file
230
test/jdk/com/sun/net/httpserver/bugs/8199849/ParamTest.java
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8199849 8235976
|
||||
* @summary
|
||||
* @library /test/lib
|
||||
* @run main/othervm ParamTest
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true ParamTest
|
||||
*/
|
||||
|
||||
public class ParamTest {
|
||||
|
||||
static final String[] variants = {
|
||||
" ,charset=utf-8",
|
||||
" ,charset=UtF-8",
|
||||
" ,charset=\"utF-8\"",
|
||||
" ,charset=\"UtF-8\""
|
||||
};
|
||||
|
||||
static final int LOOPS = variants.length;
|
||||
|
||||
volatile static boolean error = false;
|
||||
|
||||
static class BasicServer extends Thread {
|
||||
|
||||
final ServerSocket server;
|
||||
|
||||
Socket s;
|
||||
InputStream is;
|
||||
OutputStream os;
|
||||
|
||||
static final String realm = "wallyworld";
|
||||
|
||||
String reply1 = "HTTP/1.1 401 Unauthorized\r\n"+
|
||||
"WWW-Authenticate: Basic realm=\""+realm+"\"\r\n";
|
||||
|
||||
String reply2 = "HTTP/1.1 200 OK\r\n"+
|
||||
"Date: Mon, 15 Jan 2001 12:18:21 GMT\r\n" +
|
||||
"Server: Apache/1.3.14 (Unix)\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Content-Length: 10\r\n\r\n";
|
||||
|
||||
BasicServer(ServerSocket s) {
|
||||
server = s;
|
||||
}
|
||||
|
||||
String readHeaders(Socket sock) throws IOException {
|
||||
InputStream is = sock.getInputStream();
|
||||
String s = "";
|
||||
byte[] buf = new byte[1024];
|
||||
while (!s.endsWith("\r\n\r\n")) {
|
||||
int c = is.read(buf);
|
||||
if (c == -1)
|
||||
return s;
|
||||
String f = new String(buf, 0, c, StandardCharsets.ISO_8859_1);
|
||||
s = s + f;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void check(String s, int iteration) {
|
||||
if (s.indexOf(encodedAuthString) == -1) {
|
||||
System.err.printf("On iteration %d, wrong auth string received %s\n", iteration, s);
|
||||
error = true;
|
||||
} else {
|
||||
System.err.println("check: correct auth string received");
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
for (int j = 0; j < 2; j++)
|
||||
for (int i = 0; i < LOOPS; i++) {
|
||||
System.out.println("Server 1: accept");
|
||||
s = server.accept();
|
||||
readHeaders(s);
|
||||
System.out.println("accepted");
|
||||
os = s.getOutputStream();
|
||||
String str = reply1 + variants[i] + "\r\n\r\n";
|
||||
os.write(str.getBytes());
|
||||
|
||||
System.out.println("Server 2: accept");
|
||||
Socket s1 = server.accept();
|
||||
String request = readHeaders(s1);
|
||||
check(request, i);
|
||||
System.out.println("accepted");
|
||||
os = s1.getOutputStream();
|
||||
os.write((reply2 + "HelloWorld").getBytes());
|
||||
os.flush();
|
||||
s.close();
|
||||
s1.close();
|
||||
finished();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void finished() {
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final String password = "Selam D\u00fcnya.";
|
||||
|
||||
// "user : <password above>" encoded in UTF-8 and converted to Base 64
|
||||
|
||||
static final String encodedAuthString = "dXNlcjpTZWxhbSBEw7xueWEu";
|
||||
|
||||
static class MyAuthenticator extends Authenticator {
|
||||
MyAuthenticator() {
|
||||
super();
|
||||
}
|
||||
|
||||
public PasswordAuthentication getPasswordAuthentication()
|
||||
{
|
||||
System.out.println("Auth called");
|
||||
return (new PasswordAuthentication ("user", password.toCharArray()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void read(InputStream is) throws IOException {
|
||||
int c;
|
||||
System.out.println("reading");
|
||||
while ((c=is.read()) != -1) {
|
||||
System.out.write(c);
|
||||
}
|
||||
System.out.println("");
|
||||
System.out.println("finished reading");
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
MyAuthenticator auth = new MyAuthenticator();
|
||||
Authenticator.setDefault(auth);
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
ServerSocket ss = new ServerSocket();
|
||||
ss.bind(new InetSocketAddress(loopback, 0));
|
||||
int port = ss.getLocalPort();
|
||||
BasicServer server = new BasicServer(ss);
|
||||
synchronized (server) {
|
||||
server.start();
|
||||
System.out.println("client 1");
|
||||
String base = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.path("/")
|
||||
.build()
|
||||
.toString();
|
||||
URL url = new URL(base + "d1/d2/d3/foo.html");
|
||||
|
||||
for (int i = 0; i < LOOPS; i++) {
|
||||
URLConnection urlc = url.openConnection(Proxy.NO_PROXY);
|
||||
InputStream is = urlc.getInputStream();
|
||||
read(is);
|
||||
System.out.println("Client: waiting for notify");
|
||||
server.wait();
|
||||
System.out.println("Client: continue");
|
||||
// check if authenticator was called once (ok) or twice (not)
|
||||
if (error) {
|
||||
System.err.println("Error old client iteration " + i);
|
||||
}
|
||||
}
|
||||
|
||||
URI uri = url.toURI();
|
||||
HttpClient client = HttpClient.newBuilder()
|
||||
.authenticator(auth)
|
||||
.proxy(ProxySelector.of(null))
|
||||
.build();
|
||||
|
||||
HttpRequest request = HttpRequest
|
||||
.newBuilder(uri)
|
||||
.GET()
|
||||
.build();
|
||||
|
||||
for (int i = 0; i < LOOPS; i++) {
|
||||
HttpResponse<Void> response = client.send(request, HttpResponse.BodyHandlers.discarding());
|
||||
int status = response.statusCode();
|
||||
if (status != 200) {
|
||||
System.err.printf("Error new client (%d) iteration ",
|
||||
status, i);
|
||||
error = true;
|
||||
} else
|
||||
System.err.println("New client ok iteration " + i);
|
||||
System.out.println("New Client: waiting for notify");
|
||||
server.wait();
|
||||
System.out.println("New Client: continue");
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw new RuntimeException("Test failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2025, 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 8199849
|
||||
* @library /test/lib
|
||||
* @summary Checks that unicode bytes are being handled correctly
|
||||
* @run main/othervm -Dfile.encoding=UTF_8 TestHttpUnicode
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.*;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
public class TestHttpUnicode {
|
||||
|
||||
private static final String TEST_USER = "Selam D\u00fcnya. Ho\u015f\u00e7akal D\u00fcnya";
|
||||
private static final String TEST_PW = "Selam D\u00fcnya. Ho\u015f\u00e7akal D\u00fcnya";
|
||||
|
||||
static class ClientAuthenticator extends java.net.Authenticator {
|
||||
public PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(TEST_USER, TEST_PW.toCharArray());
|
||||
}
|
||||
}
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
InputStream is = t.getRequestBody();
|
||||
while (is.read() != -1) ;
|
||||
is.close();
|
||||
|
||||
HttpPrincipal p = t.getPrincipal();
|
||||
if (p.getUsername().equals(TEST_USER)) {
|
||||
t.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
}
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
HttpServer testHttpServer = null;
|
||||
try {
|
||||
InetSocketAddress loopbackAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
|
||||
testHttpServer = HttpServer.create(loopbackAddress, 0);
|
||||
HttpContext context = testHttpServer.createContext("/test", new Handler());
|
||||
System.setProperty("http.maxRedirects", "3");
|
||||
|
||||
BasicAuthenticator serverAuthenticator = new BasicAuthenticator("authCharacterSet@test.realm") {
|
||||
public boolean checkCredentials(String username, String pw) {
|
||||
return username.equals(TEST_USER) && pw.equals(TEST_PW);
|
||||
}
|
||||
};
|
||||
context.setAuthenticator(serverAuthenticator);
|
||||
java.net.Authenticator.setDefault(new ClientAuthenticator());
|
||||
|
||||
testHttpServer.start();
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(testHttpServer.getAddress().getPort())
|
||||
.path("/test/authCharacterSet.html")
|
||||
.toURL();
|
||||
HttpURLConnection testConnection = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
|
||||
|
||||
// Authenication CHECK
|
||||
if (testConnection.getResponseCode() == 401) {
|
||||
throw new RuntimeException("Test Authentication failed with HTTP Status 401.");
|
||||
}
|
||||
|
||||
InputStream is = testConnection.getInputStream();
|
||||
while (is.read() != -1) ;
|
||||
} finally {
|
||||
testHttpServer.stop(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 2025, 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 8300268
|
||||
* @library /test/lib
|
||||
* @modules jdk.httpserver/sun.net.httpserver
|
||||
* @build jdk.httpserver/sun.net.httpserver.HttpServerAccess MaxIdleConnectionsTest
|
||||
* @run junit/othervm -Dsun.net.httpserver.maxIdleConnections=4 MaxIdleConnectionsTest
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import sun.net.httpserver.HttpServerAccess;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class MaxIdleConnectionsTest {
|
||||
|
||||
HttpServer server;
|
||||
int maxIdleConnections, totalConnections;
|
||||
CountDownLatch reqFinishedProcessing;
|
||||
|
||||
@BeforeAll
|
||||
void before() throws Exception {
|
||||
maxIdleConnections = Integer.getInteger("sun.net.httpserver.maxIdleConnections");
|
||||
totalConnections = maxIdleConnections + 1;
|
||||
reqFinishedProcessing = new CountDownLatch(totalConnections);
|
||||
server = startServer(reqFinishedProcessing);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
void after() throws Exception {
|
||||
server.stop(0);
|
||||
}
|
||||
|
||||
// Issue one too many requests and assert that the idle connection pool doesn't
|
||||
// exceed maxIdleConnections
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
final int port = server.getAddress().getPort();
|
||||
|
||||
final List<Future<Void>> responses = new ArrayList<>();
|
||||
try (final ExecutorService requestIssuer = Executors.newFixedThreadPool(totalConnections)) {
|
||||
for (int i = 1; i <= totalConnections; i++) {
|
||||
URL requestURL = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.path("/MaxIdleConnectionTest/" + i)
|
||||
.toURL();
|
||||
final Future<Void> result = requestIssuer.submit(() -> {
|
||||
System.out.println("Issuing request " + requestURL);
|
||||
final URLConnection conn = requestURL.openConnection();
|
||||
try (final InputStream is = conn.getInputStream()) {
|
||||
is.readAllBytes();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
responses.add(result);
|
||||
}
|
||||
// wait for all the requests to reach each of the handlers
|
||||
System.out.println("Waiting for all " + totalConnections + " requests to reach" +
|
||||
" the server side request handler");
|
||||
reqFinishedProcessing.await();
|
||||
}
|
||||
|
||||
// verify every request got served before checking idle count
|
||||
for (int i = 0; i < totalConnections; i++) {
|
||||
responses.get(i).get();
|
||||
System.out.println("Received successful response for request " + i);
|
||||
}
|
||||
|
||||
// assert that the limit set by maxIdleConnections was not exceeded
|
||||
int idleConnectionCount = HttpServerAccess.getIdleConnectionCount(server);
|
||||
System.out.println("count " + idleConnectionCount);
|
||||
assertTrue(maxIdleConnections >= idleConnectionCount,
|
||||
String.format("Too many idle connections: %d, limit: %d", idleConnectionCount, maxIdleConnections));
|
||||
}
|
||||
|
||||
// Create HttpServer that will handle requests with multiple threads
|
||||
private static HttpServer startServer(final CountDownLatch reqFinishedProcessing) throws IOException {
|
||||
final var bindAddr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
|
||||
final HttpServer server = HttpServer.create(bindAddr, 0);
|
||||
|
||||
final AtomicInteger threadId = new AtomicInteger();
|
||||
server.setExecutor(Executors.newCachedThreadPool(r -> {
|
||||
final Thread t = new Thread(r);
|
||||
t.setName("http-request-handler-" + threadId.incrementAndGet());
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}));
|
||||
|
||||
server.createContext("/MaxIdleConnectionTest/", (exchange) -> {
|
||||
System.out.println("Request " + exchange.getRequestURI() + " received");
|
||||
System.out.println("Sending response for request " + exchange.getRequestURI() + " from " + exchange.getRemoteAddress());
|
||||
reqFinishedProcessing.countDown();
|
||||
exchange.sendResponseHeaders(200, RSPBODY_CHUNKED);
|
||||
exchange.getResponseBody().close();
|
||||
});
|
||||
|
||||
server.start();
|
||||
System.out.println("Server started at address " + server.getAddress());
|
||||
return server;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 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.
|
||||
*/
|
||||
|
||||
package sun.net.httpserver;
|
||||
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Set;
|
||||
|
||||
public class HttpServerAccess {
|
||||
|
||||
// Given a HttpServer object get the number of idleConnections it currently has
|
||||
public static int getIdleConnectionCount(HttpServer server) throws Exception{
|
||||
// Use reflection to get server object which is HTTPServerImpl
|
||||
Field serverField = server.getClass().getDeclaredField("server");
|
||||
serverField.setAccessible(true);
|
||||
|
||||
// Get the actual serverImpl class, then get the IdleConnection Field
|
||||
Object serverImpl = serverField.get(server);
|
||||
Field idleConnectionField = serverImpl.getClass().getDeclaredField("idleConnections");
|
||||
idleConnectionField.setAccessible(true);
|
||||
|
||||
// Finally get the IdleConnection object which is of type Set<HttpConnection>
|
||||
Object idleConnectionSet = idleConnectionField.get(serverImpl);
|
||||
Set<HttpConnection> idleConnectionPool = (Set<HttpConnection>) idleConnectionSet;
|
||||
return idleConnectionPool.size();
|
||||
}
|
||||
}
|
||||
71
test/jdk/com/sun/net/httpserver/bugs/B6339483.java
Normal file
71
test/jdk/com/sun/net/httpserver/bugs/B6339483.java
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 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 6339483
|
||||
* @summary NullPointerException when creating a HttpContext with no handler
|
||||
* @library /test/lib
|
||||
* @run main B6339483
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6339483
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
public class B6339483 {
|
||||
|
||||
public static void main (String[] args) throws Exception {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
|
||||
HttpServer server = HttpServer.create (addr, 0);
|
||||
HttpContext ctx = server.createContext ("/test");
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
server.setExecutor (executor);
|
||||
server.start ();
|
||||
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(server.getAddress().getPort())
|
||||
.path("/test/foo.html")
|
||||
.toURL();
|
||||
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
try {
|
||||
InputStream is = urlc.getInputStream();
|
||||
int c = 0;
|
||||
while (is.read()!= -1) {
|
||||
c ++;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
server.stop(0);
|
||||
executor.shutdown();
|
||||
System.out.println ("OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
109
test/jdk/com/sun/net/httpserver/bugs/B6341616.java
Normal file
109
test/jdk/com/sun/net/httpserver/bugs/B6341616.java
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2025, 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 6341616
|
||||
* @library /test/lib
|
||||
* @run main B6341616
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6341616
|
||||
* @summary Server doesnt send response if there is a RuntimeException in validate of BasicAuthFilter
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
public class B6341616 {
|
||||
|
||||
public static void main (String[] args) throws Exception {
|
||||
Handler handler = new Handler();
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
|
||||
HttpServer server = HttpServer.create (addr, 0);
|
||||
HttpContext ctx = server.createContext ("/test", handler);
|
||||
BasicAuthenticator filter = new BasicAuthenticator ("foobar@test.realm") {
|
||||
public boolean checkCredentials (String username, String pw) {
|
||||
throw new RuntimeException ("");
|
||||
}
|
||||
};
|
||||
|
||||
ctx.setAuthenticator (filter);
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
server.setExecutor (executor);
|
||||
server.start ();
|
||||
java.net.Authenticator.setDefault (new MyAuthenticator());
|
||||
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(server.getAddress().getPort())
|
||||
.path("/test/foo.html")
|
||||
.toURL();
|
||||
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
try {
|
||||
InputStream is = urlc.getInputStream();
|
||||
int c = 0;
|
||||
while (is.read()!= -1) {
|
||||
c ++;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
server.stop(0);
|
||||
executor.shutdown();
|
||||
System.out.println ("OK");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean error = false;
|
||||
|
||||
static class MyAuthenticator extends java.net.Authenticator {
|
||||
public PasswordAuthentication getPasswordAuthentication () {
|
||||
PasswordAuthentication pw;
|
||||
if (!getRequestingPrompt().equals ("foobar@test.realm")) {
|
||||
B6341616.error = true;
|
||||
}
|
||||
pw = new PasswordAuthentication ("fred", "xyz".toCharArray());
|
||||
return pw;
|
||||
}
|
||||
}
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
int invocation = 1;
|
||||
public void handle (HttpExchange t)
|
||||
throws IOException
|
||||
{
|
||||
InputStream is = t.getRequestBody();
|
||||
Headers map = t.getRequestHeaders();
|
||||
Headers rmap = t.getResponseHeaders();
|
||||
while (is.read () != -1) ;
|
||||
is.close();
|
||||
t.sendResponseHeaders (200, RSPBODY_EMPTY);
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
157
test/jdk/com/sun/net/httpserver/bugs/B6361557.java
Normal file
157
test/jdk/com/sun/net/httpserver/bugs/B6361557.java
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2025, 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 6361557
|
||||
* @run main/othervm B6361557
|
||||
* @summary Lightweight HTTP server quickly runs out of file descriptors on Linux
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.net.*;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
/**
|
||||
* The test simply opens 1,000 separate connections
|
||||
* and invokes one http request on each. The client does
|
||||
* not close any sockets until after they are closed
|
||||
* by the server. This verifies the basic ability
|
||||
* of the server to manage a reasonable number of connections
|
||||
*/
|
||||
public class B6361557 {
|
||||
|
||||
public static boolean error = false;
|
||||
static final int NUM = 1000;
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
int invocation = 1;
|
||||
public void handle (HttpExchange t)
|
||||
throws IOException
|
||||
{
|
||||
InputStream is = t.getRequestBody();
|
||||
Headers map = t.getRequestHeaders();
|
||||
Headers rmap = t.getResponseHeaders();
|
||||
while (is.read () != -1) ;
|
||||
is.close();
|
||||
t.sendResponseHeaders (200, RSPBODY_EMPTY);
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
|
||||
final static String request = "GET /test/foo.html HTTP/1.1\r\nContent-length: 0\r\n\r\n";
|
||||
final static ByteBuffer requestBuf = ByteBuffer.wrap(request.getBytes());
|
||||
|
||||
public static void main (String[] args) throws Exception {
|
||||
Handler handler = new Handler();
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
|
||||
HttpServer server = HttpServer.create (addr, 0);
|
||||
HttpContext ctx = server.createContext ("/test", handler);
|
||||
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
server.setExecutor (executor);
|
||||
server.start ();
|
||||
|
||||
InetSocketAddress destaddr = new InetSocketAddress (
|
||||
loopback, server.getAddress().getPort()
|
||||
);
|
||||
System.out.println ("destaddr " + destaddr);
|
||||
|
||||
Selector selector = Selector.open ();
|
||||
int requests = 0;
|
||||
int responses = 0;
|
||||
while (true) {
|
||||
// we need to read responses from time to time: slightly
|
||||
// increase the timeout with the amount of pending responses
|
||||
// to give a chance to the server to reply.
|
||||
int selres = selector.select (requests - responses + 1);
|
||||
Set<SelectionKey> selkeys = selector.selectedKeys();
|
||||
for (SelectionKey key : selkeys) {
|
||||
if (key.isReadable()) {
|
||||
SocketChannel chan = (SocketChannel)key.channel();
|
||||
ByteBuffer buf = (ByteBuffer)key.attachment();
|
||||
try {
|
||||
int x = chan.read(buf);
|
||||
if (x == -1 || responseComplete(buf)) {
|
||||
System.out.print("_");
|
||||
key.attach(null);
|
||||
chan.close();
|
||||
responses++;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (requests < NUM) {
|
||||
System.out.print(".");
|
||||
SocketChannel schan = SocketChannel.open(destaddr);
|
||||
requestBuf.rewind();
|
||||
int c = 0;
|
||||
while (requestBuf.remaining() > 0) {
|
||||
c += schan.write(requestBuf);
|
||||
}
|
||||
schan.configureBlocking(false);
|
||||
schan.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(100));
|
||||
requests++;
|
||||
}
|
||||
if (responses == NUM) {
|
||||
System.out.println ("Finished clients");
|
||||
break;
|
||||
}
|
||||
}
|
||||
server.stop(0);
|
||||
selector.close();
|
||||
executor.shutdown ();
|
||||
|
||||
}
|
||||
|
||||
/* Look for CR LF CR LF */
|
||||
static boolean responseComplete(ByteBuffer buf) {
|
||||
int pos = buf.position();
|
||||
buf.flip();
|
||||
byte[] lookingFor = new byte[] {'\r', '\n', '\r', '\n' };
|
||||
int lookingForCount = 0;
|
||||
while (buf.hasRemaining()) {
|
||||
byte b = buf.get();
|
||||
if (b == lookingFor[lookingForCount]) {
|
||||
lookingForCount++;
|
||||
if (lookingForCount == 4) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
lookingForCount = 0;
|
||||
}
|
||||
}
|
||||
buf.position(pos);
|
||||
buf.limit(buf.capacity());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
193
test/jdk/com/sun/net/httpserver/bugs/B6373555.java
Normal file
193
test/jdk/com/sun/net/httpserver/bugs/B6373555.java
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2019, 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 6373555
|
||||
* @library /test/lib
|
||||
* @summary HTTP Server failing to answer client requests
|
||||
* @run main B6373555
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6373555
|
||||
*/
|
||||
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import java.util.concurrent.*;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
public class B6373555 {
|
||||
|
||||
private static int s_received = 0;
|
||||
private static int sent = 0;
|
||||
|
||||
private static int received = 0;
|
||||
private static int port;
|
||||
|
||||
private static volatile boolean error = false;
|
||||
static HttpServer httpServer;
|
||||
static ExecutorService pool, execs;
|
||||
static int NUM = 1000;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try {
|
||||
if (args.length > 0) {
|
||||
NUM = Integer.parseInt (args[0]);
|
||||
}
|
||||
execs = Executors.newFixedThreadPool(5);
|
||||
httpServer = createHttpServer(execs);
|
||||
port = httpServer.getAddress().getPort();
|
||||
pool = Executors.newFixedThreadPool(10);
|
||||
httpServer.start();
|
||||
for (int i=0; i < NUM; i++) {
|
||||
pool.execute(new Client());
|
||||
if (error) {
|
||||
throw new Exception ("error in test");
|
||||
}
|
||||
}
|
||||
System.out.println("Main thread waiting");
|
||||
pool.shutdown();
|
||||
long latest = System.currentTimeMillis() + 200 * 1000;
|
||||
while (System.currentTimeMillis() < latest) {
|
||||
if (pool.awaitTermination(2000L, TimeUnit.MILLISECONDS)) {
|
||||
System.out.println("Main thread done!");
|
||||
return;
|
||||
}
|
||||
if (error) {
|
||||
throw new Exception ("error in test");
|
||||
}
|
||||
}
|
||||
throw new Exception ("error in test: timed out");
|
||||
} finally {
|
||||
httpServer.stop(0);
|
||||
pool.shutdownNow();
|
||||
execs.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Client implements Runnable {
|
||||
|
||||
byte[] getBuf () {
|
||||
byte[] buf = new byte [5200];
|
||||
for (int i=0; i< 5200; i++) {
|
||||
buf [i] = (byte)i;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
byte[] buf = getBuf();
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.path("/test")
|
||||
.toURLUnchecked();
|
||||
System.out.println("URL: " + url);
|
||||
HttpURLConnection con = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
con.setDoOutput(true);
|
||||
con.setDoInput(true);
|
||||
con.setRequestMethod("POST");
|
||||
con.setRequestProperty(
|
||||
"Content-Type",
|
||||
"Multipart/Related; type=\"application/xop+xml\"; boundary=\"----=_Part_0_6251267.1128549570165\"; start-info=\"text/xml\"");
|
||||
OutputStream out = con.getOutputStream();
|
||||
out.write(buf);
|
||||
out.close();
|
||||
InputStream in = con.getInputStream();
|
||||
byte[] newBuf = readFully(in);
|
||||
in.close();
|
||||
if (buf.length != newBuf.length) {
|
||||
System.out.println("Doesn't match");
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.print (".");
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] readFully(InputStream istream) throws IOException {
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
byte[] buf = new byte[1024];
|
||||
int num = 0;
|
||||
|
||||
if (istream != null) {
|
||||
while ((num = istream.read(buf)) != -1) {
|
||||
bout.write(buf, 0, num);
|
||||
}
|
||||
}
|
||||
byte[] ret = bout.toByteArray();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
private static HttpServer createHttpServer(ExecutorService execs)
|
||||
throws Exception {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress inetAddress = new InetSocketAddress(loopback, 0);
|
||||
HttpServer testServer = HttpServer.create(inetAddress, 15);
|
||||
testServer.setExecutor(execs);
|
||||
HttpContext context = testServer.createContext("/test");
|
||||
context.setHandler(new HttpHandler() {
|
||||
public void handle(HttpExchange msg) {
|
||||
try {
|
||||
String method = msg.getRequestMethod();
|
||||
if (method.equals("POST")) {
|
||||
InputStream is = msg.getRequestBody();
|
||||
byte[] buf = readFully(is);
|
||||
is.close();
|
||||
writePostReply(msg, buf);
|
||||
} else {
|
||||
System.out.println("****** METHOD not handled ***** "+method);
|
||||
System.out.println("Received="+s_received);
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
msg.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
return testServer;
|
||||
}
|
||||
|
||||
private static void writePostReply(HttpExchange msg, byte[] buf)
|
||||
throws Exception {
|
||||
msg.getResponseHeaders().add("Content-Type", "text/xml");
|
||||
msg.sendResponseHeaders(200, buf.length);
|
||||
OutputStream out = msg.getResponseBody();
|
||||
out.write(buf);
|
||||
out.close();
|
||||
}
|
||||
|
||||
}
|
||||
159
test/jdk/com/sun/net/httpserver/bugs/B6393710.java
Normal file
159
test/jdk/com/sun/net/httpserver/bugs/B6393710.java
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2025, 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 6393710
|
||||
* @library /test/lib
|
||||
* @summary Non authenticated call followed by authenticated call never returns
|
||||
* @run main B6393710
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6393710
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
/*
|
||||
* Test checks for following bug(s) when a POST containing a request body
|
||||
* needs to be authenticated
|
||||
*
|
||||
* 1) we were not reading the request body
|
||||
*
|
||||
* 2) we were not re-enabling the interestops for the socket channel
|
||||
*/
|
||||
|
||||
public class B6393710 {
|
||||
|
||||
static String CRLF = "\r\n";
|
||||
|
||||
/* Two post requests containing data. The second one
|
||||
* has the expected authorization credentials
|
||||
*/
|
||||
static String cmd =
|
||||
"POST /test/foo HTTP/1.1"+CRLF+
|
||||
"Content-Length: 22"+CRLF+
|
||||
"Pragma: no-cache"+CRLF+
|
||||
"Cache-Control: no-cache"+CRLF+ CRLF+
|
||||
"<item desc=\"excuse\" />"+
|
||||
"POST /test/foo HTTP/1.1"+CRLF+
|
||||
"Content-Length: 22"+CRLF+
|
||||
"Pragma: no-cache"+CRLF+
|
||||
"Authorization: Basic ZnJlZDpmcmVkcGFzc3dvcmQ="+CRLF+
|
||||
"Cache-Control: no-cache"+CRLF+ CRLF+
|
||||
"<item desc=\"excuse\" />";
|
||||
|
||||
public static void main (String[] args) throws Exception {
|
||||
Handler handler = new Handler();
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
|
||||
HttpServer server = HttpServer.create (addr, 0);
|
||||
HttpContext ctx = server.createContext ("/test", handler);
|
||||
ctx.setAuthenticator (new BasicAuthenticator ("test") {
|
||||
public boolean checkCredentials (String user, String pass) {
|
||||
return user.equals ("fred") && pass.equals("fredpassword");
|
||||
}
|
||||
});
|
||||
|
||||
server.start ();
|
||||
|
||||
Socket s = new Socket (loopback, server.getAddress().getPort());
|
||||
s.setSoTimeout ((int) Utils.adjustTimeout(5000));
|
||||
|
||||
OutputStream os = s.getOutputStream();
|
||||
os.write (cmd.getBytes());
|
||||
InputStream is = s.getInputStream ();
|
||||
try {
|
||||
ok = readAndCheck (is, "401 Unauthorized") &&
|
||||
readAndCheck (is, "200 OK");
|
||||
} catch (SocketTimeoutException e) {
|
||||
System.out.println ("Did not received expected data");
|
||||
ok = false;
|
||||
} finally {
|
||||
s.close();
|
||||
server.stop(0);
|
||||
}
|
||||
|
||||
if (requests != 1) {
|
||||
throw new RuntimeException ("server handler did not receive the request");
|
||||
}
|
||||
if (!ok) {
|
||||
throw new RuntimeException ("did not get 200 OK");
|
||||
}
|
||||
System.out.println ("OK");
|
||||
}
|
||||
|
||||
/* check for expected string and return true if found in stream */
|
||||
|
||||
static boolean readAndCheck (InputStream is, String expected) throws IOException {
|
||||
int c;
|
||||
int count = 0;
|
||||
int expLen = expected.length();
|
||||
expected = expected.toLowerCase();
|
||||
|
||||
while ((c=is.read()) != -1) {
|
||||
c = Character.toLowerCase (c);
|
||||
if (c == expected.charAt (count)) {
|
||||
count ++;
|
||||
if (count == expLen) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static volatile boolean ok = false;
|
||||
static volatile int requests = 0;
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
int invocation = 1;
|
||||
public void handle (HttpExchange t)
|
||||
throws IOException
|
||||
{
|
||||
int count = 0;
|
||||
InputStream is = t.getRequestBody();
|
||||
Headers map = t.getRequestHeaders();
|
||||
Headers rmap = t.getResponseHeaders();
|
||||
while (is.read () != -1) {
|
||||
count ++;
|
||||
}
|
||||
if (count != 22) {
|
||||
System.out.println ("Handler expected 22. got " + count);
|
||||
ok = false;
|
||||
}
|
||||
is.close();
|
||||
t.sendResponseHeaders (200, RSPBODY_EMPTY);
|
||||
t.close();
|
||||
requests ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
158
test/jdk/com/sun/net/httpserver/bugs/B6401598.java
Normal file
158
test/jdk/com/sun/net/httpserver/bugs/B6401598.java
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2025, 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
|
||||
* @library /test/lib
|
||||
* @bug 6401598
|
||||
* @summary new HttpServer cannot serve binary stream data
|
||||
* @run main B6401598
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6401598
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
|
||||
|
||||
public class B6401598 {
|
||||
|
||||
static class MyHandler implements HttpHandler {
|
||||
|
||||
public MyHandler() {
|
||||
|
||||
}
|
||||
|
||||
public void handle(HttpExchange arg0) throws IOException {
|
||||
try {
|
||||
InputStream is = arg0.getRequestBody();
|
||||
OutputStream os = arg0.getResponseBody();
|
||||
|
||||
DataInputStream dis = new DataInputStream(is);
|
||||
|
||||
short input = dis.readShort();
|
||||
while (dis.read() != -1) ;
|
||||
dis.close();
|
||||
|
||||
DataOutputStream dos = new DataOutputStream(os);
|
||||
|
||||
arg0.sendResponseHeaders(200, RSPBODY_CHUNKED);
|
||||
|
||||
dos.writeShort(input);
|
||||
|
||||
dos.flush();
|
||||
dos.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int port;
|
||||
static boolean error = false;
|
||||
static ExecutorService exec;
|
||||
static HttpServer server;
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
server = HttpServer.create(new InetSocketAddress(loopback, 0), 400);
|
||||
server.createContext("/server/", new MyHandler());
|
||||
exec = Executors.newFixedThreadPool(3);
|
||||
server.setExecutor(exec);
|
||||
port = server.getAddress().getPort();
|
||||
server.start();
|
||||
|
||||
short counter;
|
||||
|
||||
for (counter = 0; counter < 1000; counter++) {
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.path("/server/")
|
||||
.toURLUnchecked();
|
||||
System.out.println("URL: " + url);
|
||||
HttpURLConnection connection = getHttpURLConnection(url, 10000);
|
||||
|
||||
OutputStream os = connection.getOutputStream();
|
||||
|
||||
DataOutputStream dos = new DataOutputStream(os);
|
||||
|
||||
dos.writeShort(counter);
|
||||
|
||||
dos.flush();
|
||||
dos.close();
|
||||
|
||||
counter++;
|
||||
|
||||
InputStream is = connection.getInputStream();
|
||||
|
||||
DataInputStream dis = new DataInputStream(is);
|
||||
|
||||
short ret = dis.readShort();
|
||||
|
||||
dis.close();
|
||||
}
|
||||
System.out.println ("Stopping");
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Unexpected exception: " + e, e);
|
||||
} finally {
|
||||
server.stop(0);
|
||||
exec.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static HttpURLConnection getHttpURLConnection(URL url, int timeout) throws IOException {
|
||||
|
||||
HttpURLConnection httpURLConnection =
|
||||
(HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
|
||||
|
||||
httpURLConnection.setConnectTimeout(40000);
|
||||
httpURLConnection.setReadTimeout(timeout);
|
||||
httpURLConnection.setDoOutput(true);
|
||||
httpURLConnection.setDoInput(true);
|
||||
httpURLConnection.setUseCaches(false);
|
||||
httpURLConnection.setAllowUserInteraction(false);
|
||||
httpURLConnection.setRequestMethod("POST");
|
||||
|
||||
// HttpURLConnection httpURLConnection = new MyHttpURLConnection(url);
|
||||
|
||||
return httpURLConnection;
|
||||
}
|
||||
}
|
||||
118
test/jdk/com/sun/net/httpserver/bugs/B6421581.java
Normal file
118
test/jdk/com/sun/net/httpserver/bugs/B6421581.java
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2025, 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 6421581
|
||||
* @summary NPE while closing HttpExchange.getResonseBody()
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
|
||||
|
||||
public class B6421581 {
|
||||
|
||||
static boolean error = false;
|
||||
static int iter = 0;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
once();
|
||||
}
|
||||
|
||||
public static void once() throws Exception {
|
||||
InetSocketAddress inetAddress = new InetSocketAddress(
|
||||
"localhost", 0);
|
||||
HttpServer server = HttpServer.create(inetAddress, 5);
|
||||
int port = server.getAddress().getPort();
|
||||
ExecutorService e = (Executors.newFixedThreadPool(5));
|
||||
server.setExecutor(e);
|
||||
HttpContext context = server.createContext("/hello");
|
||||
server.start();
|
||||
context.setHandler(new HttpHandler() {
|
||||
public void handle(HttpExchange msg) {
|
||||
iter ++;
|
||||
System.out.println("Got request");
|
||||
switch (iter) {
|
||||
case 1:
|
||||
/* close output stream without opening inpustream */
|
||||
/* chunked encoding */
|
||||
try {
|
||||
msg.sendResponseHeaders(200, RSPBODY_CHUNKED);
|
||||
OutputStream out = msg.getResponseBody();
|
||||
out.write("hello".getBytes());
|
||||
out.close();
|
||||
} catch(Exception e) {
|
||||
error = true;
|
||||
} finally {
|
||||
msg.close();
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* close output stream without opening inpustream */
|
||||
/* fixed encoding */
|
||||
try {
|
||||
msg.sendResponseHeaders(200, 5);
|
||||
OutputStream out = msg.getResponseBody();
|
||||
out.write("hello".getBytes());
|
||||
out.close();
|
||||
} catch(Exception e) {
|
||||
error = true;
|
||||
} finally {
|
||||
msg.close();
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
/* close exchange without opening any stream */
|
||||
try {
|
||||
msg.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
msg.close();
|
||||
} catch(Exception e) {
|
||||
error = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
URL url = new URL("http://localhost:"+port+"/hello/url.text");
|
||||
doURL(url);
|
||||
doURL(url);
|
||||
doURL(url);
|
||||
e.shutdown();
|
||||
e.awaitTermination(4, TimeUnit.SECONDS);
|
||||
server.stop(0);
|
||||
if (error) {
|
||||
throw new RuntimeException ("test failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void doURL (URL url) throws Exception {
|
||||
InputStream is = url.openStream();
|
||||
while (is.read() != -1) ;
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
39
test/jdk/com/sun/net/httpserver/bugs/B6424196.java
Normal file
39
test/jdk/com/sun/net/httpserver/bugs/B6424196.java
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 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 6424196
|
||||
* @summary PIT build 85 mustang: two jhttp tests fail
|
||||
*/
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.io.IOException;
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
public class B6424196 {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
HttpServer server = HttpServer.create();
|
||||
}
|
||||
}
|
||||
87
test/jdk/com/sun/net/httpserver/bugs/B6431193.java
Normal file
87
test/jdk/com/sun/net/httpserver/bugs/B6431193.java
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2025, 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 6431193
|
||||
* @summary The new HTTP server exits immediately
|
||||
* @requires test.thread.factory != "Virtual"
|
||||
* @library /test/lib
|
||||
* @run main B6431193
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6431193
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.*;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
public class B6431193 {
|
||||
|
||||
static boolean handlerIsDaemon = true;
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
class MyHandler implements HttpHandler {
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
try (InputStream is = t.getRequestBody();
|
||||
OutputStream os = t.getResponseBody()) {
|
||||
is.readAllBytes();
|
||||
// .. read the request body
|
||||
String response = "This is the response";
|
||||
handlerIsDaemon = Thread.currentThread().isDaemon();
|
||||
t.sendResponseHeaders(200, response.length());
|
||||
os.write(response.getBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress(loopback, 0), 10);
|
||||
server.createContext("/apps", new MyHandler());
|
||||
server.setExecutor(null);
|
||||
server.start();
|
||||
|
||||
try {
|
||||
int port = server.getAddress().getPort();
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.path("/apps/foo")
|
||||
.toURL();
|
||||
try (InputStream is = url.openConnection(Proxy.NO_PROXY).getInputStream()) {
|
||||
is.readAllBytes();
|
||||
}
|
||||
if (handlerIsDaemon) {
|
||||
throw new RuntimeException("request was handled by a daemon thread");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Unexpected exception: " + e, e);
|
||||
} finally {
|
||||
server.stop(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
109
test/jdk/com/sun/net/httpserver/bugs/B6433018.java
Normal file
109
test/jdk/com/sun/net/httpserver/bugs/B6433018.java
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2025, 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 6433018
|
||||
* @summary HTTP server sometimes sends bad request for browsers javascript
|
||||
* @run main B6433018
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6433018
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
public class B6433018 {
|
||||
|
||||
static final String CRLF = "\r\n";
|
||||
|
||||
/* invalid HTTP POST with extra CRLF at end */
|
||||
/* This checks that the server is able to handle it
|
||||
* and recognise the second request */
|
||||
|
||||
static final String cmd =
|
||||
"POST /test/item HTTP/1.1"+CRLF+
|
||||
"Keep-Alive: 300"+CRLF+
|
||||
"Proxy-Connection: keep-alive"+CRLF+
|
||||
"Content-Type: text/xml"+CRLF+
|
||||
"Content-Length: 22"+CRLF+
|
||||
"Pragma: no-cache"+CRLF+
|
||||
"Cache-Control: no-cache"+CRLF+ CRLF+
|
||||
"<item desc=\"excuse\" />"+CRLF+
|
||||
"GET /test/items HTTP/1.1"+CRLF+
|
||||
"Host: araku:9999"+CRLF+
|
||||
"Accept-Language: en-us,en;q=0.5"+CRLF+
|
||||
"Accept-Encoding: gzip,deflate"+CRLF+
|
||||
"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7"+CRLF+
|
||||
"Keep-Alive: 300"+CRLF+
|
||||
"Proxy-Connection: keep-alive"+CRLF+
|
||||
"Pragma: no-cache"+CRLF+
|
||||
"Cache-Control: no-cache"+CRLF+CRLF;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
CountDownLatch finished = new CountDownLatch(2);
|
||||
Handler handler = new Handler(finished);
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
|
||||
HttpServer server = HttpServer.create(addr, 0);
|
||||
HttpContext ctx = server.createContext("/test", handler);
|
||||
|
||||
server.start();
|
||||
int port = server.getAddress().getPort();
|
||||
try (Socket s = new Socket(loopback, port);
|
||||
OutputStream os = s.getOutputStream()) {
|
||||
os.write(cmd.getBytes());
|
||||
finished.await(30, TimeUnit.SECONDS);
|
||||
} finally {
|
||||
server.stop(0);
|
||||
}
|
||||
|
||||
if (finished.getCount() != 0)
|
||||
throw new RuntimeException("did not receive the 2 requests");
|
||||
|
||||
System.out.println("OK");
|
||||
}
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
private final CountDownLatch finished;
|
||||
|
||||
Handler(CountDownLatch finished) {
|
||||
this.finished = finished;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
try (InputStream is = t.getRequestBody()) {
|
||||
Headers map = t.getRequestHeaders();
|
||||
Headers rmap = t.getResponseHeaders();
|
||||
while (is.read() != -1);
|
||||
}
|
||||
t.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
t.close();
|
||||
finished.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
105
test/jdk/com/sun/net/httpserver/bugs/B6526158.java
Normal file
105
test/jdk/com/sun/net/httpserver/bugs/B6526158.java
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (c) 2007, 2025, 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 6526158
|
||||
* @library /test/lib
|
||||
* @run main B6526158
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6526158
|
||||
* @summary HttpExchange.getRequestBody().close() throws Exception
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
public class B6526158 {
|
||||
|
||||
/* keep under 64 k */
|
||||
final static int SIZE = 60 * 1024;
|
||||
|
||||
public static void main (String[] args) throws Exception {
|
||||
Handler handler = new Handler();
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
|
||||
HttpServer server = HttpServer.create (addr, 0);
|
||||
HttpContext ctx = server.createContext ("/test", handler);
|
||||
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
server.setExecutor (executor);
|
||||
server.start ();
|
||||
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(server.getAddress().getPort())
|
||||
.path("/test/foo.html")
|
||||
.toURL();
|
||||
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
urlc.setDoOutput (true);
|
||||
try {
|
||||
OutputStream os = new BufferedOutputStream (urlc.getOutputStream());
|
||||
for (int i=0; i< SIZE; i++) {
|
||||
os.write (i);
|
||||
}
|
||||
os.close();
|
||||
InputStream is = urlc.getInputStream();
|
||||
int c = 0;
|
||||
while (is.read()!= -1) {
|
||||
c ++;
|
||||
}
|
||||
is.close();
|
||||
} finally {
|
||||
server.stop(0);
|
||||
executor.shutdown();
|
||||
}
|
||||
if (error) {
|
||||
throw new RuntimeException ("Test failed");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean error = false;
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
int invocation = 1;
|
||||
public void handle (HttpExchange t)
|
||||
throws IOException
|
||||
{
|
||||
InputStream is = t.getRequestBody();
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
error = true;
|
||||
}
|
||||
t.sendResponseHeaders (200, RSPBODY_EMPTY);
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
114
test/jdk/com/sun/net/httpserver/bugs/B6526913.java
Normal file
114
test/jdk/com/sun/net/httpserver/bugs/B6526913.java
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (c) 2007, 2025, 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 6526913
|
||||
* @library /test/lib
|
||||
* @run main/othervm -Dhttp.keepAlive=false B6526913
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true
|
||||
* -Dhttp.keepAlive=false B6526913
|
||||
* @summary HttpExchange.getResponseBody().close() throws Exception
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
|
||||
|
||||
public class B6526913 {
|
||||
|
||||
public static void main (String[] args) throws Exception {
|
||||
Handler handler = new Handler();
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
|
||||
HttpServer server = HttpServer.create (addr, 0);
|
||||
HttpContext ctx = server.createContext ("/test", handler);
|
||||
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
server.setExecutor (executor);
|
||||
server.start ();
|
||||
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(server.getAddress().getPort())
|
||||
.path("/test/foo.html")
|
||||
.toURL();
|
||||
HttpURLConnection urlc = (HttpURLConnection)url.openConnection (Proxy.NO_PROXY);
|
||||
try {
|
||||
InputStream is = urlc.getInputStream();
|
||||
int c ,count = 0;
|
||||
byte [] buf = new byte [32 * 1024];
|
||||
while (count < 32 * 1024) {
|
||||
count += is.read (buf);
|
||||
}
|
||||
is.close();
|
||||
} finally {
|
||||
server.stop(0);
|
||||
executor.shutdown();
|
||||
}
|
||||
if (error) {
|
||||
throw new RuntimeException ("Test failed");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean error = false;
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
int invocation = 1;
|
||||
public void handle (HttpExchange t)
|
||||
throws IOException
|
||||
{
|
||||
InputStream is = t.getRequestBody();
|
||||
try {
|
||||
while (is.read() != -1) ;
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
error = true;
|
||||
}
|
||||
/* send a chunked response, but wait a while before
|
||||
* sending the final empty chunk
|
||||
*/
|
||||
t.sendResponseHeaders (200, RSPBODY_CHUNKED);
|
||||
OutputStream os = t.getResponseBody();
|
||||
byte[] bb = new byte [32 * 1024];
|
||||
os.write (bb);
|
||||
os.flush();
|
||||
try {Thread.sleep (5000); } catch (InterruptedException e){}
|
||||
try {
|
||||
/* empty chunk sent here */
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
error = true;
|
||||
e.printStackTrace();
|
||||
}
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
139
test/jdk/com/sun/net/httpserver/bugs/B6529200.java
Normal file
139
test/jdk/com/sun/net/httpserver/bugs/B6529200.java
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (c) 2007, 2025, 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 6529200
|
||||
* @run main/othervm B6529200
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6529200
|
||||
* @summary lightweight http server does not work with http1.0 clients
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
|
||||
|
||||
public class B6529200 {
|
||||
|
||||
public static void main (String[] args) throws Exception {
|
||||
Handler handler = new Handler();
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
|
||||
HttpServer server = HttpServer.create (addr, 0);
|
||||
HttpContext ctx = server.createContext ("/test", handler);
|
||||
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
server.setExecutor (executor);
|
||||
server.start ();
|
||||
|
||||
/* test 1: keep-alive */
|
||||
|
||||
Socket sock = new Socket (loopback, server.getAddress().getPort());
|
||||
OutputStream os = sock.getOutputStream();
|
||||
System.out.println ("GET /test/foo HTTP/1.0\r\nConnection: keep-alive\r\n\r\n");
|
||||
os.write ("GET /test/foo HTTP/1.0\r\nConnection: keep-alive\r\n\r\n".getBytes());
|
||||
os.flush();
|
||||
InputStream is = sock.getInputStream();
|
||||
StringBuffer s = new StringBuffer();
|
||||
boolean finished = false;
|
||||
|
||||
sock.setSoTimeout (10 * 1000);
|
||||
try {
|
||||
while (!finished) {
|
||||
char c = (char) is.read();
|
||||
s.append (c);
|
||||
finished = s.indexOf ("\r\n\r\nhello") != -1;
|
||||
/* test will timeout otherwise */
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
server.stop(0);
|
||||
executor.shutdown ();
|
||||
throw new RuntimeException ("Test failed in test1");
|
||||
}
|
||||
|
||||
System.out.println (new String (s));
|
||||
|
||||
/* test 2: even though we request keep-alive, server must close
|
||||
* because it is sending unknown content length response */
|
||||
|
||||
System.out.println("GET /test/foo HTTP/1.0\r\nConnection: keep-alive\r\n\r\n");
|
||||
os.write ("GET /test/foo HTTP/1.0\r\nConnection: keep-alive\r\n\r\n".getBytes());
|
||||
os.flush();
|
||||
int i=0,c;
|
||||
byte [] buf = new byte [8*1024];
|
||||
try {
|
||||
while ((c=is.read()) != -1) {
|
||||
buf[i++] = (byte)c;
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
server.stop(0);
|
||||
executor.shutdown ();
|
||||
throw new RuntimeException ("Test failed in test2");
|
||||
}
|
||||
|
||||
String ss = new String (buf, "ISO-8859-1");
|
||||
if (ss.indexOf ("\r\n\r\nhello world") == -1) {
|
||||
server.stop(0);
|
||||
executor.shutdown ();
|
||||
throw new RuntimeException ("Test failed in test2: wrong string");
|
||||
}
|
||||
System.out.println (ss);
|
||||
is.close ();
|
||||
server.stop(0);
|
||||
executor.shutdown();
|
||||
}
|
||||
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
int invocation = 1;
|
||||
public void handle (HttpExchange t)
|
||||
throws IOException
|
||||
{
|
||||
InputStream is;
|
||||
OutputStream os;
|
||||
switch (invocation++) {
|
||||
case 1:
|
||||
is = t.getRequestBody();
|
||||
while (is.read() != -1) ;
|
||||
is.close();
|
||||
t.sendResponseHeaders (200, "hello".length());
|
||||
os = t.getResponseBody();
|
||||
os.write ("hello".getBytes());
|
||||
os.close();
|
||||
break;
|
||||
case 2:
|
||||
is = t.getRequestBody();
|
||||
while (is.read() != -1) ;
|
||||
is.close();
|
||||
t.sendResponseHeaders (200, RSPBODY_CHUNKED);
|
||||
os = t.getResponseBody();
|
||||
os.write ("hello world".getBytes());
|
||||
os.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
115
test/jdk/com/sun/net/httpserver/bugs/B6744329.java
Normal file
115
test/jdk/com/sun/net/httpserver/bugs/B6744329.java
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2025, 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 6744329
|
||||
* @summary Exception in light weight Http server
|
||||
* @library /test/lib
|
||||
* @run main B6744329
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6744329
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
|
||||
|
||||
public class B6744329 {
|
||||
|
||||
public static void main (String[] args) throws Exception {
|
||||
Handler handler = new Handler();
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
|
||||
HttpServer server = HttpServer.create (addr, 0);
|
||||
HttpContext ctx = server.createContext ("/test", handler);
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
server.setExecutor (executor);
|
||||
server.start ();
|
||||
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(server.getAddress().getPort())
|
||||
.path("/test/foo.html")
|
||||
.toURL();
|
||||
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
try {
|
||||
InputStream is = urlc.getInputStream();
|
||||
int c = 0;
|
||||
while (is.read()!= -1) {
|
||||
c ++;
|
||||
}
|
||||
System.out.println ("OK");
|
||||
} catch (IOException e) {
|
||||
System.out.println ("exception");
|
||||
e.printStackTrace();
|
||||
error = true;
|
||||
}
|
||||
server.stop(0);
|
||||
executor.shutdown();
|
||||
if (error) {
|
||||
throw new RuntimeException ("Test failed");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean error = false;
|
||||
|
||||
/* this must be the same size as in ChunkedOutputStream.java
|
||||
*/
|
||||
final static int CHUNK_SIZE = 4096;
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
int invocation = 1;
|
||||
public void handle (HttpExchange t)
|
||||
throws IOException
|
||||
{
|
||||
InputStream is = t.getRequestBody();
|
||||
Headers map = t.getRequestHeaders();
|
||||
Headers rmap = t.getResponseHeaders();
|
||||
while (is.read () != -1) ;
|
||||
is.close();
|
||||
/* chunked response */
|
||||
t.sendResponseHeaders (200, RSPBODY_CHUNKED);
|
||||
OutputStream os = t.getResponseBody();
|
||||
byte[] first = new byte [CHUNK_SIZE * 2];
|
||||
byte[] second = new byte [2];
|
||||
os.write (first);
|
||||
os.write ('x');
|
||||
os.write ('x');
|
||||
/* An index out of bounds exception will be thrown
|
||||
* below, which is caught by server, and connection
|
||||
* will be closed. resulting in IOException to client
|
||||
* - if bug present
|
||||
*/
|
||||
os.write ('x');
|
||||
os.write ('x');
|
||||
os.write ('x');
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
110
test/jdk/com/sun/net/httpserver/bugs/B6886436.java
Normal file
110
test/jdk/com/sun/net/httpserver/bugs/B6886436.java
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (c) 2009, 2026, 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.
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6886436
|
||||
* @summary HttpServer should not send a body with 204 response.
|
||||
* @library /test/lib
|
||||
* @comment We use othervm because this test configures logging handlers
|
||||
* for the system wide "com.sun.net.httpserver" logger
|
||||
* @run main/othervm ${test.main.class}
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class}
|
||||
*/
|
||||
public class B6886436 {
|
||||
|
||||
private static final Logger logger = Logger.getLogger ("com.sun.net.httpserver");
|
||||
|
||||
private static void setupLogging() {
|
||||
final ConsoleHandler c = new ConsoleHandler();
|
||||
c.setLevel(Level.WARNING);
|
||||
logger.addHandler(c);
|
||||
logger.setLevel(Level.WARNING);
|
||||
}
|
||||
|
||||
public static void main (String[] args) throws Exception {
|
||||
setupLogging(); // merely for debugging
|
||||
Handler handler = new Handler();
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
|
||||
HttpServer server = HttpServer.create (addr, 0);
|
||||
HttpContext ctx = server.createContext ("/test", handler);
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
server.setExecutor (executor);
|
||||
server.start ();
|
||||
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(server.getAddress().getPort())
|
||||
.path("/test/foo.html")
|
||||
.toURL();
|
||||
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
try {
|
||||
InputStream is = urlc.getInputStream();
|
||||
while (is.read()!= -1) ;
|
||||
is.close ();
|
||||
urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
urlc.setReadTimeout (3000);
|
||||
is = urlc.getInputStream();
|
||||
while (is.read()!= -1);
|
||||
is.close ();
|
||||
|
||||
} catch (IOException e) {
|
||||
server.stop(0);
|
||||
executor.shutdown();
|
||||
throw new RuntimeException ("Test failed");
|
||||
}
|
||||
server.stop(0);
|
||||
executor.shutdown();
|
||||
System.out.println ("OK");
|
||||
}
|
||||
|
||||
public static boolean error = false;
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
int invocation = 1;
|
||||
public void handle (HttpExchange t)
|
||||
throws IOException
|
||||
{
|
||||
InputStream is = t.getRequestBody();
|
||||
Headers map = t.getRequestHeaders();
|
||||
Headers rmap = t.getResponseHeaders();
|
||||
while (is.read () != -1) ;
|
||||
is.close();
|
||||
// send a 204 response with an empty chunked body
|
||||
t.sendResponseHeaders (204, RSPBODY_CHUNKED);
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
125
test/jdk/com/sun/net/httpserver/bugs/B8211420.java
Normal file
125
test/jdk/com/sun/net/httpserver/bugs/B8211420.java
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2026, 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.
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8211420
|
||||
* @library /test/lib
|
||||
* @comment We use othervm because this test configures logging handlers
|
||||
* for the system wide "com.sun.net.httpserver" logger
|
||||
* @run main/othervm ${test.main.class}
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class}
|
||||
* @summary
|
||||
*/
|
||||
public class B8211420 {
|
||||
|
||||
private static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
|
||||
|
||||
private static void setupLogging() {
|
||||
final ConsoleHandler c = new ConsoleHandler();
|
||||
c.setLevel(Level.WARNING);
|
||||
logger.addHandler(c);
|
||||
logger.setLevel(Level.WARNING);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
setupLogging(); // merely for debugging
|
||||
Handler handler = new Handler();
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
|
||||
HttpServer server = HttpServer.create(addr, 0);
|
||||
HttpContext ctx = server.createContext("/test", handler);
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
server.setExecutor(executor);
|
||||
server.start();
|
||||
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.host(server.getAddress().getAddress())
|
||||
.port(server.getAddress().getPort())
|
||||
.path("/test/foo.html")
|
||||
.toURL();
|
||||
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
try {
|
||||
InputStream is = urlc.getInputStream();
|
||||
while (is.read()!= -1) ;
|
||||
is.close ();
|
||||
String prop = urlc.getHeaderField("Content-length");
|
||||
System.out.println("Content-length = " + prop + " should be null");
|
||||
if (prop != null)
|
||||
throw new RuntimeException("Content-length was present");
|
||||
|
||||
urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
is = urlc.getInputStream();
|
||||
while (is.read()!= -1) ;
|
||||
is.close();
|
||||
if (urlc.getResponseCode() != 304) // expected for 2nd test
|
||||
throw new RuntimeException("wrong response code");
|
||||
String clen = urlc.getHeaderField("Content-length");
|
||||
System.out.println("Content-length = " + clen + " should be 99");
|
||||
System.out.println("len = " + clen.length());
|
||||
if (clen == null || !clen.equals("99"))
|
||||
throw new RuntimeException("Content-length not present or has wrong value");
|
||||
System.out.println("OK");
|
||||
} finally {
|
||||
server.stop(0);
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean error = false;
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
volatile int invocation = 1;
|
||||
public void handle (HttpExchange t)
|
||||
throws IOException
|
||||
{
|
||||
InputStream is = t.getRequestBody();
|
||||
Headers map = t.getRequestHeaders();
|
||||
Headers rmap = t.getResponseHeaders();
|
||||
while (is.read() != -1) ;
|
||||
is.close();
|
||||
if (invocation++ == 1) {
|
||||
// send a 204 response with no body
|
||||
t.sendResponseHeaders(204, RSPBODY_EMPTY);
|
||||
t.close();
|
||||
} else {
|
||||
// send a 304 response with no body but with content - length
|
||||
rmap.add("Content-length", "99");
|
||||
t.sendResponseHeaders(304, RSPBODY_EMPTY);
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2025, 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 8230159 8275534
|
||||
* @library /test/lib
|
||||
* @summary Ensure that correct exceptions are being thrown in
|
||||
* BasicAuthenticator constructor
|
||||
* @run junit BasicAuthenticatorExceptionCheck
|
||||
*/
|
||||
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import com.sun.net.httpserver.BasicAuthenticator;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
public class BasicAuthenticatorExceptionCheck {
|
||||
static final Class<NullPointerException> NPE = NullPointerException.class;
|
||||
static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
|
||||
|
||||
static BasicAuthenticator createBasicAuthenticator(String realm, Charset charset) {
|
||||
return new BasicAuthenticator(realm, charset) {
|
||||
public boolean checkCredentials(String username, String pw) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
static BasicAuthenticator createBasicAuthenticator(String realm) {
|
||||
return new BasicAuthenticator(realm) {
|
||||
public boolean checkCredentials(String username, String pw) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticationException() {
|
||||
|
||||
Throwable ex = Assertions.assertThrows(NPE, () ->
|
||||
createBasicAuthenticator("/test", null));
|
||||
System.out.println("Valid realm and Null charset provided - " +
|
||||
"NullPointerException thrown as expected: " + ex);
|
||||
|
||||
ex = Assertions.assertThrows(NPE, () ->
|
||||
createBasicAuthenticator(null, UTF_8));
|
||||
System.out.println("Null realm and valid charset provided - " +
|
||||
"NullPointerException thrown as expected: " + ex);
|
||||
|
||||
ex = Assertions.assertThrows(IAE, () ->
|
||||
createBasicAuthenticator("", UTF_8));
|
||||
assertEquals("realm must not be empty", ex.getMessage());
|
||||
System.out.println("Empty string for realm and valid charset provided - " +
|
||||
"IllegalArgumentException thrown as expected: " + ex);
|
||||
|
||||
ex = Assertions.assertThrows(NPE, () ->
|
||||
createBasicAuthenticator(null));
|
||||
System.out.println("Null realm provided - " +
|
||||
"NullPointerException thrown as expected: " + ex);
|
||||
|
||||
ex = Assertions.assertThrows(IAE, () ->
|
||||
createBasicAuthenticator(""));
|
||||
assertEquals("realm must not be empty", ex.getMessage());
|
||||
System.out.println("Empty string for realm provided - " +
|
||||
"IllegalArgumentException thrown as expected: " + ex);
|
||||
|
||||
ex = Assertions.assertThrows(IAE, () ->
|
||||
createBasicAuthenticator("\"/test\""));
|
||||
assertEquals("realm invalid: \"/test\"", ex.getMessage());
|
||||
System.out.println("Invalid string for realm provided - " +
|
||||
"IllegalArgumentException thrown as expected: " + ex);
|
||||
|
||||
ex = Assertions.assertThrows(IAE, () ->
|
||||
createBasicAuthenticator("\""));
|
||||
assertEquals("realm invalid: \"", ex.getMessage());
|
||||
System.out.println("Invalid string for realm provided - " +
|
||||
"IllegalArgumentException thrown as expected: " + ex);
|
||||
|
||||
createBasicAuthenticator("\\\"/test\\\"");
|
||||
}
|
||||
}
|
||||
134
test/jdk/com/sun/net/httpserver/bugs/ExceptionKeepAlive.java
Normal file
134
test/jdk/com/sun/net/httpserver/bugs/ExceptionKeepAlive.java
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 2026, 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.
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8219083
|
||||
* @summary Exceptions thrown from HttpHandler.handle should not close connection
|
||||
* if response is completed
|
||||
* @library /test/lib
|
||||
* @comment We use othervm because this test configures logging handlers
|
||||
* for the system wide "com.sun.net.httpserver" logger
|
||||
* @run junit/othervm ${test.main.class}
|
||||
*/
|
||||
public class ExceptionKeepAlive {
|
||||
|
||||
public static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
|
||||
|
||||
private static void setupLogging() {
|
||||
final Handler handler = new ConsoleHandler();
|
||||
handler.setLevel(Level.FINEST);
|
||||
LOGGER.setLevel(Level.FINEST);
|
||||
LOGGER.addHandler(handler);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() throws IOException, InterruptedException {
|
||||
HttpServer httpServer = startHttpServer();
|
||||
int port = httpServer.getAddress().getPort();
|
||||
try {
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.path("/firstCall")
|
||||
.toURLUnchecked();
|
||||
HttpURLConnection uc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
int responseCode = uc.getResponseCode();
|
||||
assertEquals(200, responseCode, "First request should succeed");
|
||||
|
||||
URL url2 = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.path("/secondCall")
|
||||
.toURLUnchecked();
|
||||
HttpURLConnection uc2 = (HttpURLConnection)url2.openConnection(Proxy.NO_PROXY);
|
||||
responseCode = uc2.getResponseCode();
|
||||
assertEquals(200, responseCode, "Second request should reuse connection");
|
||||
} finally {
|
||||
httpServer.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Http Server
|
||||
*/
|
||||
HttpServer startHttpServer() throws IOException {
|
||||
setupLogging(); // merely for debugging
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
HttpServer httpServer = HttpServer.create(new InetSocketAddress(loopback, 0), 0);
|
||||
httpServer.createContext("/", new MyHandler());
|
||||
httpServer.start();
|
||||
return httpServer;
|
||||
}
|
||||
|
||||
class MyHandler implements HttpHandler {
|
||||
|
||||
volatile int port1;
|
||||
@Override
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
String path = t.getRequestURI().getPath();
|
||||
if (path.equals("/firstCall")) {
|
||||
port1 = t.getRemoteAddress().getPort();
|
||||
System.out.println("First connection on client port = " + port1);
|
||||
|
||||
// send response
|
||||
t.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
// response is completed now; throw exception
|
||||
throw new NumberFormatException();
|
||||
// the connection should still be reusable
|
||||
} else if (path.equals("/secondCall")) {
|
||||
int port2 = t.getRemoteAddress().getPort();
|
||||
System.out.println("Second connection on client port = " + port2);
|
||||
|
||||
if (port1 == port2) {
|
||||
t.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
} else {
|
||||
t.sendResponseHeaders(500, RSPBODY_EMPTY);
|
||||
}
|
||||
}
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
162
test/jdk/com/sun/net/httpserver/bugs/FixedLengthInputStream.java
Normal file
162
test/jdk/com/sun/net/httpserver/bugs/FixedLengthInputStream.java
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2026, 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.
|
||||
*/
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.logging.*;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6756771 6755625
|
||||
* @summary com.sun.net.httpserver.HttpServer should handle POSTs larger than 2Gig
|
||||
* @library /test/lib
|
||||
* @comment We use othervm because this test configures logging handlers
|
||||
* for the system wide "com.sun.net.httpserver" logger
|
||||
* @run main/othervm ${test.main.class}
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class}
|
||||
*/
|
||||
public class FixedLengthInputStream {
|
||||
static final long POST_SIZE = 4L * 1024L * 1024L * 1024L; // 4Gig
|
||||
|
||||
private static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
|
||||
|
||||
void test(String[] args) throws IOException {
|
||||
HttpServer httpServer = startHttpServer();
|
||||
int port = httpServer.getAddress().getPort();
|
||||
try {
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.path("/flis/")
|
||||
.toURLUnchecked();
|
||||
HttpURLConnection uc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
uc.setDoOutput(true);
|
||||
uc.setRequestMethod("POST");
|
||||
uc.setFixedLengthStreamingMode(POST_SIZE);
|
||||
OutputStream os = uc.getOutputStream();
|
||||
|
||||
/* create a 32K byte array with data to POST */
|
||||
int thirtyTwoK = 32 * 1024;
|
||||
byte[] ba = new byte[thirtyTwoK];
|
||||
for (int i =0; i<thirtyTwoK; i++)
|
||||
ba[i] = (byte)i;
|
||||
|
||||
long times = POST_SIZE / thirtyTwoK;
|
||||
for (int i=0; i<times; i++) {
|
||||
os.write(ba);
|
||||
}
|
||||
|
||||
os.close();
|
||||
InputStream is = uc.getInputStream();
|
||||
while(is.read(ba) != -1);
|
||||
is.close();
|
||||
|
||||
pass();
|
||||
} finally {
|
||||
httpServer.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupLogging() {
|
||||
final ConsoleHandler handler = new ConsoleHandler();
|
||||
handler.setLevel(Level.FINEST);
|
||||
logger.setLevel(Level.FINEST);
|
||||
logger.addHandler(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Http Server
|
||||
*/
|
||||
HttpServer startHttpServer() throws IOException {
|
||||
if (debug) {
|
||||
setupLogging(); // merely for debugging
|
||||
}
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
HttpServer httpServer = HttpServer.create(new InetSocketAddress(loopback, 0), 0);
|
||||
httpServer.createContext("/flis/", new MyHandler(POST_SIZE));
|
||||
httpServer.start();
|
||||
return httpServer;
|
||||
}
|
||||
|
||||
class MyHandler implements HttpHandler {
|
||||
static final int BUFFER_SIZE = 32 * 1024;
|
||||
long expected;
|
||||
|
||||
MyHandler(long expected){
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
InputStream is = t.getRequestBody();
|
||||
byte[] ba = new byte[BUFFER_SIZE];
|
||||
int read;
|
||||
long count = 0L;
|
||||
while((read = is.read(ba)) != -1) {
|
||||
count += read;
|
||||
}
|
||||
is.close();
|
||||
|
||||
check(count == expected, "Expected: " + expected + ", received "
|
||||
+ count);
|
||||
|
||||
debug("Received " + count + " bytes");
|
||||
|
||||
t.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
boolean debug = true;
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
|
||||
void debug(String message) {if(debug) { System.out.println(message); } }
|
||||
public static void main(String[] args) throws Throwable {
|
||||
Class<?> k = new Object(){}.getClass().getEnclosingClass();
|
||||
try {k.getMethod("instanceMain",String[].class)
|
||||
.invoke( k.newInstance(), (Object) args);}
|
||||
catch (Throwable e) {throw e.getCause();}}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
|
||||
}
|
||||
438
test/jdk/com/sun/net/httpserver/bugs/HandlerConnectionClose.java
Normal file
438
test/jdk/com/sun/net/httpserver/bugs/HandlerConnectionClose.java
Normal file
|
|
@ -0,0 +1,438 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2026, 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.
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import com.sun.net.httpserver.HttpsConfigurator;
|
||||
import com.sun.net.httpserver.HttpsServer;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import jdk.test.lib.net.SimpleSSLContext;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8218554
|
||||
* @summary test that the handler can request a connection close.
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.net.SimpleSSLContext
|
||||
* @comment We use othervm because this test configures logging handlers
|
||||
* for the system wide "com.sun.net.httpserver" logger
|
||||
* @run main/othervm ${test.main.class}
|
||||
*/
|
||||
public class HandlerConnectionClose {
|
||||
static final int ONEK = 1024;
|
||||
static final long POST_SIZE = ONEK * 1L;
|
||||
private static final SSLContext sslContext = SimpleSSLContext.findSSLContext();
|
||||
private static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
|
||||
|
||||
void test(String[] args) throws Exception {
|
||||
|
||||
HttpServer httpServer = startHttpServer("http");
|
||||
try {
|
||||
testHttpURLConnection(httpServer, "http","/close/legacy/http/chunked");
|
||||
testHttpURLConnection(httpServer, "http","/close/legacy/http/fixed");
|
||||
testPlainSocket(httpServer, "http","/close/plain/http/chunked");
|
||||
testPlainSocket(httpServer, "http","/close/plain/http/fixed");
|
||||
} finally {
|
||||
httpServer.stop(0);
|
||||
}
|
||||
HttpServer httpsServer = startHttpServer("https");
|
||||
try {
|
||||
testHttpURLConnection(httpsServer, "https","/close/legacy/https/chunked");
|
||||
testHttpURLConnection(httpsServer, "https","/close/legacy/https/fixed");
|
||||
testPlainSocket(httpsServer, "https","/close/plain/https/chunked");
|
||||
testPlainSocket(httpsServer, "https","/close/plain/https/fixed");
|
||||
} finally{
|
||||
httpsServer.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
void testHttpURLConnection(HttpServer httpServer, String protocol, String path) throws Exception {
|
||||
int port = httpServer.getAddress().getPort();
|
||||
String host = httpServer.getAddress().getHostString();
|
||||
URL url = new URI(protocol, null, host, port, path, null, null).toURL();
|
||||
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
|
||||
if ("https".equalsIgnoreCase(protocol)) {
|
||||
((HttpsURLConnection)uc).setSSLSocketFactory(sslContext.getSocketFactory());
|
||||
((HttpsURLConnection)uc).setHostnameVerifier((String hostname, SSLSession session) -> true);
|
||||
}
|
||||
uc.setDoOutput(true);
|
||||
uc.setRequestMethod("POST");
|
||||
uc.setFixedLengthStreamingMode(POST_SIZE);
|
||||
OutputStream os = uc.getOutputStream();
|
||||
|
||||
/* create a 1K byte array with data to POST */
|
||||
byte[] ba = new byte[ONEK];
|
||||
for (int i = 0; i < ONEK; i++)
|
||||
ba[i] = (byte) i;
|
||||
|
||||
System.out.println("\n" + uc.getClass().getSimpleName() +": POST " + url + " HTTP/1.1");
|
||||
long times = POST_SIZE / ONEK;
|
||||
for (int i = 0; i < times; i++) {
|
||||
os.write(ba);
|
||||
}
|
||||
|
||||
os.close();
|
||||
InputStream is = uc.getInputStream();
|
||||
int read;
|
||||
long count = 0;
|
||||
while ((read = is.read(ba)) != -1) {
|
||||
for (int i = 0; i < read; i++) {
|
||||
byte expected = (byte) count++;
|
||||
if (ba[i] != expected) {
|
||||
throw new IOException("byte mismatch at "
|
||||
+ (count - 1) + ": expected " + expected + " got " + ba[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count != POST_SIZE) {
|
||||
throw new IOException("Unexpected length: " + count + " expected " + POST_SIZE);
|
||||
}
|
||||
is.close();
|
||||
|
||||
pass();
|
||||
}
|
||||
|
||||
void testPlainSocket(HttpServer httpServer, String protocol, String path) throws Exception {
|
||||
int port = httpServer.getAddress().getPort();
|
||||
String host = httpServer.getAddress().getHostString();
|
||||
URL url = new URI(protocol, null, host, port, path, null, null).toURL();
|
||||
Socket socket;
|
||||
if ("https".equalsIgnoreCase(protocol)) {
|
||||
socket = sslContext.getSocketFactory().createSocket(host, port);
|
||||
} else {
|
||||
socket = new Socket(host, port);
|
||||
}
|
||||
try (Socket sock = socket) {
|
||||
OutputStream os = socket.getOutputStream();
|
||||
|
||||
// send request headers
|
||||
String request = new StringBuilder()
|
||||
.append("POST ").append(path).append(" HTTP/1.1").append("\r\n")
|
||||
.append("host: ").append(host).append(':').append(port).append("\r\n")
|
||||
.append("Content-Length: ").append(POST_SIZE).append("\r\n")
|
||||
.append("\r\n")
|
||||
.toString();
|
||||
os.write(request.getBytes(StandardCharsets.US_ASCII));
|
||||
|
||||
/* create a 1K byte array with data to POST */
|
||||
byte[] ba = new byte[ONEK];
|
||||
for (int i = 0; i < ONEK; i++)
|
||||
ba[i] = (byte) i;
|
||||
|
||||
// send request data
|
||||
long times = POST_SIZE / ONEK;
|
||||
for (int i = 0; i < times; i++) {
|
||||
os.write(ba);
|
||||
}
|
||||
os.flush();
|
||||
|
||||
InputStream is = socket.getInputStream();
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
||||
// read all response headers
|
||||
int c;
|
||||
int crlf = 0;
|
||||
while ((c = is.read()) != -1) {
|
||||
if (c == '\r') continue;
|
||||
if (c == '\n') crlf++;
|
||||
else crlf = 0;
|
||||
bos.write(c);
|
||||
if (crlf == 2) break;
|
||||
}
|
||||
String responseHeadersStr = bos.toString(StandardCharsets.US_ASCII);
|
||||
List<String> responseHeaders = List.of(responseHeadersStr.split("\n"));
|
||||
System.out.println("\nPOST " + url + " HTTP/1.1");
|
||||
responseHeaders.stream().forEach(s -> System.out.println("[reply]\t" + s));
|
||||
String statusLine = responseHeaders.get(0);
|
||||
if (!statusLine.startsWith("HTTP/1.1 200 "))
|
||||
throw new IOException("Unexpected status: " + statusLine);
|
||||
String cl = responseHeaders.stream()
|
||||
.map(s -> s.toLowerCase(Locale.ROOT))
|
||||
.filter(s -> s.startsWith("content-length: "))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
String te = responseHeaders.stream()
|
||||
.map(s -> s.toLowerCase(Locale.ROOT))
|
||||
.filter(s -> s.startsWith("transfer-encoding: "))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
// check content-length and transfer-encoding are as expected
|
||||
int read = 0;
|
||||
long count = 0;
|
||||
if (path.endsWith("/fixed")) {
|
||||
if (!("content-length: " + POST_SIZE).equalsIgnoreCase(cl)) {
|
||||
throw new IOException("Unexpected Content-Length: [" + cl + "]");
|
||||
}
|
||||
if (te != null) {
|
||||
throw new IOException("Unexpected Transfer-Encoding: [" + te + "]");
|
||||
}
|
||||
// Got expected Content-Length: 1024 - read response data
|
||||
while ((read = is.read()) != -1) {
|
||||
int expected = (int) (count & 0xFF);
|
||||
if ((read & 0xFF) != expected) {
|
||||
throw new IOException("byte mismatch at "
|
||||
+ (count - 1) + ": expected " + expected + " got " + read);
|
||||
}
|
||||
if (++count == POST_SIZE) break;
|
||||
}
|
||||
} else if (cl != null) {
|
||||
throw new IOException("Unexpected Content-Length: [" + cl + "]");
|
||||
} else {
|
||||
if (!("transfer-encoding: chunked").equalsIgnoreCase(te)) {
|
||||
throw new IOException("Unexpected Transfer-Encoding: [" + te + "]");
|
||||
}
|
||||
// This is a quick & dirty implementation of
|
||||
// chunk decoding - no trailers - no extensions
|
||||
StringBuilder chunks = new StringBuilder();
|
||||
int cs = -1;
|
||||
while (cs != 0) {
|
||||
cs = 0;
|
||||
chunks.setLength(0);
|
||||
|
||||
// read next chunk length
|
||||
while ((read = is.read()) != -1) {
|
||||
if (read == '\r') continue;
|
||||
if (read == '\n') break;
|
||||
chunks.append((char) read);
|
||||
}
|
||||
cs = Integer.parseInt(chunks.toString().trim(), 16);
|
||||
System.out.println("Got chunk length: " + cs);
|
||||
|
||||
// If chunk size is 0, then we have read the last chunk.
|
||||
if (cs == 0) break;
|
||||
|
||||
// Read the chunk data
|
||||
while (--cs >= 0) {
|
||||
read = is.read();
|
||||
if (read == -1) break; // EOF
|
||||
int expected = (int) (count & 0xFF);
|
||||
if ((read & 0xFF) != expected) {
|
||||
throw new IOException("byte mismatch at "
|
||||
+ (count - 1) + ": expected " + expected + " got " + read);
|
||||
}
|
||||
// This is cheating: we know the size :-)
|
||||
if (++count == POST_SIZE) break;
|
||||
}
|
||||
|
||||
if (read == -1) {
|
||||
throw new IOException("Unexpected EOF after " + count + " data bytes");
|
||||
}
|
||||
|
||||
// read CRLF
|
||||
if ((read = is.read()) != '\r') {
|
||||
throw new IOException("Expected CR at " + count + "after chunk data - got " + read);
|
||||
}
|
||||
if ((read = is.read()) != '\n') {
|
||||
throw new IOException("Expected LF at " + count + "after chunk data - got " + read);
|
||||
}
|
||||
|
||||
if (cs == 0 && count == POST_SIZE) {
|
||||
cs = -1;
|
||||
}
|
||||
|
||||
if (cs != -1) {
|
||||
// count == POST_SIZE, but some chunk data still to be read?
|
||||
throw new IOException("Unexpected chunk size, "
|
||||
+ cs + " bytes still to read after " + count +
|
||||
" data bytes received.");
|
||||
}
|
||||
}
|
||||
// Last CRLF?
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if ((read = is.read()) == -1) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count != POST_SIZE) {
|
||||
throw new IOException("Unexpected length: " + count + " expected " + POST_SIZE);
|
||||
}
|
||||
|
||||
if (!sock.isClosed()) {
|
||||
try {
|
||||
// We send an end request to the server to verify that the
|
||||
// connection is closed. If the server has not closed the
|
||||
// connection, it will reply. If we receive a response,
|
||||
// we should fail...
|
||||
String endrequest = new StringBuilder()
|
||||
.append("GET ").append("/close/end").append(" HTTP/1.1").append("\r\n")
|
||||
.append("host: ").append(host).append(':').append(port).append("\r\n")
|
||||
.append("Content-Length: ").append(0).append("\r\n")
|
||||
.append("\r\n")
|
||||
.toString();
|
||||
os.write(endrequest.getBytes(StandardCharsets.US_ASCII));
|
||||
os.flush();
|
||||
StringBuilder resp = new StringBuilder();
|
||||
crlf = 0;
|
||||
|
||||
// read all headers.
|
||||
// If the server closed the connection as expected
|
||||
// we should immediately read EOF
|
||||
while ((read = is.read()) != -1) {
|
||||
if (read == '\r') continue;
|
||||
if (read == '\n') crlf++;
|
||||
else crlf = 0;
|
||||
if (crlf == 2) break;
|
||||
resp.append((char) read);
|
||||
}
|
||||
|
||||
List<String> lines = List.of(resp.toString().split("\n"));
|
||||
if (read != -1 || resp.length() != 0) {
|
||||
System.err.println("Connection not closed!");
|
||||
System.err.println("Got: ");
|
||||
lines.stream().forEach(s -> System.err.println("[end]\t" + s));
|
||||
throw new AssertionError("EOF not received after " + count + " data bytes");
|
||||
}
|
||||
if (read != -1) {
|
||||
throw new AssertionError("EOF was expected after " + count + " bytes, but got: " + read);
|
||||
} else {
|
||||
System.out.println("Got expected EOF (" + read + ")");
|
||||
}
|
||||
} catch (IOException x) {
|
||||
// expected! all is well
|
||||
System.out.println("Socket closed as expected, got exception writing to it.");
|
||||
}
|
||||
} else {
|
||||
System.out.println("Socket closed as expected");
|
||||
}
|
||||
pass();
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupLogging() {
|
||||
final Handler handler = new ConsoleHandler();
|
||||
handler.setLevel(Level.FINEST);
|
||||
logger.setLevel(Level.FINEST);
|
||||
logger.addHandler(handler);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Http Server
|
||||
*/
|
||||
HttpServer startHttpServer(String protocol) throws IOException {
|
||||
if (debug) {
|
||||
setupLogging(); // merely for debugging
|
||||
}
|
||||
|
||||
InetSocketAddress serverAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
|
||||
HttpServer httpServer = null;
|
||||
if ("http".equalsIgnoreCase(protocol)) {
|
||||
httpServer = HttpServer.create(serverAddress, 0);
|
||||
}
|
||||
if ("https".equalsIgnoreCase(protocol)) {
|
||||
HttpsServer httpsServer = HttpsServer.create(serverAddress, 0);
|
||||
httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
|
||||
httpServer = httpsServer;
|
||||
}
|
||||
httpServer.createContext("/close/", new MyHandler(POST_SIZE));
|
||||
System.out.println("Server created at: " + httpServer.getAddress());
|
||||
httpServer.start();
|
||||
return httpServer;
|
||||
}
|
||||
|
||||
class MyHandler implements HttpHandler {
|
||||
static final int BUFFER_SIZE = 512;
|
||||
final long expected;
|
||||
|
||||
MyHandler(long expected){
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
System.out.println("Server: serving " + t.getRequestURI());
|
||||
boolean chunked = t.getRequestURI().getPath().endsWith("/chunked");
|
||||
boolean fixed = t.getRequestURI().getPath().endsWith("/fixed");
|
||||
boolean end = t.getRequestURI().getPath().endsWith("/end");
|
||||
long responseLength = fixed ? POST_SIZE : 0;
|
||||
responseLength = end ? -1 : responseLength;
|
||||
responseLength = chunked ? 0 : responseLength;
|
||||
|
||||
if (!end) t.getResponseHeaders().add("connection", "CLose");
|
||||
t.sendResponseHeaders(200, responseLength);
|
||||
|
||||
if (!end) {
|
||||
OutputStream os = t.getResponseBody();
|
||||
InputStream is = t.getRequestBody();
|
||||
byte[] ba = new byte[BUFFER_SIZE];
|
||||
int read;
|
||||
long count = 0L;
|
||||
while ((read = is.read(ba)) != -1) {
|
||||
count += read;
|
||||
os.write(ba, 0, read);
|
||||
}
|
||||
is.close();
|
||||
|
||||
check(count == expected, "Expected: " + expected + ", received "
|
||||
+ count);
|
||||
debug("Received " + count + " bytes");
|
||||
os.close();
|
||||
}
|
||||
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
boolean debug = true;
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
|
||||
void debug(String message) {if(debug) { System.out.println(message); } }
|
||||
public static void main(String[] args) throws Throwable {
|
||||
Class<?> k = new Object(){}.getClass().getEnclosingClass();
|
||||
try {k.getMethod("instanceMain",String[].class)
|
||||
.invoke( k.newInstance(), (Object) args);}
|
||||
catch (Throwable e) {throw e.getCause();}}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
|
||||
}
|
||||
131
test/jdk/com/sun/net/httpserver/bugs/HeadKeepAlive.java
Normal file
131
test/jdk/com/sun/net/httpserver/bugs/HeadKeepAlive.java
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 2026, 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.
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8304963
|
||||
* @summary Connection should be reusable after HEAD request
|
||||
* @library /test/lib
|
||||
* @comment We use othervm because this test configures logging handlers
|
||||
* for the system wide "com.sun.net.httpserver" logger
|
||||
* @run junit/othervm ${test.main.class}
|
||||
*/
|
||||
public class HeadKeepAlive {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
|
||||
|
||||
private static void setupLogging() {
|
||||
final Handler handler = new ConsoleHandler();
|
||||
handler.setLevel(Level.FINEST);
|
||||
LOGGER.setLevel(Level.FINEST);
|
||||
LOGGER.addHandler(handler);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() throws IOException, InterruptedException {
|
||||
HttpServer httpServer = startHttpServer();
|
||||
int port = httpServer.getAddress().getPort();
|
||||
try {
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.path("/firstCall")
|
||||
.toURLUnchecked();
|
||||
HttpURLConnection uc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
uc.setRequestMethod("HEAD");
|
||||
int responseCode = uc.getResponseCode();
|
||||
assertEquals(200, responseCode, "First request should succeed");
|
||||
|
||||
URL url2 = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.path("/secondCall")
|
||||
.toURLUnchecked();
|
||||
HttpURLConnection uc2 = (HttpURLConnection)url2.openConnection(Proxy.NO_PROXY);
|
||||
uc2.setRequestMethod("HEAD");
|
||||
responseCode = uc2.getResponseCode();
|
||||
assertEquals(200, responseCode, "Second request should reuse connection");
|
||||
} finally {
|
||||
httpServer.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Http Server
|
||||
*/
|
||||
HttpServer startHttpServer() throws IOException {
|
||||
setupLogging(); // merely for debugging
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
HttpServer httpServer = HttpServer.create(new InetSocketAddress(loopback, 0), 0);
|
||||
httpServer.createContext("/", new MyHandler());
|
||||
httpServer.start();
|
||||
return httpServer;
|
||||
}
|
||||
|
||||
class MyHandler implements HttpHandler {
|
||||
|
||||
volatile int port1;
|
||||
@Override
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
String path = t.getRequestURI().getPath();
|
||||
if (path.equals("/firstCall")) {
|
||||
port1 = t.getRemoteAddress().getPort();
|
||||
System.out.println("First connection on client port = " + port1);
|
||||
// send response
|
||||
t.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
// the connection should still be reusable
|
||||
} else if (path.equals("/secondCall")) {
|
||||
int port2 = t.getRemoteAddress().getPort();
|
||||
System.out.println("Second connection on client port = " + port2);
|
||||
if (port1 == port2) {
|
||||
t.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
} else {
|
||||
t.sendResponseHeaders(500, RSPBODY_EMPTY);
|
||||
}
|
||||
}
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
131
test/jdk/com/sun/net/httpserver/bugs/HeadTest.java
Normal file
131
test/jdk/com/sun/net/httpserver/bugs/HeadTest.java
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright (c) 2010, 2025, 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 6886723
|
||||
* @library /test/lib
|
||||
* @run main HeadTest
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true HeadTest
|
||||
* @summary light weight http server doesn't return correct status code for HEAD requests
|
||||
*/
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import com.sun.net.httpserver.HttpContext;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
public class HeadTest {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
server();
|
||||
}
|
||||
|
||||
static void server() throws Exception {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress inetAddress = new InetSocketAddress(loopback, 0);
|
||||
HttpServer server = HttpServer.create(inetAddress, 5);
|
||||
try {
|
||||
server.setExecutor(Executors.newFixedThreadPool(5));
|
||||
HttpContext chunkedContext = server.createContext("/chunked");
|
||||
chunkedContext.setHandler(new HttpHandler() {
|
||||
@Override
|
||||
public void handle(HttpExchange msg) {
|
||||
try {
|
||||
try {
|
||||
if (msg.getRequestMethod().equals("HEAD")) {
|
||||
msg.getRequestBody().close();
|
||||
msg.getResponseHeaders().add("Transfer-encoding", "chunked");
|
||||
msg.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
}
|
||||
} catch(IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
msg.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
HttpContext clContext = server.createContext("/content");
|
||||
clContext.setHandler(new HttpHandler() {
|
||||
@Override
|
||||
public void handle(HttpExchange msg) {
|
||||
try {
|
||||
try {
|
||||
if (msg.getRequestMethod().equals("HEAD")) {
|
||||
msg.getRequestBody().close();
|
||||
msg.getResponseHeaders().add("Content-length", "1024");
|
||||
msg.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
}
|
||||
} catch(IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
msg.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
server.start();
|
||||
String urlStr = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(server.getAddress().getPort())
|
||||
.path("/")
|
||||
.build()
|
||||
.toString();
|
||||
System.out.println("Server is at " + urlStr);
|
||||
|
||||
// Run the chunked client
|
||||
for(int i=0; i < 10; i++) {
|
||||
runClient(urlStr + "chunked/");
|
||||
}
|
||||
// Run the content length client
|
||||
for(int i=0; i < 10; i++) {
|
||||
runClient(urlStr + "content/");
|
||||
}
|
||||
} finally {
|
||||
// Stop the server
|
||||
((ExecutorService)server.getExecutor()).shutdown();
|
||||
server.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void runClient(String urlStr) throws Exception {
|
||||
HttpURLConnection conn = (HttpURLConnection) new URL(urlStr).openConnection(Proxy.NO_PROXY);
|
||||
conn.setRequestMethod("HEAD");
|
||||
int status = conn.getResponseCode();
|
||||
if (status != 200) {
|
||||
throw new RuntimeException("HEAD request doesn't return 200, but returns " + status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2025, 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.
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import sun.net.httpserver.HttpExchangeAccess;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8203036
|
||||
* @library /test/lib
|
||||
* @modules jdk.httpserver/sun.net.httpserver
|
||||
* @build jdk.httpserver/sun.net.httpserver.HttpExchangeAccess AutoCloseableHttpExchange
|
||||
* @run main/othervm AutoCloseableHttpExchange
|
||||
* @summary Ensure that HttpExchange closes correctly when utilising the
|
||||
* AutoCloseable interface e.g. both request InputStream and response OutputStream
|
||||
* are closed, if not already.
|
||||
*/
|
||||
|
||||
public class AutoCloseableHttpExchange {
|
||||
|
||||
static HttpServer testHttpServer;
|
||||
static AtomicBoolean exchangeCloseFail = new AtomicBoolean(false);
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
private CountDownLatch latch;
|
||||
|
||||
Handler(CountDownLatch latch) {
|
||||
this.latch = latch;
|
||||
}
|
||||
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
InputStream is = t.getRequestBody();
|
||||
try (HttpExchange e = t) {
|
||||
while (is.read() != -1) ;
|
||||
t.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
}
|
||||
if (!HttpExchangeAccess.isClosed(t)) {
|
||||
exchangeCloseFail.set(true);
|
||||
}
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
static void connectAndCheck(String realm) throws Exception {
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(testHttpServer.getAddress().getPort())
|
||||
.path(realm)
|
||||
.toURL();
|
||||
HttpURLConnection testConnection = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
|
||||
InputStream is = testConnection.getInputStream();
|
||||
while (is.read() != -1) ;
|
||||
is.close();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int CONNECTION_COUNT = 5;
|
||||
CountDownLatch latch = new CountDownLatch(CONNECTION_COUNT);
|
||||
|
||||
InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
|
||||
testHttpServer = HttpServer.create(addr, 0);
|
||||
testHttpServer.createContext("/test", new Handler(latch));
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(CONNECTION_COUNT);
|
||||
testHttpServer.setExecutor(executor);
|
||||
testHttpServer.start();
|
||||
|
||||
while (CONNECTION_COUNT-- != 0) {
|
||||
connectAndCheck("/test");
|
||||
}
|
||||
latch.await();
|
||||
testHttpServer.stop(0);
|
||||
executor.shutdown();
|
||||
|
||||
if (exchangeCloseFail.get())
|
||||
throw new RuntimeException("The exchange was not closed properly");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
package sun.net.httpserver;
|
||||
|
||||
public class HttpExchangeAccess {
|
||||
public static boolean isClosed(com.sun.net.httpserver.HttpExchange exch) {
|
||||
synchronized (exch) {
|
||||
return ((HttpExchangeImpl) exch).getExchangeImpl().closed;
|
||||
}
|
||||
}
|
||||
}
|
||||
139
test/jdk/com/sun/net/httpserver/bugs/TruncatedRequestBody.java
Normal file
139
test/jdk/com/sun/net/httpserver/bugs/TruncatedRequestBody.java
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (c) 2017, 2026, 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.
|
||||
*/
|
||||
|
||||
|
||||
import com.sun.net.httpserver.HttpContext;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8190793
|
||||
* @summary Httpserver does not detect truncated request body
|
||||
* @comment We use othervm because this test configures logging handlers
|
||||
* for the system wide "com.sun.net.httpserver" logger
|
||||
* @run main/othervm ${test.main.class}
|
||||
*/
|
||||
/*
|
||||
* Send two POST requests to the server which are both trucated
|
||||
* and socket closed. Server needs to detect this and throw an IOException
|
||||
* in getRequestBody().read(). Two variants for fixed length and chunked.
|
||||
*/
|
||||
public class TruncatedRequestBody {
|
||||
|
||||
private static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
|
||||
|
||||
static volatile boolean error = false;
|
||||
|
||||
static CountDownLatch latch = new CountDownLatch(2);
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange exch) throws IOException {
|
||||
InputStream is = exch.getRequestBody();
|
||||
int c, count = 0;
|
||||
byte[] buf = new byte[128];
|
||||
try {
|
||||
while ((c=is.read(buf)) > 0)
|
||||
count += c;
|
||||
} catch (IOException e) {
|
||||
System.out.println("Exception caught");
|
||||
latch.countDown();
|
||||
throw e;
|
||||
}
|
||||
// shouldn't get to here
|
||||
error = true;
|
||||
latch.countDown();
|
||||
System.out.println("Read " + count + " bytes");
|
||||
is.close();
|
||||
exch.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void setupLogging() {
|
||||
final ConsoleHandler h = new ConsoleHandler();
|
||||
h.setLevel(Level.ALL);
|
||||
logger.setLevel(Level.ALL);
|
||||
logger.addHandler(h);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
setupLogging(); // merely for debugging
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
|
||||
HttpServer server = HttpServer.create(addr, 10);
|
||||
HttpContext ct = server.createContext("/", new Handler());
|
||||
ExecutorService ex = Executors.newCachedThreadPool();
|
||||
server.setExecutor(ex);
|
||||
server.start();
|
||||
|
||||
int port = server.getAddress().getPort();
|
||||
|
||||
// Test 1: fixed length
|
||||
|
||||
Socket sock = new Socket(loopback, port);
|
||||
String s1 = "POST /foo HTTP/1.1\r\nContent-length: 200000\r\n"
|
||||
+ "\r\nfoo bar99";
|
||||
|
||||
OutputStream os = sock.getOutputStream();
|
||||
os.write(s1.getBytes(StandardCharsets.ISO_8859_1));
|
||||
Thread.sleep(500);
|
||||
|
||||
sock.close();
|
||||
|
||||
// Test 2: chunked
|
||||
|
||||
String s2 = "POST /foo HTTP/1.1\r\nTransfer-encoding: chunked\r\n\r\n" +
|
||||
"100\r\nFoo bar";
|
||||
sock = new Socket(loopback, port);
|
||||
os = sock.getOutputStream();
|
||||
os.write(s2.getBytes(StandardCharsets.ISO_8859_1));
|
||||
Thread.sleep(500);
|
||||
sock.close();
|
||||
latch.await();
|
||||
server.stop(0);
|
||||
ex.shutdownNow();
|
||||
if (error)
|
||||
throw new RuntimeException("Test failed");
|
||||
}
|
||||
}
|
||||
118
test/jdk/com/sun/net/httpserver/bugs/ZeroLengthOutputStream.java
Normal file
118
test/jdk/com/sun/net/httpserver/bugs/ZeroLengthOutputStream.java
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 2026, 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.
|
||||
*/
|
||||
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8219083
|
||||
* @summary HttpExchange.getResponseBody write and close should not throw
|
||||
* even when response length is zero
|
||||
* @library /test/lib
|
||||
* @comment We use othervm because this test configures logging handlers
|
||||
* for the system wide "com.sun.net.httpserver" logger
|
||||
* @run junit/othervm ${test.main.class}
|
||||
*/
|
||||
public class ZeroLengthOutputStream {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
|
||||
public volatile boolean closed;
|
||||
public CountDownLatch cdl = new CountDownLatch(1);
|
||||
|
||||
@Test
|
||||
void test() throws IOException, InterruptedException {
|
||||
HttpServer httpServer = startHttpServer();
|
||||
int port = httpServer.getAddress().getPort();
|
||||
try {
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.path("/flis/")
|
||||
.toURLUnchecked();
|
||||
HttpURLConnection uc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
uc.getResponseCode();
|
||||
cdl.await();
|
||||
assertTrue(closed, "OutputStream close did not complete");
|
||||
} finally {
|
||||
httpServer.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupLogging() {
|
||||
final Handler handler = new ConsoleHandler();
|
||||
handler.setLevel(Level.FINEST);
|
||||
LOGGER.setLevel(Level.FINEST);
|
||||
LOGGER.addHandler(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Http Server
|
||||
*/
|
||||
HttpServer startHttpServer() throws IOException {
|
||||
setupLogging(); // merely for debugging
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
HttpServer httpServer = HttpServer.create(new InetSocketAddress(loopback, 0), 0);
|
||||
httpServer.createContext("/flis/", new MyHandler());
|
||||
httpServer.start();
|
||||
return httpServer;
|
||||
}
|
||||
|
||||
class MyHandler implements HttpHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
try {
|
||||
OutputStream os = t.getResponseBody();
|
||||
t.sendResponseHeaders(200, RSPBODY_EMPTY);
|
||||
os.write(new byte[0]);
|
||||
os.close();
|
||||
System.out.println("Output stream closed");
|
||||
closed = true;
|
||||
} finally {
|
||||
cdl.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue