135

I would like to get the reason websockets closed, so I can show the right message to the user.

I have

sok.onerror=function (evt) 
     {//since there is an error, sockets will close so...
       sok.onclose=function(e){
           console.log("WebSocket Error: " , e);}

The code is always 1006 and the reason is always " ". But I want to tell different closing reasons apart.

For example the comand line gives an error reason : "you cannot delete that, because database wont let you". But on Chrome's console, the reason is still " ".

Any other way to tell different closing reasons apart?

Penny Liu
  • 15,447
  • 5
  • 79
  • 98
slevin
  • 4,166
  • 20
  • 69
  • 129
  • I think this is because of how the server is handling the connected / disconnected events. I can't say for sure but the connection closing needs to handled correctly on the server also with code. Try overriding the built in On Connected /Disconnected methods on the server and see. My assumption only is that you're closing it but the server isn't closing properly and therefore not relaying the proper closed response. – Michael Puckett II Dec 29 '18 at 20:41

7 Answers7

180

Close Code 1006 is a special code that means the connection was closed abnormally (locally) by the browser implementation.

If your browser client reports close code 1006, then you should be looking at the websocket.onerror(evt) event for details.

However, Chrome will rarely report any close code 1006 reasons to the Javascript side. This is likely due to client security rules in the WebSocket spec to prevent abusing WebSocket. (such as using it to scan for open ports on a destination server, or for generating lots of connections for a denial-of-service attack).

Note that Chrome will often report a close code 1006 if there is an error during the HTTP Upgrade to Websocket (this is the step before a WebSocket is technically "connected"). For reasons such as bad authentication or authorization, or bad protocol use (such as requesting a subprotocol, but the server itself doesn't support that same subprotocol), or even an attempt at talking to a server location that isn't a WebSocket (such as attempting to connect to ws://images.google.com/)

Fundamentally, if you see a close code 1006, you have a very low level error with WebSocket itself (similar to "Unable to Open File" or "Socket Error"), not really meant for the user, as it points to a low level issue with your code and implementation. Fix your low level issues, and then when you are connected, you can then include more reasonable error codes. You can accomplish this in terms of scope or severity in your project. Example: info and warning level are part of your project's specific protocol, and don't cause the connection to terminate. With severe or fatal messages reporting also using your project's protocol to convey as much detail as you want, and then closing the connection using the limited abilities of the WebSocket close flow.

Be aware that WebSocket close codes are very strictly defined, and the close reason phrase/message cannot exceed 123 characters in length (this is an intentional WebSocket limitation).

But not all is lost, if you are just wanting this information for debugging reasons, the detail of the closure, and its underlying reason is often reported with a fair amount of detail in Chrome's Javascript console.

Community
  • 1
  • 1
Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • 4
    Joakim, thanks, very detailed anser. If I use `sok.onerror=function (evt) {console.log(evt);}` the details are not so much. Not even a `reason` or something. So, no options at all? I just show to the user, `something is wrong, or not connencted?`Not so user-friendly, it would be nice if the user can see "You cannot delete, cause of database restrictions". Any options? Thanks – slevin Oct 10 '13 at 20:42
  • You should use `sok.onclose` instead which triggers `close event`, it has `reason` and `code` in it – Ihab Khattab Oct 29 '14 at 16:01
  • @IhabKhattab that would be close code specific, and also when the close occurs. having `sok.onclose` will work for many paths, but not all paths. Especially bad protocol, bad handshake errors (like some conditions that could cause close code `1006`). Will this change in the future? Probably. But when this answer was written it was true. – Joakim Erdfelt Oct 29 '14 at 16:20
  • @JoakimErdfelt sorry, I was replying to @slevin question about he has no `reason` returned when he used `onerror` I was pointing that this properties `code` & `reason` specific to `close` event not `error` event. so it'd be better **for him** to use `onclose` instead, am I missing something? – Ihab Khattab Oct 29 '14 at 17:47
  • @IhabKhattab yes, as his question was specific about error code `1006` which has special meaning, and special handling in the websocket spec, and javascript websocket api. The reason string/message under some `1006` conditions are specifically and intentionally not exposed anywhere in the API. (as the answer pointed out). This is not a bug in the API, its merely addressing the various specs and their concerns around abusing websocket for non-websocket purposes. – Joakim Erdfelt Oct 29 '14 at 18:01
  • 1
    In My case same 1006 Error is coming, but this is happening in case of Chrome, Firefox etc. not in case of Opera or Safari of iOS Devices. If anyone have idea then please help me out, http://stackoverflow.com/questions/30799814/file-transfer-from-browser-to-locally-connected-iphone – Mrug Jun 18 '15 at 07:40
  • @JoakimErdfelt how am I going to fix these "low level issues", when I (intentionally) don't get any information abou what issue there is? – El Mac Apr 13 '20 at 10:33
  • @ElMac check the chrome (or firefox) dev-tools console for the details on why its failing, it's usually pointing to something you'll likely have to fix in your code (eg: bad sub-protocol, bad websocket scheme, bad destination host, bad destination port, etc). On very rare occasions the fix needs to be on the server side. – Joakim Erdfelt Apr 13 '20 at 13:24
  • @JoakimErdfelt nothing there: `Error: Connection disconnected with error 'Error: WebSocket closed with status code: 1006 ().'.` – El Mac Apr 13 '20 at 13:25
  • Can’t we access the http status code returned in the failed web socket handshake request ? – Frank Q. Feb 11 '21 at 06:04
  • @FrankQ. no, the WebSocket API does not expose access to that. – Remy Lebeau Jul 05 '21 at 18:45
  • In my case it was server interrupting connection due to too many requests per second. – liaombro Mar 26 '23 at 18:41
67

In my and possibly @BIOHAZARD case it was nginx proxy timeout. In default it's 60 sec without activity in socket

I changed it to 24h in nginx and it resolved problem

proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
mixalbl4
  • 3,507
  • 1
  • 30
  • 44
  • 1
    where to set this? is this a terminal command after install the nginx? – Qadir Hussain May 23 '22 at 07:25
  • @QadirHussain You need to add these lines in the nginx configuration file (placed in /usr/local/nginx/conf, /etc/nginx, or /usr/local/etc/nginx). I'm guessing you're using nginx as reverse proxy? – nulldevops Aug 04 '22 at 17:13
  • This is a proxy issue for sure. I've tested passing my connection thru HAPROXY and then directly to the backend component. I can see even the sessionId is different. Passing thru the HAPROXY the sessionId is a full UUID and directly connecting it is a small hash like ```ghvvedi3```. – Magno C Feb 10 '23 at 17:58
20

It looks like this is the case when Chrome is not compliant with WebSocket standard. When the server initiates close and sends close frame to a client, Chrome considers this to be an error and reports it to JS side with code 1006 and no reason message. In my tests, Chrome never responds to server-initiated close frames (close code 1000) suggesting that code 1006 probably means that Chrome is reporting its own internal error.

P.S. Firefox v57.00 handles this case properly and successfully delivers server's reason message to JS side.

user10663464
  • 209
  • 2
  • 2
  • Arrrgh, I was getting a 404 due to a bad rewrite and Chrome didn't even try to show it... Thanks for the hint! – RobM Nov 01 '22 at 07:37
19

Thought this might be handy for others. Knowing regex is useful, kids. Stay in school.

Edit: Turned it into a handy dandy function!

let specificStatusCodeMappings = {
    '1000': 'Normal Closure',
    '1001': 'Going Away',
    '1002': 'Protocol Error',
    '1003': 'Unsupported Data',
    '1004': '(For future)',
    '1005': 'No Status Received',
    '1006': 'Abnormal Closure',
    '1007': 'Invalid frame payload data',
    '1008': 'Policy Violation',
    '1009': 'Message too big',
    '1010': 'Missing Extension',
    '1011': 'Internal Error',
    '1012': 'Service Restart',
    '1013': 'Try Again Later',
    '1014': 'Bad Gateway',
    '1015': 'TLS Handshake'
};

function getStatusCodeString(code) {
    if (code >= 0 && code <= 999) {
        return '(Unused)';
    } else if (code >= 1016) {
        if (code <= 1999) {
            return '(For WebSocket standard)';
        } else if (code <= 2999) {
            return '(For WebSocket extensions)';
        } else if (code <= 3999) {
            return '(For libraries and frameworks)';
        } else if (code <= 4999) {
            return '(For applications)';
        }
    }
    if (typeof(specificStatusCodeMappings[code]) !== 'undefined') {
        return specificStatusCodeMappings[code];
    }
    return '(Unknown)';
}

Usage:

getStatusCodeString(1006); //'Abnormal Closure'

{
    '0-999': '(Unused)',
    '1016-1999': '(For WebSocket standard)',
    '2000-2999': '(For WebSocket extensions)',
    '3000-3999': '(For libraries and frameworks)',
    '4000-4999': '(For applications)'
}

{
    '1000': 'Normal Closure',
    '1001': 'Going Away',
    '1002': 'Protocol Error',
    '1003': 'Unsupported Data',
    '1004': '(For future)',
    '1005': 'No Status Received',
    '1006': 'Abnormal Closure',
    '1007': 'Invalid frame payload data',
    '1008': 'Policy Violation',
    '1009': 'Message too big',
    '1010': 'Missing Extension',
    '1011': 'Internal Error',
    '1012': 'Service Restart',
    '1013': 'Try Again Later',
    '1014': 'Bad Gateway',
    '1015': 'TLS Handshake'
}

Source (with minor edits for terseness): https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes

Andrew
  • 5,839
  • 1
  • 51
  • 72
8

I've got the error while using Chrome as client and golang gorilla websocket as server under nginx proxy

And sending just some "ping" message from server to client every x second resolved problem

Update: Oh boy I implemented dozens of websocket based apps after this answer and PINGING FROM CLIENT every 5 seconds is the correct way to keep connection with server alive (I do not know what was in my mind when I was recommending to ping from server)

BIOHAZARD
  • 1,937
  • 20
  • 23
3

We had the same problem and actually AWS was our problem.

Setup Websocket connection -> AWS EC2 Loadbalancer -> Nginx Proxy -> Node.js Backend

We increase the timeout based on this answer above in the Nginx conf but didn't see any improvements. We now found out that the AWS Loadbalancer also has a timeout that defaults to 60s. You can edit it under EC2 -> Loadbalancers enter image description here.

Note that we didn't implement a ping-pong scheme. We think that implementing one would also fix the problem until then we just use this workaround and increase the idle timeout.

John Doe
  • 173
  • 2
  • 10
1

Adding this as one of the possible reasons rather than answer to the question.

The issue we had affected only chromium based browsers.

We had a load balancer & the browser was sending more bytes than negotiated during handshake resulting in the load balancer terminating the connection.

TCP windows scaling resolved the issue for us.