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