/* * 2012-3 Red Hat Inc. and/or its affiliates and other contributors. * * 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.rtgov.elasticsearch.rest; import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.message.BasicHeader; import org.apache.http.message.HeaderGroup; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ConnectException; import java.util.logging.Level; import java.util.logging.Logger; /** * This class implements the authenticating proxy for the ElasticSearch server. * * Based on the http servlet proxy implemented by David Smiley: * https://github.com/dsmiley/HTTP-Proxy-Servlet */ public class ElasticsearchRESTServer extends HttpServlet { private static final long serialVersionUID = 1L; private static final Logger LOG=Logger.getLogger(ElasticsearchRESTServer.class.getName()); private ElasticsearchHttpClient _client=new ElasticsearchHttpClient(); /** * {@inheritDoc} */ protected void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException { if (isSupported(servletRequest)) { try { HttpResponse resp=_client.process(servletRequest); servletResponse.setStatus(resp.getStatusLine().getStatusCode()); copyResponseHeaders(resp, servletResponse); // Send the content to the client copyResponseContent(resp, servletResponse); } catch (ConnectException ce) { // Return "Service Unavailable" status code servletResponse.setStatus(503); } catch (IOException ioe) { throw ioe; } catch (Exception e) { throw new IOException("Failed to process gateway request", e); } } else { if (LOG.isLoggable(Level.FINER)) { LOG.finer("Forbidden request: method="+servletRequest.getMethod()+" path="+servletRequest.getPathInfo()); } // Return "Forbidden" status code servletResponse.setStatus(403); } } /** * This method determines whether the supplied request is supported by this gateway. * * @param servletRequest The REST request * @return Whether the request is supported */ protected boolean isSupported(HttpServletRequest servletRequest) { if (servletRequest.getMethod().equalsIgnoreCase("get")) { return (true); } else if (servletRequest.getMethod().equalsIgnoreCase("post")) { if (servletRequest.getPathInfo().endsWith("/_search")) { return (true); } } else if (servletRequest.getMethod().equalsIgnoreCase("put")) { if (servletRequest.getPathInfo().startsWith("/kibana-int/dashboard")) { return (true); } } return (false); } /** * Copy proxied response headers back to the servlet client. * * @param proxyResponse The response from the target server * @param servletResponse The response back to the client */ protected void copyResponseHeaders(HttpResponse proxyResponse, HttpServletResponse servletResponse) { for (int i=0; i < proxyResponse.getAllHeaders().length; i++) { Header header=proxyResponse.getAllHeaders()[i]; if (HOPBYHOPHEADERS.containsHeader(header.getName())) { continue; } servletResponse.addHeader(header.getName(), header.getValue()); } } /** * Copy response body data (the entity) from the proxy to the servlet client. * * Ensures that proxyInputStream will be closed in any case by closing the inputStream * @see org.apache.http.HttpEntity.getContent() * * @param proxyResponse The response from the target server * @param servletResponse The response back to the client * @throws IOException Failed to copy content */ protected void copyResponseContent(HttpResponse proxyResponse, HttpServletResponse servletResponse) throws IOException { InputStream proxyInputStream = null; OutputStream servletOutputStream = null; try { proxyInputStream = proxyResponse.getEntity().getContent(); servletOutputStream = servletResponse.getOutputStream(); proxyResponse.getEntity().writeTo(servletOutputStream); } finally { secureCloseStream(proxyInputStream); secureCloseStream(servletOutputStream); } } private void secureCloseStream(Closeable servletOutputStream) { try { servletOutputStream.close(); } catch (Exception e) { log(e.getMessage(),e); } } /** These are the "hop-by-hop" headers that should not be copied. * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html * I use an HttpClient HeaderGroup class instead of Set<String> because this * approach does case insensitive lookup faster. */ private static final HeaderGroup HOPBYHOPHEADERS; static { HOPBYHOPHEADERS = new HeaderGroup(); String[] headers = new String[] { "Connection", "Keep-Alive", "Proxy-Authenticate", "Proxy-Authorization", "TE", "Trailers", "Transfer-Encoding", "Upgrade" }; for (String header : headers) { HOPBYHOPHEADERS.addHeader(new BasicHeader(header, null)); } } }