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

@ -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