diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/CachedBodyOutputMessage.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/CachedBodyOutputMessage.java index 085f8283..872a2764 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/CachedBodyOutputMessage.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/CachedBodyOutputMessage.java @@ -16,6 +16,7 @@ package org.springframework.cloud.gateway.filter.factory.rewrite; +import java.util.function.Function; import java.util.function.Supplier; import org.reactivestreams.Publisher; @@ -32,7 +33,7 @@ import org.springframework.web.server.ServerWebExchange; /** * Implementation of {@link ClientHttpRequest} that saves body as a field. */ -class CachedBodyOutputMessage implements ReactiveHttpOutputMessage { +public class CachedBodyOutputMessage implements ReactiveHttpOutputMessage { private final DataBufferFactory bufferFactory; @@ -41,7 +42,7 @@ class CachedBodyOutputMessage implements ReactiveHttpOutputMessage { private Flux body = Flux.error(new IllegalStateException( "The body is not set. " + "Did handling complete with success?")); - CachedBodyOutputMessage(ServerWebExchange exchange, HttpHeaders httpHeaders) { + public CachedBodyOutputMessage(ServerWebExchange exchange, HttpHeaders httpHeaders) { this.bufferFactory = exchange.getResponse().bufferFactory(); this.httpHeaders = httpHeaders; } @@ -74,6 +75,11 @@ class CachedBodyOutputMessage implements ReactiveHttpOutputMessage { return this.body; } + @Deprecated + public void setWriteHandler(Function, Mono> writeHandler) { + + } + public Mono writeWith(Publisher body) { this.body = Flux.from(body); return Mono.empty(); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/HttpMessageWriterResponse.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/HttpMessageWriterResponse.java new file mode 100644 index 00000000..5f07e597 --- /dev/null +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/HttpMessageWriterResponse.java @@ -0,0 +1,113 @@ +/* + * Copyright 2013-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.filter.factory.rewrite; + +import java.util.function.Supplier; + +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.MultiValueMap; + +/** + * This class is BETA and may be subject to change in a future release. Response who's job + * it is to gather the Publisher<DataBuffer> from the writeWith message during a + * call to HttpMessageWriter.write. Also gathers any headers set there. + */ +@Deprecated +public class HttpMessageWriterResponse implements ServerHttpResponse { + + private final HttpHeaders headers = new HttpHeaders(); + + private final DataBufferFactory dataBufferFactory; + + private Publisher body; + + public HttpMessageWriterResponse(DataBufferFactory dataBufferFactory) { + this.dataBufferFactory = dataBufferFactory; + } + + @Override + public HttpHeaders getHeaders() { + return this.headers; + } + + @Override + public Mono writeWith(Publisher body) { + this.body = body; + return Mono.empty(); + } + + @Override + public Mono writeAndFlushWith( + Publisher> body) { + // TODO: is this kosher? + return writeWith(Flux.from(body).flatMapSequential(p -> p)); + } + + public Publisher getBody() { + return body; + } + + @Override + public boolean setStatusCode(HttpStatus status) { + return false; + } + + @Override + public HttpStatus getStatusCode() { + return null; + } + + @Override + public MultiValueMap getCookies() { + return null; + } + + @Override + public void addCookie(ResponseCookie cookie) { + + } + + @Override + public DataBufferFactory bufferFactory() { + return this.dataBufferFactory; + } + + @Override + public void beforeCommit(Supplier> action) { + + } + + @Override + public boolean isCommitted() { + return false; + } + + @Override + public Mono setComplete() { + return null; + } + +} diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java index 9ec7514f..bb010c04 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java @@ -31,9 +31,13 @@ import org.springframework.cloud.gateway.support.BodyInserterContext; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; +import org.springframework.http.client.reactive.ClientHttpResponse; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponseDecorator; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyInserter; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.ClientResponse; @@ -234,4 +238,50 @@ public class ModifyResponseBodyGatewayFilterFactory extends } + @Deprecated + @SuppressWarnings("unchecked") + public class ResponseAdapter implements ClientHttpResponse { + + private final Flux flux; + + private final HttpHeaders headers; + + public ResponseAdapter(Publisher body, + HttpHeaders headers) { + this.headers = headers; + if (body instanceof Flux) { + flux = (Flux) body; + } + else { + flux = ((Mono) body).flux(); + } + } + + @Override + public Flux getBody() { + return flux; + } + + @Override + public HttpHeaders getHeaders() { + return headers; + } + + @Override + public HttpStatus getStatusCode() { + return null; + } + + @Override + public int getRawStatusCode() { + return 0; + } + + @Override + public MultiValueMap getCookies() { + return null; + } + + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultClientResponse.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultClientResponse.java new file mode 100644 index 00000000..29819529 --- /dev/null +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultClientResponse.java @@ -0,0 +1,246 @@ +/* + * Copyright 2013-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.support; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalLong; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseCookie; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.reactive.ClientHttpResponse; +import org.springframework.http.codec.HttpMessageReader; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.BodyExtractor; +import org.springframework.web.reactive.function.BodyExtractors; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.ExchangeStrategies; + +/** + * Default implementation of {@link ClientResponse}. + * + * @author Arjen Poutsma + * @author Brian Clozel + * @since 5.0 + * @deprecated Will be removed in future release. + */ +@Deprecated +public class DefaultClientResponse implements ClientResponse { + + private final ClientHttpResponse response; + + private final Headers headers; + + private final ExchangeStrategies strategies; + + public DefaultClientResponse(ClientHttpResponse response, + ExchangeStrategies strategies) { + this.response = response; + this.strategies = strategies; + this.headers = new DefaultHeaders(); + } + + @Override + public ExchangeStrategies strategies() { + return this.strategies; + } + + @Override + public HttpStatus statusCode() { + return this.response.getStatusCode(); + } + + @Override + public int rawStatusCode() { + return this.response.getRawStatusCode(); + } + + @Override + public Headers headers() { + return this.headers; + } + + @Override + public MultiValueMap cookies() { + return this.response.getCookies(); + } + + @Override + public T body(BodyExtractor extractor) { + return extractor.extract(this.response, new BodyExtractor.Context() { + @Override + public List> messageReaders() { + return strategies.messageReaders(); + } + + @Override + public Optional serverResponse() { + return Optional.empty(); + } + + @Override + public Map hints() { + return Collections.emptyMap(); + } + }); + } + + @Override + public Mono bodyToMono(Class elementClass) { + if (Void.class.isAssignableFrom(elementClass)) { + return consumeAndCancel(); + } + else { + return body(BodyExtractors.toMono(elementClass)); + } + } + + @SuppressWarnings("unchecked") + private Mono consumeAndCancel() { + return (Mono) this.response.getBody().map(buffer -> { + DataBufferUtils.release(buffer); + throw new ReadCancellationException(); + }).onErrorResume(ReadCancellationException.class, ex -> Mono.empty()).then(); + } + + @Override + public Mono bodyToMono(ParameterizedTypeReference typeReference) { + if (Void.class.isAssignableFrom(typeReference.getType().getClass())) { + return consumeAndCancel(); + } + else { + return body(BodyExtractors.toMono(typeReference)); + } + } + + @Override + public Flux bodyToFlux(Class elementClass) { + if (Void.class.isAssignableFrom(elementClass)) { + return Flux.from(consumeAndCancel()); + } + else { + return body(BodyExtractors.toFlux(elementClass)); + } + } + + @Override + public Flux bodyToFlux(ParameterizedTypeReference typeReference) { + if (Void.class.isAssignableFrom(typeReference.getType().getClass())) { + return Flux.from(consumeAndCancel()); + } + else { + return body(BodyExtractors.toFlux(typeReference)); + } + } + + @Override + public Mono> toEntity(Class bodyType) { + if (Void.class.isAssignableFrom(bodyType)) { + return toEntityInternal(consumeAndCancel()); + } + else { + return toEntityInternal(bodyToMono(bodyType)); + } + } + + @Override + public Mono> toEntity( + ParameterizedTypeReference typeReference) { + if (Void.class.isAssignableFrom(typeReference.getType().getClass())) { + return toEntityInternal(consumeAndCancel()); + } + else { + return toEntityInternal(bodyToMono(typeReference)); + } + } + + private Mono> toEntityInternal(Mono bodyMono) { + HttpHeaders headers = headers().asHttpHeaders(); + HttpStatus statusCode = statusCode(); + return bodyMono.map(body -> new ResponseEntity<>(body, headers, statusCode)) + .switchIfEmpty(Mono.defer( + () -> Mono.just(new ResponseEntity<>(headers, statusCode)))); + } + + @Override + public Mono>> toEntityList(Class responseType) { + return toEntityListInternal(bodyToFlux(responseType)); + } + + @Override + public Mono>> toEntityList( + ParameterizedTypeReference typeReference) { + return toEntityListInternal(bodyToFlux(typeReference)); + } + + private Mono>> toEntityListInternal(Flux bodyFlux) { + HttpHeaders headers = headers().asHttpHeaders(); + HttpStatus statusCode = statusCode(); + return bodyFlux.collectList() + .map(body -> new ResponseEntity<>(body, headers, statusCode)); + } + + @SuppressWarnings("serial") + private static class ReadCancellationException extends RuntimeException { + + } + + private class DefaultHeaders implements Headers { + + private HttpHeaders delegate() { + return response.getHeaders(); + } + + @Override + public OptionalLong contentLength() { + return toOptionalLong(delegate().getContentLength()); + } + + @Override + public Optional contentType() { + return Optional.ofNullable(delegate().getContentType()); + } + + @Override + public List header(String headerName) { + List headerValues = delegate().get(headerName); + return (headerValues != null ? headerValues : Collections.emptyList()); + } + + @Override + public HttpHeaders asHttpHeaders() { + return HttpHeaders.readOnlyHttpHeaders(delegate()); + } + + private OptionalLong toOptionalLong(long value) { + return (value != -1 ? OptionalLong.of(value) : OptionalLong.empty()); + } + + } + +} diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerRequest.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerRequest.java new file mode 100644 index 00000000..22bd0c3d --- /dev/null +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerRequest.java @@ -0,0 +1,311 @@ +/* + * Copyright 2013-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.support; + +import java.net.InetSocketAddress; +import java.net.URI; +import java.nio.charset.Charset; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalLong; +import java.util.function.Function; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpCookie; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpRange; +import org.springframework.http.HttpRequest; +import org.springframework.http.MediaType; +import org.springframework.http.codec.HttpMessageReader; +import org.springframework.http.codec.multipart.Part; +import org.springframework.http.server.PathContainer; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.BodyExtractor; +import org.springframework.web.reactive.function.BodyExtractors; +import org.springframework.web.reactive.function.UnsupportedMediaTypeException; +import org.springframework.web.reactive.function.server.HandlerStrategies; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.UnsupportedMediaTypeStatusException; +import org.springframework.web.server.WebSession; +import org.springframework.web.util.UriBuilder; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * {@code ServerRequest} implementation based on a {@link ServerWebExchange}. + * + * @author Arjen Poutsma + * @since 5.0 + * @deprecated Will be removed in a future release. + */ +@Deprecated +public class DefaultServerRequest implements ServerRequest { + + private static final Function ERROR_MAPPER = ex -> (ex + .getContentType() != null + ? new UnsupportedMediaTypeStatusException(ex.getContentType(), + ex.getSupportedMediaTypes()) + : new UnsupportedMediaTypeStatusException(ex.getMessage())); + + private final ServerWebExchange exchange; + + private final Headers headers; + + private final List> messageReaders; + + public DefaultServerRequest(ServerWebExchange exchange) { + this(exchange, HandlerStrategies.withDefaults().messageReaders()); + } + + public DefaultServerRequest(ServerWebExchange exchange, + List> messageReaders) { + this.exchange = exchange; + this.messageReaders = Collections + .unmodifiableList(new ArrayList<>(messageReaders)); + this.headers = new DefaultHeaders(); + } + + @Override + public String methodName() { + return request().getMethodValue(); + } + + @Override + public URI uri() { + return request().getURI(); + } + + @Override + public UriBuilder uriBuilder() { + return UriComponentsBuilder.fromHttpRequest(new ServerRequestAdapter()); + } + + @Override + public PathContainer pathContainer() { + return request().getPath(); + } + + @Override + public Headers headers() { + return this.headers; + } + + @Override + public MultiValueMap cookies() { + return request().getCookies(); + } + + @Override + public T body(BodyExtractor extractor) { + return body(extractor, Collections.emptyMap()); + } + + @Override + public Optional remoteAddress() { + return Optional.of(request().getRemoteAddress()); + } + + @Override + public List> messageReaders() { + return this.messageReaders; + } + + @Override + public T body(BodyExtractor extractor, + Map hints) { + return extractor.extract(request(), new BodyExtractor.Context() { + @Override + public List> messageReaders() { + return messageReaders; + } + + @Override + public Optional serverResponse() { + return Optional.of(exchange().getResponse()); + } + + @Override + public Map hints() { + return hints; + } + }); + } + + @Override + public Mono bodyToMono(Class elementClass) { + Mono mono = body(BodyExtractors.toMono(elementClass)); + return mono.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER); + } + + @Override + public Mono bodyToMono(ParameterizedTypeReference typeReference) { + Mono mono = body(BodyExtractors.toMono(typeReference)); + return mono.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER); + } + + @Override + public Flux bodyToFlux(Class elementClass) { + Flux flux = body(BodyExtractors.toFlux(elementClass)); + return flux.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER); + } + + @Override + public Flux bodyToFlux(ParameterizedTypeReference typeReference) { + Flux flux = body(BodyExtractors.toFlux(typeReference)); + return flux.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER); + } + + @Override + public Map attributes() { + return this.exchange.getAttributes(); + } + + @Override + public MultiValueMap queryParams() { + return request().getQueryParams(); + } + + @Override + public Map pathVariables() { + return this.exchange.getAttributeOrDefault( + RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE, Collections.emptyMap()); + } + + @Override + public Mono session() { + return this.exchange.getSession(); + } + + @Override + public Mono principal() { + return this.exchange.getPrincipal(); + } + + @Override + public Mono> formData() { + return this.exchange.getFormData(); + } + + @Override + public Mono> multipartData() { + return this.exchange.getMultipartData(); + } + + private ServerHttpRequest request() { + return this.exchange.getRequest(); + } + + public ServerWebExchange exchange() { + return this.exchange; + } + + @Override + public String toString() { + return String.format("%s %s", method(), path()); + } + + private class DefaultHeaders implements Headers { + + private HttpHeaders delegate() { + return request().getHeaders(); + } + + @Override + public List accept() { + return delegate().getAccept(); + } + + @Override + public List acceptCharset() { + return delegate().getAcceptCharset(); + } + + @Override + public List acceptLanguage() { + return delegate().getAcceptLanguage(); + } + + @Override + public OptionalLong contentLength() { + long value = delegate().getContentLength(); + return (value != -1 ? OptionalLong.of(value) : OptionalLong.empty()); + } + + @Override + public Optional contentType() { + return Optional.ofNullable(delegate().getContentType()); + } + + @Override + public InetSocketAddress host() { + return delegate().getHost(); + } + + @Override + public List range() { + return delegate().getRange(); + } + + @Override + public List header(String headerName) { + List headerValues = delegate().get(headerName); + return (headerValues != null ? headerValues : Collections.emptyList()); + } + + @Override + public HttpHeaders asHttpHeaders() { + return HttpHeaders.readOnlyHttpHeaders(delegate()); + } + + @Override + public String toString() { + return delegate().toString(); + } + + } + + private final class ServerRequestAdapter implements HttpRequest { + + @Override + public String getMethodValue() { + return methodName(); + } + + @Override + public URI getURI() { + return uri(); + } + + @Override + public HttpHeaders getHeaders() { + return request().getHeaders(); + } + + } + +} diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerResponse.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerResponse.java new file mode 100644 index 00000000..84ea7382 --- /dev/null +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerResponse.java @@ -0,0 +1,121 @@ +/* + * Copyright 2013-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.support; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import reactor.core.publisher.Mono; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; +import org.springframework.http.codec.HttpMessageWriter; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.Assert; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.BodyInserter; +import org.springframework.web.reactive.function.server.HandlerStrategies; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.reactive.result.view.ViewResolver; +import org.springframework.web.server.ServerWebExchange; + +@Deprecated +public class DefaultServerResponse implements ServerResponse { + + private final ServerWebExchange exchange; + + private final BodyInserter inserter; + + private final Map hints; + + public DefaultServerResponse(ServerWebExchange exchange, + BodyInserter body, Map hints) { + this.exchange = exchange; + Assert.notNull(exchange, "ServerWebExchange must not be null"); + Assert.notNull(body, "BodyInserter must not be null"); + this.inserter = body; + this.hints = hints; + } + + private ServerHttpResponse response() { + return exchange.getResponse(); + } + + @Override + public final HttpStatus statusCode() { + // TODO: non standard status code + return HttpStatus.valueOf(response().getStatusCode().value()); + } + + @Override + public final HttpHeaders headers() { + return response().getHeaders(); + } + + @Override + public MultiValueMap cookies() { + return response().getCookies(); + } + + @Override + public final Mono writeTo(ServerWebExchange exchange, Context context) { + return this.inserter.insert(exchange.getResponse(), new BodyInserter.Context() { + @Override + public List> messageWriters() { + return context.messageWriters(); + } + + @Override + public Optional serverRequest() { + return Optional.of(exchange.getRequest()); + } + + @Override + public Map hints() { + return hints; + } + }); + } + + public static class HandlerStrategiesResponseContext + implements ServerResponse.Context { + + private HandlerStrategies strategies = HandlerStrategies.withDefaults(); + + public HandlerStrategiesResponseContext() { + } + + public HandlerStrategiesResponseContext(HandlerStrategies strategies) { + this.strategies = strategies; + } + + @Override + public List> messageWriters() { + return this.strategies.messageWriters(); + } + + @Override + public List viewResolvers() { + return this.strategies.viewResolvers(); + } + + } + +}