package io.mangoo.routing.handlers;
import java.io.IOException;
import java.util.Objects;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import com.google.inject.Inject;
import io.mangoo.configuration.Config;
import io.mangoo.core.Application;
import io.mangoo.enums.Header;
import io.mangoo.enums.Required;
import io.mangoo.helpers.RequestHelper;
import io.mangoo.routing.Attachment;
import io.mangoo.routing.Response;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.Headers;
import io.undertow.util.StatusCodes;
/**
*
* @author svenkubiak
*
*/
public class ResponseHandler implements HttpHandler {
private Attachment attachment;
private Config config;
@Inject
public ResponseHandler(Config config) {
this.config = Objects.requireNonNull(config, Required.CONFIG.toString());
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
this.attachment = exchange.getAttachment(RequestHelper.ATTACHMENT_KEY);
final Response response = this.attachment.getResponse();
if (response.isRedirect()) {
handleRedirectResponse(exchange, response);
} else if (response.isBinary()) {
handleBinaryResponse(exchange, response);
} else {
handleRenderedResponse(exchange, response);
}
}
/**
* Handles a binary response to the client by sending the binary content from the response
* to the undertow output stream
*
* @param exchange The Undertow HttpServerExchange
* @param response The response object
*
* @throws IOException
*/
protected void handleBinaryResponse(HttpServerExchange exchange, Response response) {
exchange.dispatch(exchange.getDispatchExecutor(), Application.getInstance(BinaryHandler.class).withResponse(response));
}
/**
* Handles a redirect response to the client by sending a 403 status code to the client
*
* @param exchange The Undertow HttpServerExchange
* @param response The response object
*/
protected void handleRedirectResponse(HttpServerExchange exchange, Response response) {
exchange.setStatusCode(StatusCodes.FOUND);
exchange.getResponseHeaders().put(Headers.LOCATION, response.getRedirectTo());
exchange.getResponseHeaders().put(Headers.SERVER, this.config.getServerHeader());
response.getHeaders().forEach((key, value) -> exchange.getResponseHeaders().add(key, value)); //NOSONAR
exchange.endExchange();
}
/**
* Retrieves the body of the request and checks i an ETag needs to be handled
*
* @param exchange The HttpServerExchange
* @param response The Response object
* @return The body from the response object or an empty body if etag matches NONE_MATCH header
*/
protected String getResponseBody(HttpServerExchange exchange, Response response) {
String responseBody = response.getBody();
if (response.isETag()) {
final String noneMatch = exchange.getRequestHeaders().getFirst(Headers.IF_NONE_MATCH_STRING);
final String etag = DigestUtils.md5Hex(responseBody); //NOSONAR
if (StringUtils.isNotBlank(noneMatch) && StringUtils.isNotBlank(etag) && noneMatch.equals(etag)) {
exchange.setStatusCode(StatusCodes.NOT_MODIFIED);
responseBody = "";
} else {
exchange.getResponseHeaders().put(Headers.ETAG, etag);
}
}
return responseBody;
}
/**
* Handles a rendered response to the client by sending the rendered body from the response object
*
* @param exchange The Undertow HttpServerExchange
* @param response The response object
*/
protected void handleRenderedResponse(HttpServerExchange exchange, Response response) {
exchange.setStatusCode(response.getStatusCode());
exchange.getResponseHeaders().put(Header.X_XSS_PPROTECTION.toHttpString(), this.config.getXssProectionHeader());
exchange.getResponseHeaders().put(Header.X_CONTENT_TYPE_OPTIONS.toHttpString(), this.config.getXContentTypeOptionsHeader());
exchange.getResponseHeaders().put(Header.X_FRAME_OPTIONS.toHttpString(), this.config.getXFrameOptionsHeader());
exchange.getResponseHeaders().put(Header.REFERER_POLICY.toHttpString(), this.config.getRefererPolicy());
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, response.getContentType() + "; charset=" + response.getCharset());
exchange.getResponseHeaders().put(Headers.SERVER, this.config.getServerHeader());
exchange.getResponseHeaders().put(Header.CONTENT_SECURITY_POLICY.toHttpString(), this.config.getContentSecurityPolicyHeader());
response.getHeaders().forEach((key, value) -> exchange.getResponseHeaders().add(key, value)); //NOSONAR
if (this.attachment.hasTimer()) {
exchange.getResponseHeaders().put(Header.X_RESPONSE_TIME.toHttpString(), this.attachment.getResponseTime() + " ms");
}
exchange.getResponseSender().send(getResponseBody(exchange, response));
}
}