6

im using Stomp over SockJS and Spring websockets as backend. Occasionally Im having problems after my stomp client reconnect (wifi loss, server down, other). The connection is perfectly restored but passed few seconds i can see in browser network how SockJS client try and try send xhr-streaming with the old session id. The backend responde with close frame c[1000, "Go Away!"] The application its still work but this problem load the CPU and slow the application.

I could play turning off and starting the server several times (Not always happen). I cant understand how SockJS once reconnected ( We destroy and create the Sockjs instance from 0 ) send xhr-streaming requests with the id of the old session ( Maintains the pre-reconnection id session? memory leak? ). I'd like to keep it Sockjs not any state after reconnection and always starting from scratch.

enter image description here

Backend log:

2016-05-02 21:45:01.943 DEBUG [http-nio-8090-exec-7] o.s.w.s.s.t.h.XhrStreamingTransportHandler - Connection already closed (but not removed yet) for XhrStreamingSockJsSession[id=ypsjtids] 

Client log:

(New Sessión Id -> h2pystok, Old session Id -> ypsjtids)

<<< PONG browser.js:120 sockjs-client:buffered-sender send +45ms "\n" browser.js:120 sockjs-client:buffered-sender sendSchedule +0ms 1 browser.js:120 sockjs-client:ajax-based create ajax sender +2ms http://localhost/eess-services/stomp/355/h2pystok ["\n"] browser.js:120 sockjs-client:browser:xhr POST +0ms http://localhost/eess-services/stomp/355/h2pystok/xhr_send ws.js:216 >>> PING browser.js:120 sockjs-client:browser:xhr withCredentials +2ms browser.js:120 sockjs-client:browser:xhr readyState +18ms 4 browser.js:120 sockjs-client:browser:xhr status +1ms 200 browser.js:120 sockjs-client:browser:xhr finish +0ms 200 hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh o h h h h h browser.js:120 sockjs-client:receiver:xhr finish +0ms 200 hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh browser.js:120 sockjs-client:receiver:xhr _chunkHandler +0ms 200 browser.js:120 sockjs-client:receiver:xhr close +0ms network browser.js:120 sockjs-client:polling close +1ms null network undefined browser.js:120 sockjs-client:polling _scheduleReceiver +0ms browser.js:120 sockjs-client:receiver:xhr http://localhost/eess- services/stomp/266/ypsjtids/xhr_streaming +0ms browser.js:120 sockjs-client:browser:xhr POST +1ms http://localhost/eess-services/stomp/266/ypsjtids/xhr_streaming 

There is a issue in github: https://github.com/sockjs/sockjs-client/issues/308

I have this problem in production :( Regards.

EDIT:

I found where is the possible error. In polling.js i have see a special treatment when the close reason is 'network'. When is network again call the function _scheduleReceiver(). When we reconnect the infinite loop occurs. I dont know what is the reason for this treatment but I could try this deleting the special treatment of 'network' and everything works correctly. @skozin Can you try?

if (!self.pollIsClosing) { if (reason === 'network') { self._scheduleReceiver(); } else { self.emit('close', code || 1006, reason); self.removeAllListeners(); } }

the workaround is:

if (!self.pollIsClosing) { self.emit('close', code || 1006, reason); self.removeAllListeners(); }

1
  • After a few days testing , I have found that in iOS (Iphone 6 with the latest version of iOS ) can not connect . I have solved the infinite loop in desktop but now I can´t connect in mobile Commented May 20, 2016 at 12:31

2 Answers 2

1

I spent a lot of time debugging SockJS and Spring code to figure out with that issue and what I have found.

xhr.js file has next method to parse inbound text message:

XhrReceiver.prototype._chunkHandler = function(status, text) { debug('_chunkHandler', status); if (status !== 200 || !text) { return; } for (var idx = -1; ; this.bufferPosition += idx + 1) { var buf = text.slice(this.bufferPosition); idx = buf.indexOf('\n'); if (idx === -1) { break; } var msg = buf.slice(0, idx); if (msg) { debug('message', msg); this.emit('message', msg); } } }; 

That method expects \n at the end of the SockJS text frame.

But in case of "Connection already closed (but not removed yet) forsessionId" Spring Framework sends unformatted SockJsFrame.closeFrameGoAway() without ending \n.

AbstractHttpSendingTransportHandler.class

 protected void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response, AbstractHttpSockJsSession sockJsSession) throws SockJsException { if (sockJsSession.isNew()) { if (logger.isDebugEnabled()) { logger.debug(request.getMethod() + " " + request.getURI()); } sockJsSession.handleInitialRequest(request, response, getFrameFormat(request)); } else if (sockJsSession.isClosed()) { if (logger.isDebugEnabled()) { logger.debug("Connection already closed (but not removed yet) for " + sockJsSession); } SockJsFrame frame = SockJsFrame.closeFrameGoAway(); try { response.getBody().write(frame.getContentBytes()); } catch (IOException ex) { throw new SockJsException("Failed to send " + frame, sockJsSession.getId(), ex); } } 

SockJsFrame

public static SockJsFrame closeFrame(int code, @Nullable String reason) { return new SockJsFrame("c[" + code + ",\"" + (reason != null ? reason : "") + "\"]"); } 

As a quick and dirty fix I just created copy of org.springframework.web.socket.sockjs.frame.SockJsFrame and put in my classpath to substitute the original file with modified one.

public static SockJsFrame closeFrame(int code, @Nullable String reason) { return new SockJsFrame("c[" + code + ",\"" + (reason != null ? reason : "") + "\"]\n"); } 

But in reality AbstractHttpSendingTransportHandler class should be fixed to apply format to the SockJS frame.

SockJsFrameFormat frameFormat = this.getFrameFormat(request); SockJsFrame frame = SockJsFrame.closeFrameGoAway(); String formattedFrame = frameFormat.format(frame); try { response.getBody().write(formattedFrame.getBytes(SockJsFrame.CHARSET)); } 

After that fix all works as expected and c[1000, "Go Away!"] successfully routes to the 'c' section of next switch

SockJS.prototype._transportMessage = function(msg) { ... switch (type) { case 'a': if (Array.isArray(payload)) { payload.forEach(function(p) { debug('message', self.transport, p); self.dispatchEvent(new TransportMessageEvent(p)); }); } break; case 'm': debug('message', this.transport, payload); this.dispatchEvent(new TransportMessageEvent(payload)); break; case 'c': if (Array.isArray(payload) && payload.length === 2) { this._close(payload[0], payload[1], true); } break; } }; 

Without that fix message is not parsed as close message.

https://github.com/spring-projects/spring-framework/pull/28000

Sign up to request clarification or add additional context in comments.

Comments

0

I had the same issue. My app was running on secure "https://" where as I am trying to fetch the sockjs-0.3.min.js from an "unsecured CDN location" which fails to download the .js file. Which causes it to look for Xhr_Streaming instead of my sockjs methods while disconnecting the socket. Some references related to http vs https: https://developers.google.com/web/fundamentals/security/prevent-mixed-content/fixing-mixed-content

I fixed this issue by changing the "unsecured cdn path to secured cdn path" i.e.,

"< script src="https://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js">< / script >"

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.