static apr_status_t fcgi_params_decode()

in buckets/fcgi_buckets.c [387:508]


static apr_status_t fcgi_params_decode(serf_bucket_t *bucket)
{
    fcgi_params_decode_ctx_t *ctx = bucket->data;
    apr_status_t status = APR_SUCCESS;

    while (status == APR_SUCCESS) {
        apr_size_t requested;
        const char *data;
        const unsigned char *udata;
        apr_size_t len;

        switch (ctx->state) {
            case DS_SIZES:
                requested = size_data_requested(ctx);
                status = serf_bucket_read(ctx->stream,
                                          requested - ctx->tmp_size,
                                          &data, &len);
                if (SERF_BUCKET_READ_ERROR(status))
                    return status;

                if (len < requested) {
                    memcpy(ctx->size_buffer + ctx->tmp_size, data, len);
                    ctx->tmp_size += len;

                    len = ctx->tmp_size;
                    data = ctx->size_buffer;
                }

                if (size_data_requested(ctx) < len) {
                    /* Read again. More bytes needed for
                       determining lengths */
                    if (data != ctx->size_buffer) {
                        memcpy(ctx->size_buffer, data, len);
                        ctx->tmp_size = len;
                    }
                    break;
                }

                udata = (const unsigned char*)data;

                if (udata[0] & 0x80) {
                    ctx->key_sz = (udata[0] & 0x7F) << 24 | (udata[1] << 16)
                                        | (udata[2] << 8) | (udata[3]);
                    udata += 4;
                }
                else {
                    ctx->key_sz = udata[0] & 0x7F;
                    udata += 1;
                }

                if (udata[0] & 0x80) {
                    ctx->val_sz = (udata[0] & 0x7F) << 24 | (udata[1] << 16)
                                        | (udata[2] << 8) | (udata[3]);
                    udata += 4;
                }
                else {
                    ctx->val_sz = udata[0] & 0x7F;
                    udata += 1;
                }

                ctx->tmp_size = 0;
                ctx->state++;
                break;
            case DS_KEY:
                status = serf_bucket_read(ctx->stream, ctx->key_sz,
                                          &data, &len);
                if (SERF_BUCKET_READ_ERROR(status))
                    break;

                if (!ctx->key) {
                    ctx->key = serf_bucket_mem_alloc(bucket->allocator,
                                                     ctx->key_sz + 1 + 6);
                    ctx->key[ctx->key_sz] = 0;
                }

                memcpy(ctx->key + ctx->tmp_size, data, len);
                ctx->tmp_size += len;

                if (ctx->tmp_size == ctx->key_sz) {
                    ctx->state++;
                    ctx->tmp_size = 0;
                }
                break;
            case DS_VALUE:
                status = serf_bucket_read(ctx->stream, ctx->val_sz,
                                          &data, &len);
                if (SERF_BUCKET_READ_ERROR(status))
                    break;
                if (!ctx->val) {
                    ctx->val = serf_bucket_mem_alloc(bucket->allocator,
                                                     ctx->val_sz + 1);
                    ctx->val[ctx->val_sz] = 0;
                }

                if (len == ctx->val_sz)
                    ctx->state++;

                memcpy(ctx->val + ctx->tmp_size, data, len);
                ctx->tmp_size += len;

                if (ctx->tmp_size == ctx->val_sz) {

                    fcgi_handle_keypair(bucket);
                    ctx->state = DS_SIZES;
                    ctx->tmp_size = 0;
                }
                break;
        }
    }

    if (APR_STATUS_IS_EOF(status)) {
        if ((ctx->state == DS_SIZES && !ctx->tmp_size)
            || (ctx->state == DS_KEY && !ctx->key_sz && !ctx->val_sz))
        {
            return APR_SUCCESS;
        }

        return SERF_ERROR_TRUNCATED_STREAM;
    }

    return status;
}