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.client;
018
019import java.util.Collections;
020import java.util.List;
021import java.util.Optional;
022import java.util.OptionalLong;
023import java.util.function.Consumer;
024
025import reactor.core.publisher.Flux;
026import reactor.core.publisher.Mono;
027
028import org.springframework.core.ParameterizedTypeReference;
029import org.springframework.core.io.buffer.DataBuffer;
030import org.springframework.http.HttpHeaders;
031import org.springframework.http.HttpRequest;
032import org.springframework.http.HttpStatus;
033import org.springframework.http.MediaType;
034import org.springframework.http.ResponseCookie;
035import org.springframework.http.ResponseEntity;
036import org.springframework.http.client.reactive.ClientHttpResponse;
037import org.springframework.http.codec.HttpMessageReader;
038import org.springframework.http.codec.HttpMessageWriter;
039import org.springframework.util.MultiValueMap;
040import org.springframework.web.reactive.function.BodyExtractor;
041
042/**
043 * Represents an HTTP response, as returned by {@link WebClient} and also
044 * {@link ExchangeFunction}. Provides access to the response status and
045 * headers, and also methods to consume the response body.
046 *
047 * <p><strong>NOTE:</strong> When using a {@link ClientResponse}
048 * through the {@code WebClient}
049 * {@link WebClient.RequestHeadersSpec#exchange() exchange()} method,
050 * you have to make sure that the body is consumed or released by using
051 * one of the following methods:
052 * <ul>
053 * <li>{@link #body(BodyExtractor)}</li>
054 * <li>{@link #bodyToMono(Class)} or
055 *     {@link #bodyToMono(ParameterizedTypeReference)}</li>
056 * <li>{@link #bodyToFlux(Class)} or
057 *     {@link #bodyToFlux(ParameterizedTypeReference)}</li>
058 * <li>{@link #toEntity(Class)} or
059 *     {@link #toEntity(ParameterizedTypeReference)}</li>
060 * <li>{@link #toEntityList(Class)} or
061 *     {@link #toEntityList(ParameterizedTypeReference)}</li>
062*  <li>{@link #toBodilessEntity()}</li>
063 * <li>{@link #releaseBody()}</li>
064 * </ul>
065 * You can also use {@code bodyToMono(Void.class)} if no response content is
066 * expected. However keep in mind the connection will be closed, instead of
067 * being placed back in the pool, if any content does arrive. This is in
068 * contrast to {@link #releaseBody()} which does consume the full body and
069 * releases any content received.
070 *
071 * @author Brian Clozel
072 * @author Arjen Poutsma
073 * @since 5.0
074 */
075public interface ClientResponse {
076
077        /**
078         * Return the HTTP status code as an {@link HttpStatus} enum value.
079         * @return the HTTP status as an HttpStatus enum value (never {@code null})
080         * @throws IllegalArgumentException in case of an unknown HTTP status code
081         * @since #getRawStatusCode()
082         * @see HttpStatus#valueOf(int)
083         */
084        HttpStatus statusCode();
085
086        /**
087         * Return the (potentially non-standard) status code of this response.
088         * @return the HTTP status as an integer value
089         * @since 5.1
090         * @see #statusCode()
091         * @see HttpStatus#resolve(int)
092         */
093        int rawStatusCode();
094
095        /**
096         * Return the headers of this response.
097         */
098        Headers headers();
099
100        /**
101         * Return the cookies of this response.
102         */
103        MultiValueMap<String, ResponseCookie> cookies();
104
105        /**
106         * Return the strategies used to convert the body of this response.
107         */
108        ExchangeStrategies strategies();
109
110        /**
111         * Extract the body with the given {@code BodyExtractor}.
112         * @param extractor the {@code BodyExtractor} that reads from the response
113         * @param <T> the type of the body returned
114         * @return the extracted body
115         */
116        <T> T body(BodyExtractor<T, ? super ClientHttpResponse> extractor);
117
118        /**
119         * Extract the body to a {@code Mono}.
120         * @param elementClass the class of element in the {@code Mono}
121         * @param <T> the element type
122         * @return a mono containing the body of the given type {@code T}
123         */
124        <T> Mono<T> bodyToMono(Class<? extends T> elementClass);
125
126        /**
127         * Extract the body to a {@code Mono}.
128         * @param elementTypeRef the type reference of element in the {@code Mono}
129         * @param <T> the element type
130         * @return a mono containing the body of the given type {@code T}
131         */
132        <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> elementTypeRef);
133
134        /**
135         * Extract the body to a {@code Flux}.
136         * @param elementClass the class of elements in the {@code Flux}
137         * @param <T> the element type
138         * @return a flux containing the body of the given type {@code T}
139         */
140        <T> Flux<T> bodyToFlux(Class<? extends T> elementClass);
141
142        /**
143         * Extract the body to a {@code Flux}.
144         * @param elementTypeRef the type reference of elements in the {@code Flux}
145         * @param <T> the element type
146         * @return a flux containing the body of the given type {@code T}
147         */
148        <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> elementTypeRef);
149
150        /**
151         * Release the body of this response.
152         * @return a completion signal
153         * @since 5.2
154         * @see org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer)
155         */
156        Mono<Void> releaseBody();
157
158        /**
159         * Return this response as a delayed {@code ResponseEntity}.
160         * @param bodyClass the expected response body type
161         * @param <T> response body type
162         * @return {@code Mono} with the {@code ResponseEntity}
163         */
164        <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyClass);
165
166        /**
167         * Return this response as a delayed {@code ResponseEntity}.
168         * @param bodyTypeReference a type reference describing the expected response body type
169         * @param <T> response body type
170         * @return {@code Mono} with the {@code ResponseEntity}
171         */
172        <T> Mono<ResponseEntity<T>> toEntity(ParameterizedTypeReference<T> bodyTypeReference);
173
174        /**
175         * Return this response as a delayed list of {@code ResponseEntity}s.
176         * @param elementClass the expected response body list element class
177         * @param <T> the type of elements in the list
178         * @return {@code Mono} with the list of {@code ResponseEntity}s
179         */
180        <T> Mono<ResponseEntity<List<T>>> toEntityList(Class<T> elementClass);
181
182        /**
183         * Return this response as a delayed list of {@code ResponseEntity}s.
184         * @param elementTypeRef the expected response body list element reference type
185         * @param <T> the type of elements in the list
186         * @return {@code Mono} with the list of {@code ResponseEntity}s
187         */
188        <T> Mono<ResponseEntity<List<T>>> toEntityList(ParameterizedTypeReference<T> elementTypeRef);
189
190        /**
191         * Return this response as a delayed {@code ResponseEntity} containing
192         * status and headers, but no body. Calling this method will
193         * {@linkplain #releaseBody() release} the body of the response.
194         * @return {@code Mono} with the bodiless {@code ResponseEntity}
195         * @since 5.2
196         */
197        Mono<ResponseEntity<Void>> toBodilessEntity();
198
199        /**
200         * Create a {@link WebClientResponseException} that contains the response
201         * status, headers, body, and the originating request.
202         * @return a {@code Mono} with the created exception
203         * @since 5.2
204         */
205        Mono<WebClientResponseException> createException();
206
207        /**
208         * Return a log message prefix to use to correlate messages for this exchange.
209         * <p>The prefix is based on {@linkplain ClientRequest#logPrefix()}, which
210         * itself is based on the value of the {@link ClientRequest#LOG_ID_ATTRIBUTE
211         * LOG_ID_ATTRIBUTE} request attribute, further surrounded with "[" and "]".
212         * @return the log message prefix or an empty String if the
213         * {@link ClientRequest#LOG_ID_ATTRIBUTE LOG_ID_ATTRIBUTE} is not set
214         * @since 5.2.3
215         */
216        String logPrefix();
217
218
219        // Static builder methods
220
221        /**
222         * Create a builder with the status, headers, and cookies of the given response.
223         * @param other the response to copy the status, headers, and cookies from
224         * @return the created builder
225         */
226        static Builder from(ClientResponse other) {
227                return new DefaultClientResponseBuilder(other);
228        }
229
230        /**
231         * Create a response builder with the given status code and using default strategies for
232         * reading the body.
233         * @param statusCode the status code
234         * @return the created builder
235         */
236        static Builder create(HttpStatus statusCode) {
237                return create(statusCode, ExchangeStrategies.withDefaults());
238        }
239
240        /**
241         * Create a response builder with the given status code and strategies for reading the body.
242         * @param statusCode the status code
243         * @param strategies the strategies
244         * @return the created builder
245         */
246        static Builder create(HttpStatus statusCode, ExchangeStrategies strategies) {
247                return new DefaultClientResponseBuilder(strategies).statusCode(statusCode);
248        }
249
250        /**
251         * Create a response builder with the given raw status code and strategies for reading the body.
252         * @param statusCode the status code
253         * @param strategies the strategies
254         * @return the created builder
255         * @since 5.1.9
256         */
257        static Builder create(int statusCode, ExchangeStrategies strategies) {
258                return new DefaultClientResponseBuilder(strategies).rawStatusCode(statusCode);
259        }
260
261        /**
262         * Create a response builder with the given status code and message body readers.
263         * @param statusCode the status code
264         * @param messageReaders the message readers
265         * @return the created builder
266         */
267        static Builder create(HttpStatus statusCode, List<HttpMessageReader<?>> messageReaders) {
268                return create(statusCode, new ExchangeStrategies() {
269                        @Override
270                        public List<HttpMessageReader<?>> messageReaders() {
271                                return messageReaders;
272                        }
273                        @Override
274                        public List<HttpMessageWriter<?>> messageWriters() {
275                                // not used in the response
276                                return Collections.emptyList();
277                        }
278                });
279        }
280
281
282        /**
283         * Represents the headers of the HTTP response.
284         * @see ClientResponse#headers()
285         */
286        interface Headers {
287
288                /**
289                 * Return the length of the body in bytes, as specified by the
290                 * {@code Content-Length} header.
291                 */
292                OptionalLong contentLength();
293
294                /**
295                 * Return the {@linkplain MediaType media type} of the body, as specified
296                 * by the {@code Content-Type} header.
297                 */
298                Optional<MediaType> contentType();
299
300                /**
301                 * Return the header value(s), if any, for the header of the given name.
302                 * <p>Return an empty list if no header values are found.
303                 * @param headerName the header name
304                 */
305                List<String> header(String headerName);
306
307                /**
308                 * Return the headers as an {@link HttpHeaders} instance.
309                 */
310                HttpHeaders asHttpHeaders();
311        }
312
313
314        /**
315         * Defines a builder for a response.
316         */
317        interface Builder {
318
319                /**
320                 * Set the status code of the response.
321                 * @param statusCode the new status code
322                 * @return this builder
323                 */
324                Builder statusCode(HttpStatus statusCode);
325
326                /**
327                 * Set the raw status code of the response.
328                 * @param statusCode the new status code
329                 * @return this builder
330                 * @since 5.1.9
331                 */
332                Builder rawStatusCode(int statusCode);
333
334                /**
335                 * Add the given header value(s) under the given name.
336                 * @param headerName the header name
337                 * @param headerValues the header value(s)
338                 * @return this builder
339                 * @see HttpHeaders#add(String, String)
340                 */
341                Builder header(String headerName, String... headerValues);
342
343                /**
344                 * Manipulate this response's headers with the given consumer.
345                 * <p>The headers provided to the consumer are "live", so that the consumer
346                 * can be used to {@linkplain HttpHeaders#set(String, String) overwrite}
347                 * existing header values, {@linkplain HttpHeaders#remove(Object) remove}
348                 * values, or use any of the other {@link HttpHeaders} methods.
349                 * @param headersConsumer a function that consumes the {@code HttpHeaders}
350                 * @return this builder
351                 */
352                Builder headers(Consumer<HttpHeaders> headersConsumer);
353
354                /**
355                 * Add a cookie with the given name and value(s).
356                 * @param name the cookie name
357                 * @param values the cookie value(s)
358                 * @return this builder
359                 */
360                Builder cookie(String name, String... values);
361
362                /**
363                 * Manipulate this response's cookies with the given consumer.
364                 * <p>The map provided to the consumer is "live", so that the consumer can be used to
365                 * {@linkplain MultiValueMap#set(Object, Object) overwrite} existing cookie values,
366                 * {@linkplain MultiValueMap#remove(Object) remove} values, or use any of the other
367                 * {@link MultiValueMap} methods.
368                 * @param cookiesConsumer a function that consumes the cookies map
369                 * @return this builder
370                 */
371                Builder cookies(Consumer<MultiValueMap<String, ResponseCookie>> cookiesConsumer);
372
373                /**
374                 * Set the body of the response.
375                 * <p>Calling this methods will
376                 * {@linkplain org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer) release}
377                 * the existing body of the builder.
378                 * @param body the new body
379                 * @return this builder
380                 */
381                Builder body(Flux<DataBuffer> body);
382
383                /**
384                 * Set the body of the response to the UTF-8 encoded bytes of the given string.
385                 * <p>Calling this methods will
386                 * {@linkplain org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer) release}
387                 * the existing body of the builder.
388                 * @param body the new body
389                 * @return this builder
390                 */
391                Builder body(String body);
392
393                /**
394                 * Set the request associated with the response.
395                 * @param request the request
396                 * @return this builder
397                 * @since 5.2
398                 */
399                Builder request(HttpRequest request);
400
401                /**
402                 * Build the response.
403                 */
404                ClientResponse build();
405        }
406
407}