question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

HttpPostRequestDecoder may cause memory leak

See original GitHub issue

set HttpDataFactory to use disk = false like below:

HttpDataFactory factory = new DefaultHttpDataFactory(false, CharsetUtil.UTF_8);

and then create an HttpPostRequestDecoder instance:

HttpPostRequestDecoder httpDecoder = new HttpPostRequestDecoder(factory, httpRequest);

after decoder is destroyed the reference count of httpRequest is incorrect.

in the method createAttribute of DefaultHttpDataFactory, MemoryAttribute is not added to HttpData list:

@Override
    public Attribute createAttribute(HttpRequest request, String name, long definedSize) {
        if (useDisk) {
            Attribute attribute = new DiskAttribute(name, definedSize, charset, baseDir, deleteOnExit);
            attribute.setMaxSize(maxSize);
            List<HttpData> list = getList(request);
            list.add(attribute);
            return attribute;
        }
        if (checkSize) {
            Attribute attribute = new MixedAttribute(name, definedSize, minSize, charset, baseDir, deleteOnExit);
            attribute.setMaxSize(maxSize);
            List<HttpData> list = getList(request);
            list.add(attribute);
            return attribute;
        }
        MemoryAttribute attribute = new MemoryAttribute(name, definedSize);
        attribute.setMaxSize(maxSize);
        return attribute;
    }

so on destroying

@Override
    public void cleanAllHttpData() {
        Iterator<Entry<HttpRequest, List<HttpData>>> i = requestFileDeleteMap.entrySet().iterator();
        while (i.hasNext()) {
            Entry<HttpRequest, List<HttpData>> e = i.next();

            // Calling i.remove() here will cause "java.lang.IllegalStateException: Entry was removed"
            // at e.getValue() below

            List<HttpData> list = e.getValue();
            for (HttpData data : list) {
                data.release();
            }

            i.remove();
        }
    }

the MemoryAttributes created above are not released.

I’m not sure if it is a bug.

Expected behavior

the reference count of httpRequest before decoding should equals to the one after decoder is destroyed.

Actual behavior

if the original reference count of http request is 1, and the http request contains 2 fields , then after destroying the reference count is 3 !

Steps to reproduce

Minimal yet complete reproducer code (or URL to code)

send a form :

public void testSubmitForm() throws IOException {
        String boundary = "---------------------------ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        URL url = new URL("http://127.0.0.1:1084/test");
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setConnectTimeout(5000);
        connection.setReadTimeout(30000);
        connection.setDoOutput(true);
        connection.setDoInput(true);
        connection.setUseCaches(false);
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)");
        connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

        try (OutputStream out = new DataOutputStream(connection.getOutputStream())) {
            out.write(("\r\n--" + boundary + "\r\n").getBytes(StandardCharsets.UTF_8));
            out.write("Content-Disposition: form-data; name=\"username\"\r\n\r\n".getBytes(StandardCharsets.UTF_8));
            out.write("chinnsii".getBytes(StandardCharsets.UTF_8));

            out.write(("\r\n--" + boundary + "\r\n").getBytes(StandardCharsets.UTF_8));
            out.write("Content-Disposition: form-data; name=\"password\"\r\n\r\n".getBytes(StandardCharsets.UTF_8));
            out.write("chinnsii".getBytes(StandardCharsets.UTF_8));

            byte[] endData = ("\r\n--" + boundary + "--\r\n").getBytes();
            out.write(endData);
            out.flush();
        }

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        }
    }

decode to a hash map

Map<String, Object> convert(FullHttpRequest httpRequest) throws IOException {
        HttpDataFactory factory = new DefaultHttpDataFactory(false, CharsetUtil.UTF_8);
        HttpPostRequestDecoder httpDecoder = new HttpPostRequestDecoder(factory, httpRequest);
        try {
            httpDecoder.setDiscardThreshold(0);
            Map<String, Object> params = new HashMap<>();
            List<InterfaceHttpData> InterfaceHttpDataList = httpDecoder.getBodyHttpDatas();
            for (InterfaceHttpData data : InterfaceHttpDataList) {
                if (data != null) {
                    if (InterfaceHttpData.HttpDataType.FileUpload == data.getHttpDataType()) {
                        FileUpload fileUpload = (FileUpload) data;
                        byte[] bytes = fileUpload.get();
                        if (!fileUpload.isInMemory()) {
                            fileUpload.delete();
                        }
                        params.put(data.getName(), bytes);
                    }
                    if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
                        Attribute attribute = (Attribute) data;
                        params.put(attribute.getName(), attribute.getValue());
                    }
                }
            }
            return params;
        } finally {
            System.out.println("74------------->" + httpRequest.refCnt());
            httpDecoder.destroy();
            System.out.println("76------------>" + httpRequest.refCnt());
        }
}

where 74--------------> is 4, and 76----------------->is 3

Netty version

4.1.51.Final

JVM version (e.g. java -version)

1.8

OS version (e.g. uname -a)

MAC

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
aepedrazacommented, Mar 24, 2021

Hi, I’m trying to reproduce your code in a unit test but I’m not finding ContentConverterException nor UploadedFile. My version is 4.1.61.Final-SNAPSHOT (currently the last in branch 4.1) Are those some “theoretical classes” or is this in a previous version?

Thanks in advance for your time

Disclaimer: This may be my first contribution to the project. Please forgive my newbie questions

0reactions
devilyardcommented, Apr 8, 2021

@devilyard (ignore my previous comment – the web page was out of date when I posted) Does Frederics help solve the issue you are seeing? If so, then I think we can close this issue.

yes, i tried the latest 4. 1. 62 version, the issue is fixed

Read more comments on GitHub >

github_iconTop Results From Across the Web

unused @Repository class will cause memory leak
Two objects, which are connected (e.g. with @JoinColumn ) can cause an OutOfMemoryException if they fetch themselves in EAGER -mode.
Read more >
3 Troubleshoot Memory Leaks - Java - Oracle Help Center
Detect memory leaks early and prevent OutOfmemoryErrors using Java Flight Recordings. Detecting a slow memory leak can be hard. A typical symptom is...
Read more >
Java – How to decode http POST data in Java – iTecNote
I'm using Netty, and I've got to accept and parse http POST requests. As far as I can tell, Netty doesn't have built-in...
Read more >
io.netty.handler.codec.http.multipart.HttpPostRequestDecoder ...
How to use. io.netty.handler.codec.http.multipart.HttpPostRequestDecoder. constructor. Best Java code snippets using io.netty.handler.
Read more >
Understanding Memory Leaks in Java - Developer.com
As an aside, programmers can use Application Performance Monitoring (AMP) tools and software to detect memory leaks and help track down issues ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found