/**
*
* Copyright 2015 sourceforge.
*
* 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 org.gwtopenmaps.openlayers.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.MessageFormat;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* This is a transparent HTTP proxy written in Java that is similar to the proxy
* in the OpenLayers examples, which is written in Python. These proxies are
* used to circumvent browser restrictions on cross-domain requests with
* Javascript. </p> <p> To use the proxy you need to 1) configure the proxy
* servlet in your web.xml and 2) use OpenLayers.setProxyHost to set the
* url-path to the proxy. If the proxy is configured to listen to the
* url-pattern '/gwtOpenLayersProxy/*' then the proxy host should be set to
* 'gwtOpenLayersProxy?targetURL='. </p> Initial code for this proxy is based
* upon <a href=
* "http://trac.openlayers.org/changeset/8099/sandbox?format=diff&new=8099">the
* following code</a><br /> see also <a href=
* "http://java.sun.com/docs/books/tutorial/networking/urls/readingWriting.html"
* title="this networking tutorial">this networking tutorial</a> <p>
*/
@SuppressWarnings("serial")
public class GwtOpenLayersProxyServlet extends HttpServlet {
public static final Pattern PATTERN = Pattern.compile("^[0-9]+$");
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
private void processRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
HttpURLConnection connection = null;
InputStream istream = null; // input to proxy
OutputStream ostream = null; // output from proxy
InputStream connectionIstream = null; // output for the target is input for the connection
OutputStream connectionOstream = null; // input for the target is output for the connection
String remoteHost = request.getRemoteHost(); // get host address of client - for checking allowedHosts
boolean allowedHost = isAllowedHost(remoteHost); // The allowedHosts are the hosts that are allowed to use the Open Proxy.
try {
// easy way to ignore case of param?
if ((request.getParameter("targetURL") != null) && (request.getParameter(
"targetURL") != "") && allowedHost) {
// HTTPUrlConnection looks at http.proxyHost and http.proxyPort system properties.
// Make sure these properties are set these if you are behind a proxy.
// step 1: initialize
String requestMethod = request.getMethod();
URL targetURL = new URL(request.getParameter("targetURL"));
connection = (HttpURLConnection) targetURL.openConnection();
final String connectTimeout = getInitParameter("connectTimeout");
if (connectTimeout != null) {
if (PATTERN.matcher(connectTimeout.trim()).matches()) {
connection.setConnectTimeout(Integer.parseInt(
connectTimeout.trim()));
} else {
throw new IllegalStateException(MessageFormat.format(
"connectTimeout init-param {0} is non-numeric",
connectTimeout));
}
}
final String readTimeout = getInitParameter("readTimeout");
if (readTimeout != null) {
if (PATTERN.matcher(readTimeout.trim()).matches()) {
connection.setReadTimeout(Integer.parseInt(
readTimeout.trim()));
} else {
throw new IllegalStateException(MessageFormat.format(
"readTimeout init-param {0} is non-numeric",
readTimeout));
}
}
connection.setRequestMethod(requestMethod);
transferHTTPRequestHeaders(connection, request);
// step 2: proxy requests
if (requestMethod.equals("GET")) {
// default for setDoInput is true
connectionIstream = connection.getInputStream();
}
if (requestMethod.equals("POST")) {
transferHTTPRequestHeadersForPOST(connection, request);
int clength = request.getContentLength(); // clength is for checking if there is a POST body. Is that sufficient?
if (clength > 0) {
istream = request.getInputStream();
connection.setDoOutput(true); // for POST we need to write to connection
connection.setRequestProperty("Content-Length",
Integer.toString(clength)); // only valid for POST request
connectionOstream = connection.getOutputStream();
// copy the request body to remote outputStream
copy(istream, connectionOstream);
}
connectionIstream = connection.getInputStream();
}
// step 3: return output
// can output be the same for GET/POST? or different return headers?
// servlet may return 3 things: status code, response headers, response body
// status code and headers have to be set before response body
response.setContentType(connection.getContentType());
ostream = response.getOutputStream();
copy(connectionIstream, ostream);
}
// if not targetURL send page that targetURL is required param
} catch (Exception e) {
response.setStatus(500); // what will user get? default page for response code
// WMS/WFS have specific responses to errors
// response.getWriter();//will writing custom result help
e.printStackTrace();
} finally {
if (istream != null) {
istream.close();
}
if (ostream != null) {
ostream.close();
}
if (connectionIstream != null) {
connectionIstream.close();
}
if (connectionOstream != null) {
connectionOstream.close();
}
}
}
private void copy(InputStream istream,
OutputStream ostream) throws Exception {
int bufferSize = 4 * 4 * 1024; // same buffer size as in Jetty utils (2*8192)
byte[] buffer = new byte[bufferSize];
int read;
while ((read = istream.read(buffer)) != -1) {
ostream.write(buffer, 0, read);
}
}
private void transferHTTPRequestHeaders(HttpURLConnection connection,
HttpServletRequest request) {
// TODO make sure all headers are copied to target, see for HTTP headers http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
// Do request.getProperties to get request properties
if (request.getHeader("Accept") != null) {
connection.setRequestProperty("Accept", request.getHeader("Accept"));
}
if (request.getHeader("Accept-Charset") != null) {
connection.setRequestProperty("Accept-Charset", request.getHeader(
"Accept-Charset"));
}
if (request.getHeader("Accept-Encoding") != null) {
// TODO browsers accept gzipped, should proxy accept gzip and how to handle it?
// connection.setRequestProperty("Accept-Encoding", request.getHeader("Accept-Encoding"));
}
if (request.getHeader("Authorization") != null) {
connection.setRequestProperty("Authorization", request.getHeader(
"Authorization"));
}
if (request.getHeader("Connection") != null) {
// TODO HTTP/1.1 proxies MUST parse the Connection header field before a message is forwarded and, for each connection-token in this field, remove any header field(s) from the message with the same name as the connection-token.
// connection.setRequestProperty("Connection", request.getHeader("Connection"));
}
// set de-facto standard proxy headers (x-forwarded-for, others?s)
if (request.getHeader("X-Forwarded-For") != null) {
connection.setRequestProperty("X-Forwarded-For", request.getHeader(
"X-Forwarded-For")); // TODO append IP proxy
} else {
connection.setRequestProperty("X-Forwarded-For",
request.getRemoteAddr()); // TODO append IP proxy
}
}
private void transferHTTPRequestHeadersForPOST(HttpURLConnection connection,
HttpServletRequest request) {
if (request.getHeader("Content-Type") != null) {
connection.setRequestProperty("Content-Type",
request.getContentType());
} else {
// throw exception?
}
}
private boolean isAllowedHost(String remoteHost) {
// TODO checking of host
return true;
}
}