Skip to content

MockHttpServletRequest InputStream has been made static in gh-29125 #29901

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ColdFireIce opened this issue Jan 30, 2023 · 4 comments
Closed
Assignees
Labels
in: test Issues in the test module type: regression A bug that is also a regression
Milestone

Comments

@ColdFireIce
Copy link

ColdFireIce commented Jan 30, 2023

Affects: 6.0.4

Hi,

since the upgrade to 6.x I have some MockMvc Tests that fail if the content ist null. This also happens for Spring Boot Health Actuators.
The tests fail with an HttpMessageNotReadableException caused by a java.io.IOException: Stream closed

I went deep in to the debugger to find the reason:
Pull-Request spring-projects/spring-data-mongodb#4160 replaced StreamUtils.emptyInput() with InputStream.nullInputStream() for
private static final ServletInputStream EMPTY_SERVLET_INPUT_STREAM = new DelegatingServletInputStream(InputStream.nullInputStream());
see: 0770d86#diff-1a707902c32bc4ce99156e838462185a17d27d3a8c686a879894597981563124L104

EMPTY_SERVLET_INPUT_STREAM is used every time the content of a call is null:

@Override
public ServletInputStream getInputStream() {
...
	this.inputStream = (this.content != null ?
			new DelegatingServletInputStream(new ByteArrayInputStream(this.content)) :
			EMPTY_SERVLET_INPUT_STREAM);
	return this.inputStream;
}

The Problem is, that the old StreamUtils.emptyInput() was a simple new ByteArrayInputStream(new byte[0]); which could be read and closed multiple times. The new InputStream.nullInputStream() handles more like a normal stream and can't be read again if closed.

I think the problem result from the fact that public ServletInputStream getInputStream() uses the static EMPTY_SERVLET_INPUT_STREAM (every time the same instance) instead of calling new DelegatingServletInputStream(InputStream.nullInputStream());

I don't know the logic behind the caching and context creation but a fix could be in the MockHttpServletRequest getInputStream():

this.inputStream = new DelegatingServletInputStream(this.content != null ?
        new ByteArrayInputStream(this.content) : InputStream.nullInputStream());

There might be some more side effects that I don't know off.

Best regards
Daniel

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 30, 2023
@bclozel bclozel added in: test Issues in the test module type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Jan 30, 2023
@bclozel bclozel self-assigned this Jan 30, 2023
@bclozel bclozel added this to the 6.0.5 milestone Jan 30, 2023
@bclozel bclozel changed the title Static empty InputStream (EMPTY_SERVLET_INPUT_STREAM) can't be closed again. Leads to IOException MockHttpServletRequest InputStream has been made static in gh-29125 Jan 31, 2023
@bclozel bclozel added status: invalid An issue that we don't feel is valid and removed type: bug A general bug labels Jan 31, 2023
@bclozel bclozel removed this from the 6.0.5 milestone Jan 31, 2023
@bclozel
Copy link
Member

bclozel commented Jan 31, 2023

After looking into the behavior here, it looks like this change fixed an actual issue. This aligns MockHttpServletRequest with the expected behavior of a HttpServletRequest. I've replicated the exact same behavior with Tomcat.

In light of that, I'm closing this issue as this works as expected.

Thanks for your report!

@bclozel bclozel closed this as not planned Won't fix, can't repro, duplicate, stale Jan 31, 2023
@ColdFireIce
Copy link
Author

Hi ok. That might be, but could you explain how I'm supposed align with the expected behavior if I'm testing something like this:

@SpringBootTest
@AutoConfigureMockMvc
class TestControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void testNullContent() throws Exception {
        mockMvc.perform(post("/test"))
                .andDo(print()).andExpect(status().isOk());
    }
    @Test
    void testNullContentAgain() throws Exception {
        mockMvc.perform(post("/test"))
                .andDo(print()).andExpect(status().isOk());
    }
}

The 2nd tests fails with the mentioned Stream closed exception. With this controller:

@RestController
public class TestController {

    @PostMapping(path = "/test")
    public void test(HttpServletRequest request) throws IOException {
        ServletInputStream inputStream = request.getInputStream();
        // something reads the stream and closes it:
        inputStream.readAllBytes();
        inputStream.close();
    }
}
Stream closed
java.io.IOException: Stream closed
	at java.base/java.io.InputStream$1.ensureOpen(InputStream.java:91)
	at java.base/java.io.InputStream$1.read(InputStream.java:103)
	at org.springframework.mock.web.DelegatingServletInputStream.read(DelegatingServletInputStream.java:63)
	at java.base/java.io.InputStream.read(InputStream.java:284)
	at java.base/java.io.InputStream.readNBytes(InputStream.java:409)
	at java.base/java.io.InputStream.readAllBytes(InputStream.java:346)
	at com.example.demo.TestController.test(TestController.java:19)
...

Maybe you could tell me how I'm supposed to do this differently if every null content is replaced with the identical - already closed - stream?

Best regards
Daniel

@bclozel
Copy link
Member

bclozel commented Jan 31, 2023

Sorry, I initially tried to replicate the static behavior by using different requests and it worked fine; obviously my unit test was flawed. Thanks to your code snippet I managed to reproduce the issue. I'm reopening this and re-scheduling this for 6.0.5.

@bclozel bclozel reopened this Jan 31, 2023
@bclozel bclozel added type: regression A bug that is also a regression and removed status: invalid An issue that we don't feel is valid labels Jan 31, 2023
@bclozel bclozel added this to the 6.0.5 milestone Jan 31, 2023
@ColdFireIce
Copy link
Author

Ah ok, thank you. I just uploaded a working (broken) test snippet. Maybe It's of use:
https://github.com/ColdFireIce/spring-null-content-stream-bug/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: test Issues in the test module type: regression A bug that is also a regression
Projects
None yet
Development

No branches or pull requests

3 participants