234 lines
9.0 KiB
Java
234 lines
9.0 KiB
Java
/**
|
||
* 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();
|
||
}
|
||
}
|
||
}
|