52n-wps-server-gcube/src/main/java/org/n52/wps/server/ResponseURLFilter.java

234 lines
9.0 KiB
Java
Raw Normal View History

/**
* Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source
* Software GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* If the program is linked with libraries which are licensed under one of
* the following licenses, the combination of the program with the linked
* library is not considered a "derivative work" of the program:
*
* Apache License, version 2.0
* Apache Software License, version 1.0
* GNU Lesser General Public License, version 3
* Mozilla Public License, versions 1.0, 1.1 and 2.0
* Common Development and Distribution License (CDDL), version 1.0
*
* Therefore the distribution of the program linked with libraries licensed
* under the aforementioned licenses, is permitted by the copyright holders
* if the distribution is compliant with both the GNU General Public
* License version 2 and the aforementioned licenses.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*/
package org.n52.wps.server;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.n52.wps.PropertyDocument.Property;
import org.n52.wps.ServerDocument.Server;
import org.n52.wps.commons.WPSConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author tkunicki
*/
public class ResponseURLFilter implements Filter {
private final static Logger LOGGER = LoggerFactory.getLogger(ResponseURLFilter.class);
public final static String PROP_responseURLFilterEnabled = "responseURLFilterEnabled";
private String configURLString;
private boolean enabled;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Server server = WPSConfig.getInstance().getWPSConfig().getServer();
// Build URL from WPS configuration. This is the
// hardcoded URL that we expect to see in reponses and would like to
// replace with the URL from the HTTP request.
configURLString = server.getProtocol() + "://" +
server.getHostname() + ":" +
server.getHostport() + "/" +
server.getWebappPath();
// Is filtering enabled in WPS configuration?
Property[] serverProperties = server.getPropertyArray();
for (Property serverProperty : serverProperties) {
if (/* serverProperty.getActive() && */ PROP_responseURLFilterEnabled.equals(serverProperty.getName())) {
enabled = Boolean.parseBoolean(serverProperty.getStringValue());
}
}
if (enabled) {
LOGGER.info("Response URL filtering enabled using base URL of {}", configURLString);
} else {
LOGGER.info("Response URL filtering disabled.");
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest requestHTTP = (request instanceof HttpServletRequest) ?
(HttpServletRequest) request : null;
HttpServletResponse responseHTTP = (response instanceof HttpServletResponse) ?
(HttpServletResponse) response : null;
if (enabled && requestHTTP != null && responseHTTP != null) {
String requestURLString = extractRequestURLString(requestHTTP);
// extract servlet path from request URL
String baseURLString = requestURLString.replaceAll("/[^/]*$", "");
LOGGER.info("Wrapping response for URL filtering");
chain.doFilter(request, new BaseURLFilterHttpServletResponse(
responseHTTP, configURLString, baseURLString));
} else {
LOGGER.warn("Unable to to wrap response for URL filtering");
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
// nothing to do yet
}
protected static String extractRequestURLString(HttpServletRequest request) {
return request.getRequestURL().toString();
}
private static class BaseURLFilterHttpServletResponse extends HttpServletResponseWrapper {
private final String configURLString;
public final String requestURLString;
public BaseURLFilterHttpServletResponse(HttpServletResponse response, String configURLString, String requestURLString) {
super(response);
this.configURLString = configURLString;
this.requestURLString = requestURLString;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
String contentType = getResponse().getContentType();
if (contentType == null || contentType.startsWith("text/xml") || contentType.startsWith("application/xml")) {
LOGGER.info("Content-type: {}, response URL filtering enabled for response to {}", contentType, requestURLString);
return new ServletOutputStreamWrapper(
getResponse().getOutputStream(),
configURLString,
requestURLString);
} else {
LOGGER.info("Content-type: {}, response URL filtering disabled for response to {}", contentType, requestURLString);
return getResponse().getOutputStream();
}
}
@Override
public PrintWriter getWriter() throws IOException {
return new PrintWriter(getOutputStream());
}
}
private static class ServletOutputStreamWrapper extends ServletOutputStream {
private final ServletOutputStream outputStream;
private ByteBuffer find;
private ByteBuffer replace;
private boolean match;
public ServletOutputStreamWrapper(ServletOutputStream outputStream, String find, String replace) {
this.outputStream = outputStream;
this.find = ByteBuffer.wrap(find.getBytes());
this.replace = ByteBuffer.wrap(replace.getBytes());
}
@Override
public void write(int i) throws IOException {
byte b = (byte)(i & 0xff);
if (match) {
if(find.get() == b) {
if (!find.hasRemaining()) {
// COMPLETE MATCH
// 1) write out replacement buffer
// 2) unset 'match' flag
outputStream.write(replace.array());
match = false;
} // else { /* POTENTIAL MATCH ongoing, writes deferred */ }
} else {
// FAILED MATCH
// 1) write out portion of 'find' buffer that matched
// 2) write out the current byte that caused mismatch
// 3) unset 'match' flag
outputStream.write(find.array(), 0, find.position() - 1);
outputStream.write(b);
match = false;
}
} else {
if (b == find.get(0)) {
// POTENTIAL MATCH started, write deferred
// - set 'match' flag to true for next write call
// - position 'find' buffer at next byte for next check
match = true;
find.position(1);
} else {
// NO MATCH, just pass byte through to underlying outputstream
outputStream.write(b);
}
}
}
@Override
public void write(byte[] bytes) throws IOException {
write(bytes, 0, bytes.length);
}
@Override
public void write(byte[] b, int o, int l) throws IOException {
for (int i = 0; i < l; ++i) { write(b[o + i]); }
}
@Override
public void close() throws IOException {
if (match) {
// FAILED MATCH, complete deferred writes
outputStream.write(find.array(), 0, find.position());
match = false;
}
super.close();
outputStream.close();
}
@Override
public void flush() throws IOException {
super.flush();
outputStream.flush();
}
}
}