UrlPrefix.java
/*
* #%L
* wcm.io
* %%
* Copyright (C) 2022 wcm.io
* %%
* 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.
* #L%
*/
package io.wcm.handler.url.impl;
import java.util.Enumeration;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.adapter.Adaptable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implements auto-detection for Site URL prefix (author or publish).
* This works only if the adaptable is of SlingHttpServletRequest.
*/
final class UrlPrefix {
/**
* String that is provided in site URL config to enable auto-detection.
*/
public static final String AUTO_DETECTION = "<auto>";
static final String HTTP_HEADER_X_FORWARDED_HOST = "X-Forwarded-Host";
static final String HTTP_HEADER_X_FORWARDED_PROTO = "X-Forwarded-Proto";
static final String HTTP_HEADER_HOST = "Host";
static final String HTTP_HEADER_X_FORWARDED_SSL = "X-Forwarded-SSL";
static final String VALUE_ON = "on";
private static final Logger log = LoggerFactory.getLogger(UrlPrefix.class);
private UrlPrefix() {
// static methods only
}
/**
* Apply auto-detection to URL prefix.
* @param configuredUrlPrefix Configured URL prefix
* @param adaptable Adaptable
* @return Configured or auto-detected URL prefix
*/
static @Nullable String applyAutoDetection(@Nullable String configuredUrlPrefix, @NotNull Adaptable adaptable) {
String urlPrefix = configuredUrlPrefix;
if (StringUtils.contains(configuredUrlPrefix, AUTO_DETECTION)) {
// remove auto marker (detection might not be possible if adaptable is not a request)
urlPrefix = StringUtils.trimToNull(StringUtils.remove(configuredUrlPrefix, AUTO_DETECTION));
// auto-detect based on request
if (adaptable instanceof SlingHttpServletRequest) {
SlingHttpServletRequest request = (SlingHttpServletRequest)adaptable;
urlPrefix = detectFromForwardedHeader(request);
if (urlPrefix == null) {
urlPrefix = detectFromServletRequest(request);
}
}
}
return urlPrefix;
}
/**
* Try to get URL prefix from X-Forwarded header (e.g. in AEMaaCS environment).
* @param request Request
* @return Url prefix or null
*/
private static @Nullable String detectFromForwardedHeader(@NotNull SlingHttpServletRequest request) {
// output request headers to log in TRACE level
if (log.isTraceEnabled()) {
StringBuilder output = new StringBuilder();
Enumeration<String> headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String header = headers.nextElement();
if (output.length() > 0) {
output.append("; ");
}
output.append(header).append('=').append(request.getHeader(header));
}
log.trace("HTTP headers: {}", output);
}
// this should work for AEMaaCS author
String forwardedHost = request.getHeader(HTTP_HEADER_X_FORWARDED_HOST);
String forwardedProto = request.getHeader(HTTP_HEADER_X_FORWARDED_PROTO);
if (StringUtils.isNotEmpty(forwardedHost) && StringUtils.isNotEmpty(forwardedProto)) {
return forwardedProto + "://" + forwardedHost;
}
// this should work for AEMaaCS publish
String host = request.getHeader(HTTP_HEADER_HOST);
String forwardedSsl = request.getHeader(HTTP_HEADER_X_FORWARDED_SSL);
if (StringUtils.isNotEmpty(host) && StringUtils.equalsIgnoreCase(forwardedSsl, VALUE_ON)) {
return "https://" + host;
}
return null;
}
private static @NotNull String detectFromServletRequest(@NotNull SlingHttpServletRequest request) {
StringBuilder urlPrefix = new StringBuilder();
urlPrefix.append(request.getScheme()).append("://").append(request.getServerName());
int port = request.getServerPort();
if ((request.isSecure() && port != 443) || (!request.isSecure() && port != 80)) {
urlPrefix.append(':').append(Integer.toString(port));
}
return urlPrefix.toString();
}
}