提交 36a90084 编写于 作者: J jccollet

6726695: HttpURLConnection shoul support 'Expect: 100-contimue' headers for PUT

Summary: Added code triggered when 'Expect: 100-continue' header has been added
Reviewed-by: chegar
上级 be9ef9fd
......@@ -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
......
/*
* 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();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册