001/*
002 * Copyright 2022-2025 Revetware LLC.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.soklet.core;
018
019import javax.annotation.Nonnull;
020import javax.annotation.Nullable;
021import java.util.Set;
022
023/**
024 * Prepares responses for each request scenario Soklet supports (happy path, exception, CORS preflight, etc.)
025 * <p>
026 * The {@link MarshaledResponse} value returned from these methods is what is ultimately sent back to
027 * clients as bytes over the wire.
028 * <p>
029 * Full documentation is available at <a href="https://www.soklet.com/docs/response-writing">https://www.soklet.com/docs/response-writing</a>.
030 *
031 * @author <a href="https://www.revetkn.com">Mark Allen</a>
032 */
033public interface ResponseMarshaler {
034        /**
035         * Prepares a "happy path" response - the request was matched to a <em>Resource Method</em> and executed non-exceptionally.
036         * <p>
037         * Detailed documentation is available at <a href="https://www.soklet.com/docs/response-writing#happy-path">https://www.soklet.com/docs/response-writing#happy-path</a>.
038         *
039         * @param request        the HTTP request
040         * @param response       the response provided by the <em>Resource Method</em> that handled the request
041         * @param resourceMethod the <em>Resource Method</em> that handled the request
042         * @return the response to be sent over the wire
043         */
044        @Nonnull
045        MarshaledResponse forHappyPath(@Nonnull Request request,
046                                                                                                                                 @Nonnull Response response,
047                                                                                                                                 @Nonnull ResourceMethod resourceMethod);
048
049        /**
050         * Prepares a response for a request that triggers an
051         * <a href="https://httpwg.org/specs/rfc9110.html#status.404">HTTP 404 Not Found</a>.
052         * <p>
053         * Detailed documentation is available at <a href="https://www.soklet.com/docs/response-writing#404-not-found">https://www.soklet.com/docs/response-writing#404-not-found</a>.
054         *
055         * @param request the HTTP request
056         * @return the response to be sent over the wire
057         */
058        @Nonnull
059        MarshaledResponse forNotFound(@Nonnull Request request);
060
061        /**
062         * Prepares a response for a request that triggers an
063         * <a href="https://httpwg.org/specs/rfc9110.html#status.405">HTTP 405 Method Not Allowed</a>.
064         * <p>
065         * Detailed documentation is available at <a href="https://www.soklet.com/docs/response-writing#405-method-not-allowed">https://www.soklet.com/docs/response-writing#405-method-not-allowed</a>.
066         *
067         * @param request            the HTTP request
068         * @param allowedHttpMethods appropriate HTTP methods to write to the {@code Allow} response header
069         * @return the response to be sent over the wire
070         */
071        @Nonnull
072        MarshaledResponse forMethodNotAllowed(@Nonnull Request request,
073                                                                                                                                                                @Nonnull Set<HttpMethod> allowedHttpMethods);
074
075        /**
076         * Prepares a response for a request that triggers an <a href="https://httpwg.org/specs/rfc9110.html#status.413">HTTP 413 Content Too Large</a>.
077         * <p>
078         * Detailed documentation is available at <a href="https://www.soklet.com/docs/response-writing#413-content-too-large">https://www.soklet.com/docs/response-writing#413-content-too-large</a>.
079         *
080         * @param request        the HTTP request
081         * @param resourceMethod the <em>Resource Method</em> that would have handled the request, if available
082         * @return the response to be sent over the wire
083         */
084        @Nonnull
085        MarshaledResponse forContentTooLarge(@Nonnull Request request,
086                                                                                                                                                         @Nullable ResourceMethod resourceMethod);
087
088        /**
089         * Prepares a response for an HTTP {@code OPTIONS} request.
090         * <p>
091         * Note that CORS preflight responses are handled specially by {@link #forCorsPreflightAllowed(Request, CorsPreflight, CorsPreflightResponse)}
092         * and {@link #forCorsPreflightRejected(Request, CorsPreflight)} - not this method.
093         * <p>
094         * Detailed documentation is available at <a href="https://www.soklet.com/docs/response-writing#http-options">https://www.soklet.com/docs/response-writing#http-options</a>.
095         *
096         * @param request            the HTTP request
097         * @param allowedHttpMethods appropriate HTTP methods to write to the {@code Allow} response header
098         * @return the response to be sent over the wire
099         */
100        @Nonnull
101        MarshaledResponse forOptions(@Nonnull Request request,
102                                                                                                                         @Nonnull Set<HttpMethod> allowedHttpMethods);
103
104        /**
105         * Prepares a response for scenarios in which an uncaught exception is encountered.
106         * <p>
107         * Detailed documentation is available at <a href="https://www.soklet.com/docs/response-writing#uncaught-exceptions">https://www.soklet.com/docs/response-writing#uncaught-exceptions</a>.
108         *
109         * @param request        the HTTP request
110         * @param throwable      the exception that was thrown
111         * @param resourceMethod the <em>Resource Method</em> that would have handled the request, if available
112         * @return the response to be sent over the wire
113         */
114        @Nonnull
115        MarshaledResponse forThrowable(@Nonnull Request request,
116                                                                                                                                 @Nonnull Throwable throwable,
117                                                                                                                                 @Nullable ResourceMethod resourceMethod);
118
119        /**
120         * Prepares a response for an HTTP {@code HEAD} request.
121         * <p>
122         * Detailed documentation is available at <a href="https://www.soklet.com/docs/response-writing#http-head">https://www.soklet.com/docs/response-writing#http-head</a>.
123         *
124         * @param request                    the HTTP request
125         * @param getMethodMarshaledResponse the binary data that would have been sent over the wire for an equivalent {@code GET} request (necessary in order to write the {@code Content-Length} header for a {@code HEAD} response)
126         * @return the response to be sent over the wire
127         */
128        @Nonnull
129        MarshaledResponse forHead(@Nonnull Request request,
130                                                                                                                @Nonnull MarshaledResponse getMethodMarshaledResponse);
131
132        /**
133         * Prepares a response for "CORS preflight allowed" scenario when your {@link CorsAuthorizer} approves a preflight request.
134         * <p>
135         * Detailed documentation is available at <a href="https://www.soklet.com/docs/cors#writing-cors-responses">https://www.soklet.com/docs/cors#writing-cors-responses</a>.
136         *
137         * @param request               the HTTP request
138         * @param corsPreflight         the CORS preflight request data
139         * @param corsPreflightResponse the data that should be included in this CORS preflight response
140         * @return the response to be sent over the wire
141         */
142        @Nonnull
143        MarshaledResponse forCorsPreflightAllowed(@Nonnull Request request,
144                                                                                                                                                                                @Nonnull CorsPreflight corsPreflight,
145                                                                                                                                                                                @Nonnull CorsPreflightResponse corsPreflightResponse);
146
147        /**
148         * Prepares a response for "CORS preflight rejected" scenario when your {@link CorsAuthorizer} denies a preflight request.
149         * <p>
150         * Detailed documentation is available at <a href="https://www.soklet.com/docs/cors#writing-cors-responses">https://www.soklet.com/docs/cors#writing-cors-responses</a>.
151         *
152         * @param request       the HTTP request
153         * @param corsPreflight the CORS preflight request data
154         * @return the response to be sent over the wire
155         */
156        @Nonnull
157        MarshaledResponse forCorsPreflightRejected(@Nonnull Request request,
158                                                                                                                                                                                 @Nonnull CorsPreflight corsPreflight);
159
160        /**
161         * Applies "CORS is permitted for this request" data to a response.
162         * <p>
163         * Invoked for any non-preflight CORS request that your {@link CorsAuthorizer} approves.
164         * <p>
165         * This method will normally return a copy of the {@code marshaledResponse} with these headers applied
166         * based on the values of {@code corsResponse}:
167         * <ul>
168         *   <li>{@code Access-Control-Allow-Origin} (required)</li>
169         *   <li>{@code Access-Control-Allow-Credentials} (optional)</li>
170         *   <li>{@code Access-Control-Expose-Headers} (optional)</li>
171         * </ul>
172         *
173         * @param request           the HTTP request
174         * @param cors              the CORS request data
175         * @param corsResponse      CORS response data to write as specified by {@link CorsAuthorizer}
176         * @param marshaledResponse the existing response to which we should apply relevant CORS headers
177         * @return the response to be sent over the wire
178         */
179        @Nonnull
180        MarshaledResponse forCorsAllowed(@Nonnull Request request,
181                                                                                                                                         @Nonnull Cors cors,
182                                                                                                                                         @Nonnull CorsResponse corsResponse,
183                                                                                                                                         @Nonnull MarshaledResponse marshaledResponse);
184}