8221252: (sc) SocketChannel and its socket adaptor need to handle connection reset
Reviewed-by: bpb
This commit is contained in:
parent
cc590f5765
commit
3a4d5db248
src
java.base
share/classes/sun/nio/ch
unix
classes/sun/nio/ch
native/libnio/ch
windows
jdk.sctp/unix/classes/sun/nio/ch/sctp
test/jdk/java/nio/channels/SocketChannel
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
65
src/java.base/unix/native/libnio/ch/SocketDispatcher.c
Normal file
65
src/java.base/unix/native/libnio/ch/SocketDispatcher.c
Normal 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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2009, 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
|
||||
@ -465,7 +465,7 @@ public class SctpChannelImpl extends SctpChannel
|
||||
if (state != ChannelState.PENDING)
|
||||
throw new NoConnectionPendingException();
|
||||
}
|
||||
int n = 0;
|
||||
boolean connected = false;
|
||||
try {
|
||||
try {
|
||||
begin();
|
||||
@ -477,26 +477,11 @@ public class SctpChannelImpl extends SctpChannel
|
||||
receiverThread = NativeThread.current();
|
||||
}
|
||||
if (!isBlocking()) {
|
||||
for (;;) {
|
||||
n = Net.pollConnect(fd, 0);
|
||||
if ( (n == IOStatus.INTERRUPTED)
|
||||
&& isOpen())
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
connected = Net.pollConnect(fd, 0);
|
||||
} else {
|
||||
for (;;) {
|
||||
n = Net.pollConnect(fd, -1);
|
||||
if (n == 0) {
|
||||
// Loop in case of
|
||||
// spurious notifications
|
||||
continue;
|
||||
}
|
||||
if ( (n == IOStatus.INTERRUPTED)
|
||||
&& isOpen())
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
do {
|
||||
connected = Net.pollConnect(fd, -1);
|
||||
} while (!connected && isOpen());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@ -504,16 +489,10 @@ public class SctpChannelImpl extends SctpChannel
|
||||
receiverThread = 0;
|
||||
if (state == ChannelState.KILLPENDING) {
|
||||
kill();
|
||||
/* poll()/getsockopt() does not report
|
||||
* error (throws exception, with n = 0)
|
||||
* on Linux platform after dup2 and
|
||||
* signal-wakeup. Force n to 0 so the
|
||||
* end() can throw appropriate exception */
|
||||
n = 0;
|
||||
connected = false;
|
||||
}
|
||||
}
|
||||
end((n > 0) || (n == IOStatus.UNAVAILABLE));
|
||||
assert IOStatus.check(n);
|
||||
end(connected);
|
||||
}
|
||||
} catch (IOException x) {
|
||||
/* If an exception was thrown, close the channel after
|
||||
@ -523,7 +502,7 @@ public class SctpChannelImpl extends SctpChannel
|
||||
throw x;
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
if (connected) {
|
||||
synchronized (stateLock) {
|
||||
state = ChannelState.CONNECTED;
|
||||
if (!isBound()) {
|
||||
|
248
test/jdk/java/nio/channels/SocketChannel/ConnectionReset.java
Normal file
248
test/jdk/java/nio/channels/SocketChannel/ConnectionReset.java
Normal file
@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* @requires os.family != "solaris"
|
||||
* @run testng ConnectionReset
|
||||
* @summary Test behavior of SocketChannel.read and the Socket adaptor read
|
||||
* and available methods when a connection is reset
|
||||
*/
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
@Test
|
||||
public class ConnectionReset {
|
||||
|
||||
static final int REPEAT_COUNT = 5;
|
||||
|
||||
/**
|
||||
* Tests SocketChannel.read when the connection is reset and there are no
|
||||
* bytes to read.
|
||||
*/
|
||||
public void testSocketChannelReadNoData() throws IOException {
|
||||
System.out.println("testSocketChannelReadNoData");
|
||||
withResetConnection(null, sc -> {
|
||||
ByteBuffer bb = ByteBuffer.allocate(100);
|
||||
for (int i=0; i<REPEAT_COUNT; i++) {
|
||||
try {
|
||||
sc.read(bb);
|
||||
assertTrue(false);
|
||||
} catch (IOException ioe) {
|
||||
System.out.format("read => %s (expected)%n", ioe);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests SocketChannel.read when the connection is reset and there are bytes
|
||||
* to read.
|
||||
*/
|
||||
public void testSocketChannelReadData() throws IOException {
|
||||
System.out.println("testSocketChannelReadData");
|
||||
byte[] data = { 1, 2, 3 };
|
||||
withResetConnection(data, sc -> {
|
||||
int remaining = data.length;
|
||||
ByteBuffer bb = ByteBuffer.allocate(remaining + 100);
|
||||
for (int i=0; i<REPEAT_COUNT; i++) {
|
||||
try {
|
||||
int bytesRead = sc.read(bb);
|
||||
if (bytesRead == -1) {
|
||||
System.out.println("read => EOF");
|
||||
} else {
|
||||
System.out.println("read => " + bytesRead + " byte(s)");
|
||||
}
|
||||
assertTrue(bytesRead > 0);
|
||||
remaining -= bytesRead;
|
||||
assertTrue(remaining >= 0);
|
||||
} catch (IOException ioe) {
|
||||
System.out.format("read => %s%n", ioe);
|
||||
remaining = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests available before Socket read when the connection is reset and there
|
||||
* are no bytes to read.
|
||||
*/
|
||||
public void testAvailableBeforeSocketReadNoData() throws IOException {
|
||||
System.out.println("testAvailableBeforeSocketReadNoData");
|
||||
withResetConnection(null, sc -> {
|
||||
Socket s = sc.socket();
|
||||
InputStream in = s.getInputStream();
|
||||
for (int i=0; i<REPEAT_COUNT; i++) {
|
||||
int bytesAvailable = in.available();
|
||||
System.out.format("available => %d%n", bytesAvailable);
|
||||
assertTrue(bytesAvailable == 0);
|
||||
try {
|
||||
int bytesRead = in.read();
|
||||
if (bytesRead == -1) {
|
||||
System.out.println("read => EOF");
|
||||
} else {
|
||||
System.out.println("read => 1 byte");
|
||||
}
|
||||
assertTrue(false);
|
||||
} catch (IOException ioe) {
|
||||
System.out.format("read => %s (expected)%n", ioe);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests available before Socket read when the connection is reset and there
|
||||
* are bytes to read.
|
||||
*/
|
||||
public void testAvailableBeforeSocketReadData() throws IOException {
|
||||
System.out.println("testAvailableBeforeSocketReadData");
|
||||
byte[] data = { 1, 2, 3 };
|
||||
withResetConnection(data, sc -> {
|
||||
Socket s = sc.socket();
|
||||
InputStream in = s.getInputStream();
|
||||
int remaining = data.length;
|
||||
for (int i=0; i<REPEAT_COUNT; i++) {
|
||||
int bytesAvailable = in.available();
|
||||
System.out.format("available => %d%n", bytesAvailable);
|
||||
assertTrue(bytesAvailable <= remaining);
|
||||
try {
|
||||
int bytesRead = in.read();
|
||||
if (bytesRead == -1) {
|
||||
System.out.println("read => EOF");
|
||||
assertTrue(false);
|
||||
} else {
|
||||
System.out.println("read => 1 byte");
|
||||
assertTrue(remaining > 0);
|
||||
remaining--;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
System.out.format("read => %s%n", ioe);
|
||||
remaining = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Socket read before available when the connection is reset and there
|
||||
* are no bytes to read.
|
||||
*/
|
||||
public void testSocketReadNoDataBeforeAvailable() throws IOException {
|
||||
System.out.println("testSocketReadNoDataBeforeAvailable");
|
||||
withResetConnection(null, sc -> {
|
||||
Socket s = sc.socket();
|
||||
InputStream in = s.getInputStream();
|
||||
for (int i=0; i<REPEAT_COUNT; i++) {
|
||||
try {
|
||||
int bytesRead = in.read();
|
||||
if (bytesRead == -1) {
|
||||
System.out.println("read => EOF");
|
||||
} else {
|
||||
System.out.println("read => 1 byte");
|
||||
}
|
||||
assertTrue(false);
|
||||
} catch (IOException ioe) {
|
||||
System.out.format("read => %s (expected)%n", ioe);
|
||||
}
|
||||
int bytesAvailable = in.available();
|
||||
System.out.format("available => %d%n", bytesAvailable);
|
||||
assertTrue(bytesAvailable == 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Socket read before available when the connection is reset and there
|
||||
* are bytes to read.
|
||||
*/
|
||||
public void testSocketReadDataBeforeAvailable() throws IOException {
|
||||
System.out.println("testSocketReadDataBeforeAvailable");
|
||||
byte[] data = { 1, 2, 3 };
|
||||
withResetConnection(data, sc -> {
|
||||
Socket s = sc.socket();
|
||||
InputStream in = s.getInputStream();
|
||||
int remaining = data.length;
|
||||
for (int i=0; i<REPEAT_COUNT; i++) {
|
||||
try {
|
||||
int bytesRead = in.read();
|
||||
if (bytesRead == -1) {
|
||||
System.out.println("read => EOF");
|
||||
assertTrue(false);
|
||||
} else {
|
||||
System.out.println("read => 1 byte");
|
||||
assertTrue(remaining > 0);
|
||||
remaining--;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
System.out.format("read => %s%n", ioe);
|
||||
remaining = 0;
|
||||
}
|
||||
int bytesAvailable = in.available();
|
||||
System.out.format("available => %d%n", bytesAvailable);
|
||||
assertTrue(bytesAvailable <= remaining);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
interface ThrowingConsumer<T> {
|
||||
void accept(T t) throws IOException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a consumer with a SocketChannel connected to a peer that has closed
|
||||
* the connection with a "connection reset". The peer sends the given data
|
||||
* bytes before closing (when data is not null).
|
||||
*/
|
||||
static void withResetConnection(byte[] data, ThrowingConsumer<SocketChannel> consumer)
|
||||
throws IOException
|
||||
{
|
||||
var loopback = InetAddress.getLoopbackAddress();
|
||||
try (var listener = new ServerSocket()) {
|
||||
listener.bind(new InetSocketAddress(loopback, 0));
|
||||
try (var sc = SocketChannel.open()) {
|
||||
sc.connect(listener.getLocalSocketAddress());
|
||||
try (Socket peer = listener.accept()) {
|
||||
if (data != null) {
|
||||
peer.getOutputStream().write(data);
|
||||
}
|
||||
peer.setSoLinger(true, 0);
|
||||
}
|
||||
consumer.accept(sc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user