From aa90d256eccf662537db57ffb7ec117673b0100c Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Tue, 23 May 2023 12:01:58 +0200 Subject: [PATCH] Add Content-Type header to AWScJson response and writer for servlet response --- .../adapter/aws/web/WebProxyInvoker.java | 7 ++- .../web/ProxyHttpServletResponse.java | 55 ++++++++++++++++++- .../function/serverless/web/ProxyMvc.java | 21 ++++--- 3 files changed, 70 insertions(+), 13 deletions(-) diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/src/main/java/org/springframework/cloud/function/adapter/aws/web/WebProxyInvoker.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/src/main/java/org/springframework/cloud/function/adapter/aws/web/WebProxyInvoker.java index 39bb2f7b4..5b0a2330a 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/src/main/java/org/springframework/cloud/function/adapter/aws/web/WebProxyInvoker.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/src/main/java/org/springframework/cloud/function/adapter/aws/web/WebProxyInvoker.java @@ -19,6 +19,7 @@ package org.springframework.cloud.function.adapter.aws.web; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -77,7 +78,8 @@ public class WebProxyInvoker { logger.debug("httpMethod: " + httpMethod); logger.debug("path: " + path); } - ProxyHttpServletRequest httpRequest = new ProxyHttpServletRequest(null, httpMethod, path); + + ProxyHttpServletRequest httpRequest = new ProxyHttpServletRequest(this.mvc.getServletContext(), httpMethod, path); // CONTENT if (StringUtils.hasText((String) request.get("body"))) { @@ -126,13 +128,14 @@ public class WebProxyInvoker { apiGatewayResponseStructure.put("isBase64Encoded", false); apiGatewayResponseStructure.put("statusCode", HttpStatus.OK.value()); apiGatewayResponseStructure.put("body", responseString); - Map> multiValueHeaders = new HashMap<>(); Map headers = new HashMap<>(); for (String headerName : httpResponse.getHeaderNames()) { multiValueHeaders.put(headerName, httpResponse.getHeaders(headerName)); headers.put(headerName, httpResponse.getHeaders(headerName).toString()); } + headers.put(HttpHeaders.CONTENT_TYPE, httpResponse.getContentType()); + multiValueHeaders.put(HttpHeaders.CONTENT_TYPE, Collections.singletonList(httpResponse.getContentType())); apiGatewayResponseStructure.put("multiValueHeaders", multiValueHeaders); apiGatewayResponseStructure.put("headers", headers); diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletResponse.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletResponse.java index fdf799e32..841293800 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletResponse.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletResponse.java @@ -18,8 +18,10 @@ package org.springframework.cloud.function.serverless.web; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; +import java.io.Writer; import java.nio.charset.Charset; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -69,6 +71,8 @@ public class ProxyHttpServletResponse implements HttpServletResponse { private int status = HttpServletResponse.SC_OK; + private ResponsePrintWriter writer; + @Nullable private String errorMessage; @@ -89,7 +93,11 @@ public class ProxyHttpServletResponse implements HttpServletResponse { @Override public PrintWriter getWriter() throws UnsupportedEncodingException { - throw new UnsupportedOperationException(); + if (this.writer == null) { + Writer targetWriter = new OutputStreamWriter(this.content, getCharacterEncoding()); + this.writer = new ResponsePrintWriter(targetWriter); + } + return this.writer; } public byte[] getContentAsByteArray() { @@ -163,7 +171,7 @@ public class ProxyHttpServletResponse implements HttpServletResponse { @Override public boolean isCommitted() { - return false; + return this.writer == null ? false : this.writer.commited; } @Override @@ -425,4 +433,47 @@ public class ProxyHttpServletResponse implements HttpServletResponse { } } + private class ResponsePrintWriter extends PrintWriter { + + private boolean commited; + + ResponsePrintWriter(Writer out) { + super(out, true); + } + + @Override + public void write(char[] buf, int off, int len) { + super.write(buf, off, len); + super.flush(); + this.commited = true; + } + + @Override + public void write(String s, int off, int len) { + super.write(s, off, len); + super.flush(); + this.commited = true; + } + + @Override + public void write(int c) { + super.write(c); + super.flush(); + this.commited = true; + } + + @Override + public void flush() { + super.flush(); + this.commited = true; + } + + @Override + public void close() { + super.flush(); + super.close(); + this.commited = true; + } + } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java index 3762f0dcf..60cf1b19e 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java @@ -41,14 +41,13 @@ import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebApplicationContext; import org.springframework.http.HttpStatus; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.context.ConfigurableWebApplicationContext; -import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; /** @@ -85,11 +84,8 @@ public class ProxyMvc { } public static ProxyMvc INSTANCE(Class... componentClasses) { - GenericWebApplicationContext applpicationContext = new GenericWebApplicationContext(); - AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(applpicationContext); - if (!ObjectUtils.isEmpty(componentClasses)) { - reader.register(componentClasses); - } + AnnotationConfigServletWebApplicationContext applpicationContext = new AnnotationConfigServletWebApplicationContext(); + applpicationContext.scan(componentClasses[0].getPackageName()); return INSTANCE(applpicationContext); } @@ -108,10 +104,17 @@ public class ProxyMvc { reg.setLoadOnStartup(1); this.servletContext = applicationContext.getServletContext(); try { + this.dispatcher.init(new ProxyServletConfig(this.servletContext)); + try { + this.service(new ProxyHttpServletRequest(servletContext, "INFO", "/"), new ProxyHttpServletResponse()); + } + catch (Exception e) { + //ignore as this is just a pre-warming attempt + } } catch (Exception e) { - throw new IllegalStateException(e); + throw new IllegalStateException("Faild to create Spring MVC DispatcherServlet proxy", e); } } @@ -162,7 +165,7 @@ public class ProxyMvc { * * @param servlet the {@link Servlet} to invoke in this {@link FilterChain} * @param filters the {@link Filter}'s to invoke in this {@link FilterChain} - * @since 3.2 + * @since 4.0.x */ ProxyFilterChain(DispatcherServlet servlet) { List filters = new ArrayList<>();