public class RecordingOutputStream
extends java.io.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 java.lang.String |
backingFilename |
protected java.io.OutputStream |
diskStream |
protected int[] |
lastTwoBytes
While messageBodyBeginMark is not set, the last two bytes seen.
|
protected static java.util.logging.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,
java.lang.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 java.io.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(java.io.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(java.security.MessageDigest md)
Sets a digest function which may be applied to recorded data.
|
void |
setDigest(java.lang.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) |
protected static java.util.logging.Logger logger
protected long size
protected java.lang.String backingFilename
protected java.io.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,
java.lang.String backingFilename)
bufferSize - Buffer size to use.backingFilename - Name of backing file to use.public void open()
throws java.io.IOException
java.io.IOException - If failed creation of backing file.public void open(java.io.OutputStream wrappedStream)
throws java.io.IOException
wrappedStream - Stream to wrap. May be null for case where we
want to write to a file backed stream only.java.io.IOException - If failed creation of backing file.protected java.io.OutputStream ensureDiskStream()
throws java.io.FileNotFoundException
java.io.FileNotFoundExceptionpublic void write(int b)
throws java.io.IOException
write in class java.io.OutputStreamjava.io.IOExceptionpublic void write(byte[] b,
int off,
int len)
throws java.io.IOException
write in class java.io.OutputStreamjava.io.IOExceptionprotected void checkLimits()
throws RecorderIOException
RecorderIOExceptionpublic void close()
throws java.io.IOException
close in interface java.io.Closeableclose in interface java.lang.AutoCloseableclose in class java.io.OutputStreamjava.io.IOExceptionprotected void closeDiskStream()
throws java.io.IOException
java.io.IOExceptionpublic void closeRecorder()
throws java.io.IOException
java.io.IOExceptionpublic void flush()
throws java.io.IOException
flush in interface java.io.Flushableflush in class java.io.OutputStreamjava.io.IOExceptionpublic ReplayInputStream getReplayInputStream() throws java.io.IOException
java.io.IOExceptionpublic ReplayInputStream getReplayInputStream(long skip) throws java.io.IOException
java.io.IOExceptionpublic ReplayInputStream getMessageBodyReplayInputStream() throws java.io.IOException
java.io.IOExceptionpublic long getSize()
public void markMessageBodyBegin()
public long getMessageBodyBegin()
public void startDigest()
public void setSha1Digest()
setDigest(String)public void setDigest(java.lang.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(java.security.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 java.io.IOException
java.io.IOExceptionCopyright © 2019 The International Internet Preservation Consortium. All Rights Reserved.