blob: 2d32b5e797992b76c533448b0456c5d1d45cddab [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.knox.gateway.filter;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class XForwardedHeaderRequestWrapper extends GatewayRequestWrapper {
private static final String X_FORWARDED_FOR = "X-Forwarded-For";
private static final String X_FORWARDED_FOR_LOWER = X_FORWARDED_FOR.toLowerCase(Locale.ROOT);
private static final String X_FORWARDED_PROTO = "X-Forwarded-Proto";
private static final String X_FORWARDED_PROTO_LOWER = X_FORWARDED_PROTO.toLowerCase(Locale.ROOT);
private static final String X_FORWARDED_PORT = "X-Forwarded-Port";
private static final String X_FORWARDED_PORT_LOWER = X_FORWARDED_PORT.toLowerCase(Locale.ROOT);
private static final String X_FORWARDED_HOST = "X-Forwarded-Host";
private static final String X_FORWARDED_HOST_LOWER = X_FORWARDED_HOST.toLowerCase(Locale.ROOT);
private static final String X_FORWARDED_SERVER = "X-Forwarded-Server";
private static final String X_FORWARDED_SERVER_LOWER = X_FORWARDED_SERVER.toLowerCase(Locale.ROOT);
private static final String X_FORWARDED_CONTEXT = "X-Forwarded-Context";
private static final String X_FORWARDED_CONTEXT_LOWER = X_FORWARDED_CONTEXT.toLowerCase(Locale.ROOT);
private static final List<String> headerNames = new ArrayList<>();
static {
headerNames.add(X_FORWARDED_FOR);
headerNames.add(X_FORWARDED_PROTO);
headerNames.add(X_FORWARDED_PORT);
headerNames.add(X_FORWARDED_HOST);
headerNames.add(X_FORWARDED_SERVER);
headerNames.add(X_FORWARDED_CONTEXT);
}
Map<String,String> proxyHeaders = new HashMap<>();
public XForwardedHeaderRequestWrapper(HttpServletRequest request) {
super( request );
setHeaders(request, false, null);
}
public XForwardedHeaderRequestWrapper(HttpServletRequest request, final boolean isAppendServiceName, final String serviceContext) {
super( request );
setHeaders(request, isAppendServiceName, serviceContext);
}
private void setHeaders(final HttpServletRequest request, final boolean isAppendServiceName, final String serviceContext) {
setHeader( X_FORWARDED_FOR_LOWER, getForwardedFor( request ) );
setHeader( X_FORWARDED_PROTO_LOWER, getForwardedProto( request ) );
setHeader( X_FORWARDED_PORT_LOWER, getForwardedPort( request ) );
setHeader( X_FORWARDED_HOST_LOWER, getForwardedHost( request ) );
setHeader( X_FORWARDED_SERVER_LOWER, getForwardedServer( request ) );
setHeader( X_FORWARDED_CONTEXT_LOWER, getForwardedContext( request, isAppendServiceName, serviceContext) );
}
@Override
public Enumeration<String> getHeaderNames() {
return new CompositeEnumeration<>( Collections.enumeration(headerNames), super.getHeaderNames() );
}
@Override
public Enumeration<String> getHeaders( String name ) {
name = name.toLowerCase(Locale.ROOT);
Enumeration<String> values;
String value = proxyHeaders.get( name );
if( value != null ) {
values = Collections.enumeration(Collections.singletonList(value));
} else {
values = super.getHeaders( name );
}
return values;
}
@Override
public String getHeader( String name ) {
name = name.toLowerCase(Locale.ROOT);
String value = proxyHeaders.get( name );
if( value == null ) {
value = super.getHeader( name );
}
return value;
}
private void setHeader( String name, String value ) {
if( name != null && value != null ) {
proxyHeaders.put( name, value );
}
}
private static String getForwardedFor( HttpServletRequest request ) {
String value;
String curr = request.getHeader( X_FORWARDED_FOR );
String addr = request.getRemoteAddr();
if( curr == null ) {
value = addr;
} else {
value = curr + "," + addr;
}
return value;
}
private static String getForwardedProto( HttpServletRequest request ) {
String value = request.getHeader( X_FORWARDED_PROTO );
if( value == null ) {
value = request.isSecure() ? "https" : "http";
}
return value;
}
private static String getForwardedPort( HttpServletRequest request ) {
String value = request.getHeader( X_FORWARDED_PORT );
if( value == null ) {
String forwardedHost = getForwardedHost( request );
int separator = forwardedHost.indexOf(':');
if ( separator > 0 ) {
value = forwardedHost.substring(separator + 1, forwardedHost.length());
} else {
// use default ports
value = request.isSecure() ? "443" : "80";
}
}
return value;
}
private static String getForwardedHost( HttpServletRequest request ) {
String value = request.getHeader( X_FORWARDED_HOST );
if( value == null ) {
value = request.getHeader( "Host" );
}
return value;
}
private static String getForwardedServer( HttpServletRequest request ) {
return request.getServerName();
}
private static String getForwardedContext( HttpServletRequest request, final boolean isAppendServiceName, final String serviceContext) {
String remote = request.getHeader( X_FORWARDED_CONTEXT );
String local;
/* prefer parameter defined in topology over the gateway-site.xml property */
if(serviceContext != null && !serviceContext.isEmpty()) {
local = request.getContextPath() + "/" + serviceContext;
}
else if(isAppendServiceName) {
local = request.getContextPath() + "/" + extractServiceName(request.getContextPath(), request.getRequestURI());
} else {
local = request.getContextPath();
}
return ( remote == null ? "" : remote ) + ( local == null ? "" : local );
}
/**
* Given a URI path and context, this function extracts service name
*
* @param originalContext {gatewayName}/{topologyname}
* @param path request URI
* @return service name
* @since 1.3.0
*/
private static String extractServiceName(final String originalContext,
final String path) {
final String[] sub = path.split(originalContext);
String serviceName = "";
if (sub.length > 1) {
final String[] paths = sub[1].split("/");
serviceName = paths[1];
}
return serviceName;
}
}