4040
4141import org .springframework .core .ParameterizedTypeReference ;
4242import org .springframework .core .ResolvableType ;
43+ import org .springframework .http .HttpCookie ;
4344import org .springframework .http .HttpHeaders ;
4445import org .springframework .http .HttpMethod ;
4546import org .springframework .http .HttpRequest ;
6465import org .springframework .lang .Nullable ;
6566import org .springframework .util .Assert ;
6667import org .springframework .util .CollectionUtils ;
68+ import org .springframework .util .LinkedMultiValueMap ;
69+ import org .springframework .util .MultiValueMap ;
6770import org .springframework .web .util .UriBuilder ;
6871import org .springframework .web .util .UriBuilderFactory ;
6972
@@ -103,6 +106,9 @@ final class DefaultRestClient implements RestClient {
103106 @ Nullable
104107 private final HttpHeaders defaultHeaders ;
105108
109+ @ Nullable
110+ private final MultiValueMap <String , String > defaultCookies ;
111+
106112 @ Nullable
107113 private final Consumer <RequestHeadersSpec <?>> defaultRequest ;
108114
@@ -123,6 +129,7 @@ final class DefaultRestClient implements RestClient {
123129 @ Nullable List <ClientHttpRequestInitializer > initializers ,
124130 UriBuilderFactory uriBuilderFactory ,
125131 @ Nullable HttpHeaders defaultHeaders ,
132+ @ Nullable MultiValueMap <String , String > defaultCookies ,
126133 @ Nullable Consumer <RequestHeadersSpec <?>> defaultRequest ,
127134 @ Nullable List <StatusHandler > statusHandlers ,
128135 List <HttpMessageConverter <?>> messageConverters ,
@@ -135,6 +142,7 @@ final class DefaultRestClient implements RestClient {
135142 this .interceptors = interceptors ;
136143 this .uriBuilderFactory = uriBuilderFactory ;
137144 this .defaultHeaders = defaultHeaders ;
145+ this .defaultCookies = defaultCookies ;
138146 this .defaultRequest = defaultRequest ;
139147 this .defaultStatusHandlers = (statusHandlers != null ? new ArrayList <>(statusHandlers ) : new ArrayList <>());
140148 this .messageConverters = messageConverters ;
@@ -293,6 +301,8 @@ private static <T> Class<T> bodyClass(Type type) {
293301
294302 private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec {
295303
304+ private static final String COOKIE_DELIMITER = "; " ;
305+
296306 private final HttpMethod httpMethod ;
297307
298308 @ Nullable
@@ -301,6 +311,9 @@ private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec {
301311 @ Nullable
302312 private HttpHeaders headers ;
303313
314+ @ Nullable
315+ private MultiValueMap <String , String > cookies ;
316+
304317 @ Nullable
305318 private InternalBody body ;
306319
@@ -356,6 +369,13 @@ private HttpHeaders getHeaders() {
356369 return this .headers ;
357370 }
358371
372+ private MultiValueMap <String , String > getCookies () {
373+ if (this .cookies == null ) {
374+ this .cookies = new LinkedMultiValueMap <>(3 );
375+ }
376+ return this .cookies ;
377+ }
378+
359379 @ Override
360380 public DefaultRequestBodyUriSpec header (String headerName , String ... headerValues ) {
361381 for (String headerValue : headerValues ) {
@@ -382,6 +402,18 @@ public DefaultRequestBodyUriSpec acceptCharset(Charset... acceptableCharsets) {
382402 return this ;
383403 }
384404
405+ @ Override
406+ public DefaultRequestBodyUriSpec cookie (String name , String value ) {
407+ getCookies ().add (name , value );
408+ return this ;
409+ }
410+
411+ @ Override
412+ public DefaultRequestBodyUriSpec cookies (Consumer <MultiValueMap <String , String >> cookiesConsumer ) {
413+ cookiesConsumer .accept (getCookies ());
414+ return this ;
415+ }
416+
385417 @ Override
386418 public DefaultRequestBodyUriSpec contentType (MediaType contentType ) {
387419 getHeaders ().setContentType (contentType );
@@ -525,6 +557,12 @@ private <T> T exchangeInternal(ExchangeFunction<T> exchangeFunction, boolean clo
525557 try {
526558 uri = initUri ();
527559 HttpHeaders headers = initHeaders ();
560+
561+ MultiValueMap <String , String > cookies = initCookies ();
562+ if (!CollectionUtils .isEmpty (cookies )) {
563+ headers .put (HttpHeaders .COOKIE , List .of (cookiesToHeaderValue (cookies )));
564+ }
565+
528566 ClientHttpRequest clientRequest = createRequest (uri );
529567 clientRequest .getHeaders ().addAll (headers );
530568 Map <String , Object > attributes = getAttributes ();
@@ -599,6 +637,28 @@ else if (CollectionUtils.isEmpty(defaultHeaders)) {
599637 }
600638 }
601639
640+ private MultiValueMap <String , String > initCookies () {
641+ MultiValueMap <String , String > mergedCookies = new LinkedMultiValueMap <>();
642+
643+ if (!CollectionUtils .isEmpty (defaultCookies )) {
644+ mergedCookies .putAll (defaultCookies );
645+ }
646+
647+ if (!CollectionUtils .isEmpty (this .cookies )) {
648+ mergedCookies .putAll (this .cookies );
649+ }
650+
651+ return mergedCookies ;
652+ }
653+
654+ private String cookiesToHeaderValue (MultiValueMap <String , String > cookies ) {
655+ List <String > flatCookies = new ArrayList <>();
656+ cookies .forEach ((name , cookieValues ) -> cookieValues .forEach (value ->
657+ flatCookies .add (new HttpCookie (name , value ).toString ())
658+ ));
659+ return String .join (COOKIE_DELIMITER , flatCookies );
660+ }
661+
602662 private ClientHttpRequest createRequest (URI uri ) throws IOException {
603663 ClientHttpRequestFactory factory ;
604664 if (DefaultRestClient .this .interceptors != null ) {
0 commit comments