]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
res_http_websocket: Forcefully terminate on write errors. 75/1075/1
authorJoshua Colp <jcolp@digium.com>
Tue, 11 Aug 2015 10:24:30 +0000 (07:24 -0300)
committerJoshua Colp <jcolp@digium.com>
Wed, 12 Aug 2015 09:52:24 +0000 (06:52 -0300)
The res_http_websocket module will currently attempt to close
the WebSocket connection if fatal cases occur, such as when
attempting to write out data and being unable to. When the
fatal cases occur the code attempts to write a WebSocket close
frame out to have the remote side close the connection. If
writing this fails then the connection is not terminated.

This change forcefully terminates the connection if the
WebSocket is to be closed but is unable to send the close frame.

ASTERISK-25312 #close

Change-Id: I10973086671cc192a76424060d9ec8e688602845

res/res_http_websocket.c

index 4f42ef04febd82042044d0490d814a7859ad8083..8319579d589e8fb3e8746d5cdd1ed90f75c4c56e 100644 (file)
@@ -263,6 +263,17 @@ int AST_OPTIONAL_API_NAME(ast_websocket_close)(struct ast_websocket *session, ui
 
        ao2_lock(session);
        res = ast_careful_fwrite(session->f, session->fd, frame, 4, session->timeout);
+
+       /* If an error occurred when trying to close this connection explicitly terminate it now.
+        * Doing so will cause the thread polling on it to wake up and terminate.
+        */
+       if (res) {
+               fclose(session->f);
+               session->f = NULL;
+               ast_verb(2, "WebSocket connection %s '%s' forcefully closed due to fatal write error\n",
+                       session->client ? "to" : "from", ast_sockaddr_stringify(&session->address));
+       }
+
        ao2_unlock(session);
        return res;
 }
@@ -417,6 +428,14 @@ static inline int ws_safe_read(struct ast_websocket *session, char *buf, int len
        size_t rlen;
        int xlen = len;
        char *rbuf = buf;
+
+       ao2_lock(session);
+       if (!session->f) {
+               ao2_unlock(session);
+               errno = ECONNABORTED;
+               return -1;
+       }
+
        for (sanity = 10; sanity; sanity--) {
                clearerr(session->f);
                rlen = fread(rbuf, 1, xlen, session->f);
@@ -424,6 +443,7 @@ static inline int ws_safe_read(struct ast_websocket *session, char *buf, int len
                        ast_log(LOG_ERROR, "Error reading from web socket: %s\n", strerror(errno));
                        *opcode = AST_WEBSOCKET_OPCODE_CLOSE;
                        session->closing = 1;
+                       ao2_unlock(session);
                        return -1;
                }
                xlen = xlen - rlen;
@@ -435,6 +455,7 @@ static inline int ws_safe_read(struct ast_websocket *session, char *buf, int len
                        ast_log(LOG_ERROR, "ast_wait_for_input returned err: %s\n", strerror(errno));
                        *opcode = AST_WEBSOCKET_OPCODE_CLOSE;
                        session->closing = 1;
+                       ao2_unlock(session);
                        return -1;
                }
        }
@@ -442,8 +463,10 @@ static inline int ws_safe_read(struct ast_websocket *session, char *buf, int len
                ast_log(LOG_WARNING, "Websocket seems unresponsive, disconnecting ...\n");
                *opcode = AST_WEBSOCKET_OPCODE_CLOSE;
                session->closing = 1;
+               ao2_unlock(session);
                return -1;
        }
+       ao2_unlock(session);
        return 0;
 }
 
@@ -460,7 +483,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, cha
        *fragmented = 0;
 
        if (ws_safe_read(session, &buf[0], MIN_WS_HDR_SZ, opcode)) {
-               return 0;
+               return -1;
        }
        frame_size += MIN_WS_HDR_SZ;
 
@@ -478,7 +501,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, cha
                if (options_len) {
                        /* read the rest of the header options */
                        if (ws_safe_read(session, &buf[frame_size], options_len, opcode)) {
-                               return 0;
+                               return -1;
                        }
                        frame_size += options_len;
                }
@@ -507,7 +530,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, cha
                }
 
                if (ws_safe_read(session, *payload, *payload_len, opcode)) {
-                       return 0;
+                       return -1;
                }
                /* If a mask is present unmask the payload */
                if (mask_present) {
@@ -530,7 +553,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, cha
                                        session->payload, session->payload_len, *payload_len);
                                *payload_len = 0;
                                ast_websocket_close(session, 1009);
-                               return 0;
+                               return -1;
                        }
 
                        session->payload = new_payload;
@@ -567,7 +590,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, cha
                /* Make the payload available so the user can look at the reason code if they so desire */
                if ((*payload_len) && (new_payload = ast_realloc(session->payload, *payload_len))) {
                        if (ws_safe_read(session, &buf[frame_size], (*payload_len), opcode)) {
-                               return 0;
+                               return -1;
                        }
                        session->payload = new_payload;
                        memcpy(session->payload, &buf[frame_size], *payload_len);