All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ning.jetty.utils.servlets.HttpProxyServlet Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2010-2011 Ning, Inc.
 *
 * Ning licenses this file to you 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 com.ning.jetty.utils.servlets;

import com.google.common.io.ByteStreams;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.mogwee.executors.Executors;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.Request;
import com.ning.http.client.RequestBuilder;
import com.ning.http.client.Response;
import com.ning.http.client.generators.InputStreamBodyGenerator;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.concurrent.ExecutionException;

@Singleton
public class HttpProxyServlet extends HttpServlet
{
    protected HashSet dontProxyHeaders = new HashSet();

    {
        dontProxyHeaders.add("proxy-connection");
        dontProxyHeaders.add("connection");
        dontProxyHeaders.add("keep-alive");
        dontProxyHeaders.add("transfer-encoding");
        dontProxyHeaders.add("te");
        dontProxyHeaders.add("trailer");
        dontProxyHeaders.add("proxy-authorization");
        dontProxyHeaders.add("proxy-authenticate");
        dontProxyHeaders.add("upgrade");
    }

    @Inject
    private ServiceFinder serviceFinder;

    private ServletConfig config;
    private AsyncHttpClient client;

    @Override
    public void init(final ServletConfig config) throws ServletException
    {
        this.config = config;

        // Don't limit the number of connections per host
        // See https://github.com/ning/async-http-client/issues/issue/28
        final AsyncHttpClientConfig.Builder builder = new AsyncHttpClientConfig.Builder();
        builder.setMaximumConnectionsPerHost(-1);
        builder.setAllowPoolingConnection(true);
        builder.setExecutorService(Executors.newCachedThreadPool("HttpProxyServlet-AsyncHttpClient"));
        builder.setUserAgent("ning-service/1.0");
        client = new AsyncHttpClient(builder.build());

        config.getServletContext().log("Created new HttpProxyServlet");
    }

    @Override
    public ServletConfig getServletConfig()
    {
        return config;
    }

    private void proxyService(final ServletRequest req, ServletResponse res) throws ServletException, IOException
    {
        while (res instanceof HttpServletResponseWrapper) {
            res = ((HttpServletResponseWrapper) res).getResponse();
        }
        final HttpServletRequest request = (HttpServletRequest) req;
        final HttpServletResponse response = (HttpServletResponse) res;

        // Find the remote host to talk to
        final String remoteHost;
        try {
            remoteHost = serviceFinder.getRemoteHost();
        }
        catch (Exception e) {
            // The Servlet is temporarily unavailable
            throw new UnavailableException("The service finder couldn't find the remote host: " + (e.getCause() == null ? e.toString() : e.getCause().toString()), -1);
        }
        final String proxyTo = "http://" + remoteHost;

        // Create the new http request to the proxied host
        final Request newRequest = cloneRequest(request, proxyTo).build();
        final AsyncHttpClient.BoundRequestBuilder builder = client.prepareRequest(newRequest);

        Response proxiedResponse = null;
        try {
            // Me want Servlet 3.0 :(
            proxiedResponse = builder.execute().get();
        }
        catch (InterruptedException e) {
            config.getServletContext().log("Interrupted while waiting on the remote host", e);
            return;
        }
        catch (ExecutionException e) {
            config.getServletContext().log("Error talking to the remote host", e);
            return;
        }

        response.setStatus(proxiedResponse.getStatusCode());
        // Copy headers
        for (final String headerName : proxiedResponse.getHeaders().keySet()) {
            if (dontProxyHeaders.contains(headerName)) {
                continue;
            }

            for (final String headerValue : proxiedResponse.getHeaders().get(headerName)) {
                response.addHeader(headerName, headerValue);
            }
        }
        // Copy response body
        final ServletOutputStream responseOutputStream = response.getOutputStream();
        final InputStream stream = proxiedResponse.getResponseBodyAsStream();
        ByteStreams.copy(stream, responseOutputStream);
    }

    private RequestBuilder cloneRequest(final HttpServletRequest request, final String proxyTo) throws IOException
    {
        final RequestBuilder builder = new RequestBuilder();
        boolean hasContent = false;
        boolean hasXff = false;

        String connectionHdr = request.getHeader("Connection");
        if (connectionHdr != null) {
            connectionHdr = connectionHdr.toLowerCase();
            if (!connectionHdr.contains("keep-alive") && !connectionHdr.contains("close")) {
                connectionHdr = null;
            }
        }

        // We are guaranteed that headers are Strings
        @SuppressWarnings("unchecked")
        final Collection headerNames = Collections.list(request.getHeaderNames());
        for (final Object headerObjectName : headerNames) {
            final String headerName = (String) headerObjectName;

            // Don't copy headers on close
            if (connectionHdr != null && connectionHdr.contains(headerName)) {
                continue;
            }

            if (dontProxyHeaders.contains(headerName)) {
                continue;
            }

            if ("content-type".equalsIgnoreCase(headerName)) {
                hasContent = true;
            }
            if ("X-Forwarded-For".equalsIgnoreCase(headerName)) {
                hasXff = true;
            }

            @SuppressWarnings("unchecked")
            final Collection headerValues = Collections.list(request.getHeaders(headerName));
            for (final Object headerValue : headerValues) {
                builder.addHeader(headerName, (String) headerValue);
            }
        }
        builder.addHeader("Via", "Ning proxy");
        if (!hasXff) {
            builder.addHeader("X-Forwarded-For", request.getRemoteAddr());
        }

        // Need to set the Method before setting the body
        builder.setMethod(request.getMethod());
        if (hasContent) {
            final InputStream in = request.getInputStream();
            builder.setBody(new InputStreamBodyGenerator(in));
        }

        String uri = proxyTo + request.getRequestURI();
        if (request.getQueryString() != null) {
            uri += "?" + request.getQueryString();
        }
        builder.setUrl(uri);

        return builder;
    }

    @Override
    public String getServletInfo()
    {
        return "HttpProxyServlet";
    }

    @Override
    public void destroy()
    {
        if (client != null) {
            client.close();
        }
    }

    @Override
    protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException
    {
        proxyService(req, resp);
    }

    @Override
    public void service(final ServletRequest req, final ServletResponse res) throws ServletException, IOException
    {
        proxyService(req, res);
    }

    @Override
    protected void doDelete(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException
    {
        proxyService(req, resp);
    }

    @Override
    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException
    {
        proxyService(req, resp);
    }

    @Override
    protected void doHead(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException
    {
        proxyService(req, resp);
    }

    @Override
    protected void doOptions(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException
    {
        proxyService(req, resp);
    }

    @Override
    protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException
    {
        proxyService(req, resp);
    }

    @Override
    protected void doPut(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException
    {
        proxyService(req, resp);
    }

    @Override
    protected void doTrace(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException
    {
        proxyService(req, resp);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy