Affects: spring-web-5.2.6.RELEASE


Hello there

Having this particular issue with this spring versions

worun@rn-aosd-d00-lapp01:/Local/AS/Sentinel/packages/NemoDataSetsService-21-2-0-20200720-224046-4/lib$ ls -alt | grep spring-web -rw-r--r--. 1 worun worun 1436459 Jun 25 21:20 spring-web-5.2.6.RELEASE.jar -rw-r--r--. 1 worun worun 954967 Jun 25 21:20 spring-webmvc-5.2.6.RELEASE.jar

org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Header Folding at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:79) at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:122) at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:102) at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63) at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:778) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:736) at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:637) at com.apple.store.datasets.service.spring.proxy.DataSetsAuthoringServiceSpringAPIProxy.meta(DataSetsAuthoringServiceSpringAPIProxy.java:601)

400 Header Folding

Any idea what could be wrong?

This is the snippet of code:

@Secured({ "datasets:view" })
@RequestMapping(value = "dataSet/v1/{site}/{type}/{versionKey}/meta.json", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
@ResponseBody
public ResponseEntity<Map<String, Object>> meta(@PathVariable String site, @PathVariable String type,
    @PathVariable String versionKey, HttpServletRequest request, Principal principal) {

    try {

        String uriString = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);

        String servicebaseURL = null;
        TypeProxy typeProxy = dataSetsAPIProxyConfiguration.getProxyConfiguration().get(site + "/" + type);
        if (typeProxy != null) {
            servicebaseURL = typeProxy.getAuthoringProxy().getBaseURL();
        }

        if (servicebaseURL == null) {
            throw new IllegalArgumentException("No proxy endpoints configured for Site/Type " + site + "/" + type);
        }

        URI uri = new URI(servicebaseURL + "/" + uriString);
      //  HttpHeaders headers = new HttpHeaders();
        /*
         * headers.add("content-type", "application/json");
         * dataSetsHeadersUtil.updateHttpHeaders(headers); transferAudit(principal,
         * headers);
         */


        RequestEntity<Map<String, Object>> requestEntity = new RequestEntity<Map<String, Object>>(HttpMethod.GET, uri);

        ResponseEntity<DataTypeDefinition> dataTypeDefinitionResponse = dataSetsRestTemplate.exchange(requestEntity, DataTypeDefinition.class);

        if (dataTypeDefinitionResponse.getStatusCode() == HttpStatus.OK) {
            Map<String, Object> response = new HashMap<String, Object>();
            ObjectMapper objectMapper = new ObjectMapper();
            TypeReference<Map<String, Object>> typeReference = new TypeReference<Map<String, Object>>() {
            };
            response.put("status", "SUCCESS");
            response.put("data", objectMapper.convertValue(dataTypeDefinitionResponse.getBody(), typeReference));

            return new ResponseEntity<Map<String, Object>>(response, HttpStatus.OK);
        } else {
            return new ResponseEntity<Map<String, Object>>(HttpStatus.NOT_FOUND);
        }

    } catch (HttpClientErrorException httpClientErrorException) {
        log.error("Critical error proxying keys call", httpClientErrorException);
        return new ResponseEntity<Map<String, Object>>(httpClientErrorException.getStatusCode());
    } catch (Exception exception) {
        log.error("Critical error proxying keys call", exception);
        return new ResponseEntity<Map<String, Object>>(HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

Comment From: quaff

I guess your header value contains CRLF, please check dataSetsHeadersUtil.updateHttpHeaders(headers)

Comment From: maugomez77

@quaff that part is commented out btw

Comment From: maugomez77

also those values are in this form:

Header -> [content-type:"application/json", Authorization:"Bearer 4521c8567c55f21b7e61477d25ff6ec409439773c3718183357aace64b04cba86299a2dc116636efa5f6cc0369306bedb75d9149f38a03e7d057da578f2147d94649467123bdcd1b4c6b130fc00b234fee859542e82d1d03fd09da4f89187bc8f646833e24740c2d0870e0cb0eef3aa9680b9ddad30f81040b3cfbdd8395ec75b34b0269c37da45658b2380155eb0e71c6bb03412c7132d3e3043435b20a87543a2e6e9c13326850aab41aacdb8bcdef78aa4fa0c55b906ee2fed9e57d814b97c7a61df118b3c9cdcf938726f08dd56d970a68ea957f83a29f2e0638af35a2357fff71ed1c9f447758b5145ca88e1d7faa279ace76ddafdbb3169dab0332c4f3a06326abb70e78945e534ec52e5531236522e5ae1dde9039e9cac3bc13a17ad200fac1df2108280719c29b6b509f7a51bcef8a78d1071fcd723b84bb735494cf89a46521790827b6c2d8ccda171f4892f0148c150d0cde43abb941ec9543c42f389b215f7475a3415e102ff99acca753d2bdbbac2907c444cbbd18baf63a3d6308db9b1b8c1b2a4380b77c15a82f4422057ac3b8bbf5fd49cc0edb480c9e933c", X-DataSets-UserId:"joe_relentless", X-DataSets-UserEmail:"joe_relentless@gato.com"]

do you see any CRLF?

Comment From: quaff

@quaff that part is commented out btw

@maugomez77 Are you facing this error even comment out headers? maybe you are running the wrong version code, please double check.

Comment From: maugomez77

I already double check even in local @quaff

Not sure if also could be realted to the rest template being used there:

public class RestTemplatesUtils { private static Logger logger = LoggerFactory.getLogger(RestTemplatesUtils.class);

final public static int ConnectTimeout = 1000;
final public static int ReadTimeout = 30000;

public static RestTemplate getRestTemplate() {
    // Obtain a connection factory based on whether we are enabling TLS going out
    final HttpClientManager httpClientManager = HttpClientManager.getInstance();
    final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClientManager.getClient());

    // Prime timeouts on the factory and create a REST Template out of it
    factory.setConnectTimeout(ConnectTimeout);
    factory.setReadTimeout(ReadTimeout);
    RestTemplate restTemplate = new RestTemplate(factory);
    logger.info("RestTemplate initialized with TLS");
    if (logger.isDebugEnabled()) {
        logger.debug("RestTemplate initialized with TLS");
    }
    return restTemplate;
}

}

Comment From: quaff

It's possible default headers are added in HttpClientManager.getInstance() via HttpClientBuilder::setDefaultHeaders, I suggest you add a ClientHttpRequestInterceptor to RestTemplate then print all headers sent.

Comment From: maugomez77

The interesting thing is that if we called via browser a simple get:

https://rn-aosd-d00-lapp01.rno.apple.com:9999/8555/dataSet/v1/retail/family/datasettestv2/meta.json

port 9999 is a nginx port 8555 is a jetty application

is giving back:

Bad Message 400 reason: Header Folding

Comment From: quaff

The interesting thing is that if we called via browser a simple get:

https://rn-aosd-d00-lapp01.rno.apple.com:9999/8555/dataSet/v1/retail/family/datasettestv2/meta.json

port 9999 is a nginx port 8555 is a jetty application

is giving back:

Bad Message 400 reason: Header Folding

Then it's not an issue of spring, you should check jetty side.

Comment From: maugomez77

Issue was with one of headers in nginx:

https://github.com/eclipse/jetty.project/issues/5067

            switch (_fieldState)
            {
                case FIELD:
                    switch (t.getType())
                    {
                        case COLON:
                        case SPACE:
                        case HTAB:
                        {
                            if (complianceViolation(HttpComplianceSection.NO_FIELD_FOLDING, _headerString))
                                throw new BadMessageException(HttpStatus.BAD_REQUEST_400, "Header Folding");

                            // header value without name - continuation?