8221252: (sc) SocketChannel and its socket adaptor need to handle connection reset

Reviewed-by: bpb
This commit is contained in:
Alan Bateman 2019-03-22 11:35:35 +00:00
parent cc590f5765
commit 3a4d5db248
13 changed files with 507 additions and 88 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
* 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
@ -81,4 +81,12 @@ public final class IOStatus {
return ((n > EOF) || (n < UNSUPPORTED_CASE));
}
/**
* Returns true if the error code is UNAVAILABLE or INTERRUPTED, the
* error codes to indicate that an I/O operation can be retried.
*/
static boolean okayToRetry(long n) {
return (n == IOStatus.UNAVAILABLE) || (n == IOStatus.INTERRUPTED);
}
}

View file

@ -310,6 +310,12 @@ public class Net {
static final ExtendedSocketOptions extendedOptions =
ExtendedSocketOptions.getInstance();
static void setSocketOption(FileDescriptor fd, SocketOption<?> name, Object value)
throws IOException
{
setSocketOption(fd, Net.UNSPEC, name, value);
}
static void setSocketOption(FileDescriptor fd, ProtocolFamily family,
SocketOption<?> name, Object value)
throws IOException
@ -372,8 +378,13 @@ public class Net {
setIntOption0(fd, mayNeedConversion, key.level(), key.name(), arg, isIPv6);
}
static Object getSocketOption(FileDescriptor fd, ProtocolFamily family,
SocketOption<?> name)
static Object getSocketOption(FileDescriptor fd, SocketOption<?> name)
throws IOException
{
return getSocketOption(fd, Net.UNSPEC, name);
}
static Object getSocketOption(FileDescriptor fd, ProtocolFamily family, SocketOption<?> name)
throws IOException
{
Class<?> type = name.type();
@ -426,8 +437,7 @@ public class Net {
return socket(UNSPEC, stream);
}
static FileDescriptor socket(ProtocolFamily family, boolean stream)
throws IOException {
static FileDescriptor socket(ProtocolFamily family, boolean stream) throws IOException {
boolean preferIPv6 = isIPv6Available() &&
(family != StandardProtocolFamily.INET);
return IOUtil.newFD(socket0(preferIPv6, stream, false, fastLoopback));
@ -525,20 +535,43 @@ public class Net {
int level, int opt, int arg, boolean isIPv6)
throws IOException;
/**
* Polls a file descriptor for events.
* @param timeout the timeout to wait; 0 to not wait, -1 to wait indefinitely
* @return the polled events or 0 if no events are polled
*/
static native int poll(FileDescriptor fd, int events, long timeout)
throws IOException;
/**
* Performs a non-blocking poll of a file descriptor.
* @return the polled events or 0 if no events are polled
*/
static int pollNow(FileDescriptor fd, int events) throws IOException {
return poll(fd, events, 0);
}
/**
* Polls a connecting socket to test if the connection has been established.
*
* @apiNote This method is public to allow it be used by code in jdk.sctp.
*
* @param timeout the timeout to wait; 0 to not wait, -1 to wait indefinitely
* @return 1 if connected, 0 if not connected, or IOS_INTERRUPTED
* @return true if connected
*/
public static native int pollConnect(FileDescriptor fd, long timeout)
public static native boolean pollConnect(FileDescriptor fd, long timeout)
throws IOException;
/**
* Performs a non-blocking poll of a connecting socket to test if the
* connection has been established.
*
* @return true if connected
*/
static boolean pollConnectNow(FileDescriptor fd) throws IOException {
return pollConnect(fd, 0);
}
/**
* Return the number of bytes in the socket input buffer.
*/

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 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
@ -216,6 +216,11 @@ class SocketAdaptor
}
}
}
@Override
public int available() throws IOException {
return sc.available();
}
}
private InputStream socketInputStream = null;

View file

@ -32,6 +32,7 @@ import java.net.InetSocketAddress;
import java.net.ProtocolFamily;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketOption;
import java.net.StandardProtocolFamily;
import java.net.StandardSocketOptions;
@ -52,6 +53,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import sun.net.ConnectionResetException;
import sun.net.NetHooks;
import sun.net.ext.ExtendedSocketOptions;
import sun.net.util.SocketExceptions;
@ -85,6 +87,9 @@ class SocketChannelImpl
private volatile boolean isInputClosed;
private volatile boolean isOutputClosed;
// Connection reset protected by readLock
private boolean connectionReset;
// -- The following fields are protected by stateLock
// set true when exclusive binding is on and SO_REUSEADDR is emulated
@ -230,7 +235,7 @@ class SocketChannelImpl
}
// no options that require special handling
Net.setSocketOption(fd, Net.UNSPEC, name, value);
Net.setSocketOption(fd, name, value);
return this;
}
}
@ -260,7 +265,7 @@ class SocketChannelImpl
}
// no options that require special handling
return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
return (T) Net.getSocketOption(fd, name);
}
}
@ -334,6 +339,10 @@ class SocketChannelImpl
}
}
private void throwConnectionReset() throws SocketException {
throw new SocketException("Connection reset");
}
@Override
public int read(ByteBuffer buf) throws IOException {
Objects.requireNonNull(buf);
@ -345,6 +354,10 @@ class SocketChannelImpl
try {
beginRead(blocking);
// check if connection has been reset
if (connectionReset)
throwConnectionReset();
// check if input is shutdown
if (isInputClosed)
return IOStatus.EOF;
@ -356,6 +369,9 @@ class SocketChannelImpl
} else {
n = IOUtil.read(fd, buf, -1, nd);
}
} catch (ConnectionResetException e) {
connectionReset = true;
throwConnectionReset();
} finally {
endRead(blocking, n > 0);
if (n <= 0 && isInputClosed)
@ -380,6 +396,10 @@ class SocketChannelImpl
try {
beginRead(blocking);
// check if connection has been reset
if (connectionReset)
throwConnectionReset();
// check if input is shutdown
if (isInputClosed)
return IOStatus.EOF;
@ -391,6 +411,9 @@ class SocketChannelImpl
} else {
n = IOUtil.read(fd, dsts, offset, length, nd);
}
} catch (ConnectionResetException e) {
connectionReset = true;
throwConnectionReset();
} finally {
endRead(blocking, n > 0);
if (n <= 0 && isInputClosed)
@ -769,15 +792,13 @@ class SocketChannelImpl
boolean connected = false;
try {
beginFinishConnect(blocking);
int n = 0;
if (blocking) {
do {
n = Net.pollConnect(fd, -1);
} while ((n == 0 || n == IOStatus.INTERRUPTED) && isOpen());
connected = Net.pollConnect(fd, -1);
} while (!connected && isOpen());
} else {
n = Net.pollConnect(fd, 0);
connected = Net.pollConnect(fd, 0);
}
connected = (n > 0);
} finally {
endFinishConnect(blocking, connected);
}
@ -1006,6 +1027,20 @@ class SocketChannelImpl
}
}
/**
* Return the number of bytes in the socket input buffer.
*/
int available() throws IOException {
synchronized (stateLock) {
ensureOpenAndConnected();
if (isInputClosed) {
return 0;
} else {
return Net.available(fd);
}
}
}
/**
* Translates native poll revent ops into a ready operation ops
*/

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 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
@ -34,13 +34,28 @@ import java.io.IOException;
*/
class SocketDispatcher extends NativeDispatcher {
SocketDispatcher() { }
/**
* Reads up to len bytes from a socket with special handling for "connection
* reset".
*
* @throws sun.net.ConnectionResetException if connection reset is detected
* @throws IOException if another I/O error occurs
*/
int read(FileDescriptor fd, long address, int len) throws IOException {
return FileDispatcherImpl.read0(fd, address, len);
return read0(fd, address, len);
}
/**
* Scattering read from a socket into len buffers with special handling for
* "connection reset".
*
* @throws sun.net.ConnectionResetException if connection reset is detected
* @throws IOException if another I/O error occurs
*/
long readv(FileDescriptor fd, long address, int len) throws IOException {
return FileDispatcherImpl.readv0(fd, address, len);
return readv0(fd, address, len);
}
int write(FileDescriptor fd, long address, int len) throws IOException {
@ -58,4 +73,16 @@ class SocketDispatcher extends NativeDispatcher {
void preClose(FileDescriptor fd) throws IOException {
FileDispatcherImpl.preClose0(fd);
}
// -- Native methods --
private static native int read0(FileDescriptor fd, long address, int len)
throws IOException;
private static native long readv0(FileDescriptor fd, long address, int len)
throws IOException;
static {
IOUtil.load();
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 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
@ -31,6 +31,8 @@ import java.net.*;
import java.util.concurrent.*;
import java.io.IOException;
import java.io.FileDescriptor;
import sun.net.ConnectionResetException;
import sun.net.NetHooks;
import sun.net.util.SocketExceptions;
import sun.security.action.GetPropertyAction;
@ -415,6 +417,8 @@ class UnixAsynchronousSocketChannelImpl
enableReading();
if (x instanceof ClosedChannelException)
x = new AsynchronousCloseException();
if (x instanceof ConnectionResetException)
x = new IOException(x.getMessage());
exc = x;
} finally {
// restart poll in case of concurrent write
@ -546,6 +550,8 @@ class UnixAsynchronousSocketChannelImpl
} catch (Throwable x) {
if (x instanceof ClosedChannelException)
x = new AsynchronousCloseException();
if (x instanceof ConnectionResetException)
x = new IOException(x.getMessage());
exc = x;
} finally {
if (!pending)

View file

@ -804,7 +804,7 @@ Java_sun_nio_ch_Net_poll(JNIEnv* env, jclass this, jobject fdo, jint events, jlo
}
}
JNIEXPORT jint JNICALL
JNIEXPORT jboolean JNICALL
Java_sun_nio_ch_Net_pollConnect(JNIEnv *env, jobject this, jobject fdo, jlong timeout)
{
jint fd = fdval(env, fdo);
@ -828,23 +828,22 @@ Java_sun_nio_ch_Net_pollConnect(JNIEnv *env, jobject this, jobject fdo, jlong ti
errno = 0;
result = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &n);
if (result < 0) {
return handleSocketError(env, errno);
handleSocketError(env, errno);
return JNI_FALSE;
} else if (error) {
return handleSocketError(env, error);
handleSocketError(env, error);
return JNI_FALSE;
} else if ((poller.revents & POLLHUP) != 0) {
return handleSocketError(env, ENOTCONN);
handleSocketError(env, ENOTCONN);
return JNI_FALSE;
}
// connected
return 1;
} else if (result == 0) {
return 0;
return JNI_TRUE;
} else if (result == 0 || errno == EINTR) {
return JNI_FALSE;
} else {
if (errno == EINTR) {
return IOS_INTERRUPTED;
} else {
JNU_ThrowIOExceptionWithLastError(env, "poll failed");
return IOS_THROWN;
}
JNU_ThrowIOExceptionWithLastError(env, "poll failed");
return JNI_FALSE;
}
}

View file

@ -0,0 +1,65 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include "jni.h"
#include "jni_util.h"
#include "jlong.h"
#include "nio.h"
#include "nio_util.h"
#include "sun_nio_ch_SocketDispatcher.h"
JNIEXPORT jint JNICALL
Java_sun_nio_ch_SocketDispatcher_read0(JNIEnv *env, jclass clazz,
jobject fdo, jlong address, jint len)
{
jint fd = fdval(env, fdo);
void *buf = (void *)jlong_to_ptr(address);
jint n = read(fd, buf, len);
if ((n == -1) && (errno == ECONNRESET || errno == EPIPE)) {
JNU_ThrowByName(env, "sun/net/ConnectionResetException", "Connection reset");
return IOS_THROWN;
} else {
return convertReturnVal(env, n, JNI_TRUE);
}
}
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_SocketDispatcher_readv0(JNIEnv *env, jclass clazz,
jobject fdo, jlong address, jint len)
{
jint fd = fdval(env, fdo);
struct iovec *iov = (struct iovec *)jlong_to_ptr(address);
jlong n = readv(fd, iov, len);
if ((n == -1) && (errno == ECONNRESET || errno == EPIPE)) {
JNU_ThrowByName(env, "sun/net/ConnectionResetException", "Connection reset");
return IOS_THROWN;
} else {
return convertLongReturnVal(env, n, JNI_TRUE);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 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
@ -25,19 +25,16 @@
package sun.nio.ch;
import java.io.*;
import java.io.FileDescriptor;
import java.io.IOException;
/**
* Allows different platforms to call different native methods
* for read and write operations.
*/
class SocketDispatcher extends NativeDispatcher
{
static {
IOUtil.load();
}
class SocketDispatcher extends NativeDispatcher {
SocketDispatcher() { }
int read(FileDescriptor fd, long address, int len) throws IOException {
return read0(fd, address, len);
@ -63,7 +60,8 @@ class SocketDispatcher extends NativeDispatcher
close0(fd);
}
//-- Native methods
// -- Native methods --
static native int read0(FileDescriptor fd, long address, int len)
throws IOException;
@ -79,4 +77,8 @@ class SocketDispatcher extends NativeDispatcher
static native void preClose0(FileDescriptor fd) throws IOException;
static native void close0(FileDescriptor fd) throws IOException;
static {
IOUtil.load();
}
}

View file

@ -660,7 +660,7 @@ Java_sun_nio_ch_Net_poll(JNIEnv* env, jclass this, jobject fdo, jint events, jlo
return rv;
}
JNIEXPORT jint JNICALL
JNIEXPORT jboolean JNICALL
Java_sun_nio_ch_Net_pollConnect(JNIEnv* env, jclass this, jobject fdo, jlong timeout)
{
int optError = 0;
@ -684,13 +684,13 @@ Java_sun_nio_ch_Net_pollConnect(JNIEnv* env, jclass this, jobject fdo, jlong tim
if (result == SOCKET_ERROR) {
handleSocketError(env, WSAGetLastError());
return IOS_THROWN;
return JNI_FALSE;
} else if (result == 0) {
return 0;
return JNI_FALSE;
} else {
// connection established if writable and no error to check
if (FD_ISSET(fd, &wr) && !FD_ISSET(fd, &ex)) {
return 1;
return JNI_TRUE;
}
result = getsockopt((SOCKET)fd,
SOL_SOCKET,
@ -699,17 +699,13 @@ Java_sun_nio_ch_Net_pollConnect(JNIEnv* env, jclass this, jobject fdo, jlong tim
&n);
if (result == SOCKET_ERROR) {
int lastError = WSAGetLastError();
if (lastError == WSAEINPROGRESS) {
return IOS_UNAVAILABLE;
if (lastError != WSAEINPROGRESS) {
NET_ThrowNew(env, lastError, "getsockopt");
}
NET_ThrowNew(env, lastError, "getsockopt");
return IOS_THROWN;
}
if (optError != NO_ERROR) {
} else if (optError != NO_ERROR) {
handleSocketError(env, optError);
return IOS_THROWN;
}
return 0;
return JNI_FALSE;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 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
@ -72,7 +72,11 @@ Java_sun_nio_ch_SocketDispatcher_read0(JNIEnv *env, jclass clazz, jobject fdo,
if (theErr == WSAEWOULDBLOCK) {
return IOS_UNAVAILABLE;
}
JNU_ThrowIOExceptionWithLastError(env, "Read failed");
if (theErr == WSAECONNRESET) {
JNU_ThrowByName(env, "sun/net/ConnectionResetException", "Connection reset");
} else {
JNU_ThrowIOExceptionWithLastError(env, "Read failed");
}
return IOS_THROWN;
}
@ -128,7 +132,11 @@ Java_sun_nio_ch_SocketDispatcher_readv0(JNIEnv *env, jclass clazz, jobject fdo,
if (theErr == WSAEWOULDBLOCK) {
return IOS_UNAVAILABLE;
}
JNU_ThrowIOExceptionWithLastError(env, "Vector read failed");
if (theErr == WSAECONNRESET) {
JNU_ThrowByName(env, "sun/net/ConnectionResetException", "Connection reset");
} else {
JNU_ThrowIOExceptionWithLastError(env, "Vector read failed");
}
return IOS_THROWN;
}
@ -174,7 +182,11 @@ Java_sun_nio_ch_SocketDispatcher_write0(JNIEnv *env, jclass clazz, jobject fdo,
if (theErr == WSAEWOULDBLOCK) {
return IOS_UNAVAILABLE;
}
JNU_ThrowIOExceptionWithLastError(env, "Write failed");
if (theErr == WSAECONNRESET) {
JNU_ThrowIOException(env, "Connection reset by peer");
} else {
JNU_ThrowIOExceptionWithLastError(env, "Write failed");
}
return IOS_THROWN;
}
}
@ -256,7 +268,11 @@ Java_sun_nio_ch_SocketDispatcher_writev0(JNIEnv *env, jclass clazz,
if (theErr == WSAEWOULDBLOCK) {
return IOS_UNAVAILABLE;
}
JNU_ThrowIOExceptionWithLastError(env, "Vector write failed");
if (theErr == WSAECONNRESET) {
JNU_ThrowIOException(env, "Connection reset by peer");
} else {
JNU_ThrowIOExceptionWithLastError(env, "Vector write failed");
}
return IOS_THROWN;
}