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

com.tangosol.internal.http.Response Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2022, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * https://oss.oracle.com/licenses/upl.
 */
package com.tangosol.internal.http;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.tangosol.io.WriteBuffer;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * A builder for a http response.
 *
 * @author Jonathan Knight 2022.01.25
 * @since 22.06
 */
public class Response
    {
    /**
     * Private constructor. Instances are created using the {@link Builder}.
     *
     * @param status   the response status
     * @param oEntity  an optional response entity
     */
    private Response(StatusType status, Object oEntity)
        {
        f_status    = status;
        f_oEntity = oEntity;
        }

    /**
     * Return the response status code.
     *
     * @return the response status code
     */
    public StatusType getStatus()
        {
        return f_status;
        }

    /**
     * Return the optional response entity.
     *
     * @return the optional response entity
     */
    public Object getEntity()
        {
        return f_oEntity;
        }

    /**
     * Return the response headers.
     *
     * @return the response headers
     */
    public Headers getHeaders()
        {
        return f_headers;
        }

    /**
     * Add the specified headers.
     *
     * @param headers  the headers to add
     *
     * @return this response
     */
    Response addHeaders(Map> headers)
        {
        for (Map.Entry> entry : headers.entrySet())
            {
            String sKey = entry.getKey();
            for (String sValue : entry.getValue())
                {
                f_headers.add(sKey, sValue);
                }
            }
        return this;
        }

    /**
     * Add the specified headers, if not present.
     *
     * @param headers  the headers to add
     *
     * @return this response
     */
    Response addHeadersIfNotPresent(Map> headers)
        {
        for (Map.Entry> entry : headers.entrySet())
            {
            String sKey = entry.getKey();
            for (String sValue : entry.getValue())
                {
                f_headers.putIfAbsent(sKey, Collections.singletonList(sValue));
                }
            }
        return this;
        }

    /**
     * Return this {@link Response} as a {@link Builder}.
     *
     * @return this {@link Response} as a {@link Builder}
     */
    public Builder toBuilder()
        {
        return new Builder()
                .status(f_status)
                .setEntity(f_oEntity)
                .addHeaders(f_headers);
        }

    /**
     * Create a new {@link Builder} with the specified status.
     *
     * @param nStatus  the response status code
     *
     * @return a new response {@link Builder} with the specified response status
     */
    public static Builder status(int nStatus)
        {
        return new Builder().status(nStatus);
        }

    /**
     * Create a new {@link Builder} with the specified status.
     *
     * @param status  the response status
     *
     * @return a new response {@link Builder} with the specified response status
     */
    public static Builder status(StatusType status)
        {
        return new Builder().status(status);
        }

    /**
     * Create a new {@link Builder} with an OK status.
     *
     * @return a new response {@link Builder}
     */
    public static Builder ok()
        {
        return status(Status.OK);
        }

    /**
     * Create a new {@link Builder} that contains a representation.
     *
     * @param entity the response entity data
     *
     * @return a new response {@link Builder}
     */
    public static Builder ok(Map entity)
        {
        return ok().entity(entity);
        }

    /**
     * Create a new {@link Builder} that contains a representation.
     *
     * @param entity the response entity data
     *
     * @return a new response {@link Builder}
     */
    public static Builder ok(InputStream entity)
        {
        return ok().entity(entity);
        }

    /**
     * Create a new {@link Builder} that contains a representation.
     *
     * @param entity the response entity data
     *
     * @return a new response {@link Builder}
     */
    public static Builder ok(WriteBuffer entity)
        {
        return ok().entity(entity);
        }

    /**
     * Create a new {@link Builder} with a not found status.
     *
     * @return a new response {@link Builder}
     */
    public static Builder notFound()
        {
        return status(Status.NOT_FOUND);
        }

    /**
     * Create a new {@link Builder} with an internal server error status.
     *
     * @return a new response {@link Builder}
     */
    public static Builder serverError()
        {
        return status(Status.INTERNAL_SERVER_ERROR);
        }

    // ----- inner class: Builder -------------------------------------------

    /**
     * A builder for {@link Response} instances.
     */
    public static class Builder
        {
        /**
         * Instances of {@link Builder} are created from the static methods on the {@link Response} class.
         */
        private Builder()
            {
            }

        /**
         * Build a {@link Response} from the state held in this builder.
         *
         * @return a {@link Response} built from the state held in this builder
         */
        public Response build()
            {
            return new Response(m_status, m_oEntity);
            }

        /**
         * Set the response entity in this {@link Builder}.
         *
         * @param mapEntity the response entity
         *
         * @return this {@link Builder}
         */
        public Builder entity(Map mapEntity)
            {
            return setEntity(mapEntity);
            }

        /**
         * Set the response entity in this {@link Builder}.
         *
         * @param stream the response entity
         *
         * @return this {@link Builder}
         */
        public Builder entity(InputStream stream)
            {
            return setEntity(stream);
            }

        /**
         * Set the response entity in this {@link Builder}.
         *
         * @param buf the response entity
         *
         * @return this {@link Builder}
         */
        public Builder entity(WriteBuffer buf)
            {
            return setEntity(buf);
            }

        /**
         * Set the response entity in this {@link Builder}.
         *
         * @param sEntity the response entity
         *
         * @return this {@link Builder}
         */
        public Builder entity(String sEntity)
            {
            return setEntity(sEntity);
            }

        /**
         * Set the response entity in this {@link Builder}.
         *
         * @param oEntity the response entity
         *
         * @return this {@link Builder}
         */
        private Builder setEntity(Object oEntity)
            {
            m_oEntity = oEntity;
            return this;
            }

        /**
         * Set the response status in this {@link Builder}.
         *
         * @param nStatus  the response status
         *
         * @return this {@link Builder}
         * @throws IllegalArgumentException if the status code does not map to a valid {@link Status}
         */
        public Builder status(int nStatus)
            {
            StatusType statusType = Status.fromStatusCode(nStatus);
            if (statusType == null)
                {
                throw new IllegalArgumentException("Cannot map status " + nStatus + " to a Status enum value");
                }
            return status(statusType);
            }

        /**
         * Set the response status in this {@link Builder}.
         *
         * @param status  the response status
         *
         * @return this {@link Builder}
         * @throws NullPointerException if the status is {@code null}
         */
        public Builder status(StatusType status)
            {
            m_status = Objects.requireNonNull(status);
            return this;
            }

        /**
         * Add the specified headers to the response.
         *
         * @param mapHeaders  the headers to add
         *
         * @return this {@link Builder}
         */
        public Builder addHeaders(Map> mapHeaders)
            {
            for (Map.Entry> entry : mapHeaders.entrySet())
                {
                String sKey = entry.getKey();
                for (String sValue : entry.getValue())
                    {
                    m_headers.add(sKey, sValue);
                    }
                }
            return this;
            }

        // ----- data members -----------------------------------------------

        /**
         * The response status.
         */
        private StatusType m_status = Status.OK;

        /**
         * An optional response entity.
         */
        private Object m_oEntity;

        /**
         * The response headers.
         */
        private Headers m_headers = new Headers();
        }

    // ----- inner interface: StatusType -----------------------------------------

    /**
     * A representation of a http response code.
     */
    public interface StatusType
        {
        /**
         * Get the class of status code.
         *
         * @return the class of status code.
         */
        Status.Family getFamily();

        /**
         * Get the associated status code.
         *
         * @return the status code.
         */
        int getStatusCode();

        /**
         * Get the reason phrase.
         *
         * @return the reason phrase.
         */
        public String getReasonPhrase();
        }

    // ----- inner enum: Status ---------------------------------------------

    /**
     * Common http response codes, see
     * {@link HTTP/1.1 documentation}
     * for the complete list.
     * 

* Additional status codes can be added by applications by creating an implementation of {@link StatusType}. */ public enum Status implements StatusType { /** * 200 OK, see {@link HTTP/1.1 documentation}. */ OK(200, "OK"), /** * 201 Created, see {@link HTTP/1.1 documentation}. */ CREATED(201, "Created"), /** * 202 Accepted, see {@link HTTP/1.1 documentation}. */ ACCEPTED(202, "Accepted"), /** * 204 No Content, see {@link HTTP/1.1 documentation}. */ NO_CONTENT(204, "No Content"), /** * 205 Reset Content, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ RESET_CONTENT(205, "Reset Content"), /** * 206 Reset Content, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ PARTIAL_CONTENT(206, "Partial Content"), /** * 301 Moved Permanently, see {@link HTTP/1.1 documentation}. */ MOVED_PERMANENTLY(301, "Moved Permanently"), /** * 302 Found, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ FOUND(302, "Found"), /** * 303 See Other, see {@link HTTP/1.1 documentation}. */ SEE_OTHER(303, "See Other"), /** * 304 Not Modified, see {@link HTTP/1.1 documentation}. */ NOT_MODIFIED(304, "Not Modified"), /** * 305 Use Proxy, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ USE_PROXY(305, "Use Proxy"), /** * 307 Temporary Redirect, see {@link HTTP/1.1 documentation}. */ TEMPORARY_REDIRECT(307, "Temporary Redirect"), /** * 400 Bad Request, see {@link HTTP/1.1 documentation}. */ BAD_REQUEST(400, "Bad Request"), /** * 401 Unauthorized, see {@link HTTP/1.1 documentation}. */ UNAUTHORIZED(401, "Unauthorized"), /** * 402 Payment Required, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ PAYMENT_REQUIRED(402, "Payment Required"), /** * 403 Forbidden, see {@link HTTP/1.1 documentation}. */ FORBIDDEN(403, "Forbidden"), /** * 404 Not Found, see {@link HTTP/1.1 documentation}. */ NOT_FOUND(404, "Not Found"), /** * 405 Method Not Allowed, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ METHOD_NOT_ALLOWED(405, "Method Not Allowed"), /** * 406 Not Acceptable, see {@link HTTP/1.1 documentation}. */ NOT_ACCEPTABLE(406, "Not Acceptable"), /** * 407 Proxy Authentication Required, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"), /** * 408 Request Timeout, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ REQUEST_TIMEOUT(408, "Request Timeout"), /** * 409 Conflict, see {@link HTTP/1.1 documentation}. */ CONFLICT(409, "Conflict"), /** * 410 Gone, see {@link HTTP/1.1 documentation}. */ GONE(410, "Gone"), /** * 411 Length Required, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ LENGTH_REQUIRED(411, "Length Required"), /** * 412 Precondition Failed, see {@link HTTP/1.1 documentation}. */ PRECONDITION_FAILED(412, "Precondition Failed"), /** * 413 Request Entity Too Large, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"), /** * 414 Request-URI Too Long, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"), /** * 415 Unsupported Media Type, see {@link HTTP/1.1 documentation}. */ UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), /** * 416 Requested Range Not Satisfiable, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"), /** * 417 Expectation Failed, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ EXPECTATION_FAILED(417, "Expectation Failed"), /** * 428 Precondition required, see {@link RFC 6585: Additional HTTP Status Codes}. * * @since 2.1 */ PRECONDITION_REQUIRED(428, "Precondition Required"), /** * 429 Too Many Requests, see {@link RFC 6585: Additional HTTP Status Codes}. * * @since 2.1 */ TOO_MANY_REQUESTS(429, "Too Many Requests"), /** * 431 Request Header Fields Too Large, see {@link RFC 6585: Additional HTTP Status Codes}. * * @since 2.1 */ REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"), /** * 500 Internal Server Error, see {@link HTTP/1.1 documentation}. */ INTERNAL_SERVER_ERROR(500, "Internal Server Error"), /** * 501 Not Implemented, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ NOT_IMPLEMENTED(501, "Not Implemented"), /** * 502 Bad Gateway, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ BAD_GATEWAY(502, "Bad Gateway"), /** * 503 Service Unavailable, see {@link HTTP/1.1 documentation}. */ SERVICE_UNAVAILABLE(503, "Service Unavailable"), /** * 504 Gateway Timeout, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ GATEWAY_TIMEOUT(504, "Gateway Timeout"), /** * 505 HTTP Version Not Supported, see {@link HTTP/1.1 documentation}. * * @since 2.0 */ HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version Not Supported"), /** * 511 Network Authentication Required, see {@link RFC 6585: Additional HTTP Status Codes}. * * @since 2.1 */ NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required"); // ----- constructors ----------------------------------------------- Status(final int statusCode, final String reasonPhrase) { f_nStatusCode = statusCode; f_sReason = reasonPhrase; f_family = Family.familyOf(statusCode); } // ----- Status methods --------------------------------------------- @Override public Family getFamily() { return f_family; } @Override public int getStatusCode() { return f_nStatusCode; } @Override public String getReasonPhrase() { return toString(); } @Override public String toString() { return f_sReason; } /** * Convert a numerical status code into the corresponding Status. * * @param statusCode the numerical status code. * @return the matching Status or null is no matching Status is defined. */ public static Status fromStatusCode(int statusCode) { for (Status s : Status.values()) { if (s.f_nStatusCode == statusCode) { return s; } } return null; } // ----- data members ----------------------------------------------- private final int f_nStatusCode; private final String f_sReason; private final Family f_family; /** * An enumeration representing the class of status code. Family is used * here since class is overloaded in Java. */ public enum Family { /** * {@code 1xx} HTTP status codes. */ INFORMATIONAL, /** * {@code 2xx} HTTP status codes. */ SUCCESSFUL, /** * {@code 3xx} HTTP status codes. */ REDIRECTION, /** * {@code 4xx} HTTP status codes. */ CLIENT_ERROR, /** * {@code 5xx} HTTP status codes. */ SERVER_ERROR, /** * Other, unrecognized HTTP status codes. */ OTHER; /** * Get the response status family for the status code. * * @param statusCode response status code to get the family for * * @return family of the response status code */ public static Family familyOf(final int statusCode) { switch (statusCode / 100) { case 1: return Family.INFORMATIONAL; case 2: return Family.SUCCESSFUL; case 3: return Family.REDIRECTION; case 4: return Family.CLIENT_ERROR; case 5: return Family.SERVER_ERROR; default: return Family.OTHER; } } } } // ----- data members --------------------------------------------------- /** * The response status. */ private final StatusType f_status; /** * An optional response entity. */ private final Object f_oEntity; /** * The response headers. */ private final Headers f_headers = new Headers(); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy