apr_status_t serf__bucket_fcgi_unframe_read_info()

in buckets/fcgi_buckets.c [72:161]


apr_status_t serf__bucket_fcgi_unframe_read_info(serf_bucket_t *bucket,
                                                 apr_uint16_t *stream_id,
                                                 apr_uint16_t *frame_type)
{
    fcgi_unframe_ctx_t *ctx = bucket->data;
    const unsigned char *header;
    apr_status_t status;

    if (ctx->record_remaining == 0)
    {
        if (stream_id)
            *stream_id = ctx->streamid;
        if (frame_type)
            *frame_type = ctx->frame_type;

        return APR_SUCCESS;
    }

    do
    {
        const char *data;
        apr_size_t len;

        status = serf_bucket_read(ctx->stream, ctx->record_remaining, &data, &len);

        if (SERF_BUCKET_READ_ERROR(status))
            return status;
        else if (!len && !status)
            return SERF_ERROR_EMPTY_READ;

        if (len < FCGI_RECORD_SIZE) {
            memcpy(ctx->buffer + FCGI_RECORD_SIZE - ctx->record_remaining,
                   data, len);

            ctx->record_remaining -= len;
            header = ctx->buffer;
        }
        else {
            header = (const void *)data;
            ctx->record_remaining = 0;
        }
    } while (!status && ctx->record_remaining > 0);

    if (ctx->record_remaining == 0)
    {
        /* We combine version and frametype in a single value */
        ctx->frame_type = (header[0] << 8) | header[1];
        ctx->streamid = (header[2] << 8) | header[3];
        ctx->payload_remaining = (header[4] << 8) | header[5];
        /* header[6] is reserved */
        ctx->pad_remaining = header[7];

        /* Fill output arguments if necessary */
        if (stream_id)
            *stream_id = ctx->streamid;
        if (frame_type)
            *frame_type = ctx->frame_type;

        if (ctx->payload_remaining == 0)
            status = APR_EOF;
        else if (APR_STATUS_IS_EOF(status))
            status = SERF_ERROR_TRUNCATED_STREAM;

        /* If we hava a zero-length frame we have to call the eof callback
           now, as the read operations will just shortcut to APR_EOF */
        if (ctx->payload_remaining == 0 && ctx->end_of_frame)
        {
            apr_status_t cb_status;

            cb_status = (*ctx->end_of_frame)(ctx->end_of_frame_baton,
                                                bucket);

            ctx->end_of_frame = NULL;

            if (SERF_BUCKET_READ_ERROR(cb_status))
                status = cb_status;
        }
    }
    else if (APR_STATUS_IS_EOF(status))
    {
        /* Reading frame failed because we couldn't read the header. Report
            a read failure instead of semi-success */
        if (ctx->record_remaining == FCGI_RECORD_SIZE)
            status = SERF_ERROR_EMPTY_STREAM;
        else
            status = SERF_ERROR_TRUNCATED_STREAM;
    }

    return status;
}