[Asterisk-code-review] res http websocket.c: prevent avoidable disconnections cause... (asterisk[master])

Joshua Colp asteriskteam at digium.com
Wed Dec 30 18:43:34 CST 2015


Joshua Colp has submitted this change and it was merged.

Change subject: res_http_websocket.c: prevent avoidable disconnections caused by write errors
......................................................................


res_http_websocket.c: prevent avoidable disconnections caused by write errors

Updated ast_websocket_write to encode the entire frame in to one
write operation, to ensure that we don't end up with a situation
where the websocket header has been sent, while the body can not
be written.

Previous to August's patch in commit b9bd3c14, certain network
conditions could cause the header to be written, and then the
sub-sequent body to fail - which would cause the next successful
write to contain a new header, and a new body (resulting in
the peer receiving two headers - the second of which would be
read as part of the body for the first header).

This was patched to have both write operations individually fail
by closing the websocket.

In a case available to the submitter of this patch, the same
body which would consistently fail to write, would succeed
if written at the same time as the header.

This update merges the two operations in to one, adds debug messages
indicating the reason for a websocket connection being closed during
a write operation, and clarifies some variable names for code legibility.

Change-Id: I4db7a586af1c7a57184c31d3d55bf146f1a40598
---
M include/asterisk/http_websocket.h
M res/res_http_websocket.c
2 files changed, 19 insertions(+), 18 deletions(-)

Approvals:
  Anonymous Coward #1000019: Verified
  Ashley Sanders: Looks good to me, but someone else must approve
  Matt Jordan: Looks good to me, but someone else must approve
  Joshua Colp: Looks good to me, approved



diff --git a/include/asterisk/http_websocket.h b/include/asterisk/http_websocket.h
index 23492ff..cd49dbe 100644
--- a/include/asterisk/http_websocket.h
+++ b/include/asterisk/http_websocket.h
@@ -266,12 +266,12 @@
  * \param session Pointer to the WebSocket session
  * \param opcode WebSocket operation code to place in the frame
  * \param payload Optional pointer to a payload to add to the frame
- * \param actual_length Length of the payload (0 if no payload)
+ * \param payload_size Length of the payload (0 if no payload)
  *
  * \retval 0 if successfully written
  * \retval -1 if error occurred
  */
-AST_OPTIONAL_API(int, ast_websocket_write, (struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t actual_length), { errno = ENOSYS; return -1;});
+AST_OPTIONAL_API(int, ast_websocket_write, (struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t payload_size), { errno = ENOSYS; return -1;});
 
 /*!
  * \brief Construct and transmit a WebSocket frame containing string data.
diff --git a/res/res_http_websocket.c b/res/res_http_websocket.c
index c40aae6..2654578 100644
--- a/res/res_http_websocket.c
+++ b/res/res_http_websocket.c
@@ -332,18 +332,19 @@
 }
 
 /*! \brief Write function for websocket traffic */
-int AST_OPTIONAL_API_NAME(ast_websocket_write)(struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t actual_length)
+int AST_OPTIONAL_API_NAME(ast_websocket_write)(struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t payload_size)
 {
 	size_t header_size = 2; /* The minimum size of a websocket frame is 2 bytes */
 	char *frame;
 	uint64_t length;
+	uint64_t frame_size;
 
 	ast_debug(3, "Writing websocket %s frame, length %" PRIu64 "\n",
-			websocket_opcode2str(opcode), actual_length);
+			websocket_opcode2str(opcode), payload_size);
 
-	if (actual_length < 126) {
-		length = actual_length;
-	} else if (actual_length < (1 << 16)) {
+	if (payload_size < 126) {
+		length = payload_size;
+	} else if (payload_size < (1 << 16)) {
 		length = 126;
 		/* We need an additional 2 bytes to store the extended length */
 		header_size += 2;
@@ -353,37 +354,37 @@
 		header_size += 8;
 	}
 
-	frame = ast_alloca(header_size);
-	memset(frame, 0, header_size);
+	frame_size = header_size + payload_size;
+
+	frame = ast_alloca(frame_size + 1);
+	memset(frame, 0, frame_size + 1);
 
 	frame[0] = opcode | 0x80;
 	frame[1] = length;
 
 	/* Use the additional available bytes to store the length */
 	if (length == 126) {
-		put_unaligned_uint16(&frame[2], htons(actual_length));
+		put_unaligned_uint16(&frame[2], htons(payload_size));
 	} else if (length == 127) {
-		put_unaligned_uint64(&frame[2], htonll(actual_length));
+		put_unaligned_uint64(&frame[2], htonll(payload_size));
 	}
+
+	memcpy(&frame[header_size], payload, payload_size);
 
 	ao2_lock(session);
 	if (session->closing) {
 		ao2_unlock(session);
 		return -1;
 	}
-	if (ast_careful_fwrite(session->f, session->fd, frame, header_size, session->timeout)) {
+
+	if (ast_careful_fwrite(session->f, session->fd, frame, frame_size, session->timeout)) {
 		ao2_unlock(session);
 		/* 1011 - server terminating connection due to not being able to fulfill the request */
+		ast_debug(1, "Closing WS with 1011 because we can't fulfill a write request\n");
 		ast_websocket_close(session, 1011);
 		return -1;
 	}
 
-	if (ast_careful_fwrite(session->f, session->fd, payload, actual_length, session->timeout)) {
-		ao2_unlock(session);
-		/* 1011 - server terminating connection due to not being able to fulfill the request */
-		ast_websocket_close(session, 1011);
-		return -1;
-	}
 	fflush(session->f);
 	ao2_unlock(session);
 

-- 
To view, visit https://gerrit.asterisk.org/1867
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I4db7a586af1c7a57184c31d3d55bf146f1a40598
Gerrit-PatchSet: 2
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Dade Brandon <dade at xencall.com>
Gerrit-Reviewer: Anonymous Coward #1000019
Gerrit-Reviewer: Ashley Sanders <asanders at digium.com>
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Matt Jordan <mjordan at digium.com>



More information about the asterisk-code-review mailing list