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; }
static mrb_value mrb_wslay_get_rsv1(mrb_state *mrb, mrb_value self) { mrb_int rsv; mrb_get_args(mrb, "i", &rsv); return mrb_fixnum_value(wslay_get_rsv1(rsv)); }
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; }
static void on_msg_recv_cb(wslay_event_context_ptr ev, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { struct transaction_t *txn = (struct transaction_t *) user_data; struct ws_context *ctx = (struct ws_context *) txn->ws_ctx; struct buf inbuf = BUF_INITIALIZER, outbuf = BUF_INITIALIZER; struct wslay_event_msg msgarg = { arg->opcode, NULL, 0 }; uint8_t rsv = WSLAY_RSV_NONE; double cmdtime, nettime; const char *err_msg; int r, err_code = 0; /* Place client request into a buf */ buf_init_ro(&inbuf, (const char *) arg->msg, arg->msg_length); /* Decompress request, if necessary */ if (wslay_get_rsv1(arg->rsv)) { /* Add trailing 4 bytes */ buf_appendmap(&inbuf, "\x00\x00\xff\xff", 4); r = zlib_decompress(txn, buf_base(&inbuf), buf_len(&inbuf)); if (r) { syslog(LOG_ERR, "on_msg_recv_cb(): zlib_decompress() failed"); err_code = WSLAY_CODE_PROTOCOL_ERROR; err_msg = DECOMP_FAILED_ERR; goto err; } buf_move(&inbuf, &txn->zbuf); } /* Log the uncompressed client request */ buf_truncate(&ctx->log, ctx->log_tail); buf_appendcstr(&ctx->log, " ("); if (txn->strm_ctx) { buf_printf(&ctx->log, "stream-id=%d; ", http2_get_streamid(txn->strm_ctx)); } buf_printf(&ctx->log, "opcode=%s; rsv=0x%x; length=%ld", wslay_str_opcode(arg->opcode), arg->rsv, arg->msg_length); switch (arg->opcode) { case WSLAY_CONNECTION_CLOSE: buf_printf(&ctx->log, "; status=%d; msg='%s'", arg->status_code, buf_len(&inbuf) ? buf_cstring(&inbuf)+2 : ""); txn->flags.conn = CONN_CLOSE; break; case WSLAY_TEXT_FRAME: case WSLAY_BINARY_FRAME: if (txn->conn->logfd != -1) { /* Telemetry logging */ struct iovec iov[2]; int niov = 0; assert(!buf_len(&txn->buf)); buf_printf(&txn->buf, "<%ld<", time(NULL)); /* timestamp */ WRITEV_ADD_TO_IOVEC(iov, niov, buf_base(&txn->buf), buf_len(&txn->buf)); WRITEV_ADD_TO_IOVEC(iov, niov, buf_base(&inbuf), buf_len(&inbuf)); writev(txn->conn->logfd, iov, niov); buf_reset(&txn->buf); } /* Process the request */ r = ctx->data_cb(&inbuf, &outbuf, &ctx->log, &ctx->cb_rock); if (r) { err_code = (r == HTTP_SERVER_ERROR ? WSLAY_CODE_INTERNAL_SERVER_ERROR : WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA); err_msg = error_message(r); goto err; } if (txn->conn->logfd != -1) { /* Telemetry logging */ struct iovec iov[2]; int niov = 0; assert(!buf_len(&txn->buf)); buf_printf(&txn->buf, ">%ld>", time(NULL)); /* timestamp */ WRITEV_ADD_TO_IOVEC(iov, niov, buf_base(&txn->buf), buf_len(&txn->buf)); WRITEV_ADD_TO_IOVEC(iov, niov, buf_base(&outbuf), buf_len(&outbuf)); writev(txn->conn->logfd, iov, niov); buf_reset(&txn->buf); } /* Compress the server response, if supported by the client */ if (ctx->ext & EXT_PMCE_DEFLATE) { r = zlib_compress(txn, ctx->pmce.deflate.no_context ? COMPRESS_START : 0, buf_base(&outbuf), buf_len(&outbuf)); if (r) { syslog(LOG_ERR, "on_msg_recv_cb(): zlib_compress() failed"); err_code = WSLAY_CODE_INTERNAL_SERVER_ERROR; err_msg = COMP_FAILED_ERR; goto err; } /* Trim the trailing 4 bytes */ buf_truncate(&txn->zbuf, buf_len(&txn->zbuf) - 4); buf_move(&outbuf, &txn->zbuf); rsv |= WSLAY_RSV1_BIT; } /* Queue the server response */ msgarg.msg = (const uint8_t *) buf_base(&outbuf); msgarg.msg_length = buf_len(&outbuf); wslay_event_queue_msg_ex(ev, &msgarg, rsv); /* Log the server response */ buf_printf(&ctx->log, ") => \"Success\" (opcode=%s; rsv=0x%x; length=%ld", wslay_str_opcode(msgarg.opcode), rsv, msgarg.msg_length); break; } err: if (err_code) { size_t err_msg_len = strlen(err_msg); syslog(LOG_DEBUG, "wslay_event_queue_close()"); wslay_event_queue_close(ev, err_code, (uint8_t *) err_msg, err_msg_len); /* Log the server response */ buf_printf(&ctx->log, ") => \"Fail\" (opcode=%s; rsv=0x%x; length=%ld" "; status=%d; msg='%s'", wslay_str_opcode(WSLAY_CONNECTION_CLOSE), rsv, err_msg_len, err_code, err_msg); } /* Add timing stats */ cmdtime_endtimer(&cmdtime, &nettime); buf_printf(&ctx->log, ") [timing: cmd=%f net=%f total=%f]", cmdtime, nettime, cmdtime + nettime); syslog(LOG_INFO, "%s", buf_cstring(&ctx->log)); buf_free(&inbuf); buf_free(&outbuf); }