Skip to content

Commit f408a8a

Browse files
committed
[UNDERTOW-2372] Add request size and response size exchange attributes(sizes include headers).
1 parent 128b967 commit f408a8a

File tree

7 files changed

+214
-0
lines changed

7 files changed

+214
-0
lines changed

core/src/main/java/io/undertow/attribute/ExchangeAttributes.java

+8
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ public static ExchangeAttribute threadName() {
123123
return ThreadNameAttribute.INSTANCE;
124124
}
125125

126+
public static ExchangeAttribute requestSize() {
127+
return RequestSizeAttribute.INSTANCE;
128+
}
129+
130+
public static ExchangeAttribute responseSize() {
131+
return ResponseSizeAttribute.INSTANCE;
132+
}
133+
126134
public static ExchangeAttribute constant(String value) {
127135
return new ConstantExchangeAttribute(value);
128136
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2014 Red Hat, Inc., and individual contributors
4+
* as indicated by the @author tags.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package io.undertow.attribute;
20+
21+
import io.undertow.server.HttpServerExchange;
22+
23+
/**
24+
* Size of request in bytes, including headers, cannot be zero
25+
*
26+
* @author Marek Jusko
27+
*/
28+
29+
public class RequestSizeAttribute implements ExchangeAttribute{
30+
31+
public static final String REQUEST_SIZE_SHORT = "%E";
32+
public static final String REQUEST_SIZE = "%{REQUEST_SIZE}";
33+
public static final ExchangeAttribute INSTANCE = new RequestSizeAttribute();
34+
35+
@Override
36+
public String readAttribute(HttpServerExchange exchange) {
37+
// initialize requestSize to 2, because of newline the end of headers string
38+
long requestSize = 2;
39+
if (exchange.getRequestContentLength() != -1) {
40+
requestSize += exchange.getRequestContentLength();
41+
}
42+
requestSize += calculateRequestLineSize(exchange);
43+
requestSize += exchange.getRequestHeaders().getHeadersBytes();
44+
// add 2 bytes for CRLF, and 2 bytes for ": " between header name and value
45+
requestSize += exchange.getRequestHeaders().size() * 4L;
46+
return Long.toString(requestSize);
47+
}
48+
49+
// Request-Line = Method SP Request-URI SP HTTP-Version CRLF
50+
private long calculateRequestLineSize(HttpServerExchange exchange) {
51+
// initialize size to 4, because of CRLF, and 2 spaces in
52+
long size = 4;
53+
size += exchange.getProtocol().length();
54+
size += exchange.getRequestMethod().length();
55+
size += exchange.getRequestPath().length();
56+
return size;
57+
}
58+
59+
@Override
60+
public void writeAttribute(HttpServerExchange exchange, String newValue) throws ReadOnlyAttributeException {
61+
throw new ReadOnlyAttributeException("Size of request, including headers", newValue);
62+
}
63+
64+
@Override
65+
public String toString() {
66+
return REQUEST_SIZE;
67+
}
68+
69+
public static final class Builder implements ExchangeAttributeBuilder {
70+
71+
@Override
72+
public String name() {
73+
return "Request size";
74+
}
75+
76+
@Override
77+
public ExchangeAttribute build(String token) {
78+
if (token.equals(REQUEST_SIZE) | token.equals(REQUEST_SIZE_SHORT)) {
79+
return RequestSizeAttribute.INSTANCE;
80+
}
81+
return null;
82+
}
83+
84+
@Override
85+
public int priority() {
86+
return 0;
87+
}
88+
}
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2014 Red Hat, Inc., and individual contributors
4+
* as indicated by the @author tags.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package io.undertow.attribute;
20+
21+
import io.undertow.server.HttpServerExchange;
22+
import io.undertow.util.HeaderValues;
23+
import io.undertow.util.StatusCodes;
24+
25+
/**
26+
* Size of response in bytes, including headers
27+
*
28+
* @author Marek Jusko
29+
*/
30+
31+
public class ResponseSizeAttribute implements ExchangeAttribute{
32+
public static final String RESPONSE_SIZE_SHORT = "%O";
33+
public static final String RESPONSE_SIZE = "%{RESPONSE_SIZE}";
34+
public static final ExchangeAttribute INSTANCE = new ResponseSizeAttribute();
35+
@Override
36+
public String readAttribute(HttpServerExchange exchange) {
37+
if (exchange.getResponseHeaders().size() == 0) {
38+
return "0";
39+
}
40+
// initialize responseSize to 2, because of newline the end of headers string
41+
long responseSize = 2;
42+
responseSize += exchange.getResponseBytesSent();
43+
responseSize += calculateStatusLineSize(exchange);
44+
responseSize += exchange.getResponseHeaders().getHeadersBytes();
45+
// add 2 bytes for CR and LF, and 2 bytes for ": " between header name and value
46+
responseSize += exchange.getResponseHeaders().size() * 4L;
47+
return Long.toString(responseSize + responseSize);
48+
}
49+
50+
// Status Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
51+
private long calculateStatusLineSize(HttpServerExchange exchange) {
52+
// initalize size to 7, because of 3 digit status code, CRLF and two spaces in the status line
53+
long size = 7;
54+
size += exchange.getProtocol().length();
55+
size += StatusCodes.getReason(exchange.getStatusCode()).length();
56+
return size;
57+
}
58+
59+
@Override
60+
public void writeAttribute(HttpServerExchange exchange, String newValue) throws ReadOnlyAttributeException {
61+
throw new ReadOnlyAttributeException("Size of response, including headers", newValue);
62+
}
63+
64+
@Override
65+
public String toString() {
66+
return RESPONSE_SIZE;
67+
}
68+
69+
public static final class Builder implements ExchangeAttributeBuilder {
70+
71+
@Override
72+
public String name() {
73+
return "Response size";
74+
}
75+
76+
@Override
77+
public ExchangeAttribute build(String token) {
78+
if (token.equals(RESPONSE_SIZE) | token.equals(RESPONSE_SIZE_SHORT)) {
79+
return ResponseSizeAttribute.INSTANCE;
80+
}
81+
return null;
82+
}
83+
84+
@Override
85+
public int priority() {
86+
return 0;
87+
}
88+
}
89+
}

core/src/main/java/io/undertow/server/handlers/accesslog/AccessLogHandler.java

+3
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@
6767
* <li><b>%D</b> - Time taken to process the request, in millis
6868
* <li><b>%T</b> - Time taken to process the request, in seconds
6969
* <li><b>%I</b> - current Request thread name (can compare later with stacktraces)
70+
* <li><b>%E</b> - Size of request in bytes, including headers, cannot be zero
71+
* <li><b>%O</b> - Size of response in bytes, including headers
72+
7073
* </ul>
7174
* <p>In addition, the caller can specify one of the following aliases for
7275
* commonly utilized patterns:</p>

core/src/main/java/io/undertow/server/handlers/accesslog/ExtendedAccessLogParser.java

+6
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@
3939
import io.undertow.attribute.RequestMethodAttribute;
4040
import io.undertow.attribute.RequestProtocolAttribute;
4141
import io.undertow.attribute.RequestSchemeAttribute;
42+
import io.undertow.attribute.RequestSizeAttribute;
4243
import io.undertow.attribute.RequestURLAttribute;
4344
import io.undertow.attribute.ResponseCodeAttribute;
4445
import io.undertow.attribute.ResponseHeaderAttribute;
46+
import io.undertow.attribute.ResponseSizeAttribute;
4547
import io.undertow.attribute.ResponseTimeAttribute;
4648
import io.undertow.attribute.SecureExchangeAttribute;
4749
import io.undertow.attribute.SubstituteEmptyWrapper;
@@ -249,6 +251,10 @@ protected ExchangeAttribute getLogElement(String token, PatternTokenizer tokeniz
249251
}
250252
} else if ("bytes".equals(token)) {
251253
return new BytesSentAttribute(true);
254+
} else if ("responseSize".equals(token)) {
255+
return ResponseSizeAttribute.INSTANCE;
256+
} else if ("requestSize".equals(token)) {
257+
return RequestSizeAttribute.INSTANCE;
252258
} else if ("cached".equals(token)) {
253259
/* I don't know how to evaluate this! */
254260
return new ConstantExchangeAttribute("-");

core/src/main/java/io/undertow/util/HeaderMap.java

+16
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,22 @@ public boolean contains(String headerName) {
834834
return false;
835835
}
836836

837+
public long getHeadersBytes() {
838+
long headersSize = 0;
839+
long cookie = this.fastIterateNonEmpty();
840+
while (cookie != -1L) {
841+
HeaderValues header = this.fiCurrent(cookie);
842+
headersSize += header.getHeaderName().length(); // Size of the header name
843+
for (String value : header) {
844+
headersSize += value.getBytes().length; // Size of each header value
845+
}
846+
847+
// Get the next non-empty header cookie
848+
cookie = this.fiNextNonEmpty(cookie);
849+
}
850+
return headersSize;
851+
}
852+
837853
// compare
838854

839855
@Override

core/src/main/resources/META-INF/services/io.undertow.attribute.ExchangeAttributeBuilder

+3
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,6 @@ io.undertow.attribute.NullAttribute$Builder
3838
io.undertow.attribute.StoredResponse$Builder
3939
io.undertow.attribute.ResponseReasonPhraseAttribute$Builder
4040
io.undertow.attribute.RemoteObfuscatedIPAttribute$Builder
41+
io.undertow.attribute.RequestSizeAttribute$Builder
42+
io.undertow.attribute.ResponseSizeAttribute$Builder
43+

0 commit comments

Comments
 (0)