/**
* Copyright (c) 2014-2017 by the respective copyright holders.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.io.rest.internal.filter;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A filter used to update both base and request URIs in Jersey's request
* context if proxy headers are detected.
*
* @author Ivan Iliev
*
*/
@Provider
@PreMatching
public class ProxyFilter implements ContainerRequestFilter {
private static final String PROTO_PROXY_HEADER = "x-forwarded-proto";
private static final String HOST_PROXY_HEADER = "x-forwarded-host";
private final transient Logger logger = LoggerFactory.getLogger(ProxyFilter.class);
@Override
public void filter(ContainerRequestContext ctx) throws IOException {
String host = getValue(ctx.getHeaders(), HOST_PROXY_HEADER);
String scheme = getValue(ctx.getHeaders(), PROTO_PROXY_HEADER);
// if our request does not have neither headers end right here
if (scheme == null && host == null) {
return;
}
UriInfo uriInfo = ctx.getUriInfo();
URI requestUri = uriInfo.getRequestUri();
UriBuilder baseBuilder = uriInfo.getBaseUriBuilder();
UriBuilder requestBuilder = uriInfo.getRequestUriBuilder();
// if only one of our headers is missing replace it with default value
if (scheme == null) {
scheme = requestUri.getScheme();
}
if (host == null) {
host = requestUri.getHost();
int port = requestUri.getPort();
if (port != -1) {
host += (":" + port);
}
}
// create a new URI from the current scheme + host in order to validate
// it
String uriString = scheme + "://" + host;
URI newBaseUri = null;
try {
newBaseUri = new URI(uriString);
} catch (URISyntaxException e) {
logger.error("Invalid X-Forwarded-Proto + X-Forwarded-Host header combination: " + uriString, e);
return;
}
// URI is valid replace base and request builder parts with ones
// obtained from the given headers
host = newBaseUri.getHost();
if (host != null) {
baseBuilder.host(host);
requestBuilder.host(host);
}
int port = newBaseUri.getPort();
baseBuilder.port(port);
requestBuilder.port(port);
scheme = newBaseUri.getScheme();
if (scheme != null) {
baseBuilder.scheme(scheme);
requestBuilder.scheme(scheme);
}
ctx.setRequestUri(baseBuilder.build(), requestBuilder.build());
}
private String getValue(MultivaluedMap<String, String> headers, String header) {
List<String> values = headers.get(header);
if (values == null || values.isEmpty()) {
return null;
}
return values.get(0);
}
}