001/*
002 * Copyright 2002-2020 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.reactive.function.server;
018
019import java.net.InetSocketAddress;
020import java.net.URI;
021import java.nio.charset.Charset;
022import java.security.Principal;
023import java.time.Instant;
024import java.util.List;
025import java.util.Locale;
026import java.util.Map;
027import java.util.Optional;
028import java.util.OptionalLong;
029import java.util.function.Consumer;
030
031import reactor.core.publisher.Flux;
032import reactor.core.publisher.Mono;
033
034import org.springframework.core.ParameterizedTypeReference;
035import org.springframework.core.io.buffer.DataBuffer;
036import org.springframework.http.HttpCookie;
037import org.springframework.http.HttpHeaders;
038import org.springframework.http.HttpMethod;
039import org.springframework.http.HttpRange;
040import org.springframework.http.MediaType;
041import org.springframework.http.codec.HttpMessageReader;
042import org.springframework.http.codec.json.Jackson2CodecSupport;
043import org.springframework.http.codec.multipart.Part;
044import org.springframework.http.server.PathContainer;
045import org.springframework.http.server.reactive.ServerHttpRequest;
046import org.springframework.lang.Nullable;
047import org.springframework.util.Assert;
048import org.springframework.util.CollectionUtils;
049import org.springframework.util.MultiValueMap;
050import org.springframework.web.reactive.function.BodyExtractor;
051import org.springframework.web.server.ServerWebExchange;
052import org.springframework.web.server.WebSession;
053import org.springframework.web.util.UriBuilder;
054
055/**
056 * Represents a server-side HTTP request, as handled by a {@code HandlerFunction}.
057 *
058 * <p>Access to headers and body is offered by {@link Headers} and
059 * {@link #body(BodyExtractor)}, respectively.
060 *
061 * @author Arjen Poutsma
062 * @author Sebastien Deleuze
063 * @since 5.0
064 */
065public interface ServerRequest {
066
067        /**
068         * Get the HTTP method.
069         * @return the HTTP method as an HttpMethod enum value, or {@code null}
070         * if not resolvable (e.g. in case of a non-standard HTTP method)
071         */
072        @Nullable
073        default HttpMethod method() {
074                return HttpMethod.resolve(methodName());
075        }
076
077        /**
078         * Get the name of the HTTP method.
079         * @return the HTTP method as a String
080         */
081        String methodName();
082
083        /**
084         * Get the request URI.
085         */
086        URI uri();
087
088        /**
089         * Get a {@code UriBuilderComponents} from the URI associated with this
090         * {@code ServerRequest}.
091         * <p><strong>Note:</strong> as of 5.1 this method ignores {@code "Forwarded"}
092         * and {@code "X-Forwarded-*"} headers that specify the
093         * client-originated address. Consider using the {@code ForwardedHeaderFilter}
094         * to extract and use, or to discard such headers.
095         * @return a URI builder
096         */
097        UriBuilder uriBuilder();
098
099        /**
100         * Get the request path.
101         */
102        default String path() {
103                return uri().getRawPath();
104        }
105
106        /**
107         * Get the request path as a {@code PathContainer}.
108         */
109        default PathContainer pathContainer() {
110                return PathContainer.parsePath(path());
111        }
112
113        /**
114         * Get the headers of this request.
115         */
116        Headers headers();
117
118        /**
119         * Get the cookies of this request.
120         */
121        MultiValueMap<String, HttpCookie> cookies();
122
123        /**
124         * Get the remote address to which this request is connected, if available.
125         * @since 5.1
126         */
127        Optional<InetSocketAddress> remoteAddress();
128
129        /**
130         * Get the local address to which this request is connected, if available.
131         * @since 5.2.3
132         */
133        Optional<InetSocketAddress> localAddress();
134
135        /**
136         * Get the readers used to convert the body of this request.
137         * @since 5.1
138         */
139        List<HttpMessageReader<?>> messageReaders();
140
141        /**
142         * Extract the body with the given {@code BodyExtractor}.
143         * @param extractor the {@code BodyExtractor} that reads from the request
144         * @param <T> the type of the body returned
145         * @return the extracted body
146         * @see #body(BodyExtractor, Map)
147         */
148        <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor);
149
150        /**
151         * Extract the body with the given {@code BodyExtractor} and hints.
152         * @param extractor the {@code BodyExtractor} that reads from the request
153         * @param hints the map of hints like {@link Jackson2CodecSupport#JSON_VIEW_HINT}
154         * to use to customize body extraction
155         * @param <T> the type of the body returned
156         * @return the extracted body
157         */
158        <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor, Map<String, Object> hints);
159
160        /**
161         * Extract the body to a {@code Mono}.
162         * @param elementClass the class of element in the {@code Mono}
163         * @param <T> the element type
164         * @return the body as a mono
165         */
166        <T> Mono<T> bodyToMono(Class<? extends T> elementClass);
167
168        /**
169         * Extract the body to a {@code Mono}.
170         * @param typeReference a type reference describing the expected response request type
171         * @param <T> the element type
172         * @return a mono containing the body of the given type {@code T}
173         */
174        <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> typeReference);
175
176        /**
177         * Extract the body to a {@code Flux}.
178         * @param elementClass the class of element in the {@code Flux}
179         * @param <T> the element type
180         * @return the body as a flux
181         */
182        <T> Flux<T> bodyToFlux(Class<? extends T> elementClass);
183
184        /**
185         * Extract the body to a {@code Flux}.
186         * @param typeReference a type reference describing the expected request body type
187         * @param <T> the element type
188         * @return a flux containing the body of the given type {@code T}
189         */
190        <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> typeReference);
191
192        /**
193         * Get the request attribute value if present.
194         * @param name the attribute name
195         * @return the attribute value
196         */
197        default Optional<Object> attribute(String name) {
198                return Optional.ofNullable(attributes().get(name));
199        }
200
201        /**
202         * Get a mutable map of request attributes.
203         * @return the request attributes
204         */
205        Map<String, Object> attributes();
206
207        /**
208         * Get the first query parameter with the given name, if present.
209         * @param name the parameter name
210         * @return the parameter value
211         */
212        default Optional<String> queryParam(String name) {
213                List<String> queryParamValues = queryParams().get(name);
214                if (CollectionUtils.isEmpty(queryParamValues)) {
215                        return Optional.empty();
216                }
217                else {
218                        String value = queryParamValues.get(0);
219                        if (value == null) {
220                                value = "";
221                        }
222                        return Optional.of(value);
223                }
224        }
225
226        /**
227         * Get all query parameters for this request.
228         */
229        MultiValueMap<String, String> queryParams();
230
231        /**
232         * Get the path variable with the given name, if present.
233         * @param name the variable name
234         * @return the variable value
235         * @throws IllegalArgumentException if there is no path variable with the given name
236         */
237        default String pathVariable(String name) {
238                Map<String, String> pathVariables = pathVariables();
239                if (pathVariables.containsKey(name)) {
240                        return pathVariables().get(name);
241                }
242                else {
243                        throw new IllegalArgumentException("No path variable with name \"" + name + "\" available");
244                }
245        }
246
247        /**
248         * Get all path variables for this request.
249         */
250        Map<String, String> pathVariables();
251
252        /**
253         * Get the web session for this request.
254         * <p>Always guaranteed to return an instance either matching the session id
255         * requested by the client, or with a new session id either because the client
256         * did not specify one or because the underlying session had expired.
257         * <p>Use of this method does not automatically create a session.
258         */
259        Mono<WebSession> session();
260
261        /**
262         * Get the authenticated user for the request, if any.
263         */
264        Mono<? extends Principal> principal();
265
266        /**
267         * Get the form data from the body of the request if the Content-Type is
268         * {@code "application/x-www-form-urlencoded"} or an empty map otherwise.
269         * <p><strong>Note:</strong> calling this method causes the request body to
270         * be read and parsed in full, and the resulting {@code MultiValueMap} is
271         * cached so that this method is safe to call more than once.
272         */
273        Mono<MultiValueMap<String, String>> formData();
274
275        /**
276         * Get the parts of a multipart request if the Content-Type is
277         * {@code "multipart/form-data"} or an empty map otherwise.
278         * <p><strong>Note:</strong> calling this method causes the request body to
279         * be read and parsed in full, and the resulting {@code MultiValueMap} is
280         * cached so that this method is safe to call more than once.
281         */
282        Mono<MultiValueMap<String, Part>> multipartData();
283
284        /**
285         * Get the web exchange that this request is based on.
286         * <p>Note: Manipulating the exchange directly (instead of using the methods provided on
287         * {@code ServerRequest} and {@code ServerResponse}) can lead to irregular results.
288         * @since 5.1
289         */
290        ServerWebExchange exchange();
291
292        /**
293         * Check whether the requested resource has been modified given the
294         * supplied last-modified timestamp (as determined by the application).
295         * <p>If not modified, this method returns a response with corresponding
296         * status code and headers, otherwise an empty result.
297         * <p>Typical usage:
298         * <pre class="code">
299         * public Mono&lt;ServerResponse&gt; myHandleMethod(ServerRequest request) {
300         *   Instant lastModified = // application-specific calculation
301         *       return request.checkNotModified(lastModified)
302         *         .switchIfEmpty(Mono.defer(() -> {
303         *           // further request processing, actually building content
304         *               return ServerResponse.ok().body(...);
305         *         }));
306         * }</pre>
307         * <p>This method works with conditional GET/HEAD requests, but
308         * also with conditional POST/PUT/DELETE requests.
309         * <p><strong>Note:</strong> you can use either
310         * this {@code #checkNotModified(Instant)} method; or
311         * {@link #checkNotModified(String)}. If you want enforce both
312         * a strong entity tag and a Last-Modified value,
313         * as recommended by the HTTP specification,
314         * then you should use {@link #checkNotModified(Instant, String)}.
315         * @param lastModified the last-modified timestamp that the
316         * application determined for the underlying resource
317         * @return a corresponding response if the request qualifies as not
318         * modified, or an empty result otherwise
319         * @since 5.2.5
320         */
321        default Mono<ServerResponse> checkNotModified(Instant lastModified) {
322                Assert.notNull(lastModified, "LastModified must not be null");
323                return DefaultServerRequest.checkNotModified(exchange(), lastModified, null);
324        }
325
326        /**
327         * Check whether the requested resource has been modified given the
328         * supplied {@code ETag} (entity tag), as determined by the application.
329         * <p>If not modified, this method returns a response with corresponding
330         * status code and headers, otherwise an empty result.
331         * <p>Typical usage:
332         * <pre class="code">
333         * public Mono&lt;ServerResponse&gt; myHandleMethod(ServerRequest request) {
334         *   String eTag = // application-specific calculation
335         *       return request.checkNotModified(eTag)
336         *         .switchIfEmpty(Mono.defer(() -> {
337         *           // further request processing, actually building content
338         *               return ServerResponse.ok().body(...);
339         *         }));
340         * }</pre>
341         * <p>This method works with conditional GET/HEAD requests, but
342         * also with conditional POST/PUT/DELETE requests.
343         * <p><strong>Note:</strong> you can use either
344         * this {@link #checkNotModified(Instant)} method; or
345         * {@code #checkNotModified(String)}. If you want enforce both
346         * a strong entity tag and a Last-Modified value,
347         * as recommended by the HTTP specification,
348         * then you should use {@link #checkNotModified(Instant, String)}.
349         * @param etag the entity tag that the application determined
350         * for the underlying resource. This parameter will be padded
351         * with quotes (") if necessary.
352         * @return a corresponding response if the request qualifies as not
353         * modified, or an empty result otherwise
354         * @since 5.2.5
355         */
356        default Mono<ServerResponse> checkNotModified(String etag) {
357                Assert.notNull(etag, "Etag must not be null");
358                return DefaultServerRequest.checkNotModified(exchange(), null, etag);
359        }
360
361        /**
362         * Check whether the requested resource has been modified given the
363         * supplied {@code ETag} (entity tag) and last-modified timestamp,
364         * as determined by the application.
365         * <p>If not modified, this method returns a response with corresponding
366         * status code and headers, otherwise an empty result.
367         * <p>Typical usage:
368         * <pre class="code">
369         * public Mono&lt;ServerResponse&gt; myHandleMethod(ServerRequest request) {
370         *   Instant lastModified = // application-specific calculation
371         *   String eTag = // application-specific calculation
372         *       return request.checkNotModified(lastModified, eTag)
373         *         .switchIfEmpty(Mono.defer(() -> {
374         *           // further request processing, actually building content
375         *               return ServerResponse.ok().body(...);
376         *         }));
377         * }</pre>
378         * <p>This method works with conditional GET/HEAD requests, but
379         * also with conditional POST/PUT/DELETE requests.
380         * @param lastModified the last-modified timestamp that the
381         * application determined for the underlying resource
382         * @param etag the entity tag that the application determined
383         * for the underlying resource. This parameter will be padded
384         * with quotes (") if necessary.
385         * @return a corresponding response if the request qualifies as not
386         * modified, or an empty result otherwise.
387         * @since 5.2.5
388         */
389        default Mono<ServerResponse> checkNotModified(Instant lastModified, String etag) {
390                Assert.notNull(lastModified, "LastModified must not be null");
391                Assert.notNull(etag, "Etag must not be null");
392                return DefaultServerRequest.checkNotModified(exchange(), lastModified, etag);
393        }
394
395        // Static builder methods
396
397        /**
398         * Create a new {@code ServerRequest} based on the given {@code ServerWebExchange} and
399         * message readers.
400         * @param exchange the exchange
401         * @param messageReaders the message readers
402         * @return the created {@code ServerRequest}
403         */
404        static ServerRequest create(ServerWebExchange exchange, List<HttpMessageReader<?>> messageReaders) {
405                return new DefaultServerRequest(exchange, messageReaders);
406        }
407
408        /**
409         * Create a builder with the {@linkplain HttpMessageReader message readers},
410         * method name, URI, headers, cookies, and attributes of the given request.
411         * @param other the request to copy the message readers, method name, URI,
412         * headers, and attributes from
413         * @return the created builder
414         * @since 5.1
415         */
416        static Builder from(ServerRequest other) {
417                return new DefaultServerRequestBuilder(other);
418        }
419
420
421        /**
422         * Represents the headers of the HTTP request.
423         * @see ServerRequest#headers()
424         */
425        interface Headers {
426
427                /**
428                 * Get the list of acceptable media types, as specified by the {@code Accept}
429                 * header.
430                 * <p>Returns an empty list if the acceptable media types are unspecified.
431                 */
432                List<MediaType> accept();
433
434                /**
435                 * Get the list of acceptable charsets, as specified by the
436                 * {@code Accept-Charset} header.
437                 */
438                List<Charset> acceptCharset();
439
440                /**
441                 * Get the list of acceptable languages, as specified by the
442                 * {@code Accept-Language} header.
443                 */
444                List<Locale.LanguageRange> acceptLanguage();
445
446                /**
447                 * Get the length of the body in bytes, as specified by the
448                 * {@code Content-Length} header.
449                 */
450                OptionalLong contentLength();
451
452                /**
453                 * Get the media type of the body, as specified by the
454                 * {@code Content-Type} header.
455                 */
456                Optional<MediaType> contentType();
457
458                /**
459                 * Get the value of the {@code Host} header, if available.
460                 * <p>If the header value does not contain a port, the
461                 * {@linkplain InetSocketAddress#getPort() port} in the returned address will
462                 * be {@code 0}.
463                 */
464                @Nullable
465                InetSocketAddress host();
466
467                /**
468                 * Get the value of the {@code Range} header.
469                 * <p>Returns an empty list when the range is unknown.
470                 */
471                List<HttpRange> range();
472
473                /**
474                 * Get the header value(s), if any, for the header with the given name.
475                 * <p>Returns an empty list if no header values are found.
476                 * @param headerName the header name
477                 */
478                List<String> header(String headerName);
479
480                /**
481                 * Get the first header value, if any, for the header with the given name.
482                 * <p>Returns {@code null} if no header values are found.
483                 * @param headerName the header name
484                 * @since 5.2.5
485                 */
486                @Nullable
487                default String firstHeader(String headerName) {
488                        List<String> list = header(headerName);
489                        return list.isEmpty() ? null : list.get(0);
490                }
491
492                /**
493                 * Get the headers as an instance of {@link HttpHeaders}.
494                 */
495                HttpHeaders asHttpHeaders();
496        }
497
498
499        /**
500         * Defines a builder for a request.
501         * @since 5.1
502         */
503        interface Builder {
504
505                /**
506                 * Set the method of the request.
507                 * @param method the new method
508                 * @return this builder
509                 */
510                Builder method(HttpMethod method);
511
512                /**
513                 * Set the URI of the request.
514                 * @param uri the new URI
515                 * @return this builder
516                 */
517                Builder uri(URI uri);
518
519                /**
520                 * Add the given header value(s) under the given name.
521                 * @param headerName the header name
522                 * @param headerValues the header value(s)
523                 * @return this builder
524                 * @see HttpHeaders#add(String, String)
525                 */
526                Builder header(String headerName, String... headerValues);
527
528                /**
529                 * Manipulate this request's headers with the given consumer.
530                 * <p>The headers provided to the consumer are "live", so that the consumer can be used to
531                 * {@linkplain HttpHeaders#set(String, String) overwrite} existing header values,
532                 * {@linkplain HttpHeaders#remove(Object) remove} values, or use any of the other
533                 * {@link HttpHeaders} methods.
534                 * @param headersConsumer a function that consumes the {@code HttpHeaders}
535                 * @return this builder
536                 */
537                Builder headers(Consumer<HttpHeaders> headersConsumer);
538
539                /**
540                 * Add a cookie with the given name and value(s).
541                 * @param name the cookie name
542                 * @param values the cookie value(s)
543                 * @return this builder
544                 */
545                Builder cookie(String name, String... values);
546
547                /**
548                 * Manipulate this request's cookies with the given consumer.
549                 * <p>The map provided to the consumer is "live", so that the consumer can be used to
550                 * {@linkplain MultiValueMap#set(Object, Object) overwrite} existing cookies,
551                 * {@linkplain MultiValueMap#remove(Object) remove} cookies, or use any of the other
552                 * {@link MultiValueMap} methods.
553                 * @param cookiesConsumer a function that consumes the cookies map
554                 * @return this builder
555                 */
556                Builder cookies(Consumer<MultiValueMap<String, HttpCookie>> cookiesConsumer);
557
558                /**
559                 * Set the body of the request.
560                 * <p>Calling this methods will
561                 * {@linkplain org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer) release}
562                 * the existing body of the builder.
563                 * @param body the new body
564                 * @return this builder
565                 */
566                Builder body(Flux<DataBuffer> body);
567
568                /**
569                 * Set the body of the request to the UTF-8 encoded bytes of the given string.
570                 * <p>Calling this methods will
571                 * {@linkplain org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer) release}
572                 * the existing body of the builder.
573                 * @param body the new body
574                 * @return this builder
575                 */
576                Builder body(String body);
577
578                /**
579                 * Add an attribute with the given name and value.
580                 * @param name the attribute name
581                 * @param value the attribute value
582                 * @return this builder
583                 */
584                Builder attribute(String name, Object value);
585
586                /**
587                 * Manipulate this request's attributes with the given consumer.
588                 * <p>The map provided to the consumer is "live", so that the consumer can be used
589                 * to {@linkplain Map#put(Object, Object) overwrite} existing attributes,
590                 * {@linkplain Map#remove(Object) remove} attributes, or use any of the other
591                 * {@link Map} methods.
592                 * @param attributesConsumer a function that consumes the attributes map
593                 * @return this builder
594                 */
595                Builder attributes(Consumer<Map<String, Object>> attributesConsumer);
596
597                /**
598                 * Build the request.
599                 * @return the built request
600                 */
601                ServerRequest build();
602        }
603
604}