ssize_t wslay_frame_recv()

in network/WebSocket/wslay/wslay_frame.cpp [215:339]


ssize_t wslay_frame_recv(wslay_frame_context_ptr ctx,
                         struct wslay_frame_iocb *iocb) {
    ssize_t r;
    if (ctx->istate == RECV_HEADER1) {
        uint8_t fin, opcode, rsv, payloadlen;
        if (WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
            if ((r = wslay_recv(ctx)) <= 0) {
                return r;
            }
        }
        if (WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
            return WSLAY_ERR_WANT_READ;
        }
        fin = (ctx->ibufmark[0] >> 7) & 1;
        rsv = (ctx->ibufmark[0] >> 4) & 7;
        opcode = ctx->ibufmark[0] & 0xfu;
        ctx->iom.opcode = opcode;
        ctx->iom.fin = fin;
        ctx->iom.rsv = rsv;
        ++ctx->ibufmark;
        ctx->imask = (ctx->ibufmark[0] >> 7) & 1;
        payloadlen = ctx->ibufmark[0] & 0x7fu;
        ++ctx->ibufmark;
        if (wslay_is_ctrl_frame(opcode) && (payloadlen > 125 || !fin)) {
            return WSLAY_ERR_PROTO;
        }
        if (payloadlen == 126) {
            ctx->istate = RECV_EXT_PAYLOADLEN;
            ctx->ireqread = 2;
        } else if (payloadlen == 127) {
            ctx->istate = RECV_EXT_PAYLOADLEN;
            ctx->ireqread = 8;
        } else {
            ctx->ipayloadlen = payloadlen;
            ctx->ipayloadoff = 0;
            if (ctx->imask) {
                ctx->istate = RECV_MASKKEY;
                ctx->ireqread = 4;
            } else {
                ctx->istate = RECV_PAYLOAD;
                ctx->ireqread = ctx->ipayloadlen;
            }
        }
    }
    if (ctx->istate == RECV_EXT_PAYLOADLEN) {
        if (WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
            if ((r = wslay_recv(ctx)) <= 0) {
                return r;
            }
            if (WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
                return WSLAY_ERR_WANT_READ;
            }
        }
        ctx->ipayloadlen = 0;
        ctx->ipayloadoff = 0;
        memcpy((uint8_t *) &ctx->ipayloadlen + (8 - ctx->ireqread),
               ctx->ibufmark, ctx->ireqread);
        ctx->ipayloadlen = ntoh64(ctx->ipayloadlen);
        ctx->ibufmark += ctx->ireqread;
        if (ctx->ireqread == 8) {
            if (ctx->ipayloadlen < (1 << 16) ||
                ctx->ipayloadlen & (1ull << 63)) {
                return WSLAY_ERR_PROTO;
            }
        } else if (ctx->ipayloadlen < 126) {
            return WSLAY_ERR_PROTO;
        }
        if (ctx->imask) {
            ctx->istate = RECV_MASKKEY;
            ctx->ireqread = 4;
        } else {
            ctx->istate = RECV_PAYLOAD;
            ctx->ireqread = ctx->ipayloadlen;
        }
    }
    if (ctx->istate == RECV_MASKKEY) {
        if (WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
            if ((r = wslay_recv(ctx)) <= 0) {
                return r;
            }
            if (WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
                return WSLAY_ERR_WANT_READ;
            }
        }
        memcpy(ctx->imaskkey, ctx->ibufmark, 4);
        ctx->ibufmark += 4;
        ctx->istate = RECV_PAYLOAD;
        ctx->ireqread = ctx->ipayloadlen;
    }
    if (ctx->istate == RECV_PAYLOAD) {
        uint8_t *readlimit, *readmark;
        uint64_t rempayloadlen = ctx->ipayloadlen - ctx->ipayloadoff;
        ctx->ireqread = rempayloadlen;
        if (WSLAY_AVAIL_IBUF(ctx) == 0 && rempayloadlen > 0) {
            if ((r = wslay_recv(ctx)) <= 0) {
                return r;
            }
        }
        readmark = ctx->ibufmark;
        readlimit = WSLAY_AVAIL_IBUF(ctx) < rempayloadlen ?
                    ctx->ibuflimit : ctx->ibufmark + rempayloadlen;
        if (ctx->imask) {
            for (; ctx->ibufmark != readlimit;
                   ++ctx->ibufmark, ++ctx->ipayloadoff) {
                ctx->ibufmark[0] ^= ctx->imaskkey[ctx->ipayloadoff % 4];
            }
        } else {
            ctx->ibufmark = readlimit;
            ctx->ipayloadoff += readlimit - readmark;
        }
        iocb->fin = ctx->iom.fin;
        iocb->rsv = ctx->iom.rsv;
        iocb->opcode = ctx->iom.opcode;
        iocb->payload_length = ctx->ipayloadlen;
        iocb->mask = ctx->imask;
        iocb->data = readmark;
        iocb->data_length = ctx->ibufmark - readmark;
        if (ctx->ipayloadlen == ctx->ipayloadoff) {
            ctx->istate = RECV_HEADER1;
            ctx->ireqread = 2;
        }
        return iocb->data_length;
    }
    return WSLAY_ERR_INVALID_ARGUMENT;
}