/*
* Copyright 2013 JBoss Inc
*
* 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.overlord.gadgets.web.server.servlets;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.List;
import java.util.Map.Entry;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.overlord.gadgets.server.ConfiguredModule;
/**
* A simple proxy servlet for REST invokations. The primary purpose of this servlet is
* to provide a way to inject appropriate authentication mechanisms when invoking a
* protected REST service directly from the Browser. When that is impossible (because
* the browser does not have the appropriate credentials available to it) then the
* browser can invoke this servlet instead. This servlet will in turn invoke the
* protected REST service and proxy the response.
*
* @author eric.wittmann@redhat.com
*/
public class RestProxyServlet extends HttpServlet {
private static final long serialVersionUID = 8689929059530563599L;
private String proxyName;
private String proxyUrl;
private String authProviderClassName;
/**
* Constructor.
*/
public RestProxyServlet() {
}
/**
* @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
*/
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
proxyName = config.getInitParameter("proxy-name");
if (proxyName == null)
throw new ServletException("Missing init parameter 'proxy-name'.");
proxyUrl = config.getInitParameter("proxy-url");
if (proxyUrl == null)
throw new ServletException("Missing init parameter 'proxy-url'.");
authProviderClassName = config.getInitParameter("authentication-provider");
}
/**
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
System.out.println("Proxy inbound endpoint: " + req.getRequestURI());
// Connect to proxy URL.
String urlStr = getProxyUrl(req);
String queryString = req.getQueryString();
if (queryString != null) {
urlStr = urlStr + "?" + queryString;
}
System.out.println("Proxying to: " + urlStr);
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
// Proxy all of the request headers.
Enumeration<?> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = String.valueOf(headerNames.nextElement());
Enumeration<?> headerValues = req.getHeaders(headerName);
while (headerValues.hasMoreElements()) {
String headerValue = String.valueOf(headerValues.nextElement());
conn.addRequestProperty(headerName, headerValue);
}
}
// Handle authentication
RestProxyAuthProvider authProvider = getAuthProvider();
if (authProvider != null) {
authProvider.provideAuthentication(conn);
}
// Now connect and proxy the response.
InputStream proxyUrlResponseStream = null;
try {
proxyUrlResponseStream = conn.getInputStream();
resp.setStatus(conn.getResponseCode());
// Proxy the response headers
for (Entry<String, List<String>> entry : conn.getHeaderFields().entrySet()) {
String respHeaderName = entry.getKey();
if (respHeaderName != null && !respHeaderName.equalsIgnoreCase("transfer-encoding")) {
for (String respHeaderValue : entry.getValue()) {
resp.addHeader(respHeaderName, respHeaderValue);
}
}
}
// Proxy the response body.
IOUtils.copy(proxyUrlResponseStream, resp.getOutputStream());
} finally {
IOUtils.closeQuietly(proxyUrlResponseStream);
conn.disconnect();
}
}
/**
* @param req the inbound http request
* @return the proxy url, with property substitutions
*/
private String getProxyUrl(HttpServletRequest req) {
String scheme = req.getScheme();
String host = req.getServerName();
String port = String.valueOf(req.getServerPort());
return this.proxyUrl.replace("SCHEME", scheme).replace("HOST", host).replace("PORT", port);
}
/**
* Gets the authentication provider. This will look up the auth provider to used either from a
* servlet init param or from the configuration properties.
*/
private RestProxyAuthProvider getAuthProvider() throws ServletException {
String classname = authProviderClassName;
if (classname == null) {
classname = ConfiguredModule.properties.getProperty("gadget-server.rest-proxy." + proxyName + ".authentication-provider");
}
if (classname != null) {
try {
Class<?> authProviderClass = Class.forName(classname);
RestProxyAuthProvider authProvider = (RestProxyAuthProvider) authProviderClass.newInstance();
authProvider.setConfiguration(proxyName, ConfiguredModule.properties);
return authProvider;
} catch (Exception e) {
throw new ServletException(e);
}
}
return null;
}
}