When processing a "close" notification from the server make an effort
to cancel any outstanding heartbeat but avoid going as far as acquiring
the responseLock since the server itself may already hold a lock of its
own leading to a potential deadlock.
The heartbeat task is now also further protected with an isClosed()
check in case the heartbeat does not get cancelled in a concurrent
scenario.
Issue: SPR-14917
This commits adds a validation check whether the SockJS session type
matches the transport type and rejects requests for which they
don't match.
Issue: SPR-14867
Create an explicit heartbeat task with an experiration flag so that
it can be cancelled reliably vs relying on the ScheduledFutureTask
cancel method which may return true even if the task is already
running.
Issue: SPR-14356
This commit adds a test runtime dependency on log4j 2 for every project
and migrates all log4j.properties files to log4j2-test.xml files.
Issue: SPR-14431
Prior to this commit, any attempt to include a bean of type
ServletServerContainerFactoryBean in the WebApplicationContext for an
integration test class annotated with @WebAppConfiguration in
conjunction the Spring TestContext Framework (TCF) would have resulted
in an IllegalStateException stating that "A ServletContext is required
to access the javax.websocket.server.ServerContainer instance."
In such scenarios, the MockServletContext was in fact present in the
WebApplicationContext; however there was no WebSocket ServerContainer
stored in the ServletContext.
This commit addresses this issue by introducing the following.
- MockServerContainer: a private mock implementation of the
javax.websocket.server.ServerContainer interface.
- MockServerContainerContextCustomizer: a ContextCustomizer that
instantiates a new MockServerContainer and stores it in the
ServletContext under the attribute named
"javax.websocket.server.ServerContainer".
- MockServerContainerContextCustomizerFactory: a
ContextCustomizerFactory which creates a
MockServerContainerContextCustomizer if WebSocket support is present
in the classpath and the test class is annotated with
@WebAppConfiguration. This factory is registered by default via the
spring.factories mechanism.
Issue: SPR-14367
Even before this change SockJS sessions always cancelled the heartbeat
task first prior to sending messages. However when the heartbeat task
is already in progress, cancellation of it is not enough and we must
wait until the heartbeat is sent.
This commit adds a heartbeat write lock which is obtained and held
during the sending of a heartbeat. Now when sessions send a message
they still cancel the heartbeat task but if that fails they also wait
for the heartbeat write lock.
Issue: SPR-14356
Prior to this change, getting header values with `HttpHeaders` when
headers are multi-valued would cause issues.
For example, for a given HTTP message with headers:
Cache-Control: public, s-maxage=50
Cache-Control: max-age=42
Getting a `List` of all values would return <"public", "s-maxage=50">
and getting the header value would return "public, s-maxage=50".
This commit takes now into account multi-valued HTTP headers and adds
new getters/setters for "If-Match" and "If-Unmodified-Since" headers.
Note that for ETag-related headers such as "If-Match" and
"If-None-Match", a special parser has been implemented since ETag values
can contain separator characters.
Issue: SPR-14223, SPR-14228
Previous refactoring (fcf6ae and also 43d937) in the SockJsSession
hierarchy consolidated access to the request and response in the
base class AbstractHttpSockJsSession in order to keep synchronization
concerns there. However that also unintentionally removed the call to
resetRequest() after sending a heartbeat for any of the
PollingSockJsSession classes. In general a polling session should call
resetRequest after every frame written.
This commit brings back the writeFrame override in PollingSockJsSession
with an extra call to resetRequest().
Issue: SPR-14107