Skip to content

Commit 0c0ffc4

Browse files
EarthJefffl4via
authored andcommitted
[UNDERTOW-2422] Return the protocol field of the HttpServerExchange into the status line.
Add tests for this and the default behavior. Signed-off-by: Flavia Rainone <[email protected]>
1 parent c37d94c commit 0c0ffc4

File tree

3 files changed

+158
-2
lines changed

3 files changed

+158
-2
lines changed

Diff for: core/src/main/java/io/undertow/UndertowMessages.java

+3
Original file line numberDiff line numberDiff line change
@@ -647,4 +647,7 @@ public interface UndertowMessages {
647647
@Message(id = 208, value = "Failed to allocate resource")
648648
IOException failedToAllocateResource();
649649

650+
@Message(id = 209, value = "Protocol string was too large for the buffer. Either provide a smaller message or a bigger buffer. Protocol: %s")
651+
IllegalStateException protocolTooLargeForBuffer(String protocolString);
652+
650653
}

Diff for: core/src/main/java/io/undertow/server/protocol/http/HttpResponseConduit.java

+14-2
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,20 @@ private int processWrite(int state, final Object userData, int pos, int length)
179179
// we don't have a dangling flag that won't be cleared at the finally block
180180
this.state |= POOLED_BUFFER_IN_USE;
181181
assert buffer.remaining() >= 50;
182-
// append response status and headers
183-
Protocols.HTTP_1_1.appendTo(buffer);
182+
// append protocol
183+
HttpString protocol = exchange.getProtocol();
184+
String protocolString = protocol.toString();
185+
if (protocolString.isEmpty()) {
186+
protocol = Protocols.HTTP_1_1;
187+
}
188+
if (protocol.length() > buffer.remaining()) {
189+
pooledBuffer.close();
190+
pooledBuffer = null;
191+
truncateWrites();
192+
throw UndertowMessages.MESSAGES.protocolTooLargeForBuffer(protocolString);
193+
}
194+
protocol.appendTo(buffer);
195+
// append status code, reason phrase, and headers
184196
buffer.put((byte) ' ');
185197
int code = exchange.getStatusCode();
186198
assert 999 >= code && code >= 100;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2024 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.server.handlers;
20+
21+
import io.undertow.io.Sender;
22+
import io.undertow.server.HttpHandler;
23+
import io.undertow.server.HttpServerExchange;
24+
import io.undertow.server.ServerConnection;
25+
import io.undertow.testutils.DefaultServer;
26+
import io.undertow.testutils.ProxyIgnore;
27+
import io.undertow.testutils.TestHttpClient;
28+
import io.undertow.util.Headers;
29+
import io.undertow.util.HttpString;
30+
import io.undertow.util.StatusCodes;
31+
import org.apache.http.HttpResponse;
32+
import org.apache.http.ProtocolVersion;
33+
import org.apache.http.client.methods.HttpGet;
34+
import org.junit.Assert;
35+
import org.junit.Test;
36+
import org.junit.runner.RunWith;
37+
38+
import java.io.IOException;
39+
40+
/**
41+
* Tests that if the protocol is set to a value, that value is returned on the
42+
* status line.
43+
*
44+
* @author Jeff Okamoto
45+
*/
46+
@RunWith(DefaultServer.class)
47+
@ProxyIgnore
48+
public class StatusLineTestCase {
49+
50+
/*
51+
* For the purposes of the test, the protocol name has to be "HTTP" because the test
52+
* framework runs through a parser, and it rejects other strings.
53+
*/
54+
private static final String DEFAULT_PROTOCOL_NAME = "HTTP";
55+
private static final String DEFAULT_PROTOCOL_MAJOR = "1";
56+
private static final String DEFAULT_PROTOCOL_MINOR = "1";
57+
private static final String PROTOCOL_NAME = "HTTP";
58+
private static final String PROTOCOL_MAJOR = "3";
59+
private static final String PROTOCOL_MINOR = "4";
60+
private static final String PROTOCOL_STRING = PROTOCOL_NAME + "/" + PROTOCOL_MAJOR + "." + PROTOCOL_MINOR;
61+
private static final String REASON_PHRASE = "Reason-Phrase";
62+
private static final String MESSAGE = "My HTTP Request!";
63+
64+
private static volatile ServerConnection connection;
65+
66+
@Test
67+
public void verifyStatusLine() throws IOException {
68+
DefaultServer.setRootHandler(new HttpHandler() {
69+
70+
@Override
71+
public void handleRequest(final HttpServerExchange exchange) throws Exception {
72+
if (connection == null) {
73+
connection = exchange.getConnection();
74+
} else if (!DefaultServer.isAjp() && !DefaultServer.isProxy() && connection != exchange.getConnection()) {
75+
Sender sender = exchange.getResponseSender();
76+
sender.send("Connection not persistent");
77+
return;
78+
}
79+
exchange.setProtocol(new HttpString(PROTOCOL_STRING));
80+
exchange.setReasonPhrase(REASON_PHRASE);
81+
exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, MESSAGE.length() + "");
82+
final Sender sender = exchange.getResponseSender();
83+
sender.send(MESSAGE);
84+
}
85+
});
86+
87+
connection = null;
88+
HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/path");
89+
TestHttpClient client = new TestHttpClient();
90+
try {
91+
HttpResponse result = client.execute(get);
92+
Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
93+
94+
ProtocolVersion protocolVersion = result.getStatusLine().getProtocolVersion();
95+
Assert.assertEquals(PROTOCOL_NAME, protocolVersion.getProtocol());
96+
Assert.assertEquals(Integer.parseInt(PROTOCOL_MAJOR), protocolVersion.getMajor());
97+
Assert.assertEquals(Integer.parseInt(PROTOCOL_MINOR), protocolVersion.getMinor());
98+
99+
Assert.assertEquals(REASON_PHRASE, result.getStatusLine().getReasonPhrase());
100+
} finally {
101+
client.getConnectionManager().shutdown();
102+
}
103+
}
104+
105+
@Test
106+
public void verifyDefaultStatusLine() throws IOException {
107+
DefaultServer.setRootHandler(new HttpHandler() {
108+
109+
@Override
110+
public void handleRequest(final HttpServerExchange exchange) throws Exception {
111+
if (connection == null) {
112+
connection = exchange.getConnection();
113+
} else if (!DefaultServer.isAjp() && !DefaultServer.isProxy() && connection != exchange.getConnection()) {
114+
Sender sender = exchange.getResponseSender();
115+
sender.send("Connection not persistent");
116+
return;
117+
}
118+
exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, MESSAGE.length() + "");
119+
final Sender sender = exchange.getResponseSender();
120+
sender.send(MESSAGE);
121+
}
122+
});
123+
124+
connection = null;
125+
HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/path");
126+
TestHttpClient client = new TestHttpClient();
127+
try {
128+
HttpResponse result = client.execute(get);
129+
Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
130+
131+
ProtocolVersion protocolVersion = result.getStatusLine().getProtocolVersion();
132+
Assert.assertEquals(DEFAULT_PROTOCOL_NAME, protocolVersion.getProtocol());
133+
Assert.assertEquals(Integer.parseInt(DEFAULT_PROTOCOL_MAJOR), protocolVersion.getMajor());
134+
Assert.assertEquals(Integer.parseInt(DEFAULT_PROTOCOL_MINOR), protocolVersion.getMinor());
135+
136+
} finally {
137+
client.getConnectionManager().shutdown();
138+
}
139+
}
140+
141+
}

0 commit comments

Comments
 (0)