static int lcb_parse_single_with_packet_forward(lcb_server_t *c, hrtime_t stop) { protocol_binary_request_header req; protocol_binary_response_header header; lcb_size_t nr; lcb_size_t avail; int was_connected = c->connection_ready; uint16_t status; lcb_size_t packetsize; struct lcb_command_data_st ct; lcb_connection_t conn = &c->connection; lcb_t instance = c->instance; /* Check if we have the entire packet */ 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); avail = lcb_ringbuffer_get_nbytes(&conn->input.buffer->ringbuffer); if (avail < packetsize) { /* We need more data! */ 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; } /* We've got the entire packet!! */ assert(header.response.magic == PROTOCOL_BINARY_RES); nr = lcb_ringbuffer_peek(&c->output_cookies, &ct, sizeof(ct)); if (nr != sizeof(ct)) { lcb_error_handler(c->instance, LCB_EINTERNAL, NULL); return -1; } ct.vbucket = ntohs(req.request.vbucket); if (lcb_server_purge_implicit_responses(c, header.response.opaque, stop, 0)) { return -1; } if (c->instance->histogram) { lcb_record_metrics(c->instance, stop - ct.start, header.response.opcode); } status = ntohs(header.response.status); if (status != PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET || header.response.opcode == CMD_GET_REPLICA || header.response.opcode == CMD_OBSERVE) { /* Prepare the callback data */ lcb_packet_fwd_resp_t resp; lcb_error_t err = LCB_SUCCESS; resp.version = 0; resp.v.v0.buffer = conn->input.buffer; lcb_ringbuffer_get_iov(&resp.v.v0.buffer->ringbuffer, LCB_RINGBUFFER_READ, resp.v.v0.iov); /* The IOV should only contain the exact bits for the packet */ if (resp.v.v0.iov[0].iov_len > packetsize) { resp.v.v0.iov[0].iov_len = packetsize; resp.v.v0.iov[1].iov_len = 0; } else { resp.v.v0.iov[1].iov_len = packetsize - resp.v.v0.iov[0].iov_len; } /* fire the callback */ conn->input.locked |= instance->callbacks.packet_fwd(instance, ct.cookie, err, &resp); lcb_ringbuffer_consumed(&conn->input.buffer->ringbuffer, packetsize); /* 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) { /* Prepare the callback data */ lcb_packet_fwd_resp_t resp; lcb_error_t err = LCB_SUCCESS; resp.version = 0; resp.v.v0.buffer = conn->input.buffer; lcb_ringbuffer_get_iov(&resp.v.v0.buffer->ringbuffer, LCB_RINGBUFFER_READ, resp.v.v0.iov); /* The IOV should only contain the exact bits for the packet */ if (resp.v.v0.iov[0].iov_len > packetsize) { resp.v.v0.iov[0].iov_len = packetsize; resp.v.v0.iov[1].iov_len = 0; } else { resp.v.v0.iov[1].iov_len = packetsize - resp.v.v0.iov[0].iov_len; } /* fire the callback */ conn->input.locked |= instance->callbacks.packet_fwd(instance, ct.cookie, err, &resp); lcb_ringbuffer_consumed(&conn->input.buffer->ringbuffer, packetsize); /* keep command and cookie until we get complete STAT response */ swallow_command(c, &header, was_connected); } } return 1; }
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; }
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; }