ssize_t wslay_frame_send()

in network/WebSocket/wslay/wslay_frame.cpp [55:186]


ssize_t wslay_frame_send(wslay_frame_context_ptr ctx,
                         struct wslay_frame_iocb *iocb) {
    if (iocb->data_length > iocb->payload_length) {
        return WSLAY_ERR_INVALID_ARGUMENT;
    }
    if (ctx->ostate == PREP_HEADER) {
        uint8_t *hdptr = ctx->oheader;
        memset(ctx->oheader, 0, sizeof(ctx->oheader));
        *hdptr |= (iocb->fin << 7) & 0x80u;
        *hdptr |= (iocb->rsv << 4) & 0x70u;
        *hdptr |= iocb->opcode & 0xfu;
        ++hdptr;
        *hdptr |= (iocb->mask << 7) & 0x80u;
        if (wslay_is_ctrl_frame(iocb->opcode) && iocb->payload_length > 125) {
            return WSLAY_ERR_INVALID_ARGUMENT;
        }
        if (iocb->payload_length < 126) {
            *hdptr |= iocb->payload_length;
            ++hdptr;
        } else if (iocb->payload_length < (1 << 16)) {
            uint16_t len = htons((uint16_t) iocb->payload_length);
            *hdptr |= 126;
            ++hdptr;
            memcpy(hdptr, &len, 2);
            hdptr += 2;
        } else if (iocb->payload_length < (1ull << 63)) {
            uint64_t len = hton64(iocb->payload_length);
            *hdptr |= 127;
            ++hdptr;
            memcpy(hdptr, &len, 8);
            hdptr += 8;
        } else {
            /* Too large payload length */
            return WSLAY_ERR_INVALID_ARGUMENT;
        }
        if (iocb->mask) {
            if (ctx->callbacks.genmask_callback(ctx->omaskkey, 4,
                                                ctx->user_data) != 0) {
                return WSLAY_ERR_INVALID_CALLBACK;
            } else {
                ctx->omask = 1;
                memcpy(hdptr, ctx->omaskkey, 4);
                hdptr += 4;
            }
        }
        ctx->ostate = SEND_HEADER;
        ctx->oheadermark = ctx->oheader;
        ctx->oheaderlimit = hdptr;
        ctx->opayloadlen = iocb->payload_length;
        ctx->opayloadoff = 0;
    }
    if (ctx->ostate == SEND_HEADER) {
        ptrdiff_t len = ctx->oheaderlimit - ctx->oheadermark;
        ssize_t r;
        int flags = 0;
        if (iocb->data_length > 0) {
            flags |= WSLAY_MSG_MORE;
        };
        r = ctx->callbacks.send_callback(ctx->oheadermark, len, flags,
                                         ctx->user_data);
        if (r > 0) {
            if (r > len) {
                return WSLAY_ERR_INVALID_CALLBACK;
            } else {
                ctx->oheadermark += r;
                if (ctx->oheadermark == ctx->oheaderlimit) {
                    ctx->ostate = SEND_PAYLOAD;
                } else {
                    return WSLAY_ERR_WANT_WRITE;
                }
            }
        } else {
            return WSLAY_ERR_WANT_WRITE;
        }
    }
    if (ctx->ostate == SEND_PAYLOAD) {
        size_t totallen = 0;
        if (iocb->data_length > 0) {
            if (ctx->omask) {
                uint8_t temp[4096];
                const uint8_t *datamark = iocb->data,
                    *datalimit = iocb->data + iocb->data_length;
                while (datamark < datalimit) {
                    size_t datalen = datalimit - datamark;
                    const uint8_t *writelimit = datamark +
                        wslay_min(sizeof(temp), datalen);
                    size_t writelen = writelimit - datamark;
                    ssize_t r;
                    size_t i;
                    for (i = 0; i < writelen; ++i) {
                        temp[i] = datamark[i] ^ ctx->omaskkey[(ctx->opayloadoff + i) % 4];
                    }
                    r = ctx->callbacks.send_callback(temp, writelen, 0, ctx->user_data);
                    if (r > 0) {
                        if ((size_t) r > writelen) {
                            return WSLAY_ERR_INVALID_CALLBACK;
                        } else {
                            datamark += r;
                            ctx->opayloadoff += r;
                            totallen += r;
                        }
                    } else {
                        if (totallen > 0) {
                            break;
                        } else {
                            return WSLAY_ERR_WANT_WRITE;
                        }
                    }
                }
            } else {
                ssize_t r;
                r = ctx->callbacks.send_callback(iocb->data, iocb->data_length, 0,
                                                 ctx->user_data);
                if (r > 0) {
                    if ((size_t) r > iocb->data_length) {
                        return WSLAY_ERR_INVALID_CALLBACK;
                    } else {
                        ctx->opayloadoff += r;
                        totallen = (size_t) r;
                    }
                } else {
                    return WSLAY_ERR_WANT_WRITE;
                }
            }
        }
        if (ctx->opayloadoff == ctx->opayloadlen) {
            ctx->ostate = PREP_HEADER;
        }
        return totallen;
    }
    return WSLAY_ERR_INVALID_ARGUMENT;
}