/**
- * Return the number of bytes actually written to the output stream.
+ * Return the number of bytes the application has actually written to the
+ * output stream. This excludes chunking, compression, etc. as well as
+ * headers.
*/
- public long getContentCount() {
+ public long getContentWritten() {
return outputBuffer.getContentWritten();
}
+
+ /**
+ * Return the number of bytes the actually written to the socket. This
+ * includes chunking, compression, etc. but excludes headers.
+ */
+ public long getBytesWritten(boolean flush) {
+ if (flush) {
+ try {
+ outputBuffer.flush();
+ } catch (IOException ioe) {
+ // Ignore - the client has probably closed the connection
+ }
+ }
+ return coyoteResponse.getBytesWritten(flush);
+ }
+
/**
* Set the application commit flag.
*
public boolean isAppCommitted() {
return (this.appCommitted || isCommitted() || isSuspended()
|| ((getContentLength() > 0)
- && (getContentCount() >= getContentLength())));
+ && (getContentWritten() >= getContentLength())));
}
@Override
public void addElement(StringBuilder buf, Date date, Request request,
Response response, long time) {
- long length = response.getContentCount() ;
+ long length = response.getBytesWritten(true);
if (length <= 0 && conversion) {
buf.append('-');
} else {
// Do nothing on a 1xx, 2xx and 3xx status
// Do nothing if anything has been written already
- if ((statusCode < 400) || (response.getContentCount() > 0))
+ if ((statusCode < 400) || (response.getContentWritten() > 0))
return;
String message = RequestUtil.filter(response.getMessage());
String user = request.getRemoteUser();
String query=request.getRequestURI();
- long bytes = response.getContentCount() ;
+ long bytes = response.getBytesWritten(true);
if(bytes < 0)
bytes = 0;
int status = response.getStatus();
/**
* Output buffer.
*
- * This class is used internally by the protocol implementation. All writes from higher level code should happen
- * via Resonse.doWrite().
+ * This class is used internally by the protocol implementation. All writes from
+ * higher level code should happen via Resonse.doWrite().
*
* @author Remy Maucherat
*/
* Write the response. The caller ( tomcat ) owns the chunks.
*
* @param chunk data to write
- * @param response used to allow buffers that can be shared by multiple responses.
+ * @param response used to allow buffers that can be shared by multiple
+ * responses.
* @throws IOException
*/
public int doWrite(ByteChunk chunk, Response response)
throws IOException;
-
+ /**
+ * Bytes written to the underlying socket. This includes the effects of
+ * chunking, compression, etc.
+ *
+ * @return Bytes written for the current request
+ */
+ public long getBytesWritten();
}
}
public long getRequestBytesSent() {
- return req.getResponse().getBytesWritten();
+ return req.getResponse().getContentWritten();
}
public long getRequestProcessingTime() {
*/
void updateCounters() {
bytesReceived+=req.getBytesRead();
- bytesSent+=req.getResponse().getBytesWritten();
+ bytesSent+=req.getResponse().getContentWritten();
requestCount++;
if( req.getResponse().getStatus() >=400 )
private Locale locale = DEFAULT_LOCALE;
// General informations
- private long bytesWritten=0;
+ private long contentWritten = 0;
/**
* Holds request error exception.
throws IOException
{
outputBuffer.doWrite(chunk, this);
- bytesWritten+=chunk.getLength();
+ contentWritten+=chunk.getLength();
}
// --------------------
headers.clear();
// update counters
- bytesWritten=0;
+ contentWritten=0;
}
- public long getBytesWritten() {
- return bytesWritten;
+ /**
+ * Bytes written by application - i.e. before compression, chunking, etc.
+ */
+ public long getContentWritten() {
+ return contentWritten;
+ }
+
+ /**
+ * Bytes written to socket - i.e. after compression, chunking, etc.
+ */
+ public long getBytesWritten(boolean flush) {
+ if (flush) {
+ action(ActionCode.CLIENT_FLUSH, this);
+ }
+ return outputBuffer.getBytesWritten();
}
}
*/
protected AsyncStateMachine asyncStateMachine = new AsyncStateMachine(this);
+
+ /**
+ * Bytes written to client for the current request
+ */
+ protected long byteCount = 0;
+
// ------------------------------------------------------------- Properties
request.recycle();
response.recycle();
certificates.recycle();
+ byteCount = 0;
}
// ------------------------------------------------------ Connector Methods
off += thisTime;
}
+ byteCount += chunk.getLength();
return chunk.getLength();
}
+
+ @Override
+ public long getBytesWritten() {
+ return byteCount;
+ }
}
}
* This class is an output buffer which will write data to an output
* stream.
*/
- protected class SocketOutputBuffer
- implements OutputBuffer {
-
+ protected class SocketOutputBuffer implements OutputBuffer {
/**
* Write chunk.
off += thisTime;
}
+ byteCount += chunk.getLength();
return chunk.getLength();
}
+
+ @Override
+ public long getBytesWritten() {
+ return byteCount;
+ }
}
}
*/
protected OutputBuffer outputStreamOutputBuffer;
+ /**
+ * Bytes written to client for the current request
+ */
+ protected long byteCount = 0;
+
// -------------------------------------------------------------- Variables
return activeFilters[lastActiveFilter].doWrite(chunk, res);
}
-
+
+
+ @Override
+ public long getBytesWritten() {
+ if (lastActiveFilter == -1) {
+ return outputStreamOutputBuffer.getBytesWritten();
+ } else {
+ return activeFilters[lastActiveFilter].getBytesWritten();
+ }
+ }
+
+
// --------------------------------------------------------- Public Methods
if (lastActiveFilter != -1)
activeFilters[lastActiveFilter].end();
finished = true;
-
+ byteCount = 0;
}
public abstract void sendAck() throws IOException;
* This class is an output buffer which will write data to an output
* stream.
*/
- protected class SocketOutputBuffer
- implements OutputBuffer {
+ protected class SocketOutputBuffer implements OutputBuffer {
/**
len = len - thisTime;
start = start + thisTime;
}
+ byteCount += chunk.getLength();
return chunk.getLength();
-
}
-
+ @Override
+ public long getBytesWritten() {
+ return byteCount;
+ }
}
int start = chunk.getStart();
byte[] b = chunk.getBuffer();
addToBB(b, start, len);
+ byteCount += chunk.getLength();
return chunk.getLength();
-
}
-
+ @Override
+ public long getBytesWritten() {
+ return byteCount;
+ }
}
*/
protected boolean useSocketBuffer = false;
+
/**
* Set the underlying socket output stream.
*/
outputStream.write(chunk.getBuffer(), chunk.getStart(),
length);
}
- return length;
-
+ byteCount += chunk.getLength();
+ return chunk.getLength();
}
-
+ @Override
+ public long getBytesWritten() {
+ return byteCount;
+ }
}
}
+ @Override
+ public long getBytesWritten() {
+ return buffer.getBytesWritten();
+ }
+
+
// --------------------------------------------------- OutputFilter Methods
}
+ @Override
+ public long getBytesWritten() {
+ return buffer.getBytesWritten();
+ }
+
+
// --------------------------------------------------- OutputFilter Methods
/**
}
+ @Override
+ public long getBytesWritten() {
+ return buffer.getBytesWritten();
+ }
+
+
// --------------------------------------------------- OutputFilter Methods
}
+ @Override
+ public long getBytesWritten() {
+ return 0;
+ }
+
+
// --------------------------------------------------- OutputFilter Methods
(markt)
</update>
<fix>
+ <bug>50496</bug>: Bytes sent in the access log are now counted after
+ compression, chunking etc rather than before. (markt)
+ </fix>
+ <fix>
<bug>50550</bug>: When a new directory is created (e.g. via WebDAV)
ensure that a subsequent request for that directory does not result in a
404 response. (markt)