com.caucho.vfs.JniSocketImpl Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
*
* @author Scott Ferguson
*/
package com.caucho.vfs;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.caucho.inject.Module;
import com.caucho.server.util.CauchoSystem;
import com.caucho.util.CurrentTime;
import com.caucho.util.JniTroubleshoot;
import com.caucho.util.JniUtil;
import com.caucho.util.L10N;
/**
* Abstract socket to handle both normal sockets and bin/resin sockets.
*/
@Module
public final class JniSocketImpl extends QSocket {
private static final L10N L = new L10N(JniSocketImpl.class);
private static final Logger log
= Logger.getLogger(JniSocketImpl.class.getName());
private static final JniTroubleshoot _jniTroubleshoot;
private JniServerSocketImpl _ss;
private final long _socketFd;
private int _nativeFd;
private JniStream _stream;
private final byte []_localAddrBuffer = new byte[16];
private final char []_localAddrCharBuffer = new char[256];
private int _localAddrLength;
private String _localName;
private InetAddress _localAddr;
private int _localPort;
private final byte []_remoteAddrBuffer = new byte[16];
private final char []_remoteAddrCharBuffer = new char[256];
private int _remoteAddrLength;
private String _remoteName;
private InetAddress _remoteAddr;
private int _remotePort;
private boolean _isSecure;
private Object _readLock = new Object();
private Object _writeLock = new Object();
private boolean _isNativeFlushRequired;
private long _socketTimeout;
private long _requestExpireTime;
private final AtomicBoolean _isClosed = new AtomicBoolean();
// private ByteBuffer _byteBuffer = ByteBuffer.allocate(TempBuffer.SIZE);
// private ByteBuffer _byteBuffer = ByteBuffer.wrap(new byte[TempBuffer.SIZE]);
// private ByteBuffer _byteBuffer = createByteBuffer(TempBuffer.SIZE);
public JniSocketImpl()
{
_socketFd = nativeAllocate();
}
public static boolean isEnabled()
{
return _jniTroubleshoot.isEnabled();
}
public static String getInitMessage()
{
if (! _jniTroubleshoot.isEnabled()) {
return _jniTroubleshoot.getMessage();
}
else {
return null;
}
}
public static JniSocketImpl connect(String host, int port)
throws IOException
{
JniSocketImpl socket = new JniSocketImpl();
if (socket.connectImpl(host, port)) {
return socket;
}
else {
socket.close();
return null;
}
}
/**
* Creates the new server socket.
*/
public boolean connectImpl(String host, int port)
throws IOException
{
_socketTimeout = 10000;
_nativeFd = -1;
_isClosed.set(false);
synchronized (_writeLock) {
return nativeConnect(_socketFd, host, port);
}
}
boolean accept(JniServerSocketImpl ss,
long serverSocketFd,
long socketTimeout)
{
_ss = ss;
_localName = null;
_localAddr = null;
_localAddrLength = 0;
_remoteName = null;
_remoteAddr = null;
_remoteAddrLength = 0;
_socketTimeout = socketTimeout;
_requestExpireTime = 0;
_nativeFd = -1;
_isSecure = false;
_isClosed.set(false);
synchronized (_writeLock) {
// initialize fields from the _fd
/*
return nativeAccept(serverSocketFd, _socketFd, _localAddrBuffer, _remoteAddrBuffer);
*/
return nativeAccept(serverSocketFd, _socketFd);
}
}
/**
* Initialize new connection from the socket.
*
* @param buf the initial buffer
* @param offset the initial buffer offset
* @param length the initial buffer length
*
* @return int as if by read
*/
@Override
public int acceptInitialRead(byte []buffer, int offset, int length)
{
synchronized (_readLock) {
// initialize fields from the _fd
int result = nativeAcceptInit(_socketFd, _localAddrBuffer, _remoteAddrBuffer,
buffer, offset, length);
return result;
}
}
public long getFd()
{
return _socketFd;
}
public int getNativeFd()
{
if (_nativeFd < 0) {
_nativeFd = getNativeFd(_socketFd);
}
return _nativeFd;
}
/**
* Returns the server port that accepted the request.
*/
@Override
public int getLocalPort()
{
return _localPort;
// return getLocalPort(_fd);
}
/**
* Returns the remote client's host name.
*/
@Override
public String getRemoteHost()
{
if (_remoteName == null) {
byte []remoteAddrBuffer = _remoteAddrBuffer;
char []remoteAddrCharBuffer = _remoteAddrCharBuffer;
if (_remoteAddrLength <= 0) {
_remoteAddrLength = InetAddressUtil.createIpAddress(remoteAddrBuffer,
remoteAddrCharBuffer);
}
_remoteName = new String(remoteAddrCharBuffer, 0, _remoteAddrLength);
}
return _remoteName;
}
/**
* Returns the remote client's inet address.
*/
@Override
public InetAddress getRemoteAddress()
{
if (_remoteAddr == null) {
try {
_remoteAddr = InetAddress.getByName(getRemoteHost());
} catch (Exception e) {
log.log(Level.FINE, e.toString(), e);
}
}
return _remoteAddr;
}
/**
* Returns the remote client's inet address.
*/
@Override
public int getRemoteAddress(byte []buffer, int offset, int length)
{
int len = _remoteAddrLength;
if (len <= 0) {
len = _remoteAddrLength = InetAddressUtil.createIpAddress(_remoteAddrBuffer,
_remoteAddrCharBuffer);
}
char []charBuffer = _remoteAddrCharBuffer;
for (int i = len - 1; i >= 0; i--) {
buffer[offset + i] = (byte) charBuffer[i];
}
return len;
}
/**
* Returns the remote client's inet address.
*/
@Override
public byte []getRemoteIP()
{
return _remoteAddrBuffer;
}
/**
* Returns the remote client's port.
*/
@Override
public int getRemotePort()
{
return _remotePort;
// return getRemotePort(_fd);
}
/**
* Returns the local server's host name.
*/
@Override
public String getLocalHost()
{
if (_localName == null) {
byte []localAddrBuffer = _localAddrBuffer;
char []localAddrCharBuffer = _localAddrCharBuffer;
if (_localAddrLength <= 0) {
_localAddrLength = InetAddressUtil.createIpAddress(localAddrBuffer,
localAddrCharBuffer);
}
_localName = new String(localAddrCharBuffer, 0, _localAddrLength);
}
return _localName;
}
/**
* Returns the local server's inet address.
*/
@Override
public InetAddress getLocalAddress()
{
if (_localAddr == null) {
try {
_localAddr = InetAddress.getByName(getLocalHost());
} catch (Exception e) {
log.log(Level.FINE, e.toString(), e);
}
}
return _localAddr;
}
/**
* Returns the local server's inet address.
*/
public int getLocalAddress(byte []buffer, int offset, int length)
{
System.arraycopy(_localAddrBuffer, 0, buffer, offset, _localAddrLength);
return _localAddrLength;
}
/**
* Set true for secure.
*/
public void setSecure(boolean isSecure)
{
_isSecure = isSecure;
}
/**
* Returns true if the connection is secure.
*/
@Override
public final boolean isSecure()
{
// return isSecure(_fd);
return _isSecure;
}
/**
* Returns the cipher for an ssl connection.
*/
@Override
public String getCipherSuite()
{
synchronized (this) {
return getCipher(_socketFd);
}
}
/**
* Returns the protocol for an ssl connection.
*/
@Override
public String getSslProtocol()
{
synchronized (this) {
return getSslProtocol(_socketFd);
}
}
/**
* Returns the number of bits in the cipher for an ssl connection.
*/
@Override
public int getCipherBits()
{
return getCipherBits(_socketFd);
}
/**
* Returns the client certificate.
*/
@Override
public X509Certificate getClientCertificate()
throws java.security.cert.CertificateException
{
TempBuffer tb = TempBuffer.allocate();
byte []buffer = tb.getBuffer();
int len = getClientCertificate(_socketFd, buffer, 0, buffer.length);
X509Certificate cert = null;
if (len > 0 && len < buffer.length) {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream is = new ByteArrayInputStream(buffer, 0, len);
cert = (X509Certificate) cf.generateCertificate(is);
is.close();
} catch (IOException e) {
return null;
}
}
TempBuffer.free(tb);
tb = null;
return cert;
}
/**
* Sets the expire time
*/
@Override
public void setRequestExpireTime(long expireTime)
{
_requestExpireTime = expireTime;
}
/**
* Read non-blocking
*/
@Override
public boolean isEof()
{
synchronized (_readLock) {
return nativeIsEof(_socketFd);
}
}
/**
* Reads from the socket.
*/
public int read(byte []buffer, int offset, int length, long timeout)
throws IOException
{
if (length == 0) {
throw new IllegalArgumentException();
}
else if (buffer.length < length) {
throw new ArrayIndexOutOfBoundsException();
}
long requestExpireTime = _requestExpireTime;
if (requestExpireTime > 0 && requestExpireTime < CurrentTime.getCurrentTime()) {
close();
throw new ClientDisconnectException(L.l("{0}: request-timeout read",
getRemoteAddress()));
}
int result = 0;
synchronized (_readLock) {
long now = CurrentTime.getCurrentTimeActual();
long expires;
// gap is because getCurrentTimeActual() isn't exact
long gap = 20;
if (timeout >= 0)
expires = timeout + now - gap;
else
expires = _socketTimeout + now - gap;
do {
result = readNative(_socketFd, buffer, offset, length, timeout);
now = CurrentTime.getCurrentTimeActual();
timeout = expires - now;
} while (result == JniStream.TIMEOUT_EXN && timeout > 0);
}
return result;
}
/**
* Writes to the socket.
*/
public int write(byte []buffer, int offset, int length, boolean isEnd)
throws IOException
{
int result;
long requestExpireTime = _requestExpireTime;
if (requestExpireTime > 0 && requestExpireTime < CurrentTime.getCurrentTime()) {
close();
throw new ClientDisconnectException(L.l("{0}: request-timeout write exp={0}s",
getRemoteAddress(),
CurrentTime.getCurrentTime() - requestExpireTime));
}
synchronized (_writeLock) {
long now = CurrentTime.getCurrentTimeActual();
long expires = _socketTimeout + now;
// _isNativeFlushRequired = true;
do {
result = writeNative(_socketFd, buffer, offset, length);
//byte []tempBuffer = _byteBuffer.array();
//System.out.println("TEMP: " + tempBuffer);
//System.arraycopy(buffer, offset, tempBuffer, 0, length);
//_byteBuffer.position(0);
//_byteBuffer.put(buffer, offset, length);
//result = writeNativeNio(_fd, _byteBuffer, 0, length);
} while (result == JniStream.TIMEOUT_EXN
&& CurrentTime.getCurrentTimeActual() < expires);
}
if (isEnd) {
close();
}
return result;
}
/*
public int writeMmap(long mmapAddress, long mmapOffset, int mmapLength)
throws IOException
{
int result;
long requestExpireTime = _requestExpireTime;
if (requestExpireTime > 0 && requestExpireTime < CurrentTime.getCurrentTime()) {
close();
throw new ClientDisconnectException(L.l("{0}: request-timeout write",
getRemoteAddress()));
}
synchronized (_writeLock) {
long now = Alarm.getCurrentTimeActual();
long expires = _socketTimeout + now;
do {
result = writeMmapNative(_socketFd, null, 0, 0,
mmapAddress, mmapOffset, mmapLength);
} while (result == JniStream.TIMEOUT_EXN
&& Alarm.getCurrentTimeActual() < expires);
}
return result;
}
*/
public boolean isMmapEnabled()
{
return true;
}
public int writeMmap(long mmapAddress,
long []mmapBlocks,
long mmapOffset,
long mmapLength)
throws IOException
{
int result;
long requestExpireTime = _requestExpireTime;
if (requestExpireTime > 0 && requestExpireTime < CurrentTime.getCurrentTime()) {
close();
throw new ClientDisconnectException(L.l("{0}: request-timeout write",
getRemoteAddress()));
}
synchronized (_writeLock) {
long now = CurrentTime.getCurrentTimeActual();
long expires = _socketTimeout + now;
_isNativeFlushRequired = true;
do {
result = writeMmapNative(_socketFd,
mmapAddress, mmapBlocks, mmapOffset, mmapLength);
} while (result == JniStream.TIMEOUT_EXN
&& CurrentTime.getCurrentTimeActual() < expires);
}
return result;
}
public boolean isSendfileEnabled()
{
return (JniServerSocketImpl.isSendfileEnabledStatic()
&& _ss.isSendfileEnabled());
}
public int writeSendfile(byte []buffer, int offset, int length,
byte []fileName, int nameLength, long fileLength)
throws IOException
{
int result;
long requestExpireTime = _requestExpireTime;
if (requestExpireTime > 0 && requestExpireTime < CurrentTime.getCurrentTime()) {
close();
throw new ClientDisconnectException(L.l("{0}: request-timeout sendfile",
getRemoteAddress()));
}
synchronized (_writeLock) {
long now = CurrentTime.getCurrentTimeActual();
long expires = _socketTimeout + now;
_isNativeFlushRequired = false;
do {
result = writeSendfileNative(_socketFd, buffer, offset, length,
fileName, nameLength, fileLength);
// result = writeSendfileNative(_socketFd, fileName, nameLength, fileLength);
} while (result == JniStream.TIMEOUT_EXN
&& CurrentTime.getCurrentTimeActual() < expires);
}
return result;
}
/**
* Flushes the socket.
*/
public int flush()
throws IOException
{
// XXX: only on cork
if (! _isNativeFlushRequired) {
return 0;
}
synchronized (_writeLock) {
_isNativeFlushRequired = false;
return flushNative(_socketFd);
}
}
/**
* Returns a stream impl for the socket encapsulating the
* input and output stream.
*/
@Override
public StreamImpl getStream()
throws IOException
{
if (_stream == null)
_stream = new JniStream(this);
_stream.init();
return _stream;
}
public long getTotalReadBytes()
{
return (_stream == null) ? 0 : _stream.getTotalReadBytes();
}
public long getTotalWriteBytes()
{
return (_stream == null) ? 0 : _stream.getTotalWriteBytes();
}
/**
* Returns true if closed.
*/
@Override
public boolean isClosed()
{
return _isClosed.get();
}
/**
* Closes the socket.
*
* XXX: potential sync issues
*/
@Override
public void forceShutdown()
{
// can't be locked because of shutdown
nativeCloseFd(_socketFd);
}
@Override
public void disconnect()
{
// can't be locked because of shutdown
nativeCloseFd(_socketFd);
}
/**
* Closes the socket.
*/
@Override
public void close()
throws IOException
{
if (_isClosed.getAndSet(true))
return;
_ss = null;
if (_stream != null) {
_stream.close();
}
// XXX: can't be locked because of shutdown
_nativeFd = -1;
nativeClose(_socketFd);
}
@Override
protected void finalize()
throws Throwable
{
long fd = _socketFd;
// _socketFd = 0;
try {
super.finalize();
nativeClose(fd);
} catch (Throwable e) {
}
nativeFree(fd);
}
native int getNativeFd(long fd);
native boolean nativeIsEof(long fd);
/*
private native boolean nativeAccept(long serverSocketFd,
long socketfd,
byte []localAddress,
byte []remoteAddress);
*/
private native boolean nativeAccept(long serverSocketFd,
long socketfd);
private native int nativeAcceptInit(long socketfd,
byte []localAddress,
byte []remoteAddress,
byte []buffer,
int offset,
int length);
private native boolean nativeConnect(long socketfd,
String host,
int port);
native String getCipher(long fd);
native String getSslProtocol(long fd);
native int getCipherBits(long fd);
native int getClientCertificate(long fd,
byte []buffer,
int offset,
int length);
native int readNative(long fd, byte []buf, int offset, int length,
long timeout)
throws IOException;
private static native int writeNative(long fd, byte []buf, int offset, int length)
throws IOException;
/*
private native int writeCloseNative(long fd,
byte []buf, int offset, int length)
throws IOException;
*/
native int writeNative2(long fd,
byte []buf1, int off1, int len1,
byte []buf2, int off2, int len2)
throws IOException;
/*
native int writeMmapNative(long fd,
long mmapAddress, long mmapOffset, int mmapLength)
throws IOException;
*/
native int writeMmapNative(long fd,
long mmapAddress,
long []mmapBlocks,
long mmapOffset,
long mmapLength)
throws IOException;
native int writeSendfileNative(long socket,
byte []buffer, int offset, int length,
byte []fileName, int nameLength,
long fileLength)
throws IOException;
/*
native int writeSendfileNative(long socket,
byte []fileName, int nameLength,
long fileLength)
throws IOException;
*/
/*
native int writeNativeNio(long fd,
ByteBuffer byteBuffer, int offset, int length)
throws IOException;
native ByteBuffer createByteBuffer(int length);
*/
native int flushNative(long fd) throws IOException;
private native void nativeCloseFd(long fd);
private native void nativeClose(long fd);
native long nativeAllocate();
native void nativeFree(long fd);
@Override
public String toString()
{
return ("JniSocketImpl$" + System.identityHashCode(this)
+ "[" + Long.toHexString(_socketFd) + ",fd=" + getNativeFd(_socketFd) + "]");
}
static {
JniTroubleshoot jniTroubleshoot = null;
JniUtil.acquire();
try {
System.loadLibrary("resin_os");
jniTroubleshoot
= new JniTroubleshoot(JniSocketImpl.class, "resin_os");
}
catch (Throwable e) {
jniTroubleshoot
= new JniTroubleshoot(JniSocketImpl.class, "resin_os", e);
} finally {
JniUtil.release();
}
_jniTroubleshoot = jniTroubleshoot;
// force instantiation of client disconnect
new ClientDisconnectException();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy