Skip to content

Commit 61953d2

Browse files
authored
Merge pull request #1419 from baranowb/UNDERTOW-1735
[UNDERTOW-1735] add reason-phrase handler and make response-code obey…
2 parents 4dd163c + 6e963db commit 61953d2

File tree

10 files changed

+459
-3
lines changed

10 files changed

+459
-3
lines changed

core/src/main/java/io/undertow/UndertowMessages.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,4 +655,7 @@ public interface UndertowMessages {
655655
@Message(id = 210, value = "Buffer content underflow for exchange '%s', buffer '%s'")
656656
IOException bufferUnderflow(HttpServerExchange exchange, ByteBuffer buf);
657657

658+
@Message(id = 211, value = "Exchange '%s' already has body or is blocking.")
659+
IOException exchangeBlockingOrBlocking(HttpServerExchange e);
660+
658661
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2023 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.UndertowLogger;
22+
import io.undertow.server.HttpHandler;
23+
import io.undertow.server.HttpServerExchange;
24+
25+
/**
26+
* A handler which simply sets a response code.
27+
*
28+
* @author <a href="mailto:[email protected]">Bartosz Baranowski</a>
29+
*/
30+
public final class ReasonPhraseHandler implements HttpHandler {
31+
32+
private static final boolean debugEnabled;
33+
34+
static {
35+
debugEnabled = UndertowLogger.PREDICATE_LOGGER.isDebugEnabled();
36+
}
37+
38+
private final String reasonPhrase;
39+
40+
private final HttpHandler next;
41+
/**
42+
* Construct a new instance.
43+
*
44+
* @param reasonPhrase the reason phrase to be set in status line
45+
*/
46+
public ReasonPhraseHandler(final HttpHandler next, final String reasonPhrase) {
47+
this.next = next;
48+
this.reasonPhrase = reasonPhrase;
49+
}
50+
51+
@Override
52+
public void handleRequest(final HttpServerExchange exchange) throws Exception {
53+
exchange.setReasonPhrase(reasonPhrase);
54+
if(debugEnabled) {
55+
UndertowLogger.PREDICATE_LOGGER.debugf("Reason phrase set to [%s] for %s.", this.reasonPhrase, exchange);
56+
}
57+
if(next != null) {
58+
next.handleRequest(exchange);
59+
}
60+
}
61+
62+
@Override
63+
public String toString() {
64+
return "reason-phrase( " + this.reasonPhrase + " )";
65+
}
66+
}

core/src/main/java/io/undertow/server/handlers/ResponseCodeHandler.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,36 @@ public final class ResponseCodeHandler implements HttpHandler {
6464

6565
private final int responseCode;
6666

67+
private HttpHandler next;
6768
/**
6869
* Construct a new instance.
6970
*
7071
* @param responseCode the response code to set
72+
* @param next next handler
7173
*/
72-
public ResponseCodeHandler(final int responseCode) {
74+
public ResponseCodeHandler(final HttpHandler next, final int responseCode) {
75+
assert responseCode > 99;
76+
assert responseCode < 600;
7377
this.responseCode = responseCode;
78+
this.next = next;
79+
}
80+
81+
/**
82+
* Construct a new instance.
83+
*
84+
* @param responseCode the response code to set
85+
* @param next next handler
86+
*/
87+
public ResponseCodeHandler(final int responseCode) {
88+
this(null,responseCode);
89+
}
90+
91+
public HttpHandler getNext() {
92+
return next;
93+
}
94+
95+
public void setNext(HttpHandler next) {
96+
this.next = next;
7497
}
7598

7699
@Override
@@ -79,6 +102,9 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception {
79102
if(debugEnabled) {
80103
UndertowLogger.PREDICATE_LOGGER.debugf("Response code set to [%s] for %s.", responseCode, exchange);
81104
}
105+
if(next != null) {
106+
next.handleRequest(exchange);
107+
}
82108
}
83109

84110
@Override
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2023 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+
package io.undertow.server.handlers;
19+
20+
import io.undertow.UndertowLogger;
21+
import io.undertow.UndertowMessages;
22+
import io.undertow.server.HttpHandler;
23+
import io.undertow.server.HttpServerExchange;
24+
import io.undertow.util.HeaderMap;
25+
import io.undertow.util.Headers;
26+
27+
/**
28+
* Class which handles set operations for response: code, reason phrase and potentially body and type. Status code is required
29+
* parameter.<br>
30+
* The response handler allows to set response body as well. <br>
31+
* response(code=404, reason='dont like it') is roughly equivalent to reason-phrase('dont like it');response-code(404)"<br>
32+
*
33+
* @author <a href="mailto:[email protected]">Bartosz Baranowski</a>
34+
*/
35+
public class ResponseHandler implements HttpHandler {
36+
37+
private static final String DEFAULT_BODY_TYPE = "text/html";
38+
private static final boolean debugEnabled;
39+
40+
static {
41+
debugEnabled = UndertowLogger.PREDICATE_LOGGER.isDebugEnabled();
42+
}
43+
44+
private final String body;
45+
private final String type;
46+
private final int code;
47+
private final String reason;
48+
private HttpHandler chained;
49+
50+
// TODO: review parsing/execution rules. For some reason without next, this particular handler does not ignore
51+
// trailing handlers.
52+
public ResponseHandler(final int code, final String reason) {
53+
this(code, reason, null, null);
54+
}
55+
56+
public ResponseHandler(final int code, final String reason, final String body) {
57+
this(code, reason, body, DEFAULT_BODY_TYPE);
58+
}
59+
60+
public ResponseHandler(final int code, final String reason, final String body, final String type) {
61+
this.body = body;
62+
this.type = type;
63+
// toString only
64+
this.code = code;
65+
this.reason = reason;
66+
if (reason != null) {
67+
this.chained = new ReasonPhraseHandler(null, reason);
68+
}
69+
this.chained = new ResponseCodeHandler(this.chained, code);
70+
}
71+
72+
@Override
73+
public void handleRequest(HttpServerExchange exchange) throws Exception {
74+
this.chained.handleRequest(exchange);
75+
if (this.body != null) {
76+
final byte[] bodyBytes = this.body.getBytes("UTF-8");
77+
final HeaderMap responseHeaders = exchange.getResponseHeaders();
78+
if (responseHeaders.contains(Headers.CONTENT_LENGTH) || responseHeaders.contains(Headers.CONTENT_TYPE) || exchange.isBlocking()) {
79+
//TODO: need user feedback
80+
throw UndertowMessages.MESSAGES.exchangeBlockingOrBlocking(exchange);
81+
}
82+
responseHeaders.add(Headers.CONTENT_TYPE, this.type);
83+
responseHeaders.add(Headers.CONTENT_LENGTH, bodyBytes.length);
84+
exchange.startBlocking();
85+
if (exchange.isInIoThread()) {
86+
exchange.dispatch(new HttpHandler() {
87+
88+
@Override
89+
public void handleRequest(HttpServerExchange exchange) throws Exception {
90+
exchange.getOutputStream().write(bodyBytes);
91+
}
92+
});
93+
} else {
94+
exchange.getOutputStream().write(bodyBytes);
95+
}
96+
97+
if (debugEnabled) {
98+
UndertowLogger.PREDICATE_LOGGER.debugf("Respons body set to \n[%s]\nfor %s.", this.body, exchange);
99+
}
100+
}
101+
}
102+
103+
@Override
104+
public String toString() {
105+
return "response( code='" + code + "'" + ((this.reason != null) ? ", reason='" + this.reason + "'" : "") + ""
106+
+ ((this.body != null) ? ", type='" + this.type + "', body='" + this.body + "'" : "") + " )";
107+
}
108+
109+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2023 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.builder;
20+
21+
import io.undertow.server.HandlerWrapper;
22+
import io.undertow.server.HttpHandler;
23+
import io.undertow.server.handlers.ReasonPhraseHandler;
24+
25+
import java.util.HashMap;
26+
import java.util.HashSet;
27+
import java.util.Map;
28+
import java.util.Set;
29+
30+
/**
31+
* @author <a href="mailto:[email protected]">Bartosz Baranowski</a>
32+
*/
33+
public class ReasonPhraseHandlerBuilder implements HandlerBuilder {
34+
@Override
35+
public String name() {
36+
return "reason-phrase";
37+
}
38+
39+
@Override
40+
public Map<String, Class<?>> parameters() {
41+
Map<String, Class<?>> parameters = new HashMap<>();
42+
parameters.put("value", String.class);
43+
return parameters;
44+
}
45+
46+
@Override
47+
public Set<String> requiredParameters() {
48+
final Set<String> req = new HashSet<>();
49+
req.add("value");
50+
return req;
51+
}
52+
53+
@Override
54+
public String defaultParameter() {
55+
return "value";
56+
}
57+
58+
@Override
59+
public HandlerWrapper build(final Map<String, Object> config) {
60+
final String value = (String) config.get("value");
61+
return new HandlerWrapper() {
62+
@Override
63+
public HttpHandler wrap(HttpHandler handler) {
64+
return new ReasonPhraseHandler(handler, value);
65+
}
66+
};
67+
}
68+
}

core/src/main/java/io/undertow/server/handlers/builder/ResponseCodeHandlerBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public HandlerWrapper build(final Map<String, Object> config) {
6161
return new HandlerWrapper() {
6262
@Override
6363
public HttpHandler wrap(HttpHandler handler) {
64-
return new ResponseCodeHandler(value);
64+
return new ResponseCodeHandler(handler, value);
6565
}
6666
};
6767
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2023 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.builder;
20+
21+
import java.util.HashMap;
22+
import java.util.HashSet;
23+
import java.util.Map;
24+
import java.util.Set;
25+
26+
import io.undertow.server.HandlerWrapper;
27+
import io.undertow.server.HttpHandler;
28+
import io.undertow.server.handlers.ResponseHandler;
29+
30+
/**
31+
* @author <a href="mailto:[email protected]">Bartosz Baranowski</a>
32+
*/
33+
public class ResponseHandlerBuilder implements HandlerBuilder {
34+
@Override
35+
public String name() {
36+
return "response";
37+
}
38+
39+
@Override
40+
public Map<String, Class<?>> parameters() {
41+
Map<String, Class<?>> parameters = new HashMap<>();
42+
parameters.put("code", Integer.class);
43+
parameters.put("reason", String.class);
44+
parameters.put("type", String.class);
45+
parameters.put("body", String.class);
46+
return parameters;
47+
}
48+
49+
@Override
50+
public Set<String> requiredParameters() {
51+
final Set<String> req = new HashSet<>();
52+
req.add("code");
53+
return req;
54+
}
55+
56+
@Override
57+
public String defaultParameter() {
58+
// default parameter - name(paramValue) not supported
59+
return null;
60+
}
61+
62+
@Override
63+
public HandlerWrapper build(final Map<String, Object> config) {
64+
final Integer code = (Integer) config.get("code");
65+
final String reason = (String) config.get("reason");
66+
final String type = (String) config.get("type");
67+
final String body = (String) config.get("body");
68+
return new HandlerWrapper() {
69+
@Override
70+
public HttpHandler wrap(HttpHandler handler) {
71+
if (body == null) {
72+
return new ResponseHandler(code, reason);
73+
} else {
74+
if (type == null) {
75+
return new ResponseHandler(code, reason, body);
76+
} else {
77+
return new ResponseHandler(code, reason, body, type);
78+
}
79+
}
80+
}
81+
};
82+
}
83+
}

0 commit comments

Comments
 (0)