8220493: Prepare Socket/ServerSocket for alternative platform SocketImpl

Co-authored-by: Michael McMahon <michael.x.mcmahon@oracle.com>
Reviewed-by: chegar
This commit is contained in:
Alan Bateman 2019-03-16 19:44:12 +00:00
parent c306e3f059
commit 8743be63c4
12 changed files with 1525 additions and 188 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 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
@ -30,12 +30,16 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import sun.net.ConnectionResetException;
import sun.net.NetHooks;
import sun.net.PlatformSocketImpl;
import sun.net.ResourceManager;
import sun.net.util.SocketExceptions;
@ -46,7 +50,7 @@ import sun.net.util.SocketExceptions;
*
* @author Steven B. Byrne
*/
abstract class AbstractPlainSocketImpl extends SocketImpl {
abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSocketImpl {
/* instance variable for SO_TIMEOUT */
int timeout; // timeout in millisec
// traffic class
@ -450,15 +454,17 @@ abstract class AbstractPlainSocketImpl extends SocketImpl {
/**
* Accepts connections.
* @param s the connection
* @param si the socket impl
*/
protected void accept(SocketImpl s) throws IOException {
protected void accept(SocketImpl si) throws IOException {
si.fd = new FileDescriptor();
acquireFD();
try {
socketAccept(s);
socketAccept(si);
} finally {
releaseFD();
}
SocketCleanable.register(si.fd);
}
/**
@ -470,8 +476,14 @@ abstract class AbstractPlainSocketImpl extends SocketImpl {
throw new IOException("Socket Closed");
if (shut_rd)
throw new IOException("Socket input is shutdown");
if (socketInputStream == null)
socketInputStream = new SocketInputStream(this);
if (socketInputStream == null) {
PrivilegedExceptionAction<SocketInputStream> pa = () -> new SocketInputStream(this);
try {
socketInputStream = AccessController.doPrivileged(pa);
} catch (PrivilegedActionException e) {
throw (IOException) e.getCause();
}
}
}
return socketInputStream;
}
@ -489,8 +501,14 @@ abstract class AbstractPlainSocketImpl extends SocketImpl {
throw new IOException("Socket Closed");
if (shut_wr)
throw new IOException("Socket output is shutdown");
if (socketOutputStream == null)
socketOutputStream = new SocketOutputStream(this);
if (socketOutputStream == null) {
PrivilegedExceptionAction<SocketOutputStream> pa = () -> new SocketOutputStream(this);
try {
socketOutputStream = AccessController.doPrivileged(pa);
} catch (PrivilegedActionException e) {
throw (IOException) e.getCause();
}
}
}
return socketOutputStream;
}
@ -589,15 +607,10 @@ abstract class AbstractPlainSocketImpl extends SocketImpl {
}
}
void reset() throws IOException {
if (fd != null) {
socketClose();
}
fd = null;
super.reset();
void reset() {
throw new InternalError("should not get here");
}
/**
* Shutdown read-half of the socket connection;
*/

View file

@ -0,0 +1,171 @@
/*
* 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.
*/
package java.net;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
import java.util.Set;
import sun.net.PlatformSocketImpl;
/**
* A SocketImpl that delegates all methods to another SocketImpl.
*/
class DelegatingSocketImpl extends SocketImpl {
protected final SocketImpl delegate;
DelegatingSocketImpl(SocketImpl delegate) {
assert delegate instanceof PlatformSocketImpl;
this.delegate = Objects.requireNonNull(delegate);
}
final SocketImpl delegate() {
return delegate;
}
@Override
protected FileDescriptor getFileDescriptor() {
return delegate.getFileDescriptor();
}
@Override
protected InetAddress getInetAddress() {
return delegate.getInetAddress();
}
@Override
protected int getPort() {
return delegate.getPort();
}
@Override
protected int getLocalPort() {
return delegate.getLocalPort();
}
@Override
protected void create(boolean stream) throws IOException {
delegate.create(stream);
}
@Override
protected void connect(String host, int port) throws IOException {
delegate.connect(host, port);
}
@Override
protected void connect(InetAddress address, int port) throws IOException {
delegate.connect(address, port);
}
@Override
protected void connect(SocketAddress address, int timeout) throws IOException {
delegate.connect(address, timeout);
}
@Override
protected void bind(InetAddress host, int port) throws IOException {
delegate.bind(host, port);
}
@Override
protected void listen(int backlog) throws IOException {
delegate.listen(backlog);
}
@Override
protected void accept(SocketImpl s) throws IOException {
delegate.accept(s);
}
@Override
protected InputStream getInputStream() throws IOException {
return delegate.getInputStream();
}
@Override
protected OutputStream getOutputStream() throws IOException {
return delegate.getOutputStream();
}
@Override
protected int available() throws IOException {
return delegate.available();
}
@Override
protected void close() throws IOException {
delegate.close();
}
@Override
protected boolean supportsUrgentData() {
return delegate.supportsUrgentData();
}
@Override
protected void sendUrgentData(int data) throws IOException {
delegate.sendUrgentData(data);
}
@Override
protected Set<SocketOption<?>> supportedOptions() {
return delegate.supportedOptions();
}
@Override
protected <T> void setOption(SocketOption<T> opt, T value) throws IOException {
delegate.setOption(opt, value);
}
@Override
protected <T> T getOption(SocketOption<T> opt) throws IOException {
return delegate.getOption(opt);
}
@Override
public void setOption(int optID, Object value) throws SocketException {
delegate.setOption(optID, value);
}
@Override
public Object getOption(int optID) throws SocketException {
return delegate.getOption(optID);
}
@Override
protected void shutdownInput() throws IOException {
delegate.shutdownInput();
}
@Override
protected void shutdownOutput() throws IOException {
delegate.shutdownOutput();
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 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,14 +34,13 @@ import java.util.Set;
/**
* Basic SocketImpl that relies on the internal HTTP protocol handler
* implementation to perform the HTTP tunneling and authentication. The
* sockets impl is swapped out and replaced with the socket from the HTTP
* handler after the tunnel is successfully setup.
* implementation to perform the HTTP tunneling and authentication. Once
* connected, all socket operations delegate to a platform SocketImpl.
*
* @since 1.8
*/
/*package*/ class HttpConnectSocketImpl extends PlainSocketImpl {
/*package*/ class HttpConnectSocketImpl extends DelegatingSocketImpl {
private static final String httpURLClazzStr =
"sun.net.www.protocol.http.HttpURLConnection";
@ -76,12 +75,8 @@ import java.util.Set;
}
}
HttpConnectSocketImpl(String server, int port) {
this.server = server;
this.port = port;
}
HttpConnectSocketImpl(Proxy proxy) {
HttpConnectSocketImpl(Proxy proxy, SocketImpl delegate) {
super(delegate);
SocketAddress a = proxy.address();
if ( !(a instanceof InetSocketAddress) )
throw new IllegalArgumentException("Unsupported address type");
@ -91,6 +86,27 @@ import java.util.Set;
port = ad.getPort();
}
@Override
protected void connect(String host, int port) throws IOException {
connect(new InetSocketAddress(host, port), 0);
}
@Override
protected void connect(InetAddress address, int port) throws IOException {
connect(new InetSocketAddress(address, port), 0);
}
@Override
void setSocket(Socket socket) {
delegate.socket = socket;
super.setSocket(socket);
}
@Override
void setServerSocket(ServerSocket socket) {
throw new InternalError("should not get here");
}
@Override
protected void connect(SocketAddress endpoint, int timeout)
throws IOException
@ -117,21 +133,37 @@ import java.util.Set;
close();
// update the Sockets impl to the impl from the http Socket
AbstractPlainSocketImpl psi = (AbstractPlainSocketImpl) httpSocket.impl;
this.getSocket().impl = psi;
SocketImpl si = httpSocket.impl;
getSocket().setImpl(si);
// best effort is made to try and reset options previously set
Set<Map.Entry<Integer,Object>> options = optionsMap.entrySet();
try {
for(Map.Entry<Integer,Object> entry : options) {
psi.setOption(entry.getKey(), entry.getValue());
si.setOption(entry.getKey(), entry.getValue());
}
} catch (IOException x) { /* gulp! */ }
}
@Override
protected void listen(int backlog) {
throw new InternalError("should not get here");
}
@Override
protected void accept(SocketImpl s) {
throw new InternalError("should not get here");
}
@Override
void reset() {
throw new InternalError("should not get here");
}
@Override
public void setOption(int opt, Object val) throws SocketException {
super.setOption(opt, val);
delegate.setOption(opt, val);
if (external_address != null)
return; // we're connected, just return
@ -163,7 +195,10 @@ import java.util.Set;
URL destURL = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) destURL.openConnection(proxy);
conn.setConnectTimeout(connectTimeout);
conn.setReadTimeout(this.timeout);
int timeout = (int) getOption(SocketOptions.SO_TIMEOUT);
if (timeout > 0) {
conn.setReadTimeout(timeout);
}
conn.connect();
doTunneling(conn);
try {
@ -174,10 +209,14 @@ import java.util.Set;
}
}
private void doTunneling(HttpURLConnection conn) {
private void doTunneling(HttpURLConnection conn) throws IOException {
try {
doTunneling.invoke(conn);
} catch (ReflectiveOperationException x) {
Throwable cause = x.getCause();
if (cause instanceof IOException) {
throw (IOException) cause;
}
throw new InternalError("Should not reach here", x);
}
}
@ -187,7 +226,7 @@ import java.util.Set;
if (external_address != null)
return external_address.getAddress();
else
return super.getInetAddress();
return delegate.getInetAddress();
}
@Override
@ -195,6 +234,6 @@ import java.util.Set;
if (external_address != null)
return external_address.getPort();
else
return super.getPort();
return delegate.getPort();
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 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,9 +25,6 @@
package java.net;
import jdk.internal.access.JavaNetSocketAccess;
import jdk.internal.access.SharedSecrets;
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.reflect.Constructor;
@ -38,6 +35,10 @@ import java.security.PrivilegedExceptionAction;
import java.util.Set;
import java.util.Collections;
import jdk.internal.access.JavaNetSocketAccess;
import jdk.internal.access.SharedSecrets;
import sun.net.PlatformSocketImpl;
/**
* This class implements server sockets. A server socket waits for
* requests to come in over the network. It performs some operation
@ -290,13 +291,12 @@ class ServerSocket implements java.io.Closeable {
}
private void setImpl() {
SocketImplFactory factory = ServerSocket.factory;
if (factory != null) {
impl = factory.createSocketImpl();
checkOldImpl();
} else {
// No need to do a checkOldImpl() here, we know it's an up to date
// SocketImpl!
impl = new SocksSocketImpl();
impl = SocketImpl.createPlatformSocketImpl(true);
}
if (impl != null)
impl.setServerSocket(this);
@ -542,38 +542,134 @@ class ServerSocket implements java.io.Closeable {
* @spec JSR-51
*/
protected final void implAccept(Socket s) throws IOException {
SocketImpl si = null;
try {
if (s.impl == null)
s.setImpl();
else {
s.impl.reset();
}
si = s.impl;
s.impl = null;
si.address = new InetAddress();
si.fd = new FileDescriptor();
getImpl().accept(si);
SocketCleanable.register(si.fd); // raw fd has been set
SocketImpl si = s.impl;
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkAccept(si.getInetAddress().getHostAddress(),
si.getPort());
// Socket has no SocketImpl
if (si == null) {
si = implAccept();
s.setImpl(si);
s.postAccept();
return;
}
// Socket has a SOCKS or HTTP SocketImpl, need delegate
if (si instanceof DelegatingSocketImpl) {
si = ((DelegatingSocketImpl) si).delegate();
assert si instanceof PlatformSocketImpl;
}
// Accept connection with a platform or custom SocketImpl.
// For the platform SocketImpl case:
// - the connection is accepted with a new SocketImpl
// - the SO_TIMEOUT socket option is copied to the new SocketImpl
// - the Socket is connected to the new SocketImpl
// - the existing/old SocketImpl is closed
// For the custom SocketImpl case, the connection is accepted with the
// existing custom SocketImpl.
ensureCompatible(si);
if (impl instanceof PlatformSocketImpl) {
SocketImpl psi = platformImplAccept();
si.copyOptionsTo(psi);
s.setImpl(psi);
si.closeQuietly();
} else {
s.impl = null; // temporarily break connection to impl
try {
customImplAccept(si);
} finally {
s.impl = si; // restore connection to impl
}
} catch (IOException e) {
if (si != null)
si.reset();
s.impl = si;
throw e;
} catch (SecurityException e) {
if (si != null)
si.reset();
s.impl = si;
}
s.postAccept();
}
/**
* Accepts a connection with a new SocketImpl.
* @return the new SocketImpl
*/
private SocketImpl implAccept() throws IOException {
if (impl instanceof PlatformSocketImpl) {
return platformImplAccept();
} else {
// custom server SocketImpl, client SocketImplFactory must be set
SocketImplFactory factory = Socket.socketImplFactory();
if (factory == null) {
throw new IOException("An instance of " + impl.getClass() +
" cannot accept connection with 'null' SocketImpl:" +
" client socket implementation factory not set");
}
SocketImpl si = factory.createSocketImpl();
customImplAccept(si);
return si;
}
}
/**
* Accepts a connection with a new platform SocketImpl.
* @return the new platform SocketImpl
*/
private SocketImpl platformImplAccept() throws IOException {
assert impl instanceof PlatformSocketImpl;
// create a new platform SocketImpl and accept the connection
SocketImpl psi = SocketImpl.createPlatformSocketImpl(false);
implAccept(psi);
return psi;
}
/**
* Accepts a new connection with the given custom SocketImpl.
*/
private void customImplAccept(SocketImpl si) throws IOException {
assert !(impl instanceof PlatformSocketImpl)
&& !(si instanceof PlatformSocketImpl);
si.reset();
try {
// custom SocketImpl may expect fd/address objects to be created
si.fd = new FileDescriptor();
si.address = new InetAddress();
implAccept(si);
} catch (Exception e) {
si.reset();
throw e;
}
s.impl = si;
s.postAccept();
}
/**
* Accepts a new connection so that the given SocketImpl is connected to
* the peer. The SocketImpl and connection are closed if the connection is
* denied by the security manager.
* @throws IOException if an I/O error occurs
* @throws SecurityException if the security manager's checkAccept method fails
*/
private void implAccept(SocketImpl si) throws IOException {
assert !(si instanceof DelegatingSocketImpl);
// accept a connection
impl.accept(si);
// check permission, close SocketImpl/connection if denied
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
sm.checkAccept(si.getInetAddress().getHostAddress(), si.getPort());
} catch (SecurityException se) {
si.close();
throw se;
}
}
}
/**
* Throws IOException if the server SocketImpl and the given client
* SocketImpl are not both platform or custom SocketImpls.
*/
private void ensureCompatible(SocketImpl si) throws IOException {
if ((impl instanceof PlatformSocketImpl) != (si instanceof PlatformSocketImpl)) {
throw new IOException("An instance of " + impl.getClass() +
" cannot accept a connection with an instance of " + si.getClass());
}
}
/**
@ -778,7 +874,7 @@ class ServerSocket implements java.io.Closeable {
/**
* The factory for all server sockets.
*/
private static SocketImplFactory factory = null;
private static volatile SocketImplFactory factory;
/**
* Sets the server socket implementation factory for the

View file

@ -28,9 +28,10 @@ package java.net;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.channels.SocketChannel;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedAction;
import java.util.Set;
import java.util.Collections;
@ -75,6 +76,22 @@ class Socket implements java.io.Closeable {
*/
private boolean oldImpl = false;
/**
* Socket input/output streams
*/
private volatile InputStream in;
private volatile OutputStream out;
private static final VarHandle IN, OUT;
static {
try {
MethodHandles.Lookup l = MethodHandles.lookup();
IN = l.findVarHandle(Socket.class, "in", InputStream.class);
OUT = l.findVarHandle(Socket.class, "out", OutputStream.class);
} catch (Exception e) {
throw new InternalError(e);
}
}
/**
* Creates an unconnected socket, with the
* system-default type of SocketImpl.
@ -137,16 +154,22 @@ class Socket implements java.io.Closeable {
security.checkConnect(epoint.getAddress().getHostAddress(),
epoint.getPort());
}
impl = type == Proxy.Type.SOCKS ? new SocksSocketImpl(p)
: new HttpConnectSocketImpl(p);
// create a SOCKS or HTTP SocketImpl that delegates to a platform SocketImpl
SocketImpl delegate = SocketImpl.createPlatformSocketImpl(false);
impl = (type == Proxy.Type.SOCKS) ? new SocksSocketImpl(p, delegate)
: new HttpConnectSocketImpl(p, delegate);
impl.setSocket(this);
} else {
if (p == Proxy.NO_PROXY) {
// create a platform or custom SocketImpl for the DIRECT case
SocketImplFactory factory = Socket.factory;
if (factory == null) {
impl = new PlainSocketImpl();
impl.setSocket(this);
} else
setImpl();
impl = SocketImpl.createPlatformSocketImpl(false);
} else {
impl = factory.createSocketImpl();
}
impl.setSocket(this);
} else
throw new IllegalArgumentException("Invalid Proxy");
}
@ -491,24 +514,29 @@ class Socket implements java.io.Closeable {
});
}
void setImpl(SocketImpl si) {
impl = si;
impl.setSocket(this);
}
/**
* Sets impl to the system-default type of SocketImpl.
* @since 1.4
*/
void setImpl() {
SocketImplFactory factory = Socket.factory;
if (factory != null) {
impl = factory.createSocketImpl();
checkOldImpl();
} else {
// No need to do a checkOldImpl() here, we know it's an up to date
// SocketImpl!
impl = new SocksSocketImpl();
// create a SOCKS SocketImpl that delegates to a platform SocketImpl
SocketImpl delegate = SocketImpl.createPlatformSocketImpl(false);
impl = new SocksSocketImpl(delegate);
}
if (impl != null)
impl.setSocket(this);
}
/**
* Get the {@code SocketImpl} attached to this socket, creating
* it if necessary.
@ -907,18 +935,49 @@ class Socket implements java.io.Closeable {
throw new SocketException("Socket is not connected");
if (isInputShutdown())
throw new SocketException("Socket input is shutdown");
InputStream is = null;
try {
is = AccessController.doPrivileged(
new PrivilegedExceptionAction<>() {
public InputStream run() throws IOException {
return impl.getInputStream();
}
});
} catch (java.security.PrivilegedActionException e) {
throw (IOException) e.getException();
InputStream in = this.in;
if (in == null) {
// wrap the input stream so that the close method closes this socket
in = new SocketInputStream(this, impl.getInputStream());
if (!IN.compareAndSet(this, null, in)) {
in = this.in;
}
}
return in;
}
/**
* An InputStream that delegates read/available operations to an underlying
* input stream. The close method is overridden to close the Socket.
*
* This class is instrumented by Java Flight Recorder (JFR) to get socket
* I/O events.
*/
private static class SocketInputStream extends InputStream {
private final Socket parent;
private final InputStream in;
SocketInputStream(Socket parent, InputStream in) {
this.parent = parent;
this.in = in;
}
@Override
public int read() throws IOException {
byte[] a = new byte[1];
int n = read(a, 0, 1);
return (n > 0) ? (a[0] & 0xff) : -1;
}
@Override
public int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
@Override
public int available() throws IOException {
return in.available();
}
@Override
public void close() throws IOException {
parent.close();
}
return is;
}
/**
@ -946,18 +1005,44 @@ class Socket implements java.io.Closeable {
throw new SocketException("Socket is not connected");
if (isOutputShutdown())
throw new SocketException("Socket output is shutdown");
OutputStream os = null;
try {
os = AccessController.doPrivileged(
new PrivilegedExceptionAction<>() {
public OutputStream run() throws IOException {
return impl.getOutputStream();
}
});
} catch (java.security.PrivilegedActionException e) {
throw (IOException) e.getException();
OutputStream out = this.out;
if (out == null) {
// wrap the output stream so that the close method closes this socket
out = new SocketOutputStream(this, impl.getOutputStream());
if (!OUT.compareAndSet(this, null, out)) {
out = this.out;
}
}
return out;
}
/**
* An OutputStream that delegates write operations to an underlying output
* stream. The close method is overridden to close the Socket.
*
* This class is instrumented by Java Flight Recorder (JFR) to get socket
* I/O events.
*/
private static class SocketOutputStream extends OutputStream {
private final Socket parent;
private final OutputStream out;
SocketOutputStream(Socket parent, OutputStream out) {
this.parent = parent;
this.out = out;
}
@Override
public void write(int b) throws IOException {
byte[] a = new byte[] { (byte) b };
write(a, 0, 1);
}
@Override
public void write(byte b[], int off, int len) throws IOException {
out.write(b, off, len);
}
@Override
public void close() throws IOException {
parent.close();
}
return os;
}
/**
@ -1644,7 +1729,11 @@ class Socket implements java.io.Closeable {
/**
* The factory for all client sockets.
*/
private static SocketImplFactory factory = null;
private static volatile SocketImplFactory factory;
static SocketImplFactory socketImplFactory() {
return factory;
}
/**
* Sets the client socket implementation factory for the

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 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.io.OutputStream;
import java.io.FileDescriptor;
import java.util.Set;
import sun.net.PlatformSocketImpl;
/**
* The abstract class {@code SocketImpl} is a common superclass
* of all classes that actually implement sockets. It is used to
@ -43,6 +45,15 @@ import java.util.Set;
* @since 1.0
*/
public abstract class SocketImpl implements SocketOptions {
/**
* Creates an instance of platform's SocketImpl
*/
@SuppressWarnings("unchecked")
static <S extends SocketImpl & PlatformSocketImpl> S createPlatformSocketImpl(boolean server) {
return (S) new PlainSocketImpl();
}
/**
* The actual Socket object.
*/
@ -177,6 +188,15 @@ public abstract class SocketImpl implements SocketOptions {
*/
protected abstract void close() throws IOException;
/**
* Closes this socket, ignoring any IOException that is thrown by close.
*/
void closeQuietly() {
try {
close();
} catch (IOException ignore) { }
}
/**
* Places the input stream for this socket at "end of stream".
* Any data sent to this socket is acknowledged and then
@ -306,7 +326,8 @@ public abstract class SocketImpl implements SocketOptions {
",port=" + getPort() + ",localport=" + getLocalPort() + "]";
}
void reset() throws IOException {
void reset() {
fd = null;
address = null;
port = 0;
localport = 0;
@ -443,6 +464,19 @@ public abstract class SocketImpl implements SocketOptions {
}
}
/**
* Attempts to copy socket options from this SocketImpl to a target SocketImpl.
* At this time, only the SO_TIMEOUT make sense to copy.
*/
void copyOptionsTo(SocketImpl target) {
try {
Object timeout = getOption(SocketOptions.SO_TIMEOUT);
if (timeout instanceof Integer) {
target.setOption(SocketOptions.SO_TIMEOUT, timeout);
}
} catch (IOException ignore) { }
}
private static final Set<SocketOption<?>> socketOptions;
private static final Set<SocketOption<?>> serverSocketOptions;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2015, 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
@ -23,6 +23,7 @@
* questions.
*/
package java.net;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -36,11 +37,9 @@ import sun.net.www.ParseUtil;
/**
* SOCKS (V4 & V5) TCP socket implementation (RFC 1928).
* This is a subclass of PlainSocketImpl.
* Note this class should <b>NOT</b> be public.
*/
class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
class SocksSocketImpl extends DelegatingSocketImpl implements SocksConsts {
private String server = null;
private int serverPort = DEFAULT_PORT;
private InetSocketAddress external_address;
@ -49,11 +48,12 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
private InputStream cmdIn = null;
private OutputStream cmdOut = null;
SocksSocketImpl() {
// Nothing needed
SocksSocketImpl(SocketImpl delegate) {
super(delegate);
}
SocksSocketImpl(Proxy proxy) {
SocksSocketImpl(Proxy proxy, SocketImpl delegate) {
super(delegate);
SocketAddress a = proxy.address();
if (a instanceof InetSocketAddress) {
InetSocketAddress ad = (InetSocketAddress) a;
@ -75,7 +75,7 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
private synchronized void privilegedConnect(final String host,
final int port,
final int timeout)
throws IOException
throws IOException
{
try {
AccessController.doPrivileged(
@ -94,7 +94,7 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
private void superConnectServer(String host, int port,
int timeout) throws IOException {
super.connect(new InetSocketAddress(host, port), timeout);
delegate.connect(new InetSocketAddress(host, port), timeout);
}
private static int remainingMillis(long deadlineMillis) throws IOException {
@ -111,16 +111,23 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
private int readSocksReply(InputStream in, byte[] data, long deadlineMillis) throws IOException {
int len = data.length;
int received = 0;
while (received < len) {
int count;
try {
count = ((SocketInputStream)in).read(data, received, len - received, remainingMillis(deadlineMillis));
} catch (SocketTimeoutException e) {
throw new SocketTimeoutException("Connect timed out");
int originalTimeout = (int) getOption(SocketOptions.SO_TIMEOUT);
try {
while (received < len) {
int count;
int remaining = remainingMillis(deadlineMillis);
setOption(SocketOptions.SO_TIMEOUT, remaining);
try {
count = in.read(data, received, len - received);
} catch (SocketTimeoutException e) {
throw new SocketTimeoutException("Connect timed out");
}
if (count < 0)
throw new SocketException("Malformed reply from SOCKS server");
received += count;
}
if (count < 0)
throw new SocketException("Malformed reply from SOCKS server");
received += count;
} finally {
setOption(SocketOptions.SO_TIMEOUT, originalTimeout);
}
return received;
}
@ -239,6 +246,27 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
}
}
@Override
protected void connect(String host, int port) throws IOException {
connect(new InetSocketAddress(host, port), 0);
}
@Override
protected void connect(InetAddress address, int port) throws IOException {
connect(new InetSocketAddress(address, port), 0);
}
@Override
void setSocket(Socket soc) {
delegate.socket = soc;
super.setSocket(soc);
}
@Override
void setServerSocket(ServerSocket soc) {
throw new InternalError("should not get here");
}
/**
* Connects the Socks Socket to the specified endpoint. It will first
* connect to the SOCKS proxy and negotiate the access. If the proxy
@ -290,7 +318,7 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
/*
* No default proxySelector --> direct connection
*/
super.connect(epoint, remainingMillis(deadlineMillis));
delegate.connect(epoint, remainingMillis(deadlineMillis));
return;
}
URI uri;
@ -313,13 +341,13 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
java.util.Iterator<Proxy> iProxy = null;
iProxy = sel.select(uri).iterator();
if (iProxy == null || !(iProxy.hasNext())) {
super.connect(epoint, remainingMillis(deadlineMillis));
delegate.connect(epoint, remainingMillis(deadlineMillis));
return;
}
while (iProxy.hasNext()) {
p = iProxy.next();
if (p == null || p.type() != Proxy.Type.SOCKS) {
super.connect(epoint, remainingMillis(deadlineMillis));
delegate.connect(epoint, remainingMillis(deadlineMillis));
return;
}
@ -509,7 +537,15 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
external_address = epoint;
}
@Override
protected void listen(int backlog) {
throw new InternalError("should not get here");
}
@Override
protected void accept(SocketImpl s) {
throw new InternalError("should not get here");
}
/**
* Returns the value of this socket's {@code address} field.
@ -522,7 +558,7 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
if (external_address != null)
return external_address.getAddress();
else
return super.getInetAddress();
return delegate.getInetAddress();
}
/**
@ -536,7 +572,7 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
if (external_address != null)
return external_address.getPort();
else
return super.getPort();
return delegate.getPort();
}
@Override
@ -544,10 +580,15 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
if (cmdsock != null)
cmdsock.close();
cmdsock = null;
super.close();
delegate.close();
}
private String getUserName() {
return StaticProperty.userName();
}
@Override
void reset() {
throw new InternalError("should not get here");
}
}