From 6324a1b3fab1b797211ab01a3939caaef521dda5 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Tue, 26 Mar 2019 15:31:15 +0100 Subject: [PATCH 1/2] Copy cookies and hints in built ServerResponse Closes gh-22481 --- .../server/DefaultEntityResponseBuilder.java | 6 ++--- .../DefaultRenderingResponseBuilder.java | 4 +-- .../server/DefaultServerResponseBuilder.java | 27 +++++++++++++------ .../DefaultServerResponseBuilderTests.java | 12 ++++++--- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java index e026f5cf5c1..2e187462318 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -202,16 +202,14 @@ class DefaultEntityResponseBuilder implements EntityResponse.Builder { private final BodyInserter inserter; - private final Map hints; public DefaultEntityResponse(int statusCode, HttpHeaders headers, MultiValueMap cookies, T entity, BodyInserter inserter, Map hints) { - super(statusCode, headers, cookies); + super(statusCode, headers, cookies, hints); this.entity = entity; this.inserter = inserter; - this.hints = hints; } @Override diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseBuilder.java index ce92f1f8833..a67ae486532 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -168,7 +168,7 @@ final class DefaultRenderingResponseBuilder implements RenderingResponse.Builder public DefaultRenderingResponse(int statusCode, HttpHeaders headers, MultiValueMap cookies, String name, Map model) { - super(statusCode, headers, cookies); + super(statusCode, headers, cookies, Collections.emptyMap()); this.name = name; this.model = Collections.unmodifiableMap(new LinkedHashMap<>(model)); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java index 4b39fcec572..43143ba9d34 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.net.URI; import java.time.Instant; import java.time.ZonedDateTime; import java.util.Arrays; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.LinkedHashSet; @@ -73,9 +74,16 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { public DefaultServerResponseBuilder(ServerResponse other) { Assert.notNull(other, "ServerResponse must not be null"); - this.statusCode = (other instanceof AbstractServerResponse ? - ((AbstractServerResponse) other).statusCode : other.statusCode().value()); this.headers.addAll(other.headers()); + this.cookies.addAll(other.cookies()); + if (other instanceof AbstractServerResponse) { + AbstractServerResponse abstractOther = (AbstractServerResponse) other; + this.statusCode = abstractOther.statusCode; + this.hints.putAll(abstractOther.hints); + } + else { + this.statusCode = other.statusCode().value(); + } } public DefaultServerResponseBuilder(HttpStatus status) { @@ -289,12 +297,17 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { private final MultiValueMap cookies; + final Map hints; + + protected AbstractServerResponse( - int statusCode, HttpHeaders headers, MultiValueMap cookies) { + int statusCode, HttpHeaders headers, MultiValueMap cookies, + Map hints) { this.statusCode = statusCode; this.headers = HttpHeaders.readOnlyHttpHeaders(headers); this.cookies = CollectionUtils.unmodifiableMultiValueMap(new LinkedMultiValueMap<>(cookies)); + this.hints = hints; } @Override @@ -361,7 +374,7 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { MultiValueMap cookies, BiFunction> writeFunction) { - super(statusCode, headers, cookies); + super(statusCode, headers, cookies, Collections.emptyMap()); Assert.notNull(writeFunction, "BiFunction must not be null"); this.writeFunction = writeFunction; } @@ -377,16 +390,14 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { private final BodyInserter inserter; - private final Map hints; public BodyInserterResponse(int statusCode, HttpHeaders headers, MultiValueMap cookies, BodyInserter body, Map hints) { - super(statusCode, headers, cookies); + super(statusCode, headers, cookies, hints); Assert.notNull(body, "BodyInserter must not be null"); this.inserter = body; - this.hints = hints; } @Override diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java index eede025f952..44a5905e268 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -65,11 +65,17 @@ public class DefaultServerResponseBuilderTests { @Test public void from() { - ServerResponse other = ServerResponse.ok().header("foo", "bar").build().block(); + ResponseCookie cookie = ResponseCookie.from("foo", "bar").build(); + ServerResponse other = ServerResponse.ok().header("foo", "bar") + .cookie(cookie) + .hint("foo", "bar") + .build().block(); + Mono result = ServerResponse.from(other).build(); StepVerifier.create(result) .expectNextMatches(response -> HttpStatus.OK.equals(response.statusCode()) && - "bar".equals(response.headers().getFirst("foo"))) + "bar".equals(response.headers().getFirst("foo")) && + cookie.equals(response.cookies().getFirst("foo"))) .expectComplete() .verify(); } From c152d246a8ce99628ea5b28f91e96cfbeb434186 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Tue, 26 Mar 2019 16:19:13 +0100 Subject: [PATCH 2/2] Copy cookies and hints to ServerResponse builders Closes gh-22351 --- .../server/DefaultEntityResponseBuilder.java | 6 +++++ .../server/DefaultServerResponseBuilder.java | 24 +++++++++++++++---- .../function/server/EntityResponse.java | 12 +++++++++- .../function/server/ServerResponse.java | 11 ++++++++- .../DefaultServerResponseBuilderTests.java | 18 ++++++++++++++ 5 files changed, 64 insertions(+), 7 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java index 2e187462318..d43a4e71f1a 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java @@ -157,6 +157,12 @@ class DefaultEntityResponseBuilder implements EntityResponse.Builder { return this; } + @Override + public EntityResponse.Builder hints(Consumer> hintsConsumer) { + hintsConsumer.accept(this.hints); + return this; + } + @Override public EntityResponse.Builder lastModified(ZonedDateTime lastModified) { this.headers.setLastModified(lastModified); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java index 43143ba9d34..cabbe2bf045 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java @@ -165,6 +165,12 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { return this; } + @Override + public ServerResponse.BodyBuilder hints(Consumer> hintsConsumer) { + hintsConsumer.accept(this.hints); + return this; + } + @Override public ServerResponse.BodyBuilder lastModified(ZonedDateTime lastModified) { this.headers.setLastModified(lastModified); @@ -222,8 +228,10 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { return new DefaultEntityResponseBuilder<>(publisher, BodyInserters.fromPublisher(publisher, elementClass)) - .headers(this.headers) .status(this.statusCode) + .headers(this.headers) + .cookies(cookies -> cookies.addAll(this.cookies)) + .hints(hints -> hints.putAll(this.hints)) .build() .map(entityResponse -> entityResponse); } @@ -237,8 +245,10 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { return new DefaultEntityResponseBuilder<>(publisher, BodyInserters.fromPublisher(publisher, typeReference)) - .headers(this.headers) .status(this.statusCode) + .headers(this.headers) + .cookies(cookies -> cookies.addAll(this.cookies)) + .hints(hints -> hints.putAll(this.hints)) .build() .map(entityResponse -> entityResponse); } @@ -251,8 +261,10 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { return new DefaultEntityResponseBuilder<>(body, BodyInserters.fromObject(body)) - .headers(this.headers) .status(this.statusCode) + .headers(this.headers) + .cookies(cookies -> cookies.addAll(this.cookies)) + .hints(hints -> hints.putAll(this.hints)) .build() .map(entityResponse -> entityResponse); } @@ -266,8 +278,9 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { @Override public Mono render(String name, Object... modelAttributes) { return new DefaultRenderingResponseBuilder(name) - .headers(this.headers) .status(this.statusCode) + .headers(this.headers) + .cookies(cookies -> cookies.addAll(this.cookies)) .modelAttributes(modelAttributes) .build() .map(renderingResponse -> renderingResponse); @@ -276,8 +289,9 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { @Override public Mono render(String name, Map model) { return new DefaultRenderingResponseBuilder(name) - .headers(this.headers) .status(this.statusCode) + .headers(this.headers) + .cookies(cookies -> cookies.addAll(this.cookies)) .modelAttributes(model) .build() .map(renderingResponse -> renderingResponse); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/EntityResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/EntityResponse.java index 8e1e1d39919..cedbeb7aa46 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/EntityResponse.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/EntityResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package org.springframework.web.reactive.function.server; import java.net.URI; import java.time.Instant; import java.time.ZonedDateTime; +import java.util.Map; import java.util.Set; import java.util.function.Consumer; @@ -262,6 +263,15 @@ public interface EntityResponse extends ServerResponse { */ Builder hint(String key, Object value); + /** + * Manipulate serialization hint with the given consumer. + * + * @param hintsConsumer a function that consumes the hints + * @return this builder + * @since 5.1.6 + */ + Builder hints(Consumer> hintsConsumer); + /** * Build the response. * @return the built response diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java index 851aa725f74..78d097a2803 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -376,6 +376,15 @@ public interface ServerResponse { */ BodyBuilder hint(String key, Object value); + /** + * Manipulate serialization hint with the given consumer. + * + * @param hintsConsumer a function that consumes the hints + * @return this builder + * @since 5.1.6 + */ + BodyBuilder hints(Consumer> hintsConsumer); + /** * Set the body of the response to the given asynchronous {@code Publisher} and return it. * This convenience method combines {@link #body(BodyInserter)} and diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java index 44a5905e268..5d6bf746f45 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java @@ -41,6 +41,7 @@ import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse import org.springframework.mock.web.test.server.MockServerWebExchange; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.result.view.ViewResolver; import static org.junit.Assert.*; @@ -302,6 +303,23 @@ public class DefaultServerResponseBuilderTests { .verify(); } + @Test + public void copyCookies() { + Mono serverResponse = ServerResponse.ok() + .cookie(ResponseCookie.from("foo", "bar").build()) + .syncBody("body"); + + assertFalse(serverResponse.block().cookies().isEmpty()); + + serverResponse = ServerResponse.ok() + .cookie(ResponseCookie.from("foo", "bar").build()) + .body(BodyInserters.fromObject("body")); + + + assertFalse(serverResponse.block().cookies().isEmpty()); + } + + @Test public void build() { ResponseCookie cookie = ResponseCookie.from("name", "value").build();