Skip to content

Commit

Permalink
Add wslay_event_write and wslay_frame_write
Browse files Browse the repository at this point in the history
  • Loading branch information
tatsuhiro-t committed Jan 14, 2021
1 parent 1c97e7a commit 30fb19f
Show file tree
Hide file tree
Showing 14 changed files with 793 additions and 0 deletions.
1 change: 1 addition & 0 deletions doc/Makefile.am
Expand Up @@ -31,6 +31,7 @@ MANPAGES_WITH_SRC = \
man/wslay_event_queue_msg.3 \
man/wslay_event_recv.3 \
man/wslay_event_send.3 \
man/wslay_event_write.3 \
man/wslay_event_set_error.3 \
man/wslay_event_want_read.3 \
man/wslay_event_want_write.3 \
Expand Down
1 change: 1 addition & 0 deletions doc/sphinx/api_reference.rst
Expand Up @@ -16,6 +16,7 @@ Contents:
man/wslay_event_queue_msg
man/wslay_event_recv
man/wslay_event_send
man/wslay_event_write
man/wslay_event_set_error
man/wslay_event_want_read
man/wslay_event_want_write
Expand Down
2 changes: 2 additions & 0 deletions doc/sphinx/conf.py.in
Expand Up @@ -254,6 +254,8 @@ man_pages = [
u'Receive messages', [u'Tatsuhiro Tsujikawa'], 3),
('man/wslay_event_send', 'wslay_event_send',
u'Send any pending messages', [u'Tatsuhiro Tsujikawa'], 3),
('man/wslay_event_write', 'wslay_event_write',
u'Write any pending messages to a buffer', [u'Tatsuhiro Tsujikawa'], 3),

('man/wslay_event_set_error', 'wslay_event_set_error',
u'Set error code', [u'Tatsuhiro Tsujikawa'], 3),
Expand Down
1 change: 1 addition & 0 deletions doc/sphinx/man/wslay_event_send.rst
Expand Up @@ -60,3 +60,4 @@ SEE ALSO
:c:func:`wslay_event_queue_fragmented_msg`,
:c:func:`wslay_event_set_write_enabled`,
:c:func:`wslay_event_want_write`
:c:func:`wslay_event_write`
65 changes: 65 additions & 0 deletions doc/sphinx/man/wslay_event_write.rst
@@ -0,0 +1,65 @@
wslay_event_write
=================

SYNOPSIS
--------

#include <wslay/wslay.h>

.. c:function:: int wslay_event_write(wslay_event_context_ptr ctx, uint8_t *buf, size_t buflen)
DESCRIPTION
-----------
:c:func:`wslay_event_write` writes queued messages to a buffer.
Unlike :c:func:`wslay_event_send`, this function writes messages into
the given buffer. It does not use :c:type:`wslay_event_send_callback`
function. Single call of :c:func:`wslay_event_write` writes multiple
messages until there is not enough space left in a buffer.
If *ctx* is initialized for WebSocket client use,
:c:func:`wslay_event_write` uses
:c:type:`wslay_event_genmask_callback` to get new mask key.
*buf* is a pointer to buffer and its capacity is given in *buflen*.
It should have at least 14 bytes.
When a message queued using :c:func:`wslay_event_queue_fragmented_msg`
is sent, :c:func:`wslay_event_write` invokes
:c:type:`wslay_event_fragmented_msg_callback` for that message.
After close control frame is sent, this function calls
:c:func:`wslay_event_set_write_enabled` with second argument 0 to
disable further transmission to peer.
If there are any pending messages, :c:func:`wslay_event_want_write`
returns 1, otherwise returns 0.
In case of a fatal error which leads to negative return code, this
function calls :c:func:`wslay_event_set_write_enabled` with second
argument 0 to disable further transmission to peer.
RETURN VALUE
------------
:c:func:`wslay_event_write` returns the number of bytes written to a
buffer if it succeeds, or one of the following negative error codes:
.. describe:: WSLAY_ERR_CALLBACK_FAILURE
User defined callback function is failed.
.. describe:: WSLAY_ERR_NOMEM
Out of memory.
When negative error code is returned, application must not make any further
call of :c:func:`wslay_event_write` and must close WebSocket connection.
SEE ALSO
--------
:c:func:`wslay_event_queue_fragmented_msg`,
:c:func:`wslay_event_set_write_enabled`,
:c:func:`wslay_event_want_write`
:c:func:`wslay_event_send`
71 changes: 71 additions & 0 deletions lib/includes/wslay/wslay.h
Expand Up @@ -221,6 +221,33 @@ void wslay_frame_context_free(wslay_frame_context_ptr ctx);
ssize_t wslay_frame_send(wslay_frame_context_ptr ctx,
struct wslay_frame_iocb *iocb);

/*
* Write WebSocket frame specified in iocb to buf of length
* buflen. ctx must be initialized using wslay_frame_context_init()
* function. iocb->fin must be 1 if this is a fin frame, otherwise 0.
* iocb->rsv is reserved bits. iocb->opcode must be the opcode of
* this frame. iocb->mask must be 1 if this is masked frame,
* otherwise 0. iocb->payload_length is the payload_length of this
* frame. iocb->data must point to the payload data to be
* sent. iocb->data_length must be the length of the data. Unlike
* wslay_frame_send, this function does not call send_callback
* function. This function calls gen_mask_callback function if it
* needs new mask key. This function returns the number of bytes
* written to a buffer. Unlike wslay_frame_send, it includes the
* number of header bytes. Instead, the number of payload bytes
* written is assigned to *pwpayloadlen if this function succeeds. If
* there is not enough space left in a buffer, it returns 0. If the
* library detects error in iocb, this function returns
* WSLAY_ERR_INVALID_ARGUMENT. If callback functions report a
* failure, this function returns WSLAY_ERR_INVALID_CALLBACK. This
* function does not always send all given data in iocb. If there are
* remaining data to be sent, adjust data and data_length in iocb
* accordingly and call this function again.
*/
ssize_t wslay_frame_write(wslay_frame_context_ptr ctx,
struct wslay_frame_iocb *iocb, uint8_t *buf,
size_t buflen, size_t *pwpayloadlen);

/*
* Receives WebSocket frame and stores it in iocb. This function
* returns the number of payload bytes received. This does not
Expand Down Expand Up @@ -531,6 +558,50 @@ int wslay_event_recv(wslay_event_context_ptr ctx);
*/
int wslay_event_send(wslay_event_context_ptr ctx);

/*
* Writes queued messages to a buffer. Unlike wslay_event_send(), this
* function writes messages into the given buffer. It does not use
* wslay_event_send_callback function. Single call of
* wslay_event_write() writes multiple messages until there is not
* enough space left in a buffer.
*
* If ctx is initialized for WebSocket client use, wslay_event_write()
* uses wslay_event_genmask_callback to get new mask key.
*
* buf is a pointer to buffer and its capacity is given in buflen. It
* should have at least 14 bytes.
*
* When a message queued using wslay_event_queue_fragmented_msg() is
* sent, wslay_event_write() invokes
* wslay_event_fragmented_msg_callback for that message.
*
* After close control frame is sent, this function calls
* wslay_event_set_write_enabled() with second argument 0 to disable
* further transmission to peer.
*
* If there are any pending messages, wslay_event_want_write() returns
* 1, otherwise returns 0.
*
* In case of a fatal errror which leads to negative return code, this
* function calls wslay_event_set_write_enabled() with second argument
* 0 to disable further transmission to peer.
*
* wslay_event_write() returns the number of bytes written to a buffer
* if it succeeds, or one of the following negative error codes:
*
* WSLAY_ERR_CALLBACK_FAILURE
* User defined callback function is failed.
*
* WSLAY_ERR_NOMEM
* Out of memory.
*
* When negative error code is returned, application must not make any
* further call of wslay_event_write() and must close WebSocket
* connection.
*/
ssize_t wslay_event_write(wslay_event_context_ptr ctx, uint8_t *buf,
size_t buflen);

struct wslay_event_msg {
uint8_t opcode;
const uint8_t *msg;
Expand Down
141 changes: 141 additions & 0 deletions lib/wslay_event.c
Expand Up @@ -906,6 +906,147 @@ int wslay_event_send(wslay_event_context_ptr ctx) {
return 0;
}

ssize_t wslay_event_write(wslay_event_context_ptr ctx, uint8_t *buf,
size_t buflen) {
struct wslay_frame_iocb iocb;
ssize_t r;
uint8_t *buf_last = buf;
size_t wpayloadlen;
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;
iocb.data_length = ctx->opayloadlen;
if (ctx->opayloadoff) {
iocb.data += ctx->opayloadoff;
iocb.data_length -= ctx->opayloadoff;
}
iocb.payload_length = ctx->opayloadlen;
r = wslay_frame_write(ctx->frame_ctx, &iocb, buf_last, buflen,
&wpayloadlen);
if (r > 0) {
assert(r <= buflen);

buf_last += r;
buflen -= r;

ctx->opayloadoff += wpayloadlen;
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 == 0) {
return buf_last - buf;
} else {
return WSLAY_ERR_CALLBACK_FAILURE;
}
} 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 && eof == 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 = 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_write(ctx->frame_ctx, &iocb, buf_last, buflen,
&wpayloadlen);
if (r > 0) {
assert(r <= buflen);

buf_last += r;
buflen -= r;

ctx->obufmark += wpayloadlen;
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;
} 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 == 0) {
return buf_last - buf;
} else {
return WSLAY_ERR_CALLBACK_FAILURE;
}
}
}
return buf_last - buf;
}

void wslay_event_set_error(wslay_event_context_ptr ctx, int val) {
ctx->error = val;
}
Expand Down

0 comments on commit 30fb19f

Please sign in to comment.