mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-16 21:35:25 +00:00
6726695: HttpURLConnection shoul support 'Expect: 100-contimue' headers for PUT
Added code triggered when 'Expect: 100-continue' header has been added Reviewed-by: chegar
This commit is contained in:
parent
93614a3c7d
commit
74bfe722ce
@ -27,10 +27,8 @@ package sun.net.www.http;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import sun.net.NetworkClient;
|
||||
import sun.net.ProgressSource;
|
||||
import sun.net.ProgressMonitor;
|
||||
import sun.net.www.MessageHeader;
|
||||
import sun.net.www.HeaderParser;
|
||||
import sun.net.www.MeteredStream;
|
||||
@ -38,7 +36,6 @@ import sun.net.www.ParseUtil;
|
||||
import sun.net.www.protocol.http.HttpURLConnection;
|
||||
import sun.misc.RegexpPool;
|
||||
|
||||
import java.security.*;
|
||||
/**
|
||||
* @author Herb Jellinek
|
||||
* @author Dave Brown
|
||||
@ -60,16 +57,8 @@ public class HttpClient extends NetworkClient {
|
||||
// if we've had one io error
|
||||
boolean failedOnce = false;
|
||||
|
||||
/** regexp pool of hosts for which we should connect directly, not Proxy
|
||||
* these are intialized from a property.
|
||||
*/
|
||||
private static RegexpPool nonProxyHostsPool = null;
|
||||
|
||||
/** The string source of nonProxyHostsPool
|
||||
*/
|
||||
private static String nonProxyHostsSource = null;
|
||||
|
||||
/** Response code for CONTINUE */
|
||||
private boolean ignoreContinue = true;
|
||||
private static final int HTTP_CONTINUE = 100;
|
||||
|
||||
/** Default port number for http daemons. REMIND: make these private */
|
||||
@ -610,7 +599,10 @@ public class HttpClient extends NetworkClient {
|
||||
return (parseHTTPHeader(responses, pi, httpuc));
|
||||
} catch (SocketTimeoutException stex) {
|
||||
// We don't want to retry the request when the app. sets a timeout
|
||||
closeServer();
|
||||
// but don't close the server if timeout while waiting for 100-continue
|
||||
if (ignoreContinue) {
|
||||
closeServer();
|
||||
}
|
||||
throw stex;
|
||||
} catch (IOException e) {
|
||||
closeServer();
|
||||
@ -635,12 +627,6 @@ public class HttpClient extends NetworkClient {
|
||||
|
||||
}
|
||||
|
||||
public int setTimeout (int timeout) throws SocketException {
|
||||
int old = serverSocket.getSoTimeout ();
|
||||
serverSocket.setSoTimeout (timeout);
|
||||
return old;
|
||||
}
|
||||
|
||||
private boolean parseHTTPHeader(MessageHeader responses, ProgressSource pi, HttpURLConnection httpuc)
|
||||
throws IOException {
|
||||
/* If "HTTP/*" is found in the beginning, return true. Let
|
||||
@ -768,7 +754,7 @@ public class HttpClient extends NetworkClient {
|
||||
code = Integer.parseInt(resp.substring(ind, ind + 3));
|
||||
} catch (Exception e) {}
|
||||
|
||||
if (code == HTTP_CONTINUE) {
|
||||
if (code == HTTP_CONTINUE && ignoreContinue) {
|
||||
responses.reset();
|
||||
return parseHTTPHeader(responses, pi, httpuc);
|
||||
}
|
||||
@ -893,6 +879,7 @@ public class HttpClient extends NetworkClient {
|
||||
return serverOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName()+"("+url+")";
|
||||
}
|
||||
@ -909,6 +896,7 @@ public class HttpClient extends NetworkClient {
|
||||
return cacheRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
// This should do nothing. The stream finalizer will
|
||||
// close the fd.
|
||||
@ -919,8 +907,12 @@ public class HttpClient extends NetworkClient {
|
||||
failedOnce = value;
|
||||
}
|
||||
|
||||
public void setIgnoreContinue(boolean value) {
|
||||
ignoreContinue = value;
|
||||
}
|
||||
|
||||
/* Use only on connections in error. */
|
||||
@Override
|
||||
public void closeServer() {
|
||||
try {
|
||||
keepingAlive = false;
|
||||
|
||||
@ -105,7 +105,8 @@ public class KeepAliveStreamCleaner extends LinkedBlockingQueue<KeepAliveCleaner
|
||||
HttpClient hc = kace.getHttpClient();
|
||||
try {
|
||||
if (hc != null && !hc.isInKeepAliveCache()) {
|
||||
int oldTimeout = hc.setTimeout(TIMEOUT);
|
||||
int oldTimeout = hc.getReadTimeout();
|
||||
hc.setReadTimeout(TIMEOUT);
|
||||
long remainingToRead = kas.remainingToRead();
|
||||
if (remainingToRead > 0) {
|
||||
long n = 0;
|
||||
@ -119,7 +120,7 @@ public class KeepAliveStreamCleaner extends LinkedBlockingQueue<KeepAliveCleaner
|
||||
remainingToRead = remainingToRead - n;
|
||||
}
|
||||
if (remainingToRead == 0) {
|
||||
hc.setTimeout(oldTimeout);
|
||||
hc.setReadTimeout(oldTimeout);
|
||||
hc.finished();
|
||||
} else
|
||||
hc.closeServer();
|
||||
|
||||
@ -51,7 +51,6 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import sun.net.*;
|
||||
@ -64,7 +63,6 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.TimeZone;
|
||||
import java.net.MalformedURLException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.lang.reflect.*;
|
||||
|
||||
/**
|
||||
* A class to represent an HTTP connection to a remote object.
|
||||
@ -829,6 +827,56 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
return HttpClient.New(url, p, connectTimeout, useCache);
|
||||
}
|
||||
|
||||
private void expect100Continue() throws IOException {
|
||||
// Expect: 100-Continue was set, so check the return code for
|
||||
// Acceptance
|
||||
int oldTimeout = http.getReadTimeout();
|
||||
boolean enforceTimeOut = false;
|
||||
boolean timedOut = false;
|
||||
if (oldTimeout <= 0) {
|
||||
// 5s read timeout in case the server doesn't understand
|
||||
// Expect: 100-Continue
|
||||
http.setReadTimeout(5000);
|
||||
enforceTimeOut = true;
|
||||
}
|
||||
|
||||
try {
|
||||
http.parseHTTP(responses, pi, this);
|
||||
} catch (SocketTimeoutException se) {
|
||||
if (!enforceTimeOut) {
|
||||
throw se;
|
||||
}
|
||||
timedOut = true;
|
||||
http.setIgnoreContinue(true);
|
||||
}
|
||||
if (!timedOut) {
|
||||
// Can't use getResponseCode() yet
|
||||
String resp = responses.getValue(0);
|
||||
// Parse the response which is of the form:
|
||||
// HTTP/1.1 417 Expectation Failed
|
||||
// HTTP/1.1 100 Continue
|
||||
if (resp != null && resp.startsWith("HTTP/")) {
|
||||
String[] sa = resp.split("\\s+");
|
||||
responseCode = -1;
|
||||
try {
|
||||
// Response code is 2nd token on the line
|
||||
if (sa.length > 1)
|
||||
responseCode = Integer.parseInt(sa[1]);
|
||||
} catch (NumberFormatException numberFormatException) {
|
||||
}
|
||||
}
|
||||
if (responseCode != 100) {
|
||||
throw new ProtocolException("Server rejected operation");
|
||||
}
|
||||
}
|
||||
if (oldTimeout > 0) {
|
||||
http.setReadTimeout(oldTimeout);
|
||||
}
|
||||
responseCode = -1;
|
||||
responses.reset();
|
||||
// Proceed
|
||||
}
|
||||
|
||||
/*
|
||||
* Allowable input/output sequences:
|
||||
* [interpreted as POST/PUT]
|
||||
@ -866,14 +914,20 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
if (!checkReuseConnection())
|
||||
connect();
|
||||
|
||||
/* REMIND: This exists to fix the HttpsURLConnection subclass.
|
||||
* Hotjava needs to run on JDK1.1FCS. Do proper fix in subclass
|
||||
* for 1.2 and remove this.
|
||||
*/
|
||||
boolean expectContinue = false;
|
||||
String expects = requests.findValue("Expect");
|
||||
if ("100-Continue".equalsIgnoreCase(expects)) {
|
||||
http.setIgnoreContinue(false);
|
||||
expectContinue = true;
|
||||
}
|
||||
|
||||
if (streaming() && strOutputStream == null) {
|
||||
writeRequests();
|
||||
}
|
||||
|
||||
if (expectContinue) {
|
||||
expect100Continue();
|
||||
}
|
||||
ps = (PrintStream)http.getOutputStream();
|
||||
if (streaming()) {
|
||||
if (strOutputStream == null) {
|
||||
@ -900,6 +954,13 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
} catch (RuntimeException e) {
|
||||
disconnectInternal();
|
||||
throw e;
|
||||
} catch (ProtocolException e) {
|
||||
// Save the response code which may have been set while enforcing
|
||||
// the 100-continue. disconnectInternal() forces it to -1
|
||||
int i = responseCode;
|
||||
disconnectInternal();
|
||||
responseCode = i;
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
disconnectInternal();
|
||||
throw e;
|
||||
@ -2752,7 +2813,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
try {
|
||||
// set SO_TIMEOUT to 1/5th of the total timeout
|
||||
// remember the old timeout value so that we can restore it
|
||||
int oldTimeout = http.setTimeout(timeout4ESBuffer/5);
|
||||
int oldTimeout = http.getReadTimeout();
|
||||
http.setReadTimeout(timeout4ESBuffer/5);
|
||||
|
||||
long expected = 0;
|
||||
boolean isChunked = false;
|
||||
@ -2790,7 +2852,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
} while (count < exp && time < timeout4ESBuffer);
|
||||
|
||||
// reset SO_TIMEOUT to old value
|
||||
http.setTimeout(oldTimeout);
|
||||
http.setReadTimeout(oldTimeout);
|
||||
|
||||
// if count < cl at this point, we will not try to reuse
|
||||
// the connection
|
||||
|
||||
218
jdk/test/sun/net/www/http/HttpClient/B6726695.java
Normal file
218
jdk/test/sun/net/www/http/HttpClient/B6726695.java
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright 2009 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6726695
|
||||
* @summary HttpURLConnection shoul support 'Expect: 100-contimue' headers for PUT
|
||||
*/
|
||||
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
|
||||
public class B6726695 extends Thread {
|
||||
private ServerSocket server = null;
|
||||
private int port = 0;
|
||||
private byte[] data = new byte[512];
|
||||
private String boundary = "----------------7774563516523621";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
B6726695 test = new B6726695();
|
||||
// Exit even if server is still running
|
||||
test.setDaemon(true);
|
||||
// start server
|
||||
test.start();
|
||||
// run test
|
||||
test.test();
|
||||
}
|
||||
|
||||
public B6726695() {
|
||||
try {
|
||||
server = new ServerSocket(0);
|
||||
port = server.getLocalPort();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void test() throws Exception {
|
||||
/**
|
||||
* This is a hardcoded test. The server side expects 3 requests with a
|
||||
* Expect: 100-continue header. It will reject the 1st one and accept
|
||||
* the second one. Thus allowing us to test both scenarios.
|
||||
* The 3rd case is the simulation of a server that just plains ignore
|
||||
* the Expect: 100-Continue header. So the POST should proceed after
|
||||
* a timeout.
|
||||
*/
|
||||
URL url = new URL("http://localhost:" + port + "/foo");
|
||||
|
||||
// 1st Connection. Should be rejected. I.E. get a ProtocolException
|
||||
URLConnection con = url.openConnection();
|
||||
HttpURLConnection http = (HttpURLConnection) con;
|
||||
http.setRequestMethod("POST");
|
||||
http.setRequestProperty("Expect", "100-Continue");
|
||||
http.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
|
||||
http.setDoOutput(true);
|
||||
http.setFixedLengthStreamingMode(512);
|
||||
OutputStream out = null;
|
||||
int errorCode = -1;
|
||||
try {
|
||||
out = http.getOutputStream();
|
||||
} catch (ProtocolException e) {
|
||||
errorCode = http.getResponseCode();
|
||||
}
|
||||
if (errorCode != 417) {
|
||||
throw new RuntimeException("Didn't get the ProtocolException");
|
||||
}
|
||||
|
||||
// 2nd connection. Should be accepted by server.
|
||||
http = (HttpURLConnection) url.openConnection();
|
||||
http.setRequestMethod("POST");
|
||||
http.setRequestProperty("Expect", "100-Continue");
|
||||
http.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
|
||||
http.setDoOutput(true);
|
||||
http.setFixedLengthStreamingMode(data.length);
|
||||
out = null;
|
||||
try {
|
||||
out = http.getOutputStream();
|
||||
} catch (ProtocolException e) {
|
||||
}
|
||||
if (out == null) {
|
||||
throw new RuntimeException("Didn't get an OutputStream");
|
||||
}
|
||||
out.write(data);
|
||||
out.flush();
|
||||
errorCode = http.getResponseCode();
|
||||
if (errorCode != 200) {
|
||||
throw new RuntimeException("Response code is " + errorCode);
|
||||
}
|
||||
out.close();
|
||||
|
||||
// 3rd connection. Simulate a server that doesn't implement 100-continue
|
||||
http = (HttpURLConnection) url.openConnection();
|
||||
http.setRequestMethod("POST");
|
||||
http.setRequestProperty("Expect", "100-Continue");
|
||||
http.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
|
||||
http.setDoOutput(true);
|
||||
http.setFixedLengthStreamingMode(data.length);
|
||||
out = null;
|
||||
try {
|
||||
out = http.getOutputStream();
|
||||
} catch (ProtocolException e) {
|
||||
}
|
||||
if (out == null) {
|
||||
throw new RuntimeException("Didn't get an OutputStream");
|
||||
}
|
||||
out.write(data);
|
||||
out.flush();
|
||||
out.close();
|
||||
errorCode = http.getResponseCode();
|
||||
if (errorCode != 200) {
|
||||
throw new RuntimeException("Response code is " + errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Fist connection: don't accetpt the request
|
||||
Socket s = server.accept();
|
||||
serverReject(s);
|
||||
// Second connection: accept the request (send 100-continue)
|
||||
s = server.accept();
|
||||
serverAccept(s);
|
||||
// 3rd connection: just ignore the 'Expect:' header
|
||||
s = server.accept();
|
||||
serverIgnore(s);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void serverReject(Socket s) throws IOException {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
|
||||
PrintStream out = new PrintStream(new BufferedOutputStream(s.getOutputStream()));
|
||||
String line = null;
|
||||
do {
|
||||
line = in.readLine();
|
||||
} while (line != null && line.length() != 0);
|
||||
|
||||
out.print("HTTP/1.1 417 Expectation Failed\r\n");
|
||||
out.print("Server: Sun-Java-System-Web-Server/7.0\r\n");
|
||||
out.print("Connection: close\r\n");
|
||||
out.print("Content-Length: 0\r\n");
|
||||
out.print("\r\n");
|
||||
out.flush();
|
||||
out.close();
|
||||
in.close();
|
||||
}
|
||||
|
||||
public void serverAccept(Socket s) throws IOException {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
|
||||
PrintStream out = new PrintStream(new BufferedOutputStream(s.getOutputStream()));
|
||||
String line = null;
|
||||
do {
|
||||
line = in.readLine();
|
||||
} while (line != null && line.length() != 0);
|
||||
|
||||
// Send 100-Continue
|
||||
out.print("HTTP/1.1 100 Continue\r\n");
|
||||
out.print("\r\n");
|
||||
out.flush();
|
||||
// Then read the body
|
||||
char[] cbuf = new char[512];
|
||||
int l = in.read(cbuf);
|
||||
// finally send the 200 OK
|
||||
out.print("HTTP/1.1 200 OK");
|
||||
out.print("Server: Sun-Java-System-Web-Server/7.0\r\n");
|
||||
out.print("Connection: close\r\n");
|
||||
out.print("Content-Length: 0\r\n");
|
||||
out.print("\r\n");
|
||||
out.flush();
|
||||
out.close();
|
||||
in.close();
|
||||
}
|
||||
|
||||
public void serverIgnore(Socket s) throws IOException {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
|
||||
PrintStream out = new PrintStream(new BufferedOutputStream(s.getOutputStream()));
|
||||
String line = null;
|
||||
do {
|
||||
line = in.readLine();
|
||||
} while (line != null && line.length() != 0);
|
||||
|
||||
// Then read the body
|
||||
char[] cbuf = new char[512];
|
||||
int l = in.read(cbuf);
|
||||
// finally send the 200 OK
|
||||
out.print("HTTP/1.1 200 OK");
|
||||
out.print("Server: Sun-Java-System-Web-Server/7.0\r\n");
|
||||
out.print("Content-Length: 0\r\n");
|
||||
out.print("Connection: close\r\n");
|
||||
out.print("\r\n");
|
||||
out.flush();
|
||||
out.close();
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user