com.caucho.vfs.SocketStream Maven / Gradle / Ivy
Show all versions of resin Show documentation
/*
* Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
* Free SoftwareFoundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.vfs;
import java.io.InterruptedIOException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.logging.*;
import com.caucho.inject.Module;
/**
* Specialized stream to handle sockets.
*
* Unlike VfsStream, when the read() throws and IOException or
* a SocketException, SocketStream will throw a ClientDisconnectException.
*/
@Module
public class SocketStream extends StreamImpl {
private static final Logger log
= Logger.getLogger(SocketStream.class.getName());
private static byte []UNIX_NEWLINE = new byte[] { (byte) '\n' };
private Socket _s;
private InputStream _is;
private OutputStream _os;
private boolean _needsFlush;
private byte []_newline = UNIX_NEWLINE;
private boolean _throwReadInterrupts = false;
private long _totalReadBytes;
private long _totalWriteBytes;
public SocketStream()
{
}
public SocketStream(Socket s)
{
init(s);
}
/**
* Initialize the SocketStream with a new Socket.
*
* @param s the new socket.
*/
public void init(Socket s)
{
_s = s;
_is = null;
_os = null;
_needsFlush = false;
}
/**
* Initialize the SocketStream with a new Socket.
*
* @param s the new socket.
*/
public void init(InputStream is, OutputStream os)
{
_is = is;
_os = os;
_needsFlush = false;
}
/**
* If true, throws read interrupts instead of returning an end of
* fail. Defaults to false.
*/
public void setThrowReadInterrupts(boolean allowThrow)
{
_throwReadInterrupts = allowThrow;
}
/**
* If true, throws read interrupts instead of returning an end of
* fail. Defaults to false.
*/
public boolean getThrowReadInterrupts()
{
return _throwReadInterrupts;
}
public void setNewline(byte []newline)
{
_newline = newline;
}
@Override
public byte []getNewline()
{
return _newline;
}
/**
* Returns true if stream is readable and bytes can be skipped.
*/
@Override
public boolean hasSkip()
{
return canRead();
}
/**
* Skips bytes in the file.
*
* @param n the number of bytes to skip
*
* @return the actual bytes skipped.
*/
@Override
public long skip(long n)
throws IOException
{
if (_is == null) {
if (_s == null)
return -1;
_is = _s.getInputStream();
}
return _is.skip(n);
}
/**
* Returns true since the socket stream can be read.
*/
@Override
public boolean canRead()
{
return _is != null || _s != null;
}
/**
* Reads bytes from the socket.
*
* @param buf byte buffer receiving the bytes
* @param offset offset into the buffer
* @param length number of bytes to read
* @return number of bytes read or -1
* @exception throws ClientDisconnectException if the connection is dropped
*/
@Override
public int read(byte []buf, int offset, int length) throws IOException
{
try {
if (_is == null) {
if (_s == null)
return -1;
_is = _s.getInputStream();
}
int readLength = _is.read(buf, offset, length);
if (readLength >= 0) {
_totalReadBytes += readLength;
}
return readLength;
} catch (InterruptedIOException e) {
if (_throwReadInterrupts)
throw e;
log.log(Level.FINEST, e.toString(), e);
} catch (IOException e) {
if (_throwReadInterrupts)
throw e;
log.log(Level.FINER, e.toString(), e);
// server/0611
/*
try {
close();
} catch (IOException e1) {
}
*/
}
return -1;
}
/**
* Reads bytes from the socket.
*
* @param buf byte buffer receiving the bytes
* @param offset offset into the buffer
* @param length number of bytes to read
* @return number of bytes read or -1
* @exception throws ClientDisconnectException if the connection is dropped
*/
@Override
public int readTimeout(byte []buf, int offset, int length, long timeout)
throws IOException
{
Socket s = _s;
if (s == null)
return -1;
int oldTimeout = s.getSoTimeout();
try {
s.setSoTimeout((int) timeout);
int readLength = read(buf, offset, length);
return readLength;
} finally {
s.setSoTimeout(oldTimeout);
}
}
/**
* Returns the number of bytes available to be read from the input stream.
*/
@Override
public int getAvailable() throws IOException
{
if (_is == null) {
if (_s == null)
return -1;
_is = _s.getInputStream();
}
return _is.available();
}
@Override
public boolean canWrite()
{
return _os != null || _s != null;
}
/**
* Writes bytes to the socket.
*
* @param buf byte buffer containing the bytes
* @param offset offset into the buffer
* @param length number of bytes to read
* @param isEnd if the write is at a close.
*
* @exception throws ClientDisconnectException if the connection is dropped
*/
@Override
public void write(byte []buf, int offset, int length, boolean isEnd)
throws IOException
{
if (_os == null) {
if (_s == null)
return;
_os = _s.getOutputStream();
}
try {
_needsFlush = true;
_os.write(buf, offset, length);
_totalWriteBytes += length;
} catch (IOException e) {
IOException exn = ClientDisconnectException.create(this + ":" + e, e);
try {
close();
} catch (IOException e1) {
}
throw exn;
}
}
/**
* Flushes the socket.
*/
@Override
public void flush() throws IOException
{
if (_os == null || ! _needsFlush)
return;
_needsFlush = false;
try {
_os.flush();
} catch (IOException e) {
try {
close();
} catch (IOException e1) {
}
throw ClientDisconnectException.create(e);
}
}
public void resetTotalBytes()
{
_totalReadBytes = 0;
_totalWriteBytes = 0;
}
public long getTotalReadBytes()
{
return _totalReadBytes;
}
public long getTotalWriteBytes()
{
return _totalWriteBytes;
}
/**
* Closes the write half of the stream.
*/
@Override
public void closeWrite() throws IOException
{
OutputStream os = _os;
_os = null;
// since the output stream is opened lazily, we might
// need to open it
if (_s != null) {
try {
_s.shutdownOutput();
} catch (UnsupportedOperationException e) {
log.log(Level.FINEST, e.toString(), e);
} catch (Exception e) {
log.log(Level.FINER, e.toString(), e);
}
}
// SSLSocket doesn't support shutdownOutput()
if (os != null) {
os.close();
}
}
/**
* Closes the underlying sockets and socket streams.
*/
@Override
public void close() throws IOException
{
Socket s = _s;
_s = null;
OutputStream os = _os;
_os = null;
InputStream is = _is;
_is = null;
try {
if (os != null)
os.close();
if (is != null)
is.close();
} finally {
if (s != null)
s.close();
}
}
@Override
public String toString()
{
return getClass().getSimpleName() + "[" + _s + "]";
}
}