Skip to content

Commit

Permalink
[UNDERTOW-1735] add reason-phrase handler and make response-code obey…
Browse files Browse the repository at this point in the history
… doc contract of one-by-one execution
  • Loading branch information
baranowb committed Sep 27, 2023
1 parent 510bb19 commit 1e202da
Show file tree
Hide file tree
Showing 9 changed files with 455 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2023 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.undertow.server.handlers;

import io.undertow.UndertowLogger;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;

/**
* A handler which simply sets a response code.
*
* @author <a href="mailto:bbaranow@redhat.com">Bartosz Baranowski</a>
*/
public final class ReasonPhraseHandler implements HttpHandler {

private static final boolean debugEnabled;

static {
debugEnabled = UndertowLogger.PREDICATE_LOGGER.isDebugEnabled();
}

private final String reasonPhrase;

private final HttpHandler next;
/**
* Construct a new instance.
*
* @param reasonPhrase the reason phrase to be set in status line
*/
public ReasonPhraseHandler(final HttpHandler next, final String reasonPhrase) {
this.next = next;
this.reasonPhrase = reasonPhrase;
}

@Override
public void handleRequest(final HttpServerExchange exchange) throws Exception {
exchange.setReasonPhrase(reasonPhrase);
if(debugEnabled) {
UndertowLogger.PREDICATE_LOGGER.debugf("Reason phrase set to [%s] for %s.", this.reasonPhrase, exchange);
}
if(next != null) {
next.handleRequest(exchange);
}
}

@Override
public String toString() {
return "reason-phrase( " + this.reasonPhrase + " )";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,36 @@ public final class ResponseCodeHandler implements HttpHandler {

private final int responseCode;

private HttpHandler next;
/**
* Construct a new instance.
*
* @param responseCode the response code to set
* @param next next handler
*/
public ResponseCodeHandler(final int responseCode) {
public ResponseCodeHandler(final HttpHandler next, final int responseCode) {
assert responseCode > 99;
assert responseCode < 600;
this.responseCode = responseCode;
this.next = next;
}

/**
* Construct a new instance.
*
* @param responseCode the response code to set
* @param next next handler
*/
public ResponseCodeHandler(final int responseCode) {
this(null,responseCode);
}

public HttpHandler getNext() {
return next;
}

public void setNext(HttpHandler next) {
this.next = next;
}

@Override
Expand All @@ -79,6 +102,9 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception {
if(debugEnabled) {
UndertowLogger.PREDICATE_LOGGER.debugf("Response code set to [%s] for %s.", responseCode, exchange);
}
if(next != null) {
next.handleRequest(exchange);
}
}

@Override
Expand Down
108 changes: 108 additions & 0 deletions core/src/main/java/io/undertow/server/handlers/ResponseHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2023 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.undertow.server.handlers;

import io.undertow.UndertowLogger;
import io.undertow.server.BlockingHttpExchange;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.HeaderMap;
import io.undertow.util.Headers;

/**
* Class which handles set operations for response: code, reason phrase and potentially body and type. Status code is required
* parameter.<br>
* The response handler allows to set response body as well. <br>
* response(code=404, reason='dont like it') is roughly equivalent to reason-phrase('dont like it');response-code(404)"<br>
*
* @author <a href="mailto:bbaranow@redhat.com">Bartosz Baranowski</a>
*/
public class ResponseHandler implements HttpHandler {

private static final String DEFAULT_BODY_TYPE = "text/html";
private static final boolean debugEnabled;

static {
debugEnabled = UndertowLogger.PREDICATE_LOGGER.isDebugEnabled();
}

private final String body;
private final String type;
private final int code;
private final String reason;
private HttpHandler chained;

// TODO: review parsing/execution rules. For some reason without next, this particular handler does not ignore
// trailing handlers.
public ResponseHandler(final int code, final String reason) {
this(code, reason, null, null);
}

public ResponseHandler(final int code, final String reason, final String body) {
this(code, reason, body, DEFAULT_BODY_TYPE);
}

public ResponseHandler(final int code, final String reason, final String body, final String type) {
this.body = body;
this.type = type;
// toString only
this.code = code;
this.reason = reason;
if (reason != null) {
this.chained = new ReasonPhraseHandler(null, reason);
}
this.chained = new ResponseCodeHandler(this.chained, code);
}

@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
this.chained.handleRequest(exchange);
if (this.body != null) {
final byte[] bodyBytes = this.body.getBytes("UTF-8");
final HeaderMap responseHeaders = exchange.getResponseHeaders();
if (responseHeaders.contains(Headers.CONTENT_LENGTH) || responseHeaders.contains(Headers.CONTENT_TYPE) || exchange.isBlocking()) {
// TODO: explode
}
responseHeaders.add(Headers.CONTENT_TYPE, this.type);
responseHeaders.add(Headers.CONTENT_LENGTH, bodyBytes.length);
final BlockingHttpExchange blockingHttpExchange = exchange.startBlocking();
if (exchange.isInIoThread()) {
exchange.dispatch(new HttpHandler() {

@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getOutputStream().write(bodyBytes);
}
});
} else {
exchange.getOutputStream().write(bodyBytes);
}

if (debugEnabled) {
UndertowLogger.PREDICATE_LOGGER.debugf("Respons body set to \n[%s]\nfor %s.", this.body, exchange);
}
}
}

@Override
public String toString() {
return "response( code='" + code + "'" + ((this.reason != null) ? ", reason='" + this.reason + "'" : "") + ""
+ ((this.body != null) ? ", type='" + this.type + "', body='" + this.body + "'" : "") + " )";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2023 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.undertow.server.handlers.builder;

import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
import io.undertow.server.handlers.ReasonPhraseHandler;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
* @author <a href="mailto:bbaranow@redhat.com">Bartosz Baranowski</a>
*/
public class ReasonPhraseHandlerBuilder implements HandlerBuilder {
@Override
public String name() {
return "reason-phrase";
}

@Override
public Map<String, Class<?>> parameters() {
Map<String, Class<?>> parameters = new HashMap<>();
parameters.put("value", String.class);
return parameters;
}

@Override
public Set<String> requiredParameters() {
final Set<String> req = new HashSet<>();
req.add("value");
return req;
}

@Override
public String defaultParameter() {
return "value";
}

@Override
public HandlerWrapper build(final Map<String, Object> config) {
final String value = (String) config.get("value");
return new HandlerWrapper() {
@Override
public HttpHandler wrap(HttpHandler handler) {
return new ReasonPhraseHandler(handler, value);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public HandlerWrapper build(final Map<String, Object> config) {
return new HandlerWrapper() {
@Override
public HttpHandler wrap(HttpHandler handler) {
return new ResponseCodeHandler(value);
return new ResponseCodeHandler(handler, value);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2023 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.undertow.server.handlers.builder;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
import io.undertow.server.handlers.ResponseHandler;

/**
* @author <a href="mailto:bbaranow@redhat.com">Bartosz Baranowski</a>
*/
public class ResponseHandlerBuilder implements HandlerBuilder {
@Override
public String name() {
return "response";
}

@Override
public Map<String, Class<?>> parameters() {
Map<String, Class<?>> parameters = new HashMap<>();
parameters.put("code", Integer.class);
parameters.put("reason", String.class);
parameters.put("type", String.class);
parameters.put("body", String.class);
return parameters;
}

@Override
public Set<String> requiredParameters() {
final Set<String> req = new HashSet<>();
req.add("code");
return req;
}

@Override
public String defaultParameter() {
// default parameter - name(paramValue) not supported
return null;
}

@Override
public HandlerWrapper build(final Map<String, Object> config) {
final Integer code = (Integer) config.get("code");
final String reason = (String) config.get("reason");
final String type = (String) config.get("type");
final String body = (String) config.get("body");
return new HandlerWrapper() {
@Override
public HttpHandler wrap(HttpHandler handler) {
if (body == null) {
return new ResponseHandler(code, reason);
} else {
if (type == null) {
return new ResponseHandler(code, reason, body);
} else {
return new ResponseHandler(code, reason, body, type);
}
}
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ io.undertow.server.handlers.HttpContinueAcceptingHandler$Builder
io.undertow.server.handlers.form.EagerFormParsingHandler$Builder
io.undertow.server.handlers.SameSiteCookieHandler$Builder
io.undertow.server.handlers.SetErrorHandler$Builder
io.undertow.server.handlers.builder.ReasonPhraseHandlerBuilder
io.undertow.server.handlers.builder.ResponseHandlerBuilder
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,5 @@ private void expect(String string, boolean result1, boolean result2) {
throw new RuntimeException("String " + string, ex);
}
}

}
Loading

0 comments on commit 1e202da

Please sign in to comment.