0

I've been experiencing a strange issue with handling of a POST method in my controller after enabling the SSL in my Spring Boot application. Here's what I've done:

  1. Created a self-signed certificate using keytool (one on localhost and one on remote host)
  2. Enabled SSL in application.properties like so:
server.port=8444
server.ssl.key-store-type=PKCS12
server.ssl.key-store=classpath:keystore/cert.p12
server.ssl.key-store-password=somepwd
server.ssl.key-alias=cert
  1. Placed the cert.p12 as per property above

What I experience now is the following:

  1. On localhost
  • get requests work just fine when using HTTPS
  • post requests work just fine when using HTTPS
  1. After deploying it on another host (of course with updated certificate)
  • get requests work just fine when using HTTPS

  • post requests don't work when using HTTPS and return the following message:

    Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public org.springframework.http.ResponseEntity<java.lang.String> com.app.updateData(java.lang.String)]

Some background

Here's how my method looks like:

@CrossOrigin(origins = "*") 
@PostMapping(path="/api/data-update")
public ResponseEntity<String> updateData(@RequestBody String data) {...}

I test it in the following way (using curl).

On localhost (which works just fine):

curl -k -X POST -H "Content-Type: application/json" -H "Content-Encoding: gzip" --data-binary @./test_data.gzip https://localhost:8444/api/data-update

On remote server (which returns the error I listed above):

curl -k -X POST -H "Content-Type: application/json" -H "Content-Encoding: gzip" --data-binary @./test_data.gzip https://remoteserver:8444/api/data-update

So basically what I do is to submit via POST a zipped JSON file. Note that this method works just fine when I disable the SSL configuration (meaning that I use HTTP), as well as, when I test it on the localhost. It stops working only when trying to execute the curl above to an external server.

I hope I provided all the details that may be needed for someone to suggest a possible solution to this issue.

Many thanks in advance!

UPDATE:

Initially I thought I cannot be because of the filter I have included, in order to processes the GZIP file, but it turns out that the problem described above occurs only for the binary data I'm trying to POST using curl. If I POST text JSON data to another controller, it works just fine. I don't think it is the filter itself because other filters work, so my prime suspect is this GZIP extraction but I can't find anything wrong with it.

Here's my filter class:

package com.app.filter;

import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.zip.GZIPInputStream;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;

@Component
public class GzipBodyDecompressFilter implements Filter {

    private static final Logger logger = LogManager.getLogger(GzipBodyDecompressFilter.class);

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;

        String encoding = request.getHeader("Content-Encoding");
        if (encoding != null && encoding.equals("gzip")) {
            logger.info("GZIP extraction");
            request = new GzippedInputStreamWrapper((HttpServletRequest) servletRequest);
        }

        chain.doFilter(request, servletResponse);
    }

    final class GzippedInputStreamWrapper extends HttpServletRequestWrapper {

        public static final String DEFAULT_ENCODING = "ISO-8859-1";
        private byte[] bytes;

        public GzippedInputStreamWrapper(final HttpServletRequest request) throws IOException {
            super(request);

            try {
                final InputStream in = new GZIPInputStream(request.getInputStream());
                bytes = IOUtils.toByteArray(in);
            } catch (EOFException e) {
                bytes = new byte[0];
            }
        }

        @Override
        public ServletInputStream getInputStream() throws IOException {
            final ByteArrayInputStream sourceStream = new ByteArrayInputStream(bytes);

            return new ServletInputStream() {
                public int read() throws IOException {
                    return sourceStream.read();
                }

                @Override
                public void setReadListener(ReadListener listener) {
                    // TODO Auto-generated method stub

                }

                @Override
                public boolean isFinished() {
                    // TODO Auto-generated method stub
                    return false;
                }

                @Override
                public boolean isReady() {
                    // TODO Auto-generated method stub
                    return false;
                }

                public void close() throws IOException {
                    super.close();
                    sourceStream.close();
                }
            };
        }

        @Override
        public Map getParameterMap() {
            return super.getParameterMap();
        }
    }
}

UPDATE 2:

Just by an accident I tried to submit another JSON file that is much smaller (~1.2KB) and it worked. So there must be something in the Tomcat settings that is blocking the requests, but I can't find anything (again, it works fine for HTTP, in only rejects larger HTTPS requests).

0x4ndy
  • 1,216
  • 1
  • 12
  • 25

2 Answers2

1

This seems to have been a bug in that particular version (9.0.31) of Tomcat:

See: What may be the reason of "Unexpected end-of-input in VALUE_STRING" error

The error in the link is different to what you've described but I'm seeing behaviour very similar to yours but also the error message in that link. Upgrading the version of Tomcat used by Spring Boot fixed the issue for me.

kmxp
  • 25
  • 6
0

I didn't find the embedded Tomcat setting that would let me submit a bigger request. So, my only solution to this date was to change the Spring Boot config so that it launches the app in an embedded Jetty web server instead of Tomcat. Apparently Jetty accept such requests by default and it works out of the box.

0x4ndy
  • 1,216
  • 1
  • 12
  • 25