All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.predic8.membrane.core.interceptor.rewrite.ReverseProxyingInterceptor Maven / Gradle / Ivy

There is a newer version: 5.6.0
Show newest version
/* Copyright 2012 predic8 GmbH, www.predic8.com

   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 com.predic8.membrane.core.interceptor.rewrite;

import java.net.MalformedURLException;
import java.net.URL;

import com.predic8.membrane.annot.MCElement;
import com.predic8.membrane.core.exchange.Exchange;
import com.predic8.membrane.core.http.Header;
import com.predic8.membrane.core.interceptor.AbstractInterceptor;
import com.predic8.membrane.core.interceptor.Outcome;
import com.predic8.membrane.core.rules.AbstractServiceProxy;
import com.predic8.membrane.core.rules.Rule;
import com.predic8.membrane.core.ws.relocator.Relocator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @description Rewrites the scheme, hostname and port in the "Location" header in HTTP responses,
 * as well as in the "Destination" header in HTTP requests. The rewriting reflects the different schemes,
 * hostnames and ports used to access Membrane Service Proxy vs. the target HTTP server.
 * @topic 4. Interceptors/Features
 */
@MCElement(name="reverseProxying")
public class ReverseProxyingInterceptor extends AbstractInterceptor {
	private static final Logger log = LoggerFactory.getLogger(ReverseProxyingInterceptor.class);

	public ReverseProxyingInterceptor() {
		name = "Reverse Proxy";
	}

	/**
	 * handles "Destination" header (see RFC 2518 section 9.3; also used by WebDAV)
	 */
	@Override
	public Outcome handleRequest(Exchange exc) throws Exception {
		if (exc.getRequest() == null)
			return Outcome.CONTINUE;
		String destination = exc.getRequest().getHeader().getFirstValue(Header.DESTINATION);
		if (destination == null)
			return Outcome.CONTINUE;
		if (!destination.contains("://"))
			return Outcome.CONTINUE; // local redirect (illegal by spec)
		// do not rewrite, if the client does not refer to the same host
		if (!isSameSchemeHostAndPort(getProtocol(exc) + "://" + exc.getRequest().getHeader().getHost(), destination))
			return Outcome.CONTINUE;
		// if we cannot determine the target hostname
		if (exc.getDestinations().isEmpty()) {
			// just remove the schema/hostname/port. this is illegal (by the spec),
			// but most clients understand it
			exc.getRequest().getHeader().setValue(Header.DESTINATION, new URL(destination).getFile());
			return Outcome.CONTINUE;
		}
		URL target = new URL(exc.getDestinations().get(0));
		// rewrite to our schema, host and port
		exc.getRequest().getHeader().setValue(Header.DESTINATION,
				Relocator.getNewLocation(destination, target.getProtocol(),
						target.getHost(), target.getPort() == -1 ? target.getDefaultPort() : target.getPort()));
		return Outcome.CONTINUE;
	}

	/**
	 * Handles "Location" header.
	 */
	@Override
	public Outcome handleResponse(Exchange exc) throws Exception {
		if (exc.getResponse() == null)
			return Outcome.CONTINUE;
		String location = exc.getResponse().getHeader().getFirstValue(Header.LOCATION);
		if (location == null)
			return Outcome.CONTINUE;
		if (!location.contains("://"))
			return Outcome.CONTINUE; // local redirect (illegal by spec)
		// do not rewrite, if the server did a redirect to some other hostname/port
		// (in which case we have to hope the hostname/port is valid on the client)
		if (!isSameSchemeHostAndPort(location, exc.getDestinations().get(0)))
			return Outcome.CONTINUE;
		// if we cannot determine the hostname we have been reached with (e.g. HTTP/1.0)
		if (exc.getOriginalHostHeaderHost() == null) {
			// just remove the schema/hostname/port. this is illegal (by the spec),
			// but most clients understand it
			exc.getResponse().getHeader().setValue(Header.LOCATION, new URL(location).getFile());
			return Outcome.CONTINUE;
		}
		// rewrite to our schema, host and port
		exc.getResponse().getHeader().setValue(Header.LOCATION,
				Relocator.getNewLocation(location, getProtocol(exc),
						exc.getOriginalHostHeaderHost(), getPort(exc)));
		return Outcome.CONTINUE;
	}

	private boolean isSameSchemeHostAndPort(String location2, String location)
			throws MalformedURLException {
		try {
			if (location.startsWith("/") || location2.startsWith("/"))
				return false; // no host info available
			URL loc2 = new URL(location2);
			URL loc1 = new URL(location);
			if (loc2.getProtocol() != null && !loc2.getProtocol().equals(loc1.getProtocol()))
				return false;
			if (loc2.getHost() != null && !loc2.getHost().equals(loc1.getHost()))
				return false;
			int loc2Port = loc2.getPort() == -1 ? loc2.getDefaultPort() : loc2.getPort();
			int loc1Port = loc1.getPort() == -1 ? loc1.getDefaultPort() : loc1.getPort();
			if (loc2Port != loc1Port)
				return false;
			return true;
		} catch (MalformedURLException e) {
			log.warn("", e); // TODO: fix these cases
			return false;
		}
	}

	int getPort(Exchange exc) {
		return exc.getHandler().getLocalPort();
	}

	private String getProtocol(Exchange exc) {
		Rule r = exc.getRule();
		return r != null && r instanceof AbstractServiceProxy && ((AbstractServiceProxy)r).getSslInboundContext() != null ? "https" : "http";
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy