Merge "res_http_websocket.c: prevent avoidable disconnections caused by write errors"

changes/85/1885/2
Joshua Colp 9 years ago committed by Gerrit Code Review
commit 4e5250d5c2

@ -266,12 +266,12 @@ AST_OPTIONAL_API(int, ast_websocket_read_string,
* \param session Pointer to the WebSocket session * \param session Pointer to the WebSocket session
* \param opcode WebSocket operation code to place in the frame * \param opcode WebSocket operation code to place in the frame
* \param payload Optional pointer to a payload to add to 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 0 if successfully written
* \retval -1 if error occurred * \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. * \brief Construct and transmit a WebSocket frame containing string data.

@ -332,18 +332,19 @@ static const char *websocket_opcode2str(enum ast_websocket_opcode opcode)
} }
/*! \brief Write function for websocket traffic */ /*! \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 */ size_t header_size = 2; /* The minimum size of a websocket frame is 2 bytes */
char *frame; char *frame;
uint64_t length; uint64_t length;
uint64_t frame_size;
ast_debug(3, "Writing websocket %s frame, length %" PRIu64 "\n", ast_debug(3, "Writing websocket %s frame, length %" PRIu64 "\n",
websocket_opcode2str(opcode), actual_length); websocket_opcode2str(opcode), payload_size);
if (actual_length < 126) { if (payload_size < 126) {
length = actual_length; length = payload_size;
} else if (actual_length < (1 << 16)) { } else if (payload_size < (1 << 16)) {
length = 126; length = 126;
/* We need an additional 2 bytes to store the extended length */ /* We need an additional 2 bytes to store the extended length */
header_size += 2; header_size += 2;
@ -353,37 +354,37 @@ int AST_OPTIONAL_API_NAME(ast_websocket_write)(struct ast_websocket *session, en
header_size += 8; header_size += 8;
} }
frame = ast_alloca(header_size); frame_size = header_size + payload_size;
memset(frame, 0, header_size);
frame = ast_alloca(frame_size + 1);
memset(frame, 0, frame_size + 1);
frame[0] = opcode | 0x80; frame[0] = opcode | 0x80;
frame[1] = length; frame[1] = length;
/* Use the additional available bytes to store the length */ /* Use the additional available bytes to store the length */
if (length == 126) { if (length == 126) {
put_unaligned_uint16(&frame[2], htons(actual_length)); put_unaligned_uint16(&frame[2], htons(payload_size));
} else if (length == 127) { } 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); ao2_lock(session);
if (session->closing) { if (session->closing) {
ao2_unlock(session); ao2_unlock(session);
return -1; return -1;
} }
if (ast_careful_fwrite(session->f, session->fd, frame, header_size, 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;
}
if (ast_careful_fwrite(session->f, session->fd, payload, actual_length, session->timeout)) { if (ast_careful_fwrite(session->f, session->fd, frame, frame_size, session->timeout)) {
ao2_unlock(session); ao2_unlock(session);
/* 1011 - server terminating connection due to not being able to fulfill the request */ /* 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); ast_websocket_close(session, 1011);
return -1; return -1;
} }
fflush(session->f); fflush(session->f);
ao2_unlock(session); ao2_unlock(session);

Loading…
Cancel
Save