GH-927 Add ability to configure ignired and requestOnly http headers
Resolves #927
This commit is contained in:
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.cloud.function.web;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.function.context.FunctionProperties;
|
||||
|
||||
@@ -49,6 +52,17 @@ public class FunctionHttpProperties {
|
||||
*/
|
||||
public String delete;
|
||||
|
||||
|
||||
/**
|
||||
* List of headers to be ignored when generating HttpHeaders (request or response).
|
||||
*/
|
||||
public List<String> ignoredHeaders = Collections.emptyList();
|
||||
|
||||
/**
|
||||
* List of headers that must remain only in the request.
|
||||
*/
|
||||
public List<String> requestOnlyHeaders = Collections.emptyList();
|
||||
|
||||
public String getGet() {
|
||||
return this.get;
|
||||
}
|
||||
@@ -80,4 +94,20 @@ public class FunctionHttpProperties {
|
||||
public void setDelete(String delete) {
|
||||
this.delete = delete;
|
||||
}
|
||||
|
||||
public List<String> getIgnoredHeaders() {
|
||||
return ignoredHeaders;
|
||||
}
|
||||
|
||||
public void setIgnoredHeaders(List<String> ignoredHeaders) {
|
||||
this.ignoredHeaders = ignoredHeaders;
|
||||
}
|
||||
|
||||
public List<String> getRequestOnlyHeaders() {
|
||||
return requestOnlyHeaders;
|
||||
}
|
||||
|
||||
public void setRequestOnlyHeaders(List<String> requestOnlyHeaders) {
|
||||
this.requestOnlyHeaders = requestOnlyHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public class FunctionController {
|
||||
if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) {
|
||||
return request.getFormData().doOnSuccess(params -> wrapper.getParams().addAll(params))
|
||||
.then(Mono.defer(() -> (Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper
|
||||
.processRequest(wrapper, wrapper.getParams(), false)));
|
||||
.processRequest(wrapper, wrapper.getParams(), false, functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders())));
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition()));
|
||||
@@ -82,7 +82,8 @@ public class FunctionController {
|
||||
return request.getMultipartData()
|
||||
.doOnSuccess(params -> wrapper.getParams().addAll(multi(params)))
|
||||
.then(Mono.defer(() -> (Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper
|
||||
.processRequest(wrapper, wrapper.getParams(), false)));
|
||||
.processRequest(wrapper, wrapper.getParams(), false,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders())));
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition()));
|
||||
@@ -96,7 +97,8 @@ public class FunctionController {
|
||||
@RequestBody(required = false) String body) {
|
||||
FunctionWrapper wrapper = wrapper(request);
|
||||
if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) {
|
||||
return (Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false);
|
||||
return (Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition()));
|
||||
@@ -110,7 +112,8 @@ public class FunctionController {
|
||||
@RequestBody(required = false) String body) {
|
||||
FunctionWrapper wrapper = wrapper(request);
|
||||
if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("PUT", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) {
|
||||
return (Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false);
|
||||
return (Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("PUT", wrapper.getFunction().getFunctionDefinition()));
|
||||
@@ -124,7 +127,8 @@ public class FunctionController {
|
||||
@RequestBody(required = false) String body) {
|
||||
FunctionWrapper wrapper = wrapper(request);
|
||||
if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("DELETE", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) {
|
||||
return (Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false);
|
||||
return (Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("DELETE", wrapper.getFunction().getFunctionDefinition()));
|
||||
@@ -136,7 +140,8 @@ public class FunctionController {
|
||||
public Publisher<?> postStream(ServerWebExchange request, @RequestBody(required = false) Flux<String> body) {
|
||||
FunctionWrapper wrapper = wrapper(request);
|
||||
if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) {
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, body, true);
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, body, true,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition()));
|
||||
@@ -149,7 +154,8 @@ public class FunctionController {
|
||||
public Publisher<?> getStream(ServerWebExchange request) {
|
||||
FunctionWrapper wrapper = wrapper(request);
|
||||
if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) {
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), true);
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), true,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", wrapper.getFunction().getFunctionDefinition()));
|
||||
@@ -162,7 +168,8 @@ public class FunctionController {
|
||||
public Mono<ResponseEntity<?>> get(ServerWebExchange request) {
|
||||
FunctionWrapper wrapper = wrapper(request);
|
||||
if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) {
|
||||
return (Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), false);
|
||||
return (Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), false,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", wrapper.getFunction().getFunctionDefinition()));
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.cloud.function.context.FunctionalSpringApplication;
|
||||
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
|
||||
import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper;
|
||||
import org.springframework.cloud.function.context.config.ContextFunctionCatalogInitializer;
|
||||
import org.springframework.cloud.function.web.FunctionHttpProperties;
|
||||
import org.springframework.cloud.function.web.constants.WebRequestConstants;
|
||||
import org.springframework.cloud.function.web.util.FunctionWebRequestProcessingHelper;
|
||||
import org.springframework.cloud.function.web.util.FunctionWrapper;
|
||||
@@ -105,9 +106,10 @@ public class FunctionEndpointInitializer implements ApplicationContextInitialize
|
||||
}
|
||||
|
||||
private void registerEndpoint(GenericApplicationContext context) {
|
||||
context.registerBean(FunctionHttpProperties.class, () -> new FunctionHttpProperties());
|
||||
context.registerBean(FunctionEndpointFactory.class,
|
||||
() -> new FunctionEndpointFactory(context.getBean(FunctionProperties.class), context.getBean(FunctionCatalog.class),
|
||||
context.getEnvironment()));
|
||||
context.getEnvironment(), context.getBean(FunctionHttpProperties.class)));
|
||||
RouterFunctionRegister.register(context);
|
||||
}
|
||||
|
||||
@@ -208,7 +210,9 @@ class FunctionEndpointFactory {
|
||||
|
||||
private final FunctionProperties functionProperties;
|
||||
|
||||
FunctionEndpointFactory(FunctionProperties functionProperties, FunctionCatalog functionCatalog, Environment environment) {
|
||||
private final FunctionHttpProperties functionHttpProperties;
|
||||
|
||||
FunctionEndpointFactory(FunctionProperties functionProperties, FunctionCatalog functionCatalog, Environment environment, FunctionHttpProperties functionHttpProperties) {
|
||||
String handler = environment.resolvePlaceholders("${function.handler}");
|
||||
if (handler.startsWith("$")) {
|
||||
handler = null;
|
||||
@@ -216,6 +220,7 @@ class FunctionEndpointFactory {
|
||||
this.functionCatalog = functionCatalog;
|
||||
this.handler = handler;
|
||||
this.functionProperties = functionProperties;
|
||||
this.functionHttpProperties = functionHttpProperties;
|
||||
}
|
||||
|
||||
private FunctionInvocationWrapper extract(ServerRequest request) {
|
||||
@@ -241,7 +246,8 @@ class FunctionEndpointFactory {
|
||||
: FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(funcWrapper.getOutputType()));
|
||||
FunctionWrapper wrapper = new FunctionWrapper(funcWrapper);
|
||||
Mono<ResponseEntity<?>> stream = request.bodyToMono(String.class)
|
||||
.flatMap(content -> (Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper.processRequest(wrapper, content, false));
|
||||
.flatMap(content -> (Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper.processRequest(wrapper, content, false,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()));
|
||||
|
||||
return stream.flatMap(entity -> {
|
||||
BodyBuilder builder = status(entity.getStatusCode()).headers(headers -> headers.addAll(entity.getHeaders()));
|
||||
|
||||
@@ -95,7 +95,8 @@ public class FunctionController {
|
||||
return Mono.from(result).flatMap(body -> Mono.just(builder.body(body)));
|
||||
}
|
||||
}
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getParams(), false);
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getParams(), false,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition()));
|
||||
@@ -110,7 +111,8 @@ public class FunctionController {
|
||||
String argument = StringUtils.hasText(body) ? body : "";
|
||||
FunctionWrapper wrapper = wrapper(request);
|
||||
if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) {
|
||||
return ((Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper.processRequest(wrapper, argument, true)).map(response -> ResponseEntity.ok()
|
||||
return ((Mono<ResponseEntity<?>>) FunctionWebRequestProcessingHelper.processRequest(wrapper, argument, true,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders())).map(response -> ResponseEntity.ok()
|
||||
.headers(response.getHeaders()).body((Publisher<?>) response.getBody()));
|
||||
}
|
||||
else {
|
||||
@@ -123,7 +125,8 @@ public class FunctionController {
|
||||
public Publisher<?> getStream(WebRequest request) {
|
||||
FunctionWrapper wrapper = wrapper(request);
|
||||
if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) {
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), true);
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), true,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", wrapper.getFunction().getFunctionDefinition()));
|
||||
@@ -136,7 +139,8 @@ public class FunctionController {
|
||||
FunctionWrapper wrapper = wrapper(request);
|
||||
if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) {
|
||||
Assert.isTrue(!wrapper.getFunction().isSupplier(), "'POST' can only be mapped to Function or Consumer");
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false);
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition()));
|
||||
@@ -148,7 +152,8 @@ public class FunctionController {
|
||||
public Object put(WebRequest request, @RequestBody(required = false) String body) {
|
||||
FunctionWrapper wrapper = wrapper(request);
|
||||
if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("PUT", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) {
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false);
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("PUT", wrapper.getFunction().getFunctionDefinition()));
|
||||
@@ -161,7 +166,8 @@ public class FunctionController {
|
||||
FunctionWrapper wrapper = wrapper(request);
|
||||
if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("DELETE", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) {
|
||||
Assert.isTrue(wrapper.getFunction().isConsumer(), "'DELETE' can only be mapped to Consumer");
|
||||
FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), false);
|
||||
FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), false,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("DELETE", wrapper.getFunction().getFunctionDefinition()));
|
||||
@@ -173,7 +179,8 @@ public class FunctionController {
|
||||
public Object get(WebRequest request) {
|
||||
FunctionWrapper wrapper = wrapper(request);
|
||||
if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) {
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), false);
|
||||
return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), false,
|
||||
functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", wrapper.getFunction().getFunctionDefinition()));
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.function.Supplier;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
@@ -31,11 +30,11 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
|
||||
import org.springframework.cloud.function.web.FunctionHttpProperties;
|
||||
import org.springframework.cloud.function.web.source.FunctionExporterAutoConfiguration.SourceActiveCondition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
@@ -46,14 +45,16 @@ import org.springframework.web.reactive.function.client.WebClient;
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(WebClient.class)
|
||||
@Conditional(SourceActiveCondition.class)
|
||||
@EnableConfigurationProperties(ExporterProperties.class)
|
||||
@EnableConfigurationProperties({ExporterProperties.class, FunctionHttpProperties.class})
|
||||
public class FunctionExporterAutoConfiguration {
|
||||
|
||||
private ExporterProperties props;
|
||||
private final ExporterProperties props;
|
||||
|
||||
@Autowired
|
||||
FunctionExporterAutoConfiguration(ExporterProperties props) {
|
||||
private final FunctionHttpProperties httpProps;
|
||||
|
||||
FunctionExporterAutoConfiguration(ExporterProperties props, FunctionHttpProperties httpProps) {
|
||||
this.props = props;
|
||||
this.httpProps = httpProps;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -66,14 +67,8 @@ public class FunctionExporterAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "spring.cloud.function.web.export.source", name = "url")
|
||||
public FunctionRegistration<Supplier<Flux<?>>> origin(WebClient.Builder builder) {
|
||||
HttpSupplier supplier = new HttpSupplier(builder.build(), this.props);
|
||||
HttpSupplier supplier = new HttpSupplier(builder.build(), this.props, this.httpProps);
|
||||
FunctionRegistration<Supplier<Flux<?>>> registration = new FunctionRegistration<>(supplier);
|
||||
Type rawType = ResolvableType.forClassWithGenerics(Supplier.class, this.props.getSource().getType()).getType();
|
||||
// FunctionType functionType = FunctionType.supplier(this.props.getSource().getType()).wrap(Flux.class);
|
||||
// FunctionType type = FunctionType.of(rawType);
|
||||
// if (this.props.getSource().isIncludeHeaders()) {
|
||||
//// type = type.message();
|
||||
// }
|
||||
Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(HttpSupplier.class);
|
||||
registration = registration.type(type);
|
||||
return registration;
|
||||
@@ -81,7 +76,7 @@ public class FunctionExporterAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public RequestBuilder simpleRequestBuilder(Environment environment) {
|
||||
SimpleRequestBuilder builder = new SimpleRequestBuilder(environment);
|
||||
SimpleRequestBuilder builder = new SimpleRequestBuilder(environment, httpProps);
|
||||
if (this.props.getSink().getUrl() != null) {
|
||||
builder.setTemplateUrl(this.props.getSink().getUrl());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2023 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.
|
||||
@@ -23,6 +23,7 @@ import org.springframework.boot.web.reactive.context.ReactiveWebApplicationConte
|
||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||
import org.springframework.cloud.function.context.config.ContextFunctionCatalogInitializer;
|
||||
import org.springframework.cloud.function.web.FunctionHttpProperties;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -33,6 +34,7 @@ import org.springframework.web.reactive.function.client.WebClient.Builder;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Oleg Zhurakousky
|
||||
* @since 2.0
|
||||
*
|
||||
*/
|
||||
@@ -82,7 +84,7 @@ class FunctionExporterInitializer implements ApplicationContextInitializer<Gener
|
||||
private void registerExport(GenericApplicationContext context) {
|
||||
context.registerBean(ExporterProperties.class, () -> new ExporterProperties());
|
||||
context.registerBean(FunctionExporterAutoConfiguration.class,
|
||||
() -> new FunctionExporterAutoConfiguration(context.getBean(ExporterProperties.class)));
|
||||
() -> new FunctionExporterAutoConfiguration(context.getBean(ExporterProperties.class), context.getBean(FunctionHttpProperties.class)));
|
||||
if (context.getBeanFactory().getBeanNamesForType(DestinationResolver.class, false, false).length == 0) {
|
||||
context.registerBean(DestinationResolver.class,
|
||||
() -> context.getBean(FunctionExporterAutoConfiguration.class).simpleDestinationResolver());
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.function.web.FunctionHttpProperties;
|
||||
import org.springframework.cloud.function.web.util.HeaderUtils;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
@@ -44,15 +45,18 @@ public class HttpSupplier implements Supplier<Flux<?>> {
|
||||
|
||||
private WebClient client;
|
||||
|
||||
private ExporterProperties props;
|
||||
private final ExporterProperties props;
|
||||
|
||||
private final FunctionHttpProperties httpProperties;
|
||||
|
||||
/**
|
||||
* @param client the WebClient to use. The baseUrl should be set.
|
||||
* @param props the ExporterProperties to use to parameterize the requests.
|
||||
*/
|
||||
public HttpSupplier(WebClient client, ExporterProperties props) {
|
||||
public HttpSupplier(WebClient client, ExporterProperties props, FunctionHttpProperties httpProperties) {
|
||||
this.client = client;
|
||||
this.props = props;
|
||||
this.httpProperties = httpProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,7 +91,7 @@ public class HttpSupplier implements Supplier<Flux<?>> {
|
||||
}
|
||||
return MessageBuilder.withPayload(payload)
|
||||
.copyHeaders(HeaderUtils.fromHttp(
|
||||
HeaderUtils.sanitize(response.headers().asHttpHeaders())))
|
||||
HeaderUtils.sanitize(response.headers().asHttpHeaders(), this.httpProperties.getIgnoredHeaders(), this.httpProperties.getRequestOnlyHeaders())))
|
||||
.setHeader("scf-sink-url", this.props.getSink().getUrl())
|
||||
.setHeader("scf-func-name", this.props.getSink().getName())
|
||||
.build();
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.function.web.FunctionHttpProperties;
|
||||
import org.springframework.cloud.function.web.util.HeaderUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -30,6 +31,7 @@ import org.springframework.messaging.MessageHeaders;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Oleg Zhurkousky
|
||||
*
|
||||
*/
|
||||
class SimpleRequestBuilder implements RequestBuilder {
|
||||
@@ -38,10 +40,13 @@ class SimpleRequestBuilder implements RequestBuilder {
|
||||
|
||||
private Map<String, String> headers = new LinkedHashMap<>();
|
||||
|
||||
private Environment environment;
|
||||
private final Environment environment;
|
||||
|
||||
SimpleRequestBuilder(Environment environment) {
|
||||
private final FunctionHttpProperties httpProperties;
|
||||
|
||||
SimpleRequestBuilder(Environment environment, FunctionHttpProperties httpProperties) {
|
||||
this.environment = environment;
|
||||
this.httpProperties = httpProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,7 +56,7 @@ class SimpleRequestBuilder implements RequestBuilder {
|
||||
Message<?> message = (Message<?>) value;
|
||||
incoming = message.getHeaders();
|
||||
}
|
||||
HttpHeaders result = HeaderUtils.fromMessage(incoming);
|
||||
HttpHeaders result = HeaderUtils.fromMessage(incoming, this.httpProperties.getIgnoredHeaders());
|
||||
for (String key : this.headers.keySet()) {
|
||||
String header = this.headers.get(key);
|
||||
header = header.replace("${destination}", destination);
|
||||
|
||||
@@ -104,7 +104,7 @@ public final class FunctionWebRequestProcessingHelper {
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public static Publisher<?> processRequest(FunctionWrapper wrapper, Object argument, boolean eventStream) {
|
||||
public static Publisher<?> processRequest(FunctionWrapper wrapper, Object argument, boolean eventStream, List<String> ignoredHeaders, List<String> requestOnlyHeaders) {
|
||||
if (argument == null) {
|
||||
argument = "";
|
||||
}
|
||||
@@ -135,10 +135,10 @@ public final class FunctionWebRequestProcessingHelper {
|
||||
Mono.from((Publisher) result).subscribe();
|
||||
}
|
||||
return "DELETE".equals(wrapper.getMethod()) ?
|
||||
Mono.empty() : Mono.just(ResponseEntity.accepted().headers(HeaderUtils.sanitize(headers)).build());
|
||||
Mono.empty() : Mono.just(ResponseEntity.accepted().headers(HeaderUtils.sanitize(headers, ignoredHeaders, requestOnlyHeaders)).build());
|
||||
}
|
||||
|
||||
BodyBuilder responseOkBuilder = ResponseEntity.ok().headers(HeaderUtils.sanitize(headers));
|
||||
BodyBuilder responseOkBuilder = ResponseEntity.ok().headers(HeaderUtils.sanitize(headers, ignoredHeaders, requestOnlyHeaders));
|
||||
|
||||
Publisher pResult;
|
||||
if (result instanceof Publisher) {
|
||||
@@ -161,12 +161,12 @@ public final class FunctionWebRequestProcessingHelper {
|
||||
return Mono.from(pResult).map(v -> {
|
||||
if (v instanceof Iterable i) {
|
||||
List aggregatedResult = (List) StreamSupport.stream(i.spliterator(), false).map(m -> {
|
||||
return m instanceof Message ? processMessage(responseOkBuilder, (Message<?>) m) : m;
|
||||
return m instanceof Message ? processMessage(responseOkBuilder, (Message<?>) m, ignoredHeaders) : m;
|
||||
}).collect(Collectors.toList());
|
||||
return responseOkBuilder.header("content-type", "application/json").body(aggregatedResult);
|
||||
}
|
||||
else if (v instanceof Message) {
|
||||
return responseOkBuilder.body(processMessage(responseOkBuilder, (Message<?>) v));
|
||||
return responseOkBuilder.body(processMessage(responseOkBuilder, (Message<?>) v, ignoredHeaders));
|
||||
}
|
||||
else {
|
||||
return responseOkBuilder.body(v);
|
||||
@@ -174,8 +174,8 @@ public final class FunctionWebRequestProcessingHelper {
|
||||
});
|
||||
}
|
||||
|
||||
private static Object processMessage(BodyBuilder responseOkBuilder, Message<?> message) {
|
||||
responseOkBuilder.headers(HeaderUtils.fromMessage(message.getHeaders()));
|
||||
private static Object processMessage(BodyBuilder responseOkBuilder, Message<?> message, List<String> ignoredHeaders) {
|
||||
responseOkBuilder.headers(HeaderUtils.fromMessage(message.getHeaders(), ignoredHeaders));
|
||||
return message.getPayload();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.cloud.function.web.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -54,12 +55,12 @@ public final class HeaderUtils {
|
||||
throw new IllegalStateException("Can't instantiate a utility class");
|
||||
}
|
||||
|
||||
public static HttpHeaders fromMessage(MessageHeaders headers) {
|
||||
public static HttpHeaders fromMessage(MessageHeaders headers, List<String> ignoredHeders) {
|
||||
HttpHeaders result = new HttpHeaders();
|
||||
for (String name : headers.keySet()) {
|
||||
Object value = headers.get(name);
|
||||
name = name.toLowerCase();
|
||||
if (!IGNORED.containsKey(name)) {
|
||||
if (!IGNORED.containsKey(name) && !ignoredHeders.contains(name)) {
|
||||
Collection<?> values = multi(value);
|
||||
for (Object object : values) {
|
||||
result.set(name, object.toString());
|
||||
@@ -69,18 +70,29 @@ public final class HeaderUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static HttpHeaders sanitize(HttpHeaders request) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static HttpHeaders fromMessage(MessageHeaders headers) {
|
||||
return fromMessage(headers, Collections.EMPTY_LIST);
|
||||
}
|
||||
|
||||
|
||||
public static HttpHeaders sanitize(HttpHeaders request, List<String> ignoredHeders, List<String> requestOnlyHeaders) {
|
||||
HttpHeaders result = new HttpHeaders();
|
||||
for (String name : request.keySet()) {
|
||||
List<String> value = request.get(name);
|
||||
name = name.toLowerCase();
|
||||
if (!IGNORED.containsKey(name) && !REQUEST_ONLY.containsKey(name)) {
|
||||
if (!IGNORED.containsKey(name) && !REQUEST_ONLY.containsKey(name) && !ignoredHeders.contains(name) && !requestOnlyHeaders.contains(name)) {
|
||||
result.put(name, value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static HttpHeaders sanitize(HttpHeaders request) {
|
||||
return sanitize(request, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
|
||||
}
|
||||
|
||||
public static MessageHeaders fromHttp(HttpHeaders headers) {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
for (String name : headers.keySet()) {
|
||||
|
||||
@@ -54,7 +54,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
|
||||
"spring.main.web-application-type=servlet",
|
||||
"spring.cloud.function.web.path=/functions",
|
||||
"spring.cloud.function.routing.enabled=true"})
|
||||
"spring.cloud.function.routing.enabled=true",
|
||||
"spring.cloud.function.http.ignored-headers=abc,xyz"})
|
||||
@ContextConfiguration(classes = { RestApplication.class, TestConfiguration.class })
|
||||
public class RoutingFunctionTests {
|
||||
|
||||
@@ -73,11 +74,16 @@ public class RoutingFunctionTests {
|
||||
.exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME))
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.header("spring.cloud.function.definition", "employee")
|
||||
.header("abc", "abc")
|
||||
.header("xyz", "xyz")
|
||||
.body("{\"name\":\"Bob\",\"age\":25}"), String.class);
|
||||
assertThat(postForEntity.getBody()).isEqualTo("{\"name\":\"Bob\",\"age\":25}");
|
||||
assertThat(postForEntity.getHeaders().containsKey("x-content-type")).isTrue();
|
||||
assertThat(postForEntity.getHeaders().get("x-content-type").get(0))
|
||||
.isEqualTo("application/xml");
|
||||
assertThat(postForEntity.getHeaders().containsKey("spring.cloud.function.definition")).isTrue();
|
||||
assertThat(postForEntity.getHeaders().containsKey("abc")).isFalse();
|
||||
assertThat(postForEntity.getHeaders().containsKey("xyz")).isFalse();
|
||||
assertThat(postForEntity.getHeaders().get("foo").get(0)).isEqualTo("bar");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user