aws / serverless-java-container

A Java wrapper to run Spring, Spring Boot, Jersey, and other apps inside AWS Lambda.
https://aws.amazon.com/serverless/
Apache License 2.0
1.49k stars 551 forks source link

org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: No content to map due to end-of-input #844

Closed dada1804 closed 5 months ago

dada1804 commented 5 months ago

To help us debug your issue fill in the basic information below using the options provided

Serverless Java Container version: eg. 1.5

Implementations: Jersey / Spring / Spring Boot / Spring Boot 2 / Spark

Framework version: eg SpringBoot 3.2.3.RELEASE

Frontend service: REST API

Deployment method: eg SAM, Serverless Framework, Console

Scenario

Describe what you are trying to accomplish

Expected behavior

To read the body

Actual behavior

500 Internal Server Error

Steps to reproduce

package com.engati.api.gateway.filter;

import com.engati.api.gateway.config.SanitizationProvider; import com.engati.api.gateway.config.UrlServiceMappingConfiguration; import com.engati.api.gateway.constants.Constants; import com.engati.api.gateway.dto.UrlServicesMapping; import com.engati.api.gateway.utils.MapCollector; import com.engati.api.gateway.utils.Utils; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.server.mvc.filter.BodyFilterFunctions; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.servlet.function.HandlerFilterFunction; import org.springframework.web.servlet.function.HandlerFunction; import org.springframework.web.servlet.function.ServerRequest; import org.springframework.web.servlet.function.ServerResponse; import com.fasterxml.jackson.core.JsonProcessingException;

import java.util.List; import java.util.Map; import java.util.Objects;

import com.engati.api.gateway.dto.CustomerContext; import reactor.core.publisher.Mono;

@Component @Slf4j public class ProxyRequestBodyModificationFilter implements HandlerFilterFunction<ServerResponse, ServerResponse> {

@Autowired private UrlServiceMappingConfiguration urlServiceMappingConfiguration;

@Autowired private ObjectMapper objectMapper;

@Autowired private SanitizationProvider sanitizationProvider;

@NotNull @Override public ServerResponse filter(ServerRequest request, @NotNull HandlerFunction next) throws Exception { String targetApi = request.param(Constants.TARGET_API).orElse(null); String requestURI = request.uri().toString(); log.info("Received request for URI: {}", requestURI); try { if (StringUtils.isBlank(targetApi)) { log.error("Target API is null for request URI: {}", requestURI); return Utils.setGatewayResponse(HttpStatus.BAD_REQUEST); } UrlServicesMapping urlServicesMapping = urlServiceMappingConfiguration.getServiceMapping(targetApi); if (Objects.isNull(urlServicesMapping) || !urlServicesMapping.isValid()) { log.error("URL service mapping validation failed for request URI: {}", requestURI); return Utils.setGatewayResponse(HttpStatus.NOT_FOUND); } System.out.println("hey"); Map<String, Object> requestBody = request.body(Map.class); for (Map.Entry<String, Object> entry : requestBody.entrySet()) { System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue()); } System.out.println("heyyy"); if (eligibleForRequestBodyMutation(request, urlServicesMapping)) { // && // Integer.parseInt((request.headers().firstHeader(HttpHeaders.CONTENT_LENGTH))) != 0) { try { ServerRequest modifiedRequest = BodyFilterFunctions.modifyRequestBody( Map.class, Map.class, null, new BodyFilterFunctions.RewriteFunction<Map, Map>() {

                    @Override
                    public Map apply(ServerRequest serverRequest, Map map) {
                      log.info("Original Request Body:{}", map);
                      if (map == null) {
                        return (Map) Mono.empty();
                      }
                      if (!urlServicesMapping.isDoNotReplaceBodyContext()) {
                        map = replaceBodyContext(request, map);
                      }
                      if (urlServicesMapping.isSanitizationRequired()) {
                        map = sanitizeUserInput(map);
                      }
                      return Mono.justOrEmpty(map).block();
                    }
                  })
              .apply(request);
      return next.handle(modifiedRequest);
    } catch (Exception e) {
      log.error("Error processing request: {}", targetApi, e);
      return Utils.setGatewayResponse(HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }
  return next.handle(request);
} catch (Exception e) {
  log.error("Error occurred while processing request: {}", targetApi, e);
  return Utils.setGatewayResponse(HttpStatus.INTERNAL_SERVER_ERROR);
}

}

private Map<String, Object> replaceBodyContext( ServerRequest request, Map<String, Object> requestBody) { CustomerContext context = (CustomerContext) request.attributes().get(Constants.CUSTOMER_CONTEXT); return replaceBodyContextUsingContext(requestBody, context); }

private boolean eligibleForRequestBodyMutation( ServerRequest request, UrlServicesMapping urlServiceMapping) { if (request.method() == HttpMethod.GET || !urlServiceMapping.shouldMutateRequestBody()) { return false; } String contentType = request.headers().firstHeader(HttpHeaders.CONTENT_TYPE); return StringUtils.isNotBlank(contentType) && contentType.contains(Constants.APPLICATION_JSON); }

public Map<String, Object> replaceBodyContextUsingContext( Map<String, Object> contentToMutate, CustomerContext context) { if (Objects.nonNull(contentToMutate) && Objects.nonNull(context)) { try { String stringContent = objectMapper.writeValueAsString(contentToMutate); stringContent = Utils.replaceStringContext(stringContent, context); return Utils.mapReader.readValue(stringContent); } catch (JsonProcessingException e) { log.error( "Error while trying to replace context for content: {} with customer context: {}", contentToMutate, context, e); } } return contentToMutate; }

private Map<String, Object> sanitizeUserInput(Map<String, Object> bufferData) { return (Map<String, Object>) sanitizeData(bufferData); }

private Object sanitizeData(Object data) { if (Objects.isNull(data)) { return null; } if (data instanceof String) { return sanitizeStringData(data.toString()); } else if (data instanceof Map) { Map<String, Object> map = (Map<String, Object>) data; if (MapUtils.isEmpty(map)) { return data; } return map.entrySet().stream() .collect(MapCollector.toMap(Map.Entry::getKey, entry -> sanitizeData(entry.getValue()))); } else if (data instanceof List) { List listData = (List) data; if (listData.isEmpty()) { return listData; } return listData.stream().map(this::sanitizeData).toList(); } else { return data; } }

private String sanitizeStringData(String data) { if (StringUtils.isNotBlank(data)) { return sanitizationProvider.getSanitizationFactory().sanitize(data); } return data; } }

Full log output

org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: No content to map due to end-of-input at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:406) at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:354) at org.springframework.web.servlet.function.DefaultServerRequestBuilder$BuiltServerRequest.bodyInternal(DefaultServerRequestBuilder.java:318) at org.springframework.web.servlet.function.DefaultServerRequestBuilder$BuiltServerRequest.body(DefaultServerRequestBuilder.java:301) at com.engati.api.gateway.filter.ProxyRequestBodyModificationFilter.filter(ProxyRequestBodyModificationFilter.java:63) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$0(HandlerFilterFunction.java:58) at com.engati.api.gateway.filter.ProxyDataValidationFilter.filter(ProxyDataValidationFilter.java:116) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$0(HandlerFilterFunction.java:58) at com.engati.api.gateway.filter.ProxyPreProcessingFilter.filter(ProxyPreProcessingFilter.java:58) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$0(HandlerFilterFunction.java:58) at com.engati.api.gateway.filter.SessionValidationFilter.filter(SessionValidationFilter.java:75) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$0(HandlerFilterFunction.java:58) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$ofRequestProcessor$3(HandlerFilterFunction.java:83) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$0(HandlerFilterFunction.java:58) at org.springframework.cloud.gateway.server.mvc.config.GatewayMvcPropertiesBeanDefinitionRegistrar.lambda$getRouterFunction$3(GatewayMvcPropertiesBeanDefinitionRegistrar.java:172) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$0(HandlerFilterFunction.java:58) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$ofRequestProcessor$3(HandlerFilterFunction.java:83) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$1(HandlerFilterFunction.java:59) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$1(HandlerFilterFunction.java:59) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$1(HandlerFilterFunction.java:59) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$1(HandlerFilterFunction.java:59) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$1(HandlerFilterFunction.java:59) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$1(HandlerFilterFunction.java:59) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$1(HandlerFilterFunction.java:59) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$1(HandlerFilterFunction.java:59) at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$apply$2(HandlerFilterFunction.java:70) at org.springframework.web.servlet.function.support.HandlerFunctionAdapter.handle(HandlerFunctionAdapter.java:107) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:925) at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:593) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.springframework.cloud.gateway.server.mvc.filter.WeightCalculatorFilter.doFilter(WeightCalculatorFilter.java:229) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.springframework.cloud.gateway.server.mvc.filter.FormFilter.doFilter(FormFilter.java:97) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) at java.base/java.lang.Thread.run(Thread.java:1583) Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 0] at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59) at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1752) at com.fasterxml.jackson.databind.ObjectReader._initForReading(ObjectReader.java:360) at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2095) at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1481) at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:395) ... 76 common frames omitted

Task :Application.main() FAILED

Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/8.5/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation. 3 actionable tasks: 1 executed, 2 up-to-date

logs
dada1804 commented 5 months ago

Can somebody help me with this

deki commented 5 months ago

@dada1804 it would help if you let us know what you are trying to achieve (please fill out the whole issue template). I don't see AWS Serverless Java Container involved here, so please clarify how your project setup looks like.

dada1804 commented 5 months ago

The project is a spring boot application, which uses spring cloud gateway mvc It has few filters that get called before routing the request to the target or desired service One among which is the filter I provided in the chat This is the yml setup server: port: 10710 engati: hap: host: hap.dev.engati.local

spring: application: name: api-gateway redis: host: redis-enforcer.dev.engati.local portalHost: redis-portal.dev.engati.local cloud: gateway: mvc: routes:

  • id: sandbox uri: ${service.message-receiver.url} predicates:
    • Path=/sandbox/{provider}/webhooks/a0/{sandboxKey} filters:
    • StripPrefix=1
    • name: SandboxRoutingFilter
  • id: proxy uri: http://op predicates:
    • Path=/api-gateway filters:
    • name: SessionValidationFilter
    • name: ProxyPreProcessingFilter
    • name: ProxyDataValidationFilter
    • name: ProxyRequestBodyModificationFilter
    • name: ProxyPostProcessingFilter
    • name: ProxyRoutingFilter When I pass body with {} or some body then the code works fine but when I send no body I am getting JSON parse error, which should be handled and I am not able to figure that out somehow
deki commented 5 months ago

This project covers AWS Serverless Java Container. It seems you are not using the library. Am I wrong?

dada1804 commented 5 months ago

implementation("org.springframework.boot:spring-boot-starter") implementation("org.springframework.boot:spring-boot-starter-actuator:3.2.3") implementation "org.springframework.kafka:spring-kafka:3.1.2" implementation 'org.springframework.cloud:spring-cloud-starter-gateway-mvc' implementation("org.springframework.cloud:spring-cloud-starter-loadbalancer") implementation 'javax.annotation:javax.annotation-api:1.3.2' implementation 'org.slf4j:slf4j-api:2.0.6'

dada1804 commented 5 months ago

What exactly do you need to highlight my issue? I can not really get you

deki commented 5 months ago

This project covers com.amazonaws.serverless:aws-serverless-java-container-*. I don't see it in your dependency list.

You may want to ask your question in the Spring community: https://community.pivotal.io/s/topic/0TO0P000000IKdeWAG/spring

dada1804 commented 5 months ago

Cool, thanks!