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}