Skip to content

Commit 840f9dc

Browse files
xjuskofl4via
authored andcommitted
[UNDERTOW-2372] Add request size and response size exchange attributes(sizes include headers).
1 parent 4f308ec commit 840f9dc

File tree

7 files changed

+239
-0
lines changed

7 files changed

+239
-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,99 @@
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 bytes for the CRLF at the end of headers string
38+
long requestSize = 2;
39+
40+
// Add the request content length if it is specified
41+
if (exchange.getRequestContentLength() != -1) {
42+
requestSize += exchange.getRequestContentLength();
43+
}
44+
// Add the size of the request line
45+
requestSize += calculateRequestLineSize(exchange);
46+
47+
// Add the size of all headers
48+
requestSize += exchange.getRequestHeaders().getHeadersBytes();
49+
50+
// Add 4 bytes per header for ": " and CRLF
51+
requestSize += exchange.getRequestHeaders().size() * 4L;
52+
53+
return Long.toString(requestSize);
54+
}
55+
56+
// Request-Line = Method SP Request-URI SP HTTP-Version CRLF
57+
private long calculateRequestLineSize(HttpServerExchange exchange) {
58+
// Initialize size to 4 bytes for the 2 spaces and CRLF in the request line
59+
long size = 4; // 2 spaces + CRLF
60+
61+
// Add the length of the protocol, request method, and request path
62+
size += exchange.getProtocol().length();
63+
size += exchange.getRequestMethod().length();
64+
size += exchange.getRequestPath().length();
65+
66+
return size;
67+
}
68+
69+
@Override
70+
public void writeAttribute(HttpServerExchange exchange, String newValue) throws ReadOnlyAttributeException {
71+
throw new ReadOnlyAttributeException("Size of request, including headers", newValue);
72+
}
73+
74+
@Override
75+
public String toString() {
76+
return REQUEST_SIZE;
77+
}
78+
79+
public static final class Builder implements ExchangeAttributeBuilder {
80+
81+
@Override
82+
public String name() {
83+
return "Request size";
84+
}
85+
86+
@Override
87+
public ExchangeAttribute build(String token) {
88+
if (token.equals(REQUEST_SIZE) || token.equals(REQUEST_SIZE_SHORT)) {
89+
return RequestSizeAttribute.INSTANCE;
90+
}
91+
return null;
92+
}
93+
94+
@Override
95+
public int priority() {
96+
return 0;
97+
}
98+
}
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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.StatusCodes;
23+
24+
/**
25+
* Size of response in bytes, including headers
26+
*
27+
* @author Marek Jusko
28+
*/
29+
30+
public class ResponseSizeAttribute implements ExchangeAttribute{
31+
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+
36+
@Override
37+
public String readAttribute(HttpServerExchange exchange) {
38+
if (exchange.getResponseHeaders().size() == 0) {
39+
return "0";
40+
}
41+
// Initialize requestSize to 2 bytes for the CRLF at the end of headers string
42+
long responseSize = 2;
43+
44+
// Add the number of bytes sent in the response body
45+
responseSize += exchange.getResponseBytesSent();
46+
47+
// Add the size of the status line
48+
responseSize += calculateStatusLineSize(exchange);
49+
50+
// Add the size of the headers
51+
responseSize += exchange.getResponseHeaders().getHeadersBytes();
52+
53+
// Add 4 bytes per header for ": " and CRLF
54+
responseSize += exchange.getResponseHeaders().size() * 4L;
55+
56+
return Long.toString(responseSize);
57+
}
58+
59+
// Status Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
60+
private long calculateStatusLineSize(HttpServerExchange exchange) {
61+
// Initialize size to 7 bytes for the spaces, CRLF, and 3-digit status code
62+
long size = 7; // 3 for status code + 2 for spaces + 2 for CRLF
63+
64+
// Add the length of the HTTP version
65+
size += exchange.getProtocol().length();
66+
67+
// Add the length of the status message
68+
size += StatusCodes.getReason(exchange.getStatusCode()).length();
69+
70+
return size;
71+
}
72+
73+
@Override
74+
public void writeAttribute(HttpServerExchange exchange, String newValue) throws ReadOnlyAttributeException {
75+
throw new ReadOnlyAttributeException("Size of response, including headers", newValue);
76+
}
77+
78+
@Override
79+
public String toString() {
80+
return RESPONSE_SIZE;
81+
}
82+
83+
public static final class Builder implements ExchangeAttributeBuilder {
84+
85+
@Override
86+
public String name() {
87+
return "Response size";
88+
}
89+
90+
@Override
91+
public ExchangeAttribute build(String token) {
92+
if (token.equals(RESPONSE_SIZE) || token.equals(RESPONSE_SIZE_SHORT)) {
93+
return ResponseSizeAttribute.INSTANCE;
94+
}
95+
return null;
96+
}
97+
98+
@Override
99+
public int priority() {
100+
return 0;
101+
}
102+
}
103+
}

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

+17
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import static org.wildfly.common.Assert.checkNotNullParam;
2222

23+
import java.nio.charset.StandardCharsets;
2324
import java.util.AbstractCollection;
2425
import java.util.Arrays;
2526
import java.util.Collection;
@@ -834,6 +835,22 @@ public boolean contains(String headerName) {
834835
return false;
835836
}
836837

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

839856
@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)