protected void service()

in http/balancer/src/main/java/org/apache/karaf/cellar/http/balancer/CellarBalancerProxyServlet.java [144:232]


    protected void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
            throws ServletException, IOException {

        String location = locations.get(new Random().nextInt(locations.size()));
        URI locationUri = URI.create(location);
        HttpHost host = URIUtils.extractHost(locationUri);

        LOGGER.debug("CELLAR HTTP BALANCER: proxying to");
        LOGGER.debug("CELLAR HTTP BALANCER:     URI: {}", locationUri);
        LOGGER.debug("CELLAR HTTP BALANCER:     Host: {}", host);

        // Make the Request
        //note: we won't transfer the protocol version because I'm not sure it would truly be compatible
        String method = servletRequest.getMethod();
        LOGGER.debug("CELLAR HTTP BALANCER:     Method: {}", method);
        String proxyRequestUri = rewriteUrlFromRequest(servletRequest, location);
        LOGGER.debug("CELLAR HTTP BALANCER:     Proxy Request URI: {}", proxyRequestUri);
        HttpRequest proxyRequest;
        //spec: RFC 2616, sec 4.3: either of these two headers signal that there is a message body.
        if (servletRequest.getHeader(HttpHeaders.CONTENT_LENGTH) != null ||
                servletRequest.getHeader(HttpHeaders.TRANSFER_ENCODING) != null) {
            HttpEntityEnclosingRequest eProxyRequest = new BasicHttpEntityEnclosingRequest(method, proxyRequestUri);
            // Add the input entity (streamed)
            //  note: we don't bother ensuring we close the servletInputStream since the container handles it
            eProxyRequest.setEntity(new InputStreamEntity(servletRequest.getInputStream(), servletRequest.getContentLength()));
            proxyRequest = eProxyRequest;
        } else
            proxyRequest = new BasicHttpRequest(method, proxyRequestUri);

        LOGGER.debug("CELLAR HTTP BALANCER:     copying request headers");
        copyRequestHeaders(servletRequest, proxyRequest, host);

        LOGGER.debug("CELLAR HTTP BALANCER:     set X-Forwarded header");
        setXForwardedForHeader(servletRequest, proxyRequest);

        HttpResponse proxyResponse = null;
        try {
            // Execute the request
            LOGGER.debug("CELLAR HTTP BALANCER:     executing proxy request");
            proxyResponse = proxyClient.execute(host, proxyRequest);

            // Process the response
            int statusCode = proxyResponse.getStatusLine().getStatusCode();
            LOGGER.debug("CELLAR HTTP BALANCER:     status code: {}", statusCode);

            // copying response headers to make sure SESSIONID or other Cookie which comes from remote server
            // will be saved in client when the proxied url was redirected to another one.
            // see issue [#51](https://github.com/mitre/HTTP-Proxy-Servlet/issues/51)
            LOGGER.debug("CELLAR HTTP BALANCER:     copying response headers");
            copyResponseHeaders(proxyResponse, servletRequest, servletResponse);

            if (doResponseRedirectOrNotModifiedLogic(servletRequest, servletResponse, proxyResponse, statusCode, location)) {
                //the response is already "committed" now without any body to send
                return;
            }

            // Pass the response code. This method with the "reason phrase" is deprecated but it's the only way to pass the
            //  reason along too.
            //noinspection deprecation
            LOGGER.debug("CELLAR HTTP BALANCER:     set response status code");
            servletResponse.setStatus(statusCode, proxyResponse.getStatusLine().getReasonPhrase());

            // Send the content to the client
            LOGGER.debug("CELLAR HTTP BALANCER:     copying response entity");
            copyResponseEntity(proxyResponse, servletResponse);

        } catch (Exception e) {
            //abort request, according to best practice with HttpClient
            if (proxyRequest instanceof AbortableHttpRequest) {
                AbortableHttpRequest abortableHttpRequest = (AbortableHttpRequest) proxyRequest;
                abortableHttpRequest.abort();
            }
            if (e instanceof RuntimeException)
                throw (RuntimeException) e;
            if (e instanceof ServletException)
                throw (ServletException) e;
            //noinspection ConstantConditions
            if (e instanceof IOException)
                throw (IOException) e;
            throw new RuntimeException(e);

        } finally {
            // make sure the entire entity was consumed, so the connection is released
            if (proxyResponse != null)
                consumeQuietly(proxyResponse.getEntity());
            //Note: Don't need to close servlet outputStream:
            // http://stackoverflow.com/questions/1159168/should-one-call-close-on-httpservletresponse-getoutputstream-getwriter
        }
    }