When a send timeout is detected, the WebSocket session is now closed
with a custom close status that indicates so. This allows skipping
parts of the close logic that may cause further hanging.
Issue: SPR-11450
Before this change SockJsSession implementations of WebSocketSession
used synchronization around its method implementations protecting
internal state and ensuring only a single thread is sending messages
at a time.
A WebSocketSession is generally expected to be used from one thread
at a time and now that application messages are sent through
ConcurrentWebSocketSessionDecorator, there is no concern about
application messages sent from the different threads.
While there are some remaining concerns, those can be addressed
without using the synchronized keyword. This change removes it from
the methods of all SockJS session implementations.
Issue: SPR-11450
Before this change the decorator ensured that for a specific WebSocket
session only one thread at a time can send a message. Other threads
attempting to send would have their messages buffered and each time
that occurs, a check is also made to see if the buffer limit has been
reached or the send time limit has been exceeded and if so the session
is closed.
This change adds further protection to ensure only one thread at a time
can perform the session limit checks and attempt to close the session.
Furthermore if the session has timed out and become unresponsive,
attempts to close it may block yet another thread. Taking this into
consideration this change also ensures that state associated with the
session is cleaned first before an attempt is made to close the session.
Issue: SPR-11450
Since we now wrap the WebSocketSession with a concurrent decorator, the
synchronized keyword around message sending needed to be removed.
Issue: SPR-11586
Prior to this commit, configuring a custom handshakeHandler when setting
up a stomp-endpoint with SockJS would not be taken into account:
<websocket:stomp-endpoint path="/foo">
<websocket:handshake-handler ref="customHandler"/>
<websocket:sockjs/>
</websocket:stomp-endpoint>
This commit fixes this by creating and registering a
WebsocketTransportHandler (with this handshakeHandler) as a
transportHandler override for the SockJSService.
Issue: SPR-11568
The clientInboundChannel and clientOutboundChannel now use twice
the number of available processors by default to accomodate for some
degree of blocking in task execution on average.
In practice these settings still need to be configured explicitly in
applications but these should serve as better default values than
the default values in ThreadPoolTaskExecutor.
Issue: SPR-11450
This change exposes the WebSocketSession attributes through a message header.
The StompSubProtocolHandler adds this to incoming messages.
For now messaging handling methods can access the map via @Header, e.g.:
@Header(StompHeaderAccessor.SESSION_ATTRIBUTES) Map<String, Object> attrs) {
Issue: SPR-11566
Prior to this commit, the `relay-port` attribute of the
`<websocket:stomp-broker-relay />` tag was of type `xsd:int`.
This prevents developers from using `PropertyPlaceholderConfigurer`,
even though this configuration key is a good candidate for such use
(this value depends on prod/staging/etc environment).
This commit changes that type to `xsd:string`.
Issue: SPR-11537
After this change, AbstractSockJsService does not add CORS headers if
the response already contains an "Access-Control-Allow-Origin" header.
Essentially it backs off assuming CORS headers are handled centrally
e.g. through a Filter.
In order to support this, the ServletServerHttpResponse now returns an
instance of HttpHeaders that also provides access to headers already
present in the HttpServletResponse.
Issue: SPR-11443
The Servlet API does not provide notifications when a client
disconnects, see see https://java.net/jira/browse/SERVLET_SPEC-44.
Therefore network IO failures may occur simply because a client has
gone away. Before this change that could fill logs with unnecessary
stack traces.
After this change we make a best effort to identify such network
failures, on a per-server basis (tested with Jetty, Tomcat, Glassfish,
and WildFly), and log them under a separate log category.
A simple one-line message is logged at DEBUG level (i.e. no stack trace)
while a full stack trace is shown at TRACE level.
Issue: SPR-11438
Before this change CompositeMessageConverter had a ContentTypeResolver
field that was in turn set on all contained converters.
After this change that field is removed and effectively
CompositeMessageConverter is a simple container of other converters.
Each converter in turn must have been configured with a
ContentTypeResolver.
Doing so means it is less likely to have unexpected consequences when
configuring converters, the ContentTypeResolver set in the composite
converter overriding the one configured in a contained converter.
Also commit 676ce6 added default ContentTypeResolver initialization
to AbstractMessageConverter, which ensures that converters are still
straight forward to configure.
Issue: SPR-11462
Before this change, when a client subscribed to a "user" destination
(e.g. /user/foo), actual messages received in response to that
subscription contained the server-translated, unique user destination
(e.g. /foo-user123).
This is not an issue for clients such as stomp.js since the
subscription is unique and sufficient to match subscription responses.
However, other STOMP clients do additional checks on the destination
of the subscription and the response.
This change ensures that messages sent to clients on user destionations
always contain a destination that matches the one on the original
subscription.
Issue: SPR-11423
The interface is to be implemented in addition to
java.security.Principal when Principal.getName() is not globally unique
enough for use in user destinations.
Issue: SPR-11327
sockjs-client expects a prelude to be written on every request with
streaming transports. The protocol tests don't make this clear and
don't expose this issue.
The test case for SPR-11183 (writing 20K messages in succession) did
expose the issue and this commit addresses it.
Issue: SPR-11183
This change adds a protected method to DefaultHandshakeHandler to
determine the user for the WebSocket session. By default it's
implemeted to obtain it from the request.
Issue: SPR-11228
The Jetty ServletWebUpgradeRequest implements getUserPrincipal to
return the Principal from the HttpServletRequest on the upgrade.
This change ensures we can fall back on that.
However the JettyRequestUpgradeStrategy still passes the user from
HttpServletRequest from the upgrade, in order to work with Jetty
9.0.x and avoid running into this 9.1.x issue:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=423118
The observed behavior was that the client does not get a response from
the WebSocket HTTP handshake. On the server the handshake actually
succeeds, the response is set correctly to status 101, and the
WebSocketHandler gets notified of the successfully established
connection.
This change flushes the ServletResponse just before returning from the
GlassfishRequestUpgradeStrategy. This is actually what Glassfish's own
TyrusServletFilter does as well at the end along with a comment that it
is a possible bug.