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
307
test/jdk/java/net/URLConnection/DisconnectAfterEOF.java
Normal file
307
test/jdk/java/net/URLConnection/DisconnectAfterEOF.java
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
* Copyright (c) 2002, 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 4774503
|
||||
* @library /test/lib
|
||||
* @summary Calling HttpURLConnection's disconnect method after the
|
||||
* response has been received causes havoc with persistent
|
||||
* connections.
|
||||
*/
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import static java.net.Proxy.NO_PROXY;
|
||||
|
||||
public class DisconnectAfterEOF {
|
||||
|
||||
/*
|
||||
* Worker thread to service single connection - can service
|
||||
* multiple http requests on same connection.
|
||||
*/
|
||||
static class Worker extends Thread {
|
||||
Socket s;
|
||||
|
||||
Worker(Socket s) {
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
InputStream in = s.getInputStream();
|
||||
PrintStream out = new PrintStream(
|
||||
new BufferedOutputStream(
|
||||
s.getOutputStream() ));
|
||||
byte b[] = new byte[1024];
|
||||
int n = -1;
|
||||
int cl = -1;
|
||||
int remaining = -1;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
boolean close = false;
|
||||
|
||||
boolean inBody = false;
|
||||
for (;;) {
|
||||
boolean sendResponse = false;
|
||||
|
||||
try {
|
||||
n = in.read(b);
|
||||
} catch (IOException ioe) {
|
||||
n = -1;
|
||||
}
|
||||
if (n <= 0) {
|
||||
if (inBody) {
|
||||
System.err.println("ERROR: Client closed before " +
|
||||
"entire request received.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// reading entity-body
|
||||
if (inBody) {
|
||||
if (n > remaining) {
|
||||
System.err.println("Receiving more than expected!!!");
|
||||
return;
|
||||
}
|
||||
remaining -= n;
|
||||
|
||||
if (remaining == 0) {
|
||||
sendResponse = true;
|
||||
n = 0;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// reading headers
|
||||
for (int i=0; i<n; i++) {
|
||||
char c = (char)b[i];
|
||||
|
||||
if (c != '\n') {
|
||||
sb.append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Got end-of-line
|
||||
int len = sb.length();
|
||||
if (len > 0) {
|
||||
if (sb.charAt(len-1) != '\r') {
|
||||
System.err.println("Unexpected CR in header!!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
sb.setLength(len-1);
|
||||
|
||||
// empty line
|
||||
if (sb.length() == 0) {
|
||||
if (cl < 0) {
|
||||
System.err.println("Content-Length not found!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
// the surplus is body data
|
||||
int dataRead = n - (i+1);
|
||||
remaining = cl - dataRead;
|
||||
if (remaining > 0) {
|
||||
inBody = true;
|
||||
break;
|
||||
} else {
|
||||
// entire body has been read
|
||||
sendResponse = true;
|
||||
}
|
||||
} else {
|
||||
// non-empty line - check for Content-Length
|
||||
String line = sb.toString().toLowerCase();
|
||||
if (line.startsWith("content-length")) {
|
||||
StringTokenizer st = new StringTokenizer(line, ":");
|
||||
st.nextToken();
|
||||
cl = Integer.parseInt(st.nextToken().trim());
|
||||
}
|
||||
if (line.startsWith("connection")) {
|
||||
StringTokenizer st = new StringTokenizer(line, ":");
|
||||
st.nextToken();
|
||||
if (st.nextToken().trim().equals("close")) {
|
||||
close =true;
|
||||
}
|
||||
}
|
||||
}
|
||||
sb = new StringBuffer();
|
||||
}
|
||||
|
||||
|
||||
if (sendResponse) {
|
||||
// send a large response
|
||||
int rspLen = 32000;
|
||||
|
||||
out.print("HTTP/1.1 200 OK\r\n");
|
||||
out.print("Content-Length: " + rspLen + "\r\n");
|
||||
out.print("\r\n");
|
||||
|
||||
if (rspLen > 0)
|
||||
out.write(new byte[rspLen]);
|
||||
|
||||
out.flush();
|
||||
|
||||
if (close)
|
||||
return;
|
||||
|
||||
sendResponse = false;
|
||||
inBody = false;
|
||||
cl = -1;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException ioe) {
|
||||
} finally {
|
||||
try {
|
||||
s.close();
|
||||
} catch (Exception e) { }
|
||||
System.out.println("+ Worker thread shutdown.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Server thread to accept connection and create worker threads
|
||||
* to service each connection.
|
||||
*/
|
||||
static class Server extends Thread {
|
||||
ServerSocket ss;
|
||||
|
||||
Server(ServerSocket ss) {
|
||||
this.ss = ss;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
for (;;) {
|
||||
Socket s = ss.accept();
|
||||
Worker w = new Worker(s);
|
||||
w.start();
|
||||
}
|
||||
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
|
||||
System.out.println("+ Server shutdown.");
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
try {
|
||||
ss.close();
|
||||
} catch (IOException ioe) { }
|
||||
}
|
||||
}
|
||||
|
||||
static URLConnection doRequest(String uri) throws IOException {
|
||||
URLConnection uc = (new URL(uri)).openConnection(NO_PROXY);
|
||||
uc.setDoOutput(true);
|
||||
OutputStream out = uc.getOutputStream();
|
||||
out.write(new byte[16000]);
|
||||
|
||||
// force the request to be sent
|
||||
uc.getInputStream();
|
||||
return uc;
|
||||
}
|
||||
|
||||
static URLConnection doResponse(URLConnection uc) throws IOException {
|
||||
int cl = ((HttpURLConnection)uc).getContentLength();
|
||||
byte b[] = new byte[4096];
|
||||
int n;
|
||||
do {
|
||||
n = uc.getInputStream().read(b);
|
||||
if (n > 0) cl -= n;
|
||||
} while (n > 0);
|
||||
if (cl != 0) {
|
||||
throw new RuntimeException("ERROR: content-length mismatch");
|
||||
}
|
||||
return uc;
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
// start server
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
ServerSocket ss = new ServerSocket();
|
||||
ss.bind(new InetSocketAddress(loopback, 0));
|
||||
Server svr = new Server(ss);
|
||||
svr.start();
|
||||
|
||||
String uri = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(ss.getLocalPort())
|
||||
.path("/foo.html")
|
||||
.build().toString();
|
||||
|
||||
/*
|
||||
* The following is the test scenario we create here :-
|
||||
*
|
||||
* 1. We do a http request/response and read the response
|
||||
* to EOF. As it's a persistent connection the idle
|
||||
* connection should go into the keep-alive cache for a
|
||||
* few seconds.
|
||||
*
|
||||
* 2. We start a second request but don't read the response.
|
||||
* As the request is to the same server we can assume it
|
||||
* (for our implementation anyway) that it will use the
|
||||
* same TCP connection.
|
||||
*
|
||||
* 3. We "disconnect" the first HttpURLConnection. This
|
||||
* should be no-op because the connection is in use
|
||||
* but another request. However with 1.3.1 and 1.4/1.4.1
|
||||
* this causes the TCP connection for the second request
|
||||
* to be closed.
|
||||
*
|
||||
*/
|
||||
URLConnection uc1 = doRequest(uri);
|
||||
doResponse(uc1);
|
||||
|
||||
Thread.sleep(2000);
|
||||
|
||||
URLConnection uc2 = doRequest(uri);
|
||||
|
||||
((HttpURLConnection)uc1).disconnect();
|
||||
|
||||
IOException ioe = null;
|
||||
try {
|
||||
doResponse(uc2);
|
||||
} catch (IOException x) {
|
||||
ioe = x;
|
||||
}
|
||||
|
||||
((HttpURLConnection)uc2).disconnect();
|
||||
|
||||
/*
|
||||
* Shutdown server as we are done. Worker threads created
|
||||
* by the server will shutdown automatically when the
|
||||
* client connection closes.
|
||||
*/
|
||||
svr.shutdown();
|
||||
|
||||
if (ioe != null) {
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue