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

234 lines
9.0 KiB
Java
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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();
}
}
}