int wslay_event_queue_msg(wslay_event_context_ptr ctx, const struct wslay_event_msg *arg) { int r; struct wslay_event_omsg *omsg; if(!wslay_event_is_msg_queueable(ctx)) { return WSLAY_ERR_NO_MORE_MSG; } if(wslay_is_ctrl_frame(arg->opcode) && arg->msg_length > 125) { return WSLAY_ERR_INVALID_ARGUMENT; } if((r = wslay_event_omsg_non_fragmented_init (&omsg, arg->opcode, arg->msg, arg->msg_length)) != 0) { return r; } if(wslay_is_ctrl_frame(arg->opcode)) { if((r = wslay_queue_push(ctx->send_ctrl_queue, omsg)) != 0) { return r; } } else { if((r = wslay_queue_push(ctx->send_queue, omsg)) != 0) { return r; } } ++ctx->queued_msg_count; ctx->queued_msg_length += arg->msg_length; return 0; }
int wslay_event_queue_msg_ex(wslay_event_context_ptr ctx, const struct wslay_event_msg *arg, uint8_t rsv) { int r; struct wslay_event_omsg *omsg; if (!wslay_event_is_msg_queueable(ctx)) { return WSLAY_ERR_NO_MORE_MSG; } /* RSV1 is not allowed for control frames */ if ((wslay_is_ctrl_frame(arg->opcode) && (arg->msg_length > 125 || wslay_get_rsv1(rsv))) || !wslay_event_verify_rsv_bits(ctx, rsv)) { return WSLAY_ERR_INVALID_ARGUMENT; } if ((r = wslay_event_omsg_non_fragmented_init(&omsg, arg->opcode, rsv, arg->msg, arg->msg_length)) != 0) { return r; } if (wslay_is_ctrl_frame(arg->opcode)) { if ((r = wslay_queue_push(ctx->send_ctrl_queue, omsg)) != 0) { return r; } } else { if ((r = wslay_queue_push(ctx->send_queue, omsg)) != 0) { return r; } } ++ctx->queued_msg_count; ctx->queued_msg_length += arg->msg_length; return 0; }
void on_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { if(!wslay_is_ctrl_frame(arg->opcode)) { struct wslay_event_msg msgarg = { arg->opcode, arg->msg, arg->msg_length }; wslay_event_queue_msg(ctx, &msgarg); } }
static void on_ws_message(h2o_websocket_conn_t *conn, const struct wslay_event_on_msg_recv_arg *arg) { if (arg == NULL) { h2o_websocket_close(conn); return; } if (!wslay_is_ctrl_frame(arg->opcode)) { struct wslay_event_msg msgarg = {arg->opcode, arg->msg, arg->msg_length}; wslay_event_queue_msg(conn->ws_ctx, &msgarg); } }
int wslay_event_queue_fragmented_msg_ex(wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg, uint8_t rsv) { int r; struct wslay_event_omsg *omsg; if (!wslay_event_is_msg_queueable(ctx)) { return WSLAY_ERR_NO_MORE_MSG; } if (wslay_is_ctrl_frame(arg->opcode) || !wslay_event_verify_rsv_bits(ctx, rsv)) { return WSLAY_ERR_INVALID_ARGUMENT; } if ((r = wslay_event_omsg_fragmented_init(&omsg, arg->opcode, rsv, arg->source, arg->read_callback)) != 0) { return r; } if ((r = wslay_queue_push(ctx->send_queue, omsg)) != 0) { return r; } ++ctx->queued_msg_count; return 0; }
void on_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { (void)user_data; /* Echo back non-control message */ if(!wslay_is_ctrl_frame(arg->opcode)) { struct wslay_event_msg msgarg; msgarg.opcode = arg->opcode; msgarg.msg = arg->msg; msgarg.msg_length = arg->msg_length; wslay_event_queue_msg(ctx, &msgarg); } #if DBG_PRINT if(1) { char *data = (char*)malloc((arg->msg_length+1)*sizeof(char)); memcpy(data, arg->msg, arg->msg_length); data[arg->msg_length] = '\0'; printf("Msg recv callback, opcode: %d msg: %s\n", arg->opcode, data); } #endif }
int wslay_event_send(wslay_event_context_ptr ctx) { struct wslay_frame_iocb iocb; ssize_t r; while (ctx->write_enabled && (!wslay_queue_empty(ctx->send_queue) || !wslay_queue_empty(ctx->send_ctrl_queue) || ctx->omsg)) { if (!ctx->omsg) { if (wslay_queue_empty(ctx->send_ctrl_queue)) { ctx->omsg = wslay_queue_top(ctx->send_queue); wslay_queue_pop(ctx->send_queue); } else { ctx->omsg = wslay_event_send_ctrl_queue_pop(ctx); if (ctx->omsg == NULL) { break; } } if (ctx->omsg->type == WSLAY_NON_FRAGMENTED) { wslay_event_on_non_fragmented_msg_popped(ctx); } } else if (!wslay_is_ctrl_frame(ctx->omsg->opcode) && ctx->frame_ctx->ostate == PREP_HEADER && !wslay_queue_empty(ctx->send_ctrl_queue)) { if ((r = wslay_queue_push_front(ctx->send_queue, ctx->omsg)) != 0) { ctx->write_enabled = 0; return r; } ctx->omsg = wslay_event_send_ctrl_queue_pop(ctx); if (ctx->omsg == NULL) { break; } /* ctrl message has WSLAY_NON_FRAGMENTED */ wslay_event_on_non_fragmented_msg_popped(ctx); } if (ctx->omsg->type == WSLAY_NON_FRAGMENTED) { memset(&iocb, 0, sizeof(iocb)); iocb.fin = 1; iocb.opcode = ctx->omsg->opcode; iocb.rsv = ctx->omsg->rsv; iocb.mask = ctx->server ^ 1; iocb.data = ctx->omsg->data + ctx->opayloadoff; iocb.data_length = ctx->opayloadlen - ctx->opayloadoff; iocb.payload_length = ctx->opayloadlen; r = wslay_frame_send(ctx->frame_ctx, &iocb); if (r >= 0) { ctx->opayloadoff += r; if (ctx->opayloadoff == ctx->opayloadlen) { --ctx->queued_msg_count; ctx->queued_msg_length -= ctx->omsg->data_length; if (ctx->omsg->opcode == WSLAY_CONNECTION_CLOSE) { uint16_t status_code = 0; ctx->write_enabled = 0; ctx->close_status |= WSLAY_CLOSE_SENT; if (ctx->omsg->data_length >= 2) { memcpy(&status_code, ctx->omsg->data, 2); status_code = ntohs(status_code); } ctx->status_code_sent = status_code == 0 ? WSLAY_CODE_NO_STATUS_RCVD : status_code; } wslay_event_omsg_free(ctx->omsg); ctx->omsg = NULL; } else { break; } } else { if (r != WSLAY_ERR_WANT_WRITE || (ctx->error != WSLAY_ERR_WOULDBLOCK && ctx->error != 0)) { ctx->write_enabled = 0; return WSLAY_ERR_CALLBACK_FAILURE; } break; } } else { if (ctx->omsg->fin == 0 && ctx->obuflimit == ctx->obufmark) { int eof = 0; r = ctx->omsg->read_callback(ctx, ctx->obuf, sizeof(ctx->obuf), &ctx->omsg->source, &eof, ctx->user_data); if (r == 0) { break; } else if (r < 0) { ctx->write_enabled = 0; return WSLAY_ERR_CALLBACK_FAILURE; } ctx->obuflimit = ctx->obuf + r; if (eof) { ctx->omsg->fin = 1; } ctx->opayloadlen = (uint64_t)r; ctx->opayloadoff = 0; } memset(&iocb, 0, sizeof(iocb)); iocb.fin = ctx->omsg->fin; iocb.opcode = ctx->omsg->opcode; iocb.rsv = ctx->omsg->rsv; iocb.mask = ctx->server ? 0 : 1; iocb.data = ctx->obufmark; iocb.data_length = ctx->obuflimit - ctx->obufmark; iocb.payload_length = ctx->opayloadlen; r = wslay_frame_send(ctx->frame_ctx, &iocb); if (r >= 0) { ctx->obufmark += r; if (ctx->obufmark == ctx->obuflimit) { ctx->obufmark = ctx->obuflimit = ctx->obuf; if (ctx->omsg->fin) { --ctx->queued_msg_count; wslay_event_omsg_free(ctx->omsg); ctx->omsg = NULL; break; } else { ctx->omsg->opcode = WSLAY_CONTINUATION_FRAME; /* RSV1 is not set on continuation frames */ ctx->omsg->rsv = ctx->omsg->rsv & ~WSLAY_RSV1_BIT; } } else { break; } } else { if (r != WSLAY_ERR_WANT_WRITE || (ctx->error != WSLAY_ERR_WOULDBLOCK && ctx->error != 0)) { ctx->write_enabled = 0; return WSLAY_ERR_CALLBACK_FAILURE; } break; } } } return 0; }
int wslay_event_recv(wslay_event_context_ptr ctx) { struct wslay_frame_iocb iocb; ssize_t r; while (ctx->read_enabled) { memset(&iocb, 0, sizeof(iocb)); r = wslay_frame_recv(ctx->frame_ctx, &iocb); if (r >= 0) { int new_frame = 0; /* RSV1 is not allowed on control and continuation frames */ if ((!wslay_event_verify_rsv_bits(ctx, iocb.rsv)) || (wslay_get_rsv1(iocb.rsv) && (wslay_is_ctrl_frame(iocb.opcode) || iocb.opcode == WSLAY_CONTINUATION_FRAME)) || (ctx->server && !iocb.mask) || (!ctx->server && iocb.mask)) { if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) { return r; } break; } if (ctx->imsg->opcode == 0xffu) { if (iocb.opcode == WSLAY_TEXT_FRAME || iocb.opcode == WSLAY_BINARY_FRAME || iocb.opcode == WSLAY_CONNECTION_CLOSE || iocb.opcode == WSLAY_PING || iocb.opcode == WSLAY_PONG) { wslay_event_imsg_set(ctx->imsg, iocb.fin, iocb.rsv, iocb.opcode); new_frame = 1; } else { if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) { return r; } break; } } else if (ctx->ipayloadlen == 0 && ctx->ipayloadoff == 0) { if (iocb.opcode == WSLAY_CONTINUATION_FRAME) { ctx->imsg->fin = iocb.fin; } else if (iocb.opcode == WSLAY_CONNECTION_CLOSE || iocb.opcode == WSLAY_PING || iocb.opcode == WSLAY_PONG) { ctx->imsg = &ctx->imsgs[1]; wslay_event_imsg_set(ctx->imsg, iocb.fin, iocb.rsv, iocb.opcode); } else { if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) { return r; } break; } new_frame = 1; } if (new_frame) { if (ctx->imsg->msg_length + iocb.payload_length > ctx->max_recv_msg_length) { if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_MESSAGE_TOO_BIG, NULL, 0)) != 0) { return r; } break; } ctx->ipayloadlen = iocb.payload_length; wslay_event_call_on_frame_recv_start_callback(ctx, &iocb); if (!wslay_event_config_get_no_buffering(ctx) || wslay_is_ctrl_frame(iocb.opcode)) { if ((r = wslay_event_imsg_append_chunk(ctx->imsg, iocb.payload_length)) != 0) { ctx->read_enabled = 0; return r; } } } /* If RSV1 bit is set then it is too early for utf-8 validation */ if ((!wslay_get_rsv1(iocb.rsv) && ctx->imsg->opcode == WSLAY_TEXT_FRAME) || ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) { size_t i; if (ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) { i = 2; } else { i = 0; } for (; i < iocb.data_length; ++i) { uint32_t codep; if (decode(&ctx->imsg->utf8state, &codep, iocb.data[i]) == UTF8_REJECT) { if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA, NULL, 0)) != 0) { return r; } break; } } } if (ctx->imsg->utf8state == UTF8_REJECT) { break; } wslay_event_call_on_frame_recv_chunk_callback(ctx, &iocb); if (iocb.data_length > 0) { if (!wslay_event_config_get_no_buffering(ctx) || wslay_is_ctrl_frame(iocb.opcode)) { struct wslay_event_byte_chunk *chunk; chunk = wslay_queue_tail(ctx->imsg->chunks); wslay_event_byte_chunk_copy(chunk, ctx->ipayloadoff, iocb.data, iocb.data_length); } ctx->ipayloadoff += iocb.data_length; } if (ctx->ipayloadoff == ctx->ipayloadlen) { if (ctx->imsg->fin && (ctx->imsg->opcode == WSLAY_TEXT_FRAME || ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) && ctx->imsg->utf8state != UTF8_ACCEPT) { if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA, NULL, 0)) != 0) { return r; } break; } wslay_event_call_on_frame_recv_end_callback(ctx); if (ctx->imsg->fin) { if (ctx->callbacks.on_msg_recv_callback || ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE || ctx->imsg->opcode == WSLAY_PING || ctx->imsg->opcode == WSLAY_PONG) { struct wslay_event_on_msg_recv_arg arg; uint16_t status_code = 0; uint8_t *msg = NULL; size_t msg_length = 0; if (!wslay_event_config_get_no_buffering(ctx) || wslay_is_ctrl_frame(iocb.opcode)) { msg = wslay_event_flatten_queue(ctx->imsg->chunks, ctx->imsg->msg_length); if (ctx->imsg->msg_length && !msg) { ctx->read_enabled = 0; return WSLAY_ERR_NOMEM; } msg_length = ctx->imsg->msg_length; } if (ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) { const uint8_t *reason; size_t reason_length; if (ctx->imsg->msg_length >= 2) { memcpy(&status_code, msg, 2); status_code = ntohs(status_code); if (!wslay_event_is_valid_status_code(status_code)) { free(msg); if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) { return r; } break; } reason = msg + 2; reason_length = ctx->imsg->msg_length - 2; } else { reason = NULL; reason_length = 0; } ctx->close_status |= WSLAY_CLOSE_RECEIVED; ctx->status_code_recv = status_code == 0 ? WSLAY_CODE_NO_STATUS_RCVD : status_code; if ((r = wslay_event_queue_close_wrapper(ctx, status_code, reason, reason_length)) != 0) { free(msg); return r; } } else if (ctx->imsg->opcode == WSLAY_PING) { struct wslay_event_msg pong_arg; pong_arg.opcode = WSLAY_PONG; pong_arg.msg = msg; pong_arg.msg_length = ctx->imsg->msg_length; if ((r = wslay_event_queue_msg(ctx, &pong_arg)) && r != WSLAY_ERR_NO_MORE_MSG) { ctx->read_enabled = 0; free(msg); return r; } } else if (ctx->imsg->opcode == WSLAY_PONG) { struct websocket_info_t *info = ctx->user_data; info->data->ping_cnt = 0; } if (ctx->callbacks.on_msg_recv_callback) { arg.rsv = ctx->imsg->rsv; arg.opcode = ctx->imsg->opcode; arg.msg = msg; arg.msg_length = msg_length; arg.status_code = status_code; ctx->error = 0; ctx->callbacks.on_msg_recv_callback(ctx, &arg, ctx->user_data); if (ctx->imsg->opcode != WSLAY_CONNECTION_CLOSE) { free(msg); wslay_event_imsg_reset(ctx->imsg); if (ctx->imsg == &ctx->imsgs[1]) { ctx->imsg = &ctx->imsgs[0]; } ctx->ipayloadlen = ctx->ipayloadoff = 0; break; } } if (ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) { struct websocket_info_t *info = ctx->user_data; if (!wslay_queue_empty(ctx->send_ctrl_queue)) { wslay_event_send(ctx); } websocket_update_state(info->data, WEBSOCKET_STOP); } free(msg); } wslay_event_imsg_reset(ctx->imsg); if (ctx->imsg == &ctx->imsgs[1]) { ctx->imsg = &ctx->imsgs[0]; } } ctx->ipayloadlen = ctx->ipayloadoff = 0; } } else { if (r != WSLAY_ERR_WANT_READ || (ctx->error != WSLAY_ERR_WOULDBLOCK && ctx->error != 0)) { if ((r = wslay_event_queue_close_wrapper(ctx, 0, NULL, 0)) != 0) { return r; } return WSLAY_ERR_CALLBACK_FAILURE; } break; } } return 0; }
/** * \brief WebSocket callback function for recived message */ void vs_ws_recv_msg_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { struct vContext *C = (struct vContext*)user_data; struct VSession *session = CTX_current_session(C); struct IO_CTX *io_ctx = CTX_io_ctx(C); (void)ctx; if(!wslay_is_ctrl_frame(arg->opcode)) { /* Verse server uses binary message for communication */ if(arg->opcode == WSLAY_BINARY_FRAME) { struct wslay_event_msg msgarg; v_print_log(VRS_PRINT_DEBUG_MSG, "WS Callback received binary message\n"); /* Copy received data to IO context */ memcpy(io_ctx->buf, arg->msg, arg->msg_length); io_ctx->buf_size = arg->msg_length; if(session->stream_conn->host_state == TCP_SERVER_STATE_STREAM_OPEN) { if(v_STREAM_handle_messages(C) == 0) { /* TODO: end connection */ return; } } else { if( vs_handle_handshake(C, NULL) == -1 ) { /* TODO: end connection */ return; } msgarg.opcode = WSLAY_BINARY_FRAME; msgarg.msg = (uint8_t*)io_ctx->buf; msgarg.msg_length = io_ctx->buf_size; wslay_event_queue_msg(ctx, &msgarg); } } else if(arg->opcode == WSLAY_TEXT_FRAME) { v_print_log(VRS_PRINT_ERROR, "WebSocket text frame is not supported\n"); return; } } else { /* Print opcode of control message */ v_print_log(VRS_PRINT_DEBUG_MSG, "WS Callback Received Ctrl Message: opcode: %d\n", arg->opcode); /* Is it closing message? */ if(arg->opcode & WSLAY_CONNECTION_CLOSE) { v_print_log(VRS_PRINT_DEBUG_MSG, "Close message with code: %d, message: %s\n", arg->status_code, arg->msg); if(session->stream_conn->host_state == TCP_SERVER_STATE_CLOSING) { session->stream_conn->host_state = TCP_SERVER_STATE_CLOSED; } else { session->stream_conn->host_state = TCP_SERVER_STATE_CLOSING; wslay_event_queue_close(ctx, WSLAY_CODE_NORMAL_CLOSURE, (uint8_t*)"Closing connection", 15); } } } }
/** * \brief WebSocket callback function for received message */ void vs_ws_recv_msg_callback(wslay_event_context_ptr wslay_ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { struct vContext *C = (struct vContext*)user_data; struct VS_CTX *vs_ctx = CTX_server_ctx(C); struct VSession *session = CTX_current_session(C); struct IO_CTX *io_ctx = CTX_io_ctx(C); int ret; if(!wslay_is_ctrl_frame(arg->opcode)) { /* Verse server uses only binary message for communication */ if(arg->opcode == WSLAY_BINARY_FRAME) { struct wslay_event_msg msgarg; #if DEBUG_WEB_SOCKET unsigned int i; v_print_log(VRS_PRINT_DEBUG_MSG, "WS Callback received binary message\n"); v_print_log(VRS_PRINT_DEBUG_MSG, "Binary dump\n"); /* Print dump of received data */ for(i=0; i<arg->msg_length; i++) { v_print_log_simple(VRS_PRINT_DEBUG_MSG, "%d,", arg->msg[i]); } v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\n"); #endif /* Copy received data to IO context */ memcpy(io_ctx->buf, arg->msg, arg->msg_length); io_ctx->buf_size = arg->msg_length; if(session->stream_conn->host_state == TCP_SERVER_STATE_STREAM_OPEN) { if(v_STREAM_handle_messages(C) != 0) { /* When some payload data were received, then poke data thread */ sem_post(vs_ctx->data.sem); } else { /* End connection */ session->stream_conn->host_state = TCP_SERVER_STATE_CLOSING; /* Try to close connection with WebSocket client */ wslay_event_queue_close(wslay_ctx, WSLAY_CODE_PROTOCOL_ERROR, (uint8_t*)"Wrong command", /* Close message */ 13); /* The length of close message */ return; } } else { if( vs_handle_handshake(C) == -1 ) { /* End connection */ session->stream_conn->host_state = TCP_SERVER_STATE_CLOSING; /* Try to close connection with WebSocket client */ wslay_event_queue_close(wslay_ctx, WSLAY_CODE_PROTOCOL_ERROR, (uint8_t*)"Wrong command", /* Close message */ 13); /* The length of close message */ return; } /* During handshake send response immediately. */ /* TODO: optionally send message fragmented, when it is needed using: * wslay_event_queue_fragmented_msg() */ msgarg.opcode = WSLAY_BINARY_FRAME; msgarg.msg = (uint8_t*)io_ctx->buf; msgarg.msg_length = io_ctx->buf_size; /* Queue message for sending */ if((ret = wslay_event_queue_msg(wslay_ctx, &msgarg)) != 0) { v_print_log(VRS_PRINT_ERROR, "Unable to queue message to WebSocket: %d\n", ret); return; } else { v_print_log(VRS_PRINT_DEBUG_MSG, "WebSocket message successfully queued\n"); } } } else if(arg->opcode == WSLAY_TEXT_FRAME) { v_print_log(VRS_PRINT_ERROR, "WebSocket text frame is not supported\n"); return; } } else { /* Print opcode of control message */ v_print_log(VRS_PRINT_DEBUG_MSG, "WS Callback Received Ctrl Message: opcode: %d\n", arg->opcode); /* Is it closing message? */ if(arg->opcode & WSLAY_CONNECTION_CLOSE) { v_print_log(VRS_PRINT_DEBUG_MSG, "Close message with code: %d, message: %s\n", arg->status_code, arg->msg); /* When this control message was received at second time, then * switch to the state CLOSED. Otherwise switch to the state * CLOSING */ if(session->stream_conn->host_state == TCP_SERVER_STATE_CLOSING) { session->stream_conn->host_state = TCP_SERVER_STATE_CLOSED; } else { session->stream_conn->host_state = TCP_SERVER_STATE_CLOSING; /* When server wasn't in the state closing, then send * confirmation to the client, that this connection will be * closed */ wslay_event_queue_close(wslay_ctx, WSLAY_CODE_NORMAL_CLOSURE, (uint8_t*)"Closing connection", 15); } } } }