org.apache.hadoop.gateway.dispatch.DefaultDispatch.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.gateway.dispatch.DefaultDispatch.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF 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 org.apache.hadoop.gateway.dispatch;

import org.apache.hadoop.gateway.SpiGatewayMessages;
import org.apache.hadoop.gateway.SpiGatewayResources;
import org.apache.hadoop.gateway.audit.api.Action;
import org.apache.hadoop.gateway.audit.api.ActionOutcome;
import org.apache.hadoop.gateway.audit.api.AuditServiceFactory;
import org.apache.hadoop.gateway.audit.api.Auditor;
import org.apache.hadoop.gateway.audit.api.ResourceType;
import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
import org.apache.hadoop.gateway.config.Configure;
import org.apache.hadoop.gateway.config.Default;
import org.apache.hadoop.gateway.config.GatewayConfig;
import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
import org.apache.hadoop.gateway.i18n.resources.ResourcesFactory;
import org.apache.hadoop.gateway.util.MimeTypes;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Set;

/**
 *
 */
public class DefaultDispatch extends AbstractGatewayDispatch {

    protected static final String SET_COOKIE = "SET-COOKIE";
    protected static final String WWW_AUTHENTICATE = "WWW-AUTHENTICATE";

    protected static SpiGatewayMessages LOG = MessagesFactory.get(SpiGatewayMessages.class);
    protected static SpiGatewayResources RES = ResourcesFactory.get(SpiGatewayResources.class);
    protected static Auditor auditor = AuditServiceFactory.getAuditService().getAuditor(
            AuditConstants.DEFAULT_AUDITOR_NAME, AuditConstants.KNOX_SERVICE_NAME,
            AuditConstants.KNOX_COMPONENT_NAME);

    private Set<String> outboundResponseExcludeHeaders;

    //Buffer size in bytes
    private int replayBufferSize = -1;

    @Override
    public void init() {
        super.init();
        outboundResponseExcludeHeaders = new HashSet<>();
        outboundResponseExcludeHeaders.add(SET_COOKIE);
        outboundResponseExcludeHeaders.add(WWW_AUTHENTICATE);
    }

    @Override
    public void destroy() {

    }

    protected int getReplayBufferSize() {
        if (replayBufferSize > 0) {
            return Math.abs(replayBufferSize / 1024);
        }
        return replayBufferSize;
    }

    @Configure
    protected void setReplayBufferSize(@Default("-1") int size) {
        setReplayBufferSizeInBytes(size);
    }

    protected int getReplayBufferSizeInBytes() {
        return replayBufferSize;
    }

    protected void setReplayBufferSizeInBytes(int size) {
        if (size > 0) {
            size *= 1024;
        }
        replayBufferSize = size;
    }

    protected void executeRequest(HttpUriRequest outboundRequest, HttpServletRequest inboundRequest,
            HttpServletResponse outboundResponse) throws IOException {
        HttpResponse inboundResponse = executeOutboundRequest(outboundRequest);
        writeOutboundResponse(outboundRequest, inboundRequest, outboundResponse, inboundResponse);
    }

    protected HttpResponse executeOutboundRequest(HttpUriRequest outboundRequest) throws IOException {
        LOG.dispatchRequest(outboundRequest.getMethod(), outboundRequest.getURI());
        HttpResponse inboundResponse;

        try {
            auditor.audit(Action.DISPATCH, outboundRequest.getURI().toString(), ResourceType.URI,
                    ActionOutcome.UNAVAILABLE, RES.requestMethod(outboundRequest.getMethod()));
            if (!"true".equals(System.getProperty(GatewayConfig.HADOOP_KERBEROS_SECURED))) {
                // Hadoop cluster not Kerberos enabled
                addCredentialsToRequest(outboundRequest);
            }
            inboundResponse = client.execute(outboundRequest);

            int statusCode = inboundResponse.getStatusLine().getStatusCode();
            if (statusCode != 201) {
                LOG.dispatchResponseStatusCode(statusCode);
            } else {
                Header location = inboundResponse.getFirstHeader("Location");
                if (location == null) {
                    LOG.dispatchResponseStatusCode(statusCode);
                } else {
                    LOG.dispatchResponseCreatedStatusCode(statusCode, location.getValue());
                }
            }
            auditor.audit(Action.DISPATCH, outboundRequest.getURI().toString(), ResourceType.URI,
                    ActionOutcome.SUCCESS, RES.responseStatus(statusCode));
        } catch (Exception e) {
            // We do not want to expose back end host. port end points to clients, see JIRA KNOX-58
            auditor.audit(Action.DISPATCH, outboundRequest.getURI().toString(), ResourceType.URI,
                    ActionOutcome.FAILURE);
            LOG.dispatchServiceConnectionException(outboundRequest.getURI(), e);
            throw new IOException(RES.dispatchConnectionError());
        }
        return inboundResponse;
    }

    protected void writeOutboundResponse(HttpUriRequest outboundRequest, HttpServletRequest inboundRequest,
            HttpServletResponse outboundResponse, HttpResponse inboundResponse) throws IOException {
        // Copy the client respond header to the server respond.
        outboundResponse.setStatus(inboundResponse.getStatusLine().getStatusCode());
        Header[] headers = inboundResponse.getAllHeaders();
        Set<String> excludeHeaders = getOutboundResponseExcludeHeaders();
        boolean hasExcludeHeaders = false;
        if ((excludeHeaders != null) && !(excludeHeaders.isEmpty())) {
            hasExcludeHeaders = true;
        }
        for (Header header : headers) {
            String name = header.getName();
            if (hasExcludeHeaders && excludeHeaders.contains(name.toUpperCase())) {
                continue;
            }
            String value = header.getValue();
            outboundResponse.addHeader(name, value);
        }

        HttpEntity entity = inboundResponse.getEntity();
        if (entity != null) {
            outboundResponse.setContentType(getInboundResponseContentType(entity));
            //KM[ If this is set here it ends up setting the content length to the content returned from the server.
            // This length might not match if the the content is rewritten.
            //      long contentLength = entity.getContentLength();
            //      if( contentLength <= Integer.MAX_VALUE ) {
            //        outboundResponse.setContentLength( (int)contentLength );
            //      }
            //]
            InputStream stream = entity.getContent();
            try {
                writeResponse(inboundRequest, outboundResponse, stream);
            } finally {
                closeInboundResponse(inboundResponse, stream);
            }
        }
    }

    private String getInboundResponseContentType(final HttpEntity entity) {
        String fullContentType = null;
        if (entity != null) {
            ContentType entityContentType = ContentType.get(entity);
            if (entityContentType != null) {
                if (entityContentType.getCharset() == null) {
                    final String entityMimeType = entityContentType.getMimeType();
                    final String defaultCharset = MimeTypes.getDefaultCharsetForMimeType(entityMimeType);
                    if (defaultCharset != null) {
                        LOG.usingDefaultCharsetForEntity(entityMimeType, defaultCharset);
                        entityContentType = entityContentType.withCharset(defaultCharset);
                    }
                } else {
                    LOG.usingExplicitCharsetForEntity(entityContentType.getMimeType(),
                            entityContentType.getCharset());
                }
                fullContentType = entityContentType.toString();
            }
        }
        if (fullContentType == null) {
            LOG.unknownResponseEntityContentType();
        } else {
            LOG.inboundResponseEntityContentType(fullContentType);
        }
        return fullContentType;
    }

    protected void closeInboundResponse(HttpResponse response, InputStream stream) throws IOException {
        try {
            stream.close();
        } finally {
            if (response instanceof Closeable) {
                ((Closeable) response).close();
            }
        }
    }

    /**
     * This method provides a hook for specialized credential propagation
     * in subclasses.
     *
     * @param outboundRequest
     */
    protected void addCredentialsToRequest(HttpUriRequest outboundRequest) {
    }

    protected HttpEntity createRequestEntity(HttpServletRequest request) throws IOException {

        String contentType = request.getContentType();
        int contentLength = request.getContentLength();
        InputStream contentStream = request.getInputStream();

        HttpEntity entity;
        if (contentType == null) {
            entity = new InputStreamEntity(contentStream, contentLength);
        } else {
            entity = new InputStreamEntity(contentStream, contentLength, ContentType.parse(contentType));
        }
        GatewayConfig config = (GatewayConfig) request.getServletContext()
                .getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
        if (config != null && config.isHadoopKerberosSecured()) {
            //Check if delegation token is supplied in the request
            boolean delegationTokenPresent = false;
            String queryString = request.getQueryString();
            if (queryString != null) {
                delegationTokenPresent = queryString.startsWith("delegation=")
                        || queryString.contains("&delegation=");
            }
            if (replayBufferSize < 0) {
                replayBufferSize = config.getHttpServerRequestBuffer();
            }
            if (!delegationTokenPresent && replayBufferSize > 0) {
                entity = new PartiallyRepeatableHttpEntity(entity, replayBufferSize);
            }
        }

        return entity;
    }

    @Override
    public void doGet(URI url, HttpServletRequest request, HttpServletResponse response)
            throws IOException, URISyntaxException {
        HttpGet method = new HttpGet(url);
        // https://issues.apache.org/jira/browse/KNOX-107 - Service URLs not rewritten for WebHDFS GET redirects
        // This is now taken care of in DefaultHttpClientFactory.createHttpClient
        // and setting params here causes configuration setup there to be ignored there.
        // method.getParams().setBooleanParameter("http.protocol.handle-redirects", false);
        copyRequestHeaderFields(method, request);
        executeRequest(method, request, response);
    }

    @Override
    public void doOptions(URI url, HttpServletRequest request, HttpServletResponse response)
            throws IOException, URISyntaxException {
        HttpOptions method = new HttpOptions(url);
        executeRequest(method, request, response);
    }

    @Override
    public void doPut(URI url, HttpServletRequest request, HttpServletResponse response)
            throws IOException, URISyntaxException {
        HttpPut method = new HttpPut(url);
        HttpEntity entity = createRequestEntity(request);
        method.setEntity(entity);
        copyRequestHeaderFields(method, request);
        executeRequest(method, request, response);
    }

    @Override
    public void doPost(URI url, HttpServletRequest request, HttpServletResponse response)
            throws IOException, URISyntaxException {
        HttpPost method = new HttpPost(url);
        HttpEntity entity = createRequestEntity(request);
        method.setEntity(entity);
        copyRequestHeaderFields(method, request);
        executeRequest(method, request, response);
    }

    @Override
    public void doDelete(URI url, HttpServletRequest request, HttpServletResponse response)
            throws IOException, URISyntaxException {
        HttpDelete method = new HttpDelete(url);
        copyRequestHeaderFields(method, request);
        executeRequest(method, request, response);
    }

    public Set<String> getOutboundResponseExcludeHeaders() {
        return outboundResponseExcludeHeaders;
    }

}