int lcb_proto_parse_single(lcb_server_t *c, hrtime_t stop)
{
    int rv;
    packet_info info;
    protocol_binary_request_header req;
    lcb_size_t nr;
    lcb_connection_t conn = &c->connection;

    rv = lcb_packet_read_ringbuffer(&info, conn->input);
    if (rv == -1) {
        return -1;
    } else if (rv == 0) {
        return 0;
    }

    /* Is it already timed out? */
    nr = ringbuffer_peek(&c->cmd_log, req.bytes, sizeof(req));
    if (nr < sizeof(req) || /* the command log doesn't know about it */
            (PACKET_OPAQUE(&info) < req.request.opaque &&
             PACKET_OPAQUE(&info) > 0)) { /* sasl comes with zero opaque */
        /* already processed. */
        lcb_packet_release_ringbuffer(&info, conn->input);
        return 1;
    }


    nr = ringbuffer_peek(&c->output_cookies, &info.ct, sizeof(info.ct));
    if (nr != sizeof(info.ct)) {
        lcb_error_handler(c->instance, LCB_EINTERNAL, NULL);
        lcb_packet_release_ringbuffer(&info, conn->input);
        return -1;
    }
    info.ct.vbucket = ntohs(req.request.vbucket);

    switch (info.res.response.magic) {
    case PROTOCOL_BINARY_REQ:
        /*
         * The only way to get request packets is if someone started
         * to send us TAP requests, and we don't support that anymore
         */
        lcb_error_handler(c->instance, LCB_EINTERNAL,
                          "Protocol error. someone sent us a command!");
        return -1;
    case PROTOCOL_BINARY_RES: {
        int was_connected = c->connection_ready;
        rv = lcb_server_purge_implicit_responses(c,
                PACKET_OPAQUE(&info),
                stop, 0);
        if (rv != 0) {
            lcb_packet_release_ringbuffer(&info, conn->input);
            return -1;
        }

        if (c->instance->histogram) {
            lcb_record_metrics(c->instance, stop - info.ct.start,
                               PACKET_OPCODE(&info));
        }

        if (PACKET_STATUS(&info) != PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET
                || PACKET_OPCODE(&info) == CMD_GET_REPLICA
                || PACKET_OPCODE(&info) == CMD_OBSERVE) {
            rv = lcb_dispatch_response(c, &info);
            if (rv == -1) {
                lcb_error_handler(c->instance, LCB_EINTERNAL,
                                  "Received unknown command response");
                abort();
                return -1;
            }

            /* keep command and cookie until we get complete STAT response */
            swallow_command(c, &info.res, was_connected);

        } else {
            rv = handle_not_my_vbucket(c, &info, &req, &info.ct);

            if (rv == -1) {
                return -1;

            } else if (rv == 0) {
                lcb_dispatch_response(c, &info);
                swallow_command(c, &info.res, was_connected);
            }

        }
        break;
    }

    default:
        lcb_error_handler(c->instance, LCB_PROTOCOL_ERROR, NULL);
        lcb_packet_release_ringbuffer(&info, conn->input);
        return -1;
    }

    lcb_packet_release_ringbuffer(&info, conn->input);
    return 1;
}
Exemple #2
0
static int lcb_parse_single_with_specialized_callbacks(lcb_server_t *c,
        hrtime_t stop)
{
    protocol_binary_request_header req;
    protocol_binary_response_header header;
    lcb_size_t nr;
    char *packet;
    lcb_size_t packetsize;
    struct lcb_command_data_st ct;
    lcb_connection_t conn = &c->connection;

    if (lcb_ringbuffer_ensure_alignment(&conn->input.buffer->ringbuffer) != 0) {
        lcb_error_handler(c->instance, LCB_EINTERNAL,
                          NULL);
        return -1;
    }

    nr = lcb_ringbuffer_peek(&conn->input.buffer->ringbuffer,
                             header.bytes, sizeof(header));
    if (nr < sizeof(header)) {
        return 0;
    }

    packetsize = ntohl(header.response.bodylen) + (lcb_uint32_t)sizeof(header);
    if (conn->input.buffer->ringbuffer.nbytes < packetsize) {
        return 0;
    }

    /* Is it already timed out? */
    nr = lcb_ringbuffer_peek(&c->cmd_log, req.bytes, sizeof(req));
    if (nr < sizeof(req) || /* the command log doesn't know about it */
            (header.response.opaque < req.request.opaque &&
             header.response.opaque > 0)) { /* sasl comes with zero opaque */
        /* already processed. */
        lcb_ringbuffer_consumed(&conn->input.buffer->ringbuffer, packetsize);
        return 1;
    }

    packet = conn->input.buffer->ringbuffer.read_head;
    /* we have everything! */

    if (!lcb_ringbuffer_is_continous(&conn->input.buffer->ringbuffer,
                                     LCB_RINGBUFFER_READ, packetsize)) {
        /* The buffer isn't continous.. for now just copy it out and
        ** operate on the copy ;)
        */
        if ((packet = malloc(packetsize)) == NULL) {
            lcb_error_handler(c->instance, LCB_CLIENT_ENOMEM, NULL);
            return -1;
        }
        nr = lcb_ringbuffer_read(&conn->input.buffer->ringbuffer, packet, packetsize);
        if (nr != packetsize) {
            lcb_error_handler(c->instance, LCB_EINTERNAL,
                              NULL);
            free(packet);
            return -1;
        }
    }

    nr = lcb_ringbuffer_peek(&c->output_cookies, &ct, sizeof(ct));
    if (nr != sizeof(ct)) {
        lcb_error_handler(c->instance, LCB_EINTERNAL,
                          NULL);
        if (packet != conn->input.buffer->ringbuffer.read_head) {
            free(packet);
        }
        return -1;
    }
    ct.vbucket = ntohs(req.request.vbucket);

    switch (header.response.magic) {
    case PROTOCOL_BINARY_REQ:
        /*
         * The only way to get request packets is if someone started
         * to send us TAP requests, and we don't support that anymore
         */
        lcb_error_handler(c->instance, LCB_EINTERNAL,
                          "Protocol error. someone sent us a command!");
        return -1;
    case PROTOCOL_BINARY_RES: {
        int was_connected = c->connection_ready;
        if (lcb_server_purge_implicit_responses(c, header.response.opaque, stop, 0) != 0) {
            if (packet != conn->input.buffer->ringbuffer.read_head) {
                free(packet);
            }
            return -1;
        }

        if (c->instance->histogram) {
            lcb_record_metrics(c->instance, stop - ct.start,
                               header.response.opcode);
        }

        if (ntohs(header.response.status) != PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET
                || header.response.opcode == CMD_GET_REPLICA
                || header.response.opcode == CMD_OBSERVE) {
            if (lcb_dispatch_response(c, &ct, (void *)packet) == -1) {
                /*
                 * Internal error.. we received an unsupported response
                 * id. This should _ONLY_ happen at development time because
                 * we won't receive response packets with other opcodes
                 * than we send. Let's abort here to make it easy for
                 * the developer to know what happened..
                 */
                lcb_error_handler(c->instance, LCB_EINTERNAL,
                                  "Received unknown command response");
                abort();
                return -1;
            }

            /* keep command and cookie until we get complete STAT response */
            swallow_command(c, &header, was_connected);

        } else {
            int rv = handle_not_my_vbucket(c, &req, &ct);

            if (rv == -1) {
                return -1;

            } else if (rv == 0) {
                lcb_dispatch_response(c, &ct, (void *)packet);
                swallow_command(c, &header, was_connected);
            }

        }
        break;
    }

    default:
        lcb_error_handler(c->instance,
                          LCB_PROTOCOL_ERROR,
                          NULL);
        if (packet != conn->input.buffer->ringbuffer.read_head) {
            free(packet);
        }
        return -1;
    }

    if (packet != conn->input.buffer->ringbuffer.read_head) {
        free(packet);
    } else {
        lcb_ringbuffer_consumed(&conn->input.buffer->ringbuffer, packetsize);
    }
    return 1;
}