public class RecordingOutputStream extends OutputStream
The RecordingOutputStream uses an in-memory buffer and backing disk file to allow it to record streams of arbitrary length limited only by available disk space.
As long as the stream recorded is smaller than the in-memory buffer, no disk access will occur.
Recorded content can be recovered as a ReplayInputStream (via getReplayInputStream() or, for only the content after the content-begin-mark is set, getContentReplayInputStream() ) or as a ReplayCharSequence (via getReplayCharSequence()).
This class is also used as a straight output stream
by RecordingInputStream to which it records all reads.
RecordingInputStream is exploiting the file backed buffer
facility of this class passing null for the stream
to wrap. TODO: Make a FileBackedOutputStream class that is
subclassed by RecordingInputStream.
| Modifier and Type | Field and Description |
|---|---|
protected String |
backingFilename |
protected OutputStream |
diskStream |
protected int[] |
lastTwoBytes
While messageBodyBeginMark is not set, the last two bytes seen.
|
protected static Logger |
logger |
protected static long |
MAX_HEADER_MATERIAL
Maximum amount of header material to accept without the content
body beginning -- if more, throw a RecorderTooMuchHeaderException.
|
protected long |
maxLength
maximum length of material to record before throwing exception
|
protected long |
maxRateBytesPerMs
maximum rate to record (adds delays to hit target rate)
|
protected long |
messageBodyBeginMark
When recording HTTP, where the content-body starts.
|
protected long |
size
Size of recording.
|
protected long |
startTime
time recording begins for timeout, rate calculations
|
protected long |
timeoutMs
maximum time to record before throwing exception
|
| Constructor and Description |
|---|
RecordingOutputStream(int bufferSize,
String backingFilename)
Create a new RecordingOutputStream.
|
| Modifier and Type | Method and Description |
|---|---|
protected void |
checkLimits()
Check any enforced limits.
|
void |
chopAtMessageBodyBegin()
Forget about anything past the point where the content-body starts.
|
void |
clearForReuse() |
void |
close() |
protected void |
closeDiskStream() |
void |
closeRecorder() |
protected OutputStream |
ensureDiskStream() |
void |
flush() |
int |
getBufferLength() |
byte[] |
getDigestValue()
Return the digest value for any recorded, digested data.
|
long |
getMessageBodyBegin()
Return stored message-body-begin-mark (which is also end-of-headers)
|
ReplayInputStream |
getMessageBodyReplayInputStream()
Return a replay stream, cued up to begining of content
|
long |
getRemainingLength()
Return number of bytes that could be recorded without hitting
length limit
|
ReplayInputStream |
getReplayInputStream() |
ReplayInputStream |
getReplayInputStream(long skip) |
long |
getResponseContentLength() |
long |
getSize() |
boolean |
isOpen() |
void |
mark()
When used alongside a mark-supporting RecordingInputStream, remember
a position reachable by a future reset().
|
void |
markMessageBodyBegin()
Remember the current position as the start of the "message
body".
|
void |
open()
Wrap the given stream, both recording and passing along any data written
to this RecordingOutputStream.
|
void |
open(OutputStream wrappedStream)
Wrap the given stream, both recording and passing along any data written
to this RecordingOutputStream.
|
void |
reset()
When used alongside a mark-supporting RecordingInputStream, reset
the position to that saved by previous mark().
|
void |
resetLimits()
Reset limits to effectively-unlimited defaults
|
void |
setDigest(MessageDigest md)
Sets a digest function which may be applied to recorded data.
|
void |
setDigest(String algorithm)
Sets a digest function which may be applied to recorded data.
|
void |
setLimits(long length,
long milliseconds,
long rateKBps)
Set limits on length, time, and rate to enforce.
|
void |
setSha1Digest()
Convenience method for setting SHA1 digest.
|
void |
startDigest()
Starts digesting recorded data, if a MessageDigest has been
set.
|
void |
write(byte[] b,
int off,
int len) |
void |
write(int b) |
writeprotected static Logger logger
protected long size
protected String backingFilename
protected OutputStream diskStream
protected static final long MAX_HEADER_MATERIAL
protected long maxLength
protected long timeoutMs
protected long maxRateBytesPerMs
protected long startTime
protected long messageBodyBeginMark
protected int[] lastTwoBytes
This class does automatic detection of http message body begin (i.e. end of http headers). Unfortunately httpcomponents did not want to add functionality to help us with this, see https://issues.apache.org/jira/browse/HTTPCORE-325
It works like this: while messageBodyBeginMark is not set, we remember the last two bytes seen, and look at each byte we write. If the lastTwoBytes+currentByte is "\n\r\n", or lastTwoBytes[1]+currentByte is "\n\n" then we call markMessageBodyBegin() at the position after currentByte.
An assumption here is that protocols other than http don't have headers, and for those protocols the user of this class will call markMessageBodyBegin() at position 0 before writing anything.
public RecordingOutputStream(int bufferSize,
String backingFilename)
bufferSize - Buffer size to use.backingFilename - Name of backing file to use.public void open()
throws IOException
IOException - If failed creation of backing file.public void open(OutputStream wrappedStream) throws IOException
wrappedStream - Stream to wrap. May be null for case where we
want to write to a file backed stream only.IOException - If failed creation of backing file.protected OutputStream ensureDiskStream() throws FileNotFoundException
FileNotFoundExceptionpublic void write(int b)
throws IOException
write in class OutputStreamIOExceptionpublic void write(byte[] b,
int off,
int len)
throws IOException
write in class OutputStreamIOExceptionprotected void checkLimits()
throws RecorderIOException
RecorderIOExceptionpublic void close()
throws IOException
close in interface Closeableclose in interface AutoCloseableclose in class OutputStreamIOExceptionprotected void closeDiskStream()
throws IOException
IOExceptionpublic void closeRecorder()
throws IOException
IOExceptionpublic void flush()
throws IOException
flush in interface Flushableflush in class OutputStreamIOExceptionpublic ReplayInputStream getReplayInputStream() throws IOException
IOExceptionpublic ReplayInputStream getReplayInputStream(long skip) throws IOException
IOExceptionpublic ReplayInputStream getMessageBodyReplayInputStream() throws IOException
IOExceptionpublic long getSize()
public void markMessageBodyBegin()
public long getMessageBodyBegin()
public void startDigest()
public void setSha1Digest()
setDigest(String)public void setDigest(String algorithm)
setDigest(MessageDigest)
is that this method tries to reuse MethodDigest instance if already allocated
and of appropriate algorithm.algorithm - Message digest algorithm to use.setDigest(MessageDigest)public void setDigest(MessageDigest md)
md - Message digest function to use.public byte[] getDigestValue()
public long getResponseContentLength()
public boolean isOpen()
public int getBufferLength()
public void mark()
public void reset()
public void setLimits(long length,
long milliseconds,
long rateKBps)
length - milliseconds - rateKBps - public void resetLimits()
public long getRemainingLength()
public void chopAtMessageBodyBegin()
lastTwoBytespublic void clearForReuse()
throws IOException
IOExceptionCopyright © 2015 The International Internet Preservation Consortium. All Rights Reserved.