package com.subgraph.orchid.circuits; import java.io.IOException; import java.io.OutputStream; import com.subgraph.orchid.RelayCell; import com.subgraph.orchid.circuits.cells.RelayCellImpl; public class TorOutputStream extends OutputStream { private final StreamImpl stream; private RelayCell currentOutputCell; private volatile boolean isClosed; private long bytesSent; TorOutputStream(StreamImpl stream) { this.stream = stream; this.bytesSent = 0; } private void flushCurrentOutputCell() { if(currentOutputCell != null && currentOutputCell.cellBytesConsumed() > RelayCell.HEADER_SIZE) { stream.waitForSendWindowAndDecrement(); stream.getCircuit().sendRelayCell(currentOutputCell); bytesSent += (currentOutputCell.cellBytesConsumed() - RelayCell.HEADER_SIZE); } currentOutputCell = new RelayCellImpl(stream.getTargetNode(), stream.getCircuit().getCircuitId(), stream.getStreamId(), RelayCell.RELAY_DATA); } long getBytesSent() { return bytesSent; } @Override public synchronized void write(int b) throws IOException { checkOpen(); if(currentOutputCell == null || currentOutputCell.cellBytesRemaining() == 0) flushCurrentOutputCell(); currentOutputCell.putByte(b); } public synchronized void write(byte[] data, int offset, int length) throws IOException { checkOpen(); if(currentOutputCell == null || currentOutputCell.cellBytesRemaining() == 0) flushCurrentOutputCell(); while(length > 0) { if(length < currentOutputCell.cellBytesRemaining()) { currentOutputCell.putByteArray(data, offset, length); return; } final int writeCount = currentOutputCell.cellBytesRemaining(); currentOutputCell.putByteArray(data, offset, writeCount); flushCurrentOutputCell(); offset += writeCount; length -= writeCount; } } private void checkOpen() throws IOException { if(isClosed) throw new IOException("Output stream is closed"); } public synchronized void flush() { if(isClosed) return; flushCurrentOutputCell(); } public synchronized void close() { if(isClosed) return; flush(); isClosed = true; currentOutputCell = null; stream.close(); } public String toString() { return "TorOutputStream stream="+ stream.getStreamId() +" node="+ stream.getTargetNode(); } }