001/*
002 * Copyright 2002-2019 the original author or authors.
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 *      https://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 org.springframework.web.server;
018
019import java.security.Principal;
020import java.time.Instant;
021import java.util.Map;
022import java.util.function.Consumer;
023import java.util.function.Function;
024
025import reactor.core.publisher.Mono;
026
027import org.springframework.context.ApplicationContext;
028import org.springframework.context.i18n.LocaleContext;
029import org.springframework.http.codec.multipart.Part;
030import org.springframework.http.server.reactive.ServerHttpRequest;
031import org.springframework.http.server.reactive.ServerHttpResponse;
032import org.springframework.lang.Nullable;
033import org.springframework.util.Assert;
034import org.springframework.util.MultiValueMap;
035
036/**
037 * Contract for an HTTP request-response interaction. Provides access to the HTTP
038 * request and response and also exposes additional server-side processing
039 * related properties and features such as request attributes.
040 *
041 * @author Rossen Stoyanchev
042 * @since 5.0
043 */
044public interface ServerWebExchange {
045
046        /**
047         * Name of {@link #getAttributes() attribute} whose value can be used to
048         * correlate log messages for this exchange. Use {@link #getLogPrefix()} to
049         * obtain a consistently formatted prefix based on this attribute.
050         * @since 5.1
051         * @see #getLogPrefix()
052         */
053        String LOG_ID_ATTRIBUTE = ServerWebExchange.class.getName() + ".LOG_ID";
054
055
056        /**
057         * Return the current HTTP request.
058         */
059        ServerHttpRequest getRequest();
060
061        /**
062         * Return the current HTTP response.
063         */
064        ServerHttpResponse getResponse();
065
066        /**
067         * Return a mutable map of request attributes for the current exchange.
068         */
069        Map<String, Object> getAttributes();
070
071        /**
072         * Return the request attribute value if present.
073         * @param name the attribute name
074         * @param <T> the attribute type
075         * @return the attribute value
076         */
077        @SuppressWarnings("unchecked")
078        @Nullable
079        default <T> T getAttribute(String name) {
080                return (T) getAttributes().get(name);
081        }
082
083        /**
084         * Return the request attribute value or if not present raise an
085         * {@link IllegalArgumentException}.
086         * @param name the attribute name
087         * @param <T> the attribute type
088         * @return the attribute value
089         */
090        @SuppressWarnings("unchecked")
091        default <T> T getRequiredAttribute(String name) {
092                T value = getAttribute(name);
093                Assert.notNull(value, () -> "Required attribute '" + name + "' is missing");
094                return value;
095        }
096
097        /**
098         * Return the request attribute value, or a default, fallback value.
099         * @param name the attribute name
100         * @param defaultValue a default value to return instead
101         * @param <T> the attribute type
102         * @return the attribute value
103         */
104        @SuppressWarnings("unchecked")
105        default <T> T getAttributeOrDefault(String name, T defaultValue) {
106                return (T) getAttributes().getOrDefault(name, defaultValue);
107        }
108
109        /**
110         * Return the web session for the current request. Always guaranteed  to
111         * return an instance either matching to the session id requested by the
112         * client, or with a new session id either because the client did not
113         * specify one or because the underlying session had expired. Use of this
114         * method does not automatically create a session. See {@link WebSession}
115         * for more details.
116         */
117        Mono<WebSession> getSession();
118
119        /**
120         * Return the authenticated user for the request, if any.
121         */
122        <T extends Principal> Mono<T> getPrincipal();
123
124        /**
125         * Return the form data from the body of the request if the Content-Type is
126         * {@code "application/x-www-form-urlencoded"} or an empty map otherwise.
127         * <p><strong>Note:</strong> calling this method causes the request body to
128         * be read and parsed in full and the resulting {@code MultiValueMap} is
129         * cached so that this method is safe to call more than once.
130         */
131        Mono<MultiValueMap<String, String>> getFormData();
132
133        /**
134         * Return the parts of a multipart request if the Content-Type is
135         * {@code "multipart/form-data"} or an empty map otherwise.
136         * <p><strong>Note:</strong> calling this method causes the request body to
137         * be read and parsed in full and the resulting {@code MultiValueMap} is
138         * cached so that this method is safe to call more than once.
139         * <p><strong>Note:</strong>the {@linkplain Part#content() contents} of each
140         * part is not cached, and can only be read once.
141         */
142        Mono<MultiValueMap<String, Part>> getMultipartData();
143
144        /**
145         * Return the {@link LocaleContext} using the configured
146         * {@link org.springframework.web.server.i18n.LocaleContextResolver}.
147         */
148        LocaleContext getLocaleContext();
149
150        /**
151         * Return the {@link ApplicationContext} associated with the web application,
152         * if it was initialized with one via
153         * {@link org.springframework.web.server.adapter.WebHttpHandlerBuilder#applicationContext(ApplicationContext)}.
154         * @since 5.0.3
155         * @see org.springframework.web.server.adapter.WebHttpHandlerBuilder#applicationContext(ApplicationContext)
156         */
157        @Nullable
158        ApplicationContext getApplicationContext();
159
160        /**
161         * Returns {@code true} if the one of the {@code checkNotModified} methods
162         * in this contract were used and they returned true.
163         */
164        boolean isNotModified();
165
166        /**
167         * An overloaded variant of {@link #checkNotModified(String, Instant)} with
168         * a last-modified timestamp only.
169         * @param lastModified the last-modified time
170         * @return whether the request qualifies as not modified
171         */
172        boolean checkNotModified(Instant lastModified);
173
174        /**
175         * An overloaded variant of {@link #checkNotModified(String, Instant)} with
176         * an {@code ETag} (entity tag) value only.
177         * @param etag the entity tag for the underlying resource.
178         * @return true if the request does not require further processing.
179         */
180        boolean checkNotModified(String etag);
181
182        /**
183         * Check whether the requested resource has been modified given the supplied
184         * {@code ETag} (entity tag) and last-modified timestamp as determined by
185         * the application. Also transparently prepares the response, setting HTTP
186         * status, and adding "ETag" and "Last-Modified" headers when applicable.
187         * This method works with conditional GET/HEAD requests as well as with
188         * conditional POST/PUT/DELETE requests.
189         * <p><strong>Note:</strong> The HTTP specification recommends setting both
190         * ETag and Last-Modified values, but you can also use
191         * {@code #checkNotModified(String)} or
192         * {@link #checkNotModified(Instant)}.
193         * @param etag the entity tag that the application determined for the
194         * underlying resource. This parameter will be padded with quotes (")
195         * if necessary.
196         * @param lastModified the last-modified timestamp that the application
197         * determined for the underlying resource
198         * @return true if the request does not require further processing.
199         */
200        boolean checkNotModified(@Nullable String etag, Instant lastModified);
201
202        /**
203         * Transform the given url according to the registered transformation function(s).
204         * By default, this method returns the given {@code url}, though additional
205         * transformation functions can by registered with {@link #addUrlTransformer}
206         * @param url the URL to transform
207         * @return the transformed URL
208         */
209        String transformUrl(String url);
210
211        /**
212         * Register an additional URL transformation function for use with {@link #transformUrl}.
213         * The given function can be used to insert an id for authentication, a nonce for CSRF
214         * protection, etc.
215         * <p>Note that the given function is applied after any previously registered functions.
216         * @param transformer a URL transformation function to add
217         */
218        void addUrlTransformer(Function<String, String> transformer);
219
220        /**
221         * Return a log message prefix to use to correlate messages for this exchange.
222         * The prefix is based on the value of the attribute {@link #LOG_ID_ATTRIBUTE}
223         * along with some extra formatting so that the prefix can be conveniently
224         * prepended with no further formatting no separators required.
225         * @return the log message prefix or an empty String if the
226         * {@link #LOG_ID_ATTRIBUTE} is not set.
227         * @since 5.1
228         */
229        String getLogPrefix();
230
231        /**
232         * Return a builder to mutate properties of this exchange by wrapping it
233         * with {@link ServerWebExchangeDecorator} and returning either mutated
234         * values or delegating back to this instance.
235         */
236        default Builder mutate() {
237                return new DefaultServerWebExchangeBuilder(this);
238        }
239
240
241        /**
242         * Builder for mutating an existing {@link ServerWebExchange}.
243         * Removes the need
244         */
245        interface Builder {
246
247                /**
248                 * Configure a consumer to modify the current request using a builder.
249                 * <p>Effectively this:
250                 * <pre>
251                 * exchange.mutate().request(builder-> builder.method(HttpMethod.PUT));
252                 *
253                 * // vs...
254                 *
255                 * ServerHttpRequest request = exchange.getRequest().mutate()
256                 *     .method(HttpMethod.PUT)
257                 *     .build();
258                 *
259                 * exchange.mutate().request(request);
260                 * </pre>
261                 * @see ServerHttpRequest#mutate()
262                 */
263                Builder request(Consumer<ServerHttpRequest.Builder> requestBuilderConsumer);
264
265                /**
266                 * Set the request to use especially when there is a need to override
267                 * {@link ServerHttpRequest} methods. To simply mutate request properties
268                 * see {@link #request(Consumer)} instead.
269                 * @see org.springframework.http.server.reactive.ServerHttpRequestDecorator
270                 */
271                Builder request(ServerHttpRequest request);
272
273                /**
274                 * Set the response to use.
275                 * @see org.springframework.http.server.reactive.ServerHttpResponseDecorator
276                 */
277                Builder response(ServerHttpResponse response);
278
279                /**
280                 * Set the {@code Mono<Principal>} to return for this exchange.
281                 */
282                Builder principal(Mono<Principal> principalMono);
283
284                /**
285                 * Build a {@link ServerWebExchange} decorator with the mutated properties.
286                 */
287                ServerWebExchange build();
288        }
289
290}