Path: blob/master/test/jdk/java/net/URLConnection/DisconnectAfterEOF.java
41149 views
/*1* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/**24* @test25* @bug 477450326* @library /test/lib27* @summary Calling HttpURLConnection's disconnect method after the28* response has been received causes havoc with persistent29* connections.30*/31import java.net.*;32import java.io.*;33import java.util.*;34import jdk.test.lib.net.URIBuilder;35import static java.net.Proxy.NO_PROXY;3637public class DisconnectAfterEOF {3839/*40* Worker thread to service single connection - can service41* multiple http requests on same connection.42*/43static class Worker extends Thread {44Socket s;4546Worker(Socket s) {47this.s = s;48}4950public void run() {51try {52InputStream in = s.getInputStream();53PrintStream out = new PrintStream(54new BufferedOutputStream(55s.getOutputStream() ));56byte b[] = new byte[1024];57int n = -1;58int cl = -1;59int remaining = -1;60StringBuffer sb = new StringBuffer();61boolean close = false;6263boolean inBody = false;64for (;;) {65boolean sendResponse = false;6667try {68n = in.read(b);69} catch (IOException ioe) {70n = -1;71}72if (n <= 0) {73if (inBody) {74System.err.println("ERROR: Client closed before before " +75"entire request received.");76}77return;78}7980// reading entity-body81if (inBody) {82if (n > remaining) {83System.err.println("Receiving more than expected!!!");84return;85}86remaining -= n;8788if (remaining == 0) {89sendResponse = true;90n = 0;91} else {92continue;93}94}9596// reading headers97for (int i=0; i<n; i++) {98char c = (char)b[i];99100if (c != '\n') {101sb.append(c);102continue;103}104105106// Got end-of-line107int len = sb.length();108if (len > 0) {109if (sb.charAt(len-1) != '\r') {110System.err.println("Unexpected CR in header!!");111return;112}113}114sb.setLength(len-1);115116// empty line117if (sb.length() == 0) {118if (cl < 0) {119System.err.println("Content-Length not found!!!");120return;121}122123// the surplus is body data124int dataRead = n - (i+1);125remaining = cl - dataRead;126if (remaining > 0) {127inBody = true;128break;129} else {130// entire body has been read131sendResponse = true;132}133} else {134// non-empty line - check for Content-Length135String line = sb.toString().toLowerCase();136if (line.startsWith("content-length")) {137StringTokenizer st = new StringTokenizer(line, ":");138st.nextToken();139cl = Integer.parseInt(st.nextToken().trim());140}141if (line.startsWith("connection")) {142StringTokenizer st = new StringTokenizer(line, ":");143st.nextToken();144if (st.nextToken().trim().equals("close")) {145close =true;146}147}148}149sb = new StringBuffer();150}151152153if (sendResponse) {154// send a large response155int rspLen = 32000;156157out.print("HTTP/1.1 200 OK\r\n");158out.print("Content-Length: " + rspLen + "\r\n");159out.print("\r\n");160161if (rspLen > 0)162out.write(new byte[rspLen]);163164out.flush();165166if (close)167return;168169sendResponse = false;170inBody = false;171cl = -1;172}173}174175} catch (IOException ioe) {176} finally {177try {178s.close();179} catch (Exception e) { }180System.out.println("+ Worker thread shutdown.");181}182}183}184185/*186* Server thread to accept connection and create worker threads187* to service each connection.188*/189static class Server extends Thread {190ServerSocket ss;191192Server(ServerSocket ss) {193this.ss = ss;194}195196public void run() {197try {198for (;;) {199Socket s = ss.accept();200Worker w = new Worker(s);201w.start();202}203204} catch (IOException ioe) {205}206207System.out.println("+ Server shutdown.");208}209210public void shutdown() {211try {212ss.close();213} catch (IOException ioe) { }214}215}216217static URLConnection doRequest(String uri) throws IOException {218URLConnection uc = (new URL(uri)).openConnection(NO_PROXY);219uc.setDoOutput(true);220OutputStream out = uc.getOutputStream();221out.write(new byte[16000]);222223// force the request to be sent224uc.getInputStream();225return uc;226}227228static URLConnection doResponse(URLConnection uc) throws IOException {229int cl = ((HttpURLConnection)uc).getContentLength();230byte b[] = new byte[4096];231int n;232do {233n = uc.getInputStream().read(b);234if (n > 0) cl -= n;235} while (n > 0);236if (cl != 0) {237throw new RuntimeException("ERROR: content-length mismatch");238}239return uc;240}241242public static void main(String args[]) throws Exception {243// start server244InetAddress loopback = InetAddress.getLoopbackAddress();245ServerSocket ss = new ServerSocket();246ss.bind(new InetSocketAddress(loopback, 0));247Server svr = new Server(ss);248svr.start();249250String uri = URIBuilder.newBuilder()251.scheme("http")252.loopback()253.port(ss.getLocalPort())254.path("/foo.html")255.build().toString();256257/*258* The following is the test scenario we create here :-259*260* 1. We do a http request/response and read the response261* to EOF. As it's a persistent connection the idle262* connection should go into the keep-alive cache for a263* few seconds.264*265* 2. We start a second request but don't read the response.266* As the request is to the same server we can assume it267* (for our implementation anyway) that it will use the268* same TCP connection.269*270* 3. We "disconnect" the first HttpURLConnection. This271* should be no-op because the connection is in use272* but another request. However with 1.3.1 and 1.4/1.4.1273* this causes the TCP connection for the second request274* to be closed.275*276*/277URLConnection uc1 = doRequest(uri);278doResponse(uc1);279280Thread.sleep(2000);281282URLConnection uc2 = doRequest(uri);283284((HttpURLConnection)uc1).disconnect();285286IOException ioe = null;287try {288doResponse(uc2);289} catch (IOException x) {290ioe = x;291}292293((HttpURLConnection)uc2).disconnect();294295/*296* Shutdown server as we are done. Worker threads created297* by the server will shutdown automatically when the298* client connection closes.299*/300svr.shutdown();301302if (ioe != null) {303throw ioe;304}305}306}307308309