Closed
Description
Summary
When using mockMvc.perform
on MockMvcRequestBuilders.asyncDispatch
with a Spring Security configured applicationContext we receive a ConcurrentModificationException
in approximately every 10th test-run.
The Exception can be prevented by @DirtiesContext
, but this slows down the test alot and can not be used when calling mockMvc.perform
multiple times in a single test-method.
Actual Behavior
java.util.ConcurrentModificationException
at java.base/java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:719)
at java.base/java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:741)
at org.springframework.mock.web.HeaderValueHolder.getByName(HeaderValueHolder.java:98)
at org.springframework.mock.web.MockHttpServletResponse.doAddHeaderValue(MockHttpServletResponse.java:617)
at org.springframework.mock.web.MockHttpServletResponse.setHeaderValue(MockHttpServletResponse.java:579)
at org.springframework.mock.web.MockHttpServletResponse.setHeader(MockHttpServletResponse.java:557)
at javax.servlet.http.HttpServletResponseWrapper.setHeader(HttpServletResponseWrapper.java:165)
at org.springframework.security.web.firewall.FirewalledResponse.setHeader(FirewalledResponse.java:50)
at javax.servlet.http.HttpServletResponseWrapper.setHeader(HttpServletResponseWrapper.java:165)
at org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.writeHeaders(XFrameOptionsHeaderWriter.java:94)
at org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterResponse.writeHeaders(HeaderWriterFilter.java:109)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:182)
Expected Behavior
Test finishes without error.
Version
springBootVersion = '2.1.7.RELEASE'
Sample
@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = Controller.class)
public class ControllerTest {
@Autowired
protected WebApplicationContext wac;
@Test
public void test() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(wac).apply(SecurityMockMvcConfigurers.springSecurity()).build();
MvcResult result = mockMvc.perform(get("/test"))
.andExpect(request().asyncStarted()).andReturn();
mockMvc.perform(asyncDispatch(result)).andExpect(status().isOk());
}
}
@SpringBootApplication
@RestController
public class Controller extends WebSecurityConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(Controller.class, args);
}
@GetMapping("/test")
public ResponseEntity<StreamingResponseBody> test() {
return new ResponseEntity<>(outputStream -> outputStream.write("content".getBytes()), OK);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll();
}
}