package org.torproject.jtor.circuits.impl;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import org.torproject.jtor.circuits.cells.RelayCell;
public class TorInputStream extends InputStream {
private final BlockingQueue<RelayCell> incomingCells;
private final StreamImpl stream;
private ByteBuffer currentBuffer;
private int availableBytes;
private volatile boolean isClosed;
private boolean isEOF;
TorInputStream(StreamImpl stream) {
this.stream = stream;
this.isClosed = false;
incomingCells = new LinkedBlockingQueue<RelayCell>();
}
@Override
public synchronized int read() throws IOException {
checkOpen();
while(currentBuffer == null || !currentBuffer.hasRemaining())
fillBuffer();
if(isEOF)
return -1;
synchronized(incomingCells) {
availableBytes -= 1;
}
return currentBuffer.get() & 0xFF;
}
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public synchronized int read(byte[] b, int off, int len) throws IOException {
checkOpen();
if(b == null)
throw new NullPointerException();
if( (off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0))
throw new IndexOutOfBoundsException();
if(len == 0)
return 0;
while(!isEOF && (currentBuffer == null || !currentBuffer.hasRemaining()))
fillBuffer();
if(isEOF)
return -1;
int bytesRead = 0;
synchronized(incomingCells) {
while(availableBytes > 0) {
if(currentBuffer.remaining() >= len) {
currentBuffer.get(b, off, len);
availableBytes -= len;
bytesRead += len;
return bytesRead;
}
int rem = currentBuffer.remaining();
currentBuffer.get(b, off, rem);
availableBytes -= rem;
bytesRead += rem;
len -= rem;
off += rem;
if(availableBytes > 0) {
fillBuffer();
if(isEOF)
return bytesRead;
}
}
}
return bytesRead;
}
public int available() {
synchronized(incomingCells) {
return availableBytes;
}
}
public void close() {
isClosed = true;
stream.close();
}
void addEndCell(RelayCell cell) {
if(isClosed)
return;
incomingCells.add(cell);
}
void addInputCell(RelayCell cell) {
if(isClosed)
return;
synchronized(incomingCells) {
availableBytes += cell.cellBytesRemaining();
incomingCells.add(cell);
}
}
private void checkOpen() throws IOException {
if (isClosed)
throw new IOException("Input stream closed");
}
private void fillBuffer() throws IOException {
checkOpen();
if((currentBuffer != null && currentBuffer.hasRemaining()) || isEOF)
return;
try {
final RelayCell nextCell = incomingCells.take();
if(nextCell.getRelayCommand() == RelayCell.RELAY_END) {
isEOF = true;
return;
}
currentBuffer = nextCell.getPayloadBuffer();
return;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Read interrupted");
}
}
int unflushedCellCount() {
return incomingCells.size();
}
public String toString() {
return "TorInputStream stream="+ stream.getStreamId() +" node="+ stream.getTargetNode();
}
}