com.joyent.manta.http.HttpHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.joyent.manta.http.HttpHelper.java

Source

/*
 * Copyright (c) 2017, Joyent, Inc. All rights reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package com.joyent.manta.http;

import com.joyent.manta.client.MantaMetadata;
import com.joyent.manta.client.MantaObjectInputStream;
import com.joyent.manta.client.MantaObjectResponse;
import com.joyent.manta.client.crypto.SupportedCipherDetails;
import com.joyent.manta.exception.MantaClientEncryptionException;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.exception.ExceptionContext;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.slf4j.MDC;

import java.io.IOException;
import java.io.InputStream;
import java.util.function.Function;

import static com.joyent.manta.http.MantaHttpHeaders.REQUEST_ID;
import static com.joyent.manta.util.MantaUtils.asString;
import static org.apache.commons.lang3.builder.ToStringBuilder.reflectionToString;
import static org.apache.commons.lang3.builder.ToStringStyle.SHORT_PREFIX_STYLE;

/**
 * Interface describing the operations provided by our custom HTTP logic
 * that connects to the Manta server API.
 *
 * @author <a href="https://github.com/dekobon">Elijah Zupancic</a>
 * @since 3.0.0
 */
public interface HttpHelper extends AutoCloseable, HttpConnectionAware {
    /**
     * Executes a HTTP HEAD against the remote Manta API.
     *
     * @param path The fully qualified path of the object. i.e. /user/stor/foo/bar/baz
     * @return Apache HTTP Client response object
     * @throws IOException when there is a problem getting the object over the network
     */
    HttpResponse httpHead(String path) throws IOException;

    /**
     * Executes a HTTP GET against the remote Manta API.
     *
     * @param path The fully qualified path of the object. i.e. /user/stor/foo/bar/baz
     * @return Apache HTTP Client response object
     * @throws IOException when there is a problem getting the object over the network
     */
    HttpResponse httpGet(String path) throws IOException;

    /**
     * Executes a HTTP DELETE against the remote Manta API.
     *
     * @param path The fully qualified path of the object. i.e. /user/stor/foo/bar/baz
     * @return Apache HTTP Client response object
     * @throws IOException when there is a problem getting the object over the network
     */
    HttpResponse httpDelete(String path) throws IOException;

    /**
     * Executes a HTTP DELETE against the remote Manta API with provided headers.
     *
     * @param path The fully qualified path of the object. i.e. /user/stor/foo/bar/baz
     * @param headers HTTP headers to attach to request
     * @return Apache HTTP Client response object
     * @throws IOException when there is a problem getting the object over the network
     */
    HttpResponse httpDelete(String path, MantaHttpHeaders headers) throws IOException;

    /**
     * Utility method for handling HTTP POST to the Apache HTTP Client.
     *
     * @param path path to post to (without hostname)
     * @return HTTP response object
     * @throws IOException thrown when there is a problem POSTing over the network
     */
    HttpResponse httpPost(String path) throws IOException;

    /**
     * Utility method for handling HTTP POST to the Apache HTTP Client.
     *
     * @param path path to post to (without hostname)
     * @param headers HTTP headers to attach to request
     * @param entity content object to post
     * @return HTTP response object
     * @throws IOException thrown when there is a problem POSTing over the network
     */
    HttpResponse httpPost(String path, MantaHttpHeaders headers, HttpEntity entity) throws IOException;

    /**
     * <p>Returns the results of a HTTP request as an {@link InputStream}.</p>
     *
     * <p><strong>The underlying HttpResponse is not closed in this method.</strong></p>
     *
     * @param request The HTTP request object to be read as stream
     * @param headers optional HTTP headers to include when getting an object
     * @return {@link InputStream} that extends {@link MantaObjectResponse}.
     * @throws IOException when there is a problem getting the object over the network
     */
    MantaObjectInputStream httpRequestAsInputStream(HttpUriRequest request, MantaHttpHeaders headers)
            throws IOException;

    /**
     * Executes an HTTP PUT against the remote Manta API.
     *
     * @param path Full URL to the object on the Manta API
     * @param headers optional HTTP headers to include when copying the object
     * @param entity Apache HTTP Client content entity object
     * @param metadata optional user-supplied metadata for object
     * @return Manta response object
     * @throws IOException when there is a problem sending the object over the network
     */
    MantaObjectResponse httpPut(String path, MantaHttpHeaders headers, HttpEntity entity, MantaMetadata metadata)
            throws IOException;

    /**
     * Replaces the specified metadata to an existing Manta object using the
     * specified HTTP headers.
     *
     * @param path The fully qualified path of the object. i.e. /user/stor/foo/bar/baz
     * @param headers HTTP headers to include when copying the object
     * @param metadata user-supplied metadata for object
     * @return Manta response object
     * @throws IOException when there is a problem sending the metadata over the network
     */
    MantaObjectResponse httpPutMetadata(String path, MantaHttpHeaders headers, MantaMetadata metadata)
            throws IOException;

    /**
     * Executes a {@link HttpRequest}, logs the request and returns back the
     * response.
     *
     * @param request request object
     * @param logMessage log message associated with request that must contain
     *                   a substitution placeholder for status code and
     *                   status message
     * @param logParameters additional log placeholders
     * @return response object
     * @throws IOException thrown when we are unable to process the request on the network
     */
    CloseableHttpResponse executeRequest(HttpUriRequest request, String logMessage, Object... logParameters)
            throws IOException;

    /**
     * Executes a {@link HttpRequest}, logs the request and returns back the
     * response.
     *
     * @param request request object
     * @param expectedStatusCode status code returned that indicates success
     * @param logMessage log message associated with request that must contain
     *                   a substitution placeholder for status code and
     *                   status message
     * @param logParameters additional log placeholders
     * @return response object
     * @throws IOException thrown when we are unable to process the request on the network
     */
    CloseableHttpResponse executeRequest(HttpUriRequest request, Integer expectedStatusCode, String logMessage,
            Object... logParameters) throws IOException;

    /**
     * Executes a {@link HttpRequest}, logs the request, closes the request and
     * returns back the response.
     *
     * @param request request object
     * @param logMessage log message associated with request that must contain
     *                   a substitution placeholder for status code and
     *                   status message
     * @param logParameters additional log placeholders
     * @return response object
     * @throws IOException thrown when we are unable to process the request on the network
     */
    CloseableHttpResponse executeAndCloseRequest(HttpUriRequest request, String logMessage, Object... logParameters)
            throws IOException;

    /**
     * Executes a {@link HttpRequest}, logs the request, closes the request and
     * returns back the response.
     *
     * @param request request object
     * @param expectedStatusCode status code returned that indicates success
     * @param logMessage log message associated with request that must contain
     *                   a substitution placeholder for status code and
     *                   status message
     * @param logParameters additional log placeholders
     * @return response object
     * @throws IOException thrown when we are unable to process the request on the network
     */
    CloseableHttpResponse executeAndCloseRequest(HttpUriRequest request, Integer expectedStatusCode,
            String logMessage, Object... logParameters) throws IOException;

    /**
     * Executes a {@link HttpRequest}, logs the request, closes the request and
     * returns back the response.
     *
     * @param request request object
     * @param expectedStatusCode status code returned that indicates success
     * @param closeResponse when true we close the response before returning
     * @param logMessage log message associated with request that must contain
     *                   a substitution placeholder for status code and
     *                   status message
     * @param logParameters additional log placeholders
     * @return response object
     * @throws IOException thrown when we are unable to process the request on the network
     */
    CloseableHttpResponse executeRequest(HttpUriRequest request, Integer expectedStatusCode, boolean closeResponse,
            String logMessage, Object... logParameters) throws IOException;

    /**
     * Executes a {@link HttpRequest}, logs the request, closes the request and
     * returns back the response.
     *
     * @param <R> return value from responseAction function
     * @param request request object
     * @param responseAction action to perform against the response before it is closed
     * @param logMessage log message associated with request that must contain
     *                   a substitution placeholder for status code and
     *                   status message
     * @param logParameters additional log placeholders
     * @return response object
     * @throws IOException thrown when we are unable to process the request on the network
     */
    <R> R executeAndCloseRequest(HttpUriRequest request, Function<CloseableHttpResponse, R> responseAction,
            String logMessage, Object... logParameters) throws IOException;

    /**
     * Executes a {@link HttpRequest}, logs the request, closes the request and
     * returns back the response.
     *
     * @param <R> return value from responseAction function
     * @param request request object
     * @param expectedStatusCode status code returned that indicates success
     * @param responseAction action to perform against the response before it is closed
     * @param logMessage log message associated with request that must contain
     *                   a substitution placeholder for status code and
     *                   status message
     * @param logParameters additional log placeholders
     * @return response object
     * @throws IOException thrown when we are unable to process the request on the network
     */
    <R> R executeAndCloseRequest(HttpUriRequest request, Integer expectedStatusCode,
            Function<CloseableHttpResponse, R> responseAction, String logMessage, Object... logParameters)
            throws IOException;

    /**
     * Executes a {@link HttpRequest}, logs the request, closes the request and
     * returns back the response.
     *
     * @param <R> return value from responseAction function
     * @param request request object
     * @param expectedStatusCode status code returned that indicates success
     * @param responseAction action to perform against the response before it is closed
     * @param closeResponse when true we close the response before returning
     * @param logMessage log message associated with request that must contain
     *                   a substitution placeholder for status code and
     *                   status message
     * @param logParameters additional log placeholders
     * @return response object
     * @throws IOException thrown when we are unable to process the request on the network
     */
    <R> R executeRequest(HttpUriRequest request, Integer expectedStatusCode,
            Function<CloseableHttpResponse, R> responseAction, boolean closeResponse, String logMessage,
            Object... logParameters) throws IOException;

    /**
     * Extracts the request id from a {@link HttpRequest} object.
     *
     * @param response HTTP request object
     * @return UUID as a string representing unique request or null if not available
     */
    static String extractRequestId(final HttpResponse response) {
        if (response == null) {
            return null;
        }

        final Header responseIdHeader = response.getFirstHeader(REQUEST_ID);
        final String requestId;

        if (responseIdHeader != null) {
            requestId = responseIdHeader.getValue();
        } else if (MDC.get(RequestIdInterceptor.MDC_REQUEST_ID_STRING) != null) {
            requestId = MDC.get(RequestIdInterceptor.MDC_REQUEST_ID_STRING);
        } else {
            requestId = null;
        }

        return requestId;
    }

    /**
     * Appends context attributes for the HTTP request and HTTP response objects
     * to a {@link ExceptionContext} instance.
     *
     * @param exception exception to append to
     * @param request HTTP request object
     * @param response HTTP response object
     */
    static void annotateContextedException(final ExceptionContext exception, final HttpRequest request,
            final HttpResponse response) {
        Validate.notNull(exception, "Exception context object must not be null");

        if (request != null) {
            final String requestId = extractRequestId(response);
            exception.setContextValue("requestId", requestId);

            final String requestDump = reflectionToString(request, SHORT_PREFIX_STYLE);
            exception.setContextValue("request", requestDump);
            exception.setContextValue("requestMethod", request.getRequestLine().getMethod());
            exception.setContextValue("requestURL", request.getRequestLine().getUri());
            final String requestHeaders = asString(request.getAllHeaders());
            exception.setContextValue("requestHeaders", requestHeaders);
            exception.setContextValue("loadBalancerAddress", MDC.get("mantaLoadBalancerAddress"));
        }

        if (response != null) {
            final String responseDump = reflectionToString(response, SHORT_PREFIX_STYLE);
            exception.setContextValue("response", responseDump);
            final String responseHeaders = asString(response.getAllHeaders());
            exception.setContextValue("responseHeaders", responseHeaders);

            final StatusLine statusLine = response.getStatusLine();

            if (statusLine != null) {
                exception.setContextValue("responseStatusCode", statusLine.getStatusCode());
                exception.setContextValue("responseStatusReason", statusLine.getReasonPhrase());
            }
        }
    }

    /**
     * Parses a response object for the unencrypted plaintext size in bytes.
     *
     * @param response response to parse
     * @param ciphertextSize size in bytes of the ciphertext
     * @param cipherDetails cipher used to decrypt
     * @return plaintext size in bytes
     * @throws MantaClientEncryptionException thrown when unable to get the plaintext size
     */
    static long attemptToFindPlaintextSize(final MantaObjectResponse response, final long ciphertextSize,
            final SupportedCipherDetails cipherDetails) {
        // If the calculation is accurate, then we attempt to calculate plaintext size
        if (!cipherDetails.plaintextSizeCalculationIsAnEstimate()) {
            return cipherDetails.plaintextSize(ciphertextSize);
        }

        // We try to get the metadata about the actual plaintext size
        String plaintextLengthHeaderVal = response
                .getHeaderAsString(MantaHttpHeaders.ENCRYPTION_PLAINTEXT_CONTENT_LENGTH);

        // If it is there, we replace the plaintext length specified with the file size
        if (plaintextLengthHeaderVal != null) {
            return Long.parseLong(plaintextLengthHeaderVal);
        }

        // Otherwise we error
        String msg = "Plaintext length specified is greater than "
                + "the size of the file and there is no reliable fallback "
                + "information for getting the real plaintext value";
        MantaClientEncryptionException e = new MantaClientEncryptionException(msg);
        e.setContextValue("response", response);
        throw e;
    }
}