org.openrepose.nodeservice.httpcomponent.HttpComponentRequestProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.openrepose.nodeservice.httpcomponent.HttpComponentRequestProcessor.java

Source

/*
 * _=_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_=
 * Repose
 * _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
 * Copyright (C) 2010 - 2015 Rackspace US, 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.openrepose.nodeservice.httpcomponent;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.InputStreamEntity;
import org.openrepose.commons.utils.StringUtilities;
import org.openrepose.commons.utils.http.CommonHttpHeader;
import org.openrepose.commons.utils.io.BufferedServletInputStream;
import org.openrepose.commons.utils.io.RawInputStreamReader;
import org.openrepose.commons.utils.servlet.http.HttpServletRequestWrapper;
import org.openrepose.core.proxy.common.AbstractRequestProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Enumeration;

/**
 * Process a request to copy over header values, query string parameters, and
 * request body as necessary.
 */
class HttpComponentRequestProcessor extends AbstractRequestProcessor {

    private static final Logger LOG = LoggerFactory.getLogger(HttpComponentRequestProcessor.class);
    private static final String ENCODING = "UTF-8";

    private final boolean rewriteHostHeader;
    private final URI targetHost;

    private HttpServletRequest sourceRequest;
    private boolean isConfiguredChunked;

    public HttpComponentRequestProcessor(HttpServletRequest request, URI host, boolean rewriteHostHeader,
            boolean isConfiguredChunked) {
        this.sourceRequest = request;
        this.targetHost = host;
        this.rewriteHostHeader = rewriteHostHeader;
        this.isConfiguredChunked = isConfiguredChunked;
    }

    private void setQueryString(URIBuilder builder) throws URISyntaxException {
        String queryString = sourceRequest.getQueryString();
        if (StringUtils.isNotBlank(queryString)) {
            builder.setQuery(queryString);
            if (builder.getQueryParams().isEmpty()) {
                builder.removeQuery();
            }
        }
    }

    public URI getUri(String target) throws URISyntaxException {
        URIBuilder builder = new URIBuilder(target);
        setQueryString(builder);
        return builder.build();
    }

    /**
     * Scan header values and manipulate as necessary. Host header, if provided, may need to be updated.
     *
     * @param headerName
     * @param headerValue
     * @return
     */
    private String processHeaderValue(String headerName, String headerValue) {
        String result = headerValue;

        // In case the proxy host is running multiple virtual servers,
        // rewrite the Host header to ensure that we get content from
        // the correct virtual server
        if (rewriteHostHeader && headerName.equalsIgnoreCase(CommonHttpHeader.HOST.toString())) {
            result = targetHost.getHost() + ":" + targetHost.getPort();
        }

        return result;
    }

    /**
     * Copy header values from source request to the http method.
     *
     * @param method
     */
    private void setHeaders(HttpRequestBase method) {
        final Enumeration<String> headerNames = sourceRequest.getHeaderNames();

        while (headerNames.hasMoreElements()) {
            final String headerName = headerNames.nextElement();

            if (excludeHeader(headerName)) {
                continue;
            }

            final Enumeration<String> headerValues = sourceRequest.getHeaders(headerName);

            while (headerValues.hasMoreElements()) {
                String headerValue = headerValues.nextElement();
                method.addHeader(headerName, processHeaderValue(headerName, headerValue));
            }
        }
    }

    /**
     * Process a base http request. Base http methods will not contain a message body.
     *
     * @param method
     * @return
     */
    public HttpRequestBase process(HttpRequestBase method) {
        setHeaders(method);
        return method;
    }

    /**
     * Process an entity enclosing http method. These methods can handle a request body.
     *
     * @param method
     * @return
     * @throws IOException
     */
    // todo: remove the need for a synchronized method -- this is the only method that needs to be synchronized for now
    //       since it modifies the sourceRequest
    public synchronized HttpRequestBase process(HttpEntityEnclosingRequestBase method) throws IOException {
        final int contentLength = getEntityLength();
        setHeaders(method);
        method.setEntity(new InputStreamEntity(sourceRequest.getInputStream(), contentLength));
        return method;
    }

    private int getEntityLength() throws IOException {
        if (StringUtilities.nullSafeEqualsIgnoreCase(sourceRequest.getHeader("transfer-encoding"), "chunked")
                || isConfiguredChunked) {
            return -1;
        } else {
            // todo: optimize so subsequent calls to this method do not need to read/copy the entity
            final ByteArrayOutputStream sourceEntity = new ByteArrayOutputStream();
            RawInputStreamReader.instance().copyTo(sourceRequest.getInputStream(), sourceEntity);

            final ServletInputStream readableEntity = new BufferedServletInputStream(
                    new ByteArrayInputStream(sourceEntity.toByteArray()));
            sourceRequest = new HttpServletRequestWrapper(sourceRequest, readableEntity);

            return sourceEntity.size();
        }
    }
}