ResponseHandler.java

/*
 * 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.UndertowMessages;
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: need user feedback
                throw UndertowMessages.MESSAGES.exchangeBlockingOrBlocking(exchange);
            }
            responseHeaders.add(Headers.CONTENT_TYPE, this.type);
            responseHeaders.add(Headers.CONTENT_LENGTH, bodyBytes.length);
            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 + "'" : "") + " )";
    }

}