- * Send a preformatted packet back to the client. If the connection is in
- * pedantic mode, it will validate the packet and refuse to send it if it
- * breaks the specification.
- *
- * @param cookie client identification
- * @param request the original request packet
- * @param response the packet to send
- * @return The status of the operation
- */
-static protocol_binary_response_status
-raw_response_handler(const void *cookie,
- protocol_binary_request_header *request,
- protocol_binary_response_header *response)
-{
- struct memcached_binary_protocol_client_st *client= (void*)cookie;
-
- if (client->root->pedantic &&
- !memcached_binary_protocol_pedantic_check_response(request, response))
- {
- return PROTOCOL_BINARY_RESPONSE_EINVAL;
- }
-
- if (!drain_output(client))
- {
- return PROTOCOL_BINARY_RESPONSE_EIO;
- }
-
- size_t len= sizeof(*response) + htonl(response->response.bodylen);
- size_t offset= 0;
- char *ptr= (void*)response;
-
- if (client->output == NULL)
- {
- /* I can write directly to the socket.... */
- do
- {
- size_t num_bytes= len - offset;
- ssize_t nw= client->root->send(client,
- client->sock,
- ptr + offset,
- num_bytes);
- if (nw == -1)
- {
- if (errno == EWOULDBLOCK)
- {
- break;
- }
- else if (errno != EINTR)
- {
- client->error= errno;
- return PROTOCOL_BINARY_RESPONSE_EIO;
- }
- }
- else
- {
- offset += (size_t)nw;
- }
- } while (offset < len);
- }
-
- return spool_output(client, ptr, len - offset);
-}
-
-/*
- * Version 0 of the interface is really low level and protocol specific,
- * while the version 1 of the interface is more API focused. We need a
- * way to translate between the command codes on the wire and the
- * application level interface in V1, so let's just use the V0 of the
- * interface as a map instead of creating a huuuge switch :-)
- */
-
-/**
- * Callback for the GET/GETQ/GETK and GETKQ responses
- * @param cookie client identifier
- * @param key the key for the item
- * @param keylen the length of the key
- * @param body the length of the body
- * @param bodylen the length of the body
- * @param flags the flags for the item
- * @param cas the CAS id for the item
- */
-static protocol_binary_response_status
-get_response_handler(const void *cookie,
- const void *key,
- uint16_t keylen,
- const void *body,
- uint32_t bodylen,
- uint32_t flags,
- uint64_t cas) {
-
- struct memcached_binary_protocol_client_st *client= (void*)cookie;
- uint8_t opcode= client->current_command->request.opcode;
-
- if (opcode == PROTOCOL_BINARY_CMD_GET || opcode == PROTOCOL_BINARY_CMD_GETQ)
- {
- keylen= 0;
- }
-
- protocol_binary_response_get response= {
- .message.header.response= {
- .magic= PROTOCOL_BINARY_RES,
- .opcode= opcode,
- .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
- .opaque= client->current_command->request.opaque,
- .cas= htonll(cas),
- .keylen= htons(keylen),
- .extlen= 4,
- .bodylen= htonl(bodylen + keylen + 4),
- },
- .message.body.flags= htonl(flags),
- };
-
- protocol_binary_response_status rval;
- const protocol_binary_response_status success = PROTOCOL_BINARY_RESPONSE_SUCCESS;
- if ((rval= spool_output(client, response.bytes, sizeof(response.bytes))) != success ||
- (rval= spool_output(client, key, keylen)) != success ||
- (rval= spool_output(client, body, bodylen)) != success)
- {
- return rval;
- }
-
- return PROTOCOL_BINARY_RESPONSE_SUCCESS;
-}
-
-/**
- * Callback for the STAT responses
- * @param cookie client identifier
- * @param key the key for the item
- * @param keylen the length of the key
- * @param body the length of the body
- * @param bodylen the length of the body
- */
-static protocol_binary_response_status
-stat_response_handler(const void *cookie,
- const void *key,
- uint16_t keylen,
- const void *body,
- uint32_t bodylen) {
-
- struct memcached_binary_protocol_client_st *client= (void*)cookie;
-
- protocol_binary_response_no_extras response= {
- .message.header.response= {
- .magic= PROTOCOL_BINARY_RES,
- .opcode= client->current_command->request.opcode,
- .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
- .opaque= client->current_command->request.opaque,
- .keylen= htons(keylen),
- .bodylen= htonl(bodylen + keylen),
- },
- };
-
- protocol_binary_response_status rval;
- const protocol_binary_response_status success = PROTOCOL_BINARY_RESPONSE_SUCCESS;
- if ((rval= spool_output(client, response.bytes, sizeof(response.bytes))) != success ||
- (rval= spool_output(client, key, keylen)) != success ||
- (rval= spool_output(client, body, bodylen)) != success)
- {
- return rval;
- }
-
- return PROTOCOL_BINARY_RESPONSE_SUCCESS;
-}
-
-/**
- * Callback for the VERSION responses
- * @param cookie client identifier
- * @param text the length of the body
- * @param textlen the length of the body
- */
-static protocol_binary_response_status
-version_response_handler(const void *cookie,
- const void *text,
- uint32_t textlen) {
-
- struct memcached_binary_protocol_client_st *client= (void*)cookie;
-
- protocol_binary_response_no_extras response= {
- .message.header.response= {
- .magic= PROTOCOL_BINARY_RES,
- .opcode= client->current_command->request.opcode,
- .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
- .opaque= client->current_command->request.opaque,
- .bodylen= htonl(textlen),
- },
- };
-
- protocol_binary_response_status rval;
- const protocol_binary_response_status success = PROTOCOL_BINARY_RESPONSE_SUCCESS;
- if ((rval= spool_output(client, response.bytes, sizeof(response.bytes))) != success ||
- (rval= spool_output(client, text, textlen)) != success)
- {
- return rval;
- }
-
- return PROTOCOL_BINARY_RESPONSE_SUCCESS;
-}
-
-/**
- * Callback for ADD and ADDQ
- * @param cookie the calling client
- * @param header the add/addq command
- * @param response_handler not used
- * @return the result of the operation
- */
-static protocol_binary_response_status
-add_command_handler(const void *cookie,
- protocol_binary_request_header *header,
- memcached_binary_protocol_raw_response_handler response_handler)
-{
- protocol_binary_response_status rval;
-
- struct memcached_binary_protocol_client_st *client= (void*)cookie;
- if (client->root->callback->interface.v1.add != NULL)
- {
- uint16_t keylen= ntohs(header->request.keylen);
- uint32_t datalen= ntohl(header->request.bodylen) - keylen - 8;
- protocol_binary_request_add *request= (void*)header;
- uint32_t flags= ntohl(request->message.body.flags);
- uint32_t timeout= ntohl(request->message.body.expiration);
- char *key= ((char*)header) + sizeof(*header) + 8;
- char *data= key + keylen;
- uint64_t cas;
-
- rval= client->root->callback->interface.v1.add(cookie, key, keylen,
- data, datalen, flags,
- timeout, &cas);
-
- if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
- header->request.opcode == PROTOCOL_BINARY_CMD_ADD)
- {
- /* Send a positive request */
- protocol_binary_response_no_extras response= {
- .message= {
- .header.response= {
- .magic= PROTOCOL_BINARY_RES,
- .opcode= PROTOCOL_BINARY_CMD_ADD,
- .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
- .opaque= header->request.opaque,
- .cas= ntohll(cas)
- }
- }
- };
- rval= response_handler(cookie, header, (void*)&response);
- }
- }
- else
- {
- rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
- }
-
- return rval;
-}
-
-/**
- * Callback for DECREMENT and DECREMENTQ
- * @param cookie the calling client
- * @param header the command
- * @param response_handler not used
- * @return the result of the operation
- */
-static protocol_binary_response_status
-decrement_command_handler(const void *cookie,
- protocol_binary_request_header *header,
- memcached_binary_protocol_raw_response_handler response_handler)
-{
- (void)response_handler;
- protocol_binary_response_status rval;
-
- struct memcached_binary_protocol_client_st *client= (void*)cookie;
- if (client->root->callback->interface.v1.decrement != NULL)
- {
- uint16_t keylen= ntohs(header->request.keylen);
- protocol_binary_request_decr *request= (void*)header;
- uint64_t init= ntohll(request->message.body.initial);
- uint64_t delta= ntohll(request->message.body.delta);
- uint32_t timeout= ntohl(request->message.body.expiration);
- void *key= request->bytes + sizeof(request->bytes);
- uint64_t result;
- uint64_t cas;
-
- char buffer[1024] = {0};
- memcpy(buffer, key, keylen);
- fprintf(stderr, "%s\n", buffer);
-
-
- rval= client->root->callback->interface.v1.decrement(cookie, key, keylen,
- delta, init, timeout,
- &result, &cas);
- if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
- header->request.opcode == PROTOCOL_BINARY_CMD_DECREMENT)
- {
- /* Send a positive request */
- protocol_binary_response_decr response= {
- .message= {
- .header.response= {
- .magic= PROTOCOL_BINARY_RES,
- .opcode= PROTOCOL_BINARY_CMD_DECREMENT,
- .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
- .opaque= header->request.opaque,
- .cas= ntohll(cas),
- .bodylen= htonl(8)
- },
- .body.value = htonll(result)
- }
- };
- rval= response_handler(cookie, header, (void*)&response);
- }
- }
- else
- {
- rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
- }
-
- return rval;
-}
-
-/**
- * Callback for DELETE and DELETEQ
- * @param cookie the calling client
- * @param header the command
- * @param response_handler not used
- * @return the result of the operation
- */
-static protocol_binary_response_status
-delete_command_handler(const void *cookie,
- protocol_binary_request_header *header,
- memcached_binary_protocol_raw_response_handler response_handler)
-{
- (void)response_handler;
- protocol_binary_response_status rval;
-
- struct memcached_binary_protocol_client_st *client= (void*)cookie;
- if (client->root->callback->interface.v1.delete != NULL)
- {
- uint16_t keylen= ntohs(header->request.keylen);
- void *key= (header + 1);
- uint64_t cas= ntohll(header->request.cas);
- rval= client->root->callback->interface.v1.delete(cookie, key, keylen, cas);
- if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
- header->request.opcode == PROTOCOL_BINARY_CMD_DELETE)
- {
- /* Send a positive request */
- protocol_binary_response_no_extras response= {
- .message= {
- .header.response= {
- .magic= PROTOCOL_BINARY_RES,
- .opcode= PROTOCOL_BINARY_CMD_DELETE,
- .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
- .opaque= header->request.opaque,
- }
- }
- };
- rval= response_handler(cookie, header, (void*)&response);
- }
- }
- else
- {
- rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
- }
-
- return rval;
-}
-
-/**
- * Callback for FLUSH and FLUSHQ
- * @param cookie the calling client
- * @param header the command
- * @param response_handler not used
- * @return the result of the operation
- */
-static protocol_binary_response_status
-flush_command_handler(const void *cookie,
- protocol_binary_request_header *header,
- memcached_binary_protocol_raw_response_handler response_handler)
-{
- (void)response_handler;
- protocol_binary_response_status rval;
-
- struct memcached_binary_protocol_client_st *client= (void*)cookie;
- if (client->root->callback->interface.v1.flush != NULL)
- {
- protocol_binary_request_flush *flush= (void*)header;
- uint32_t timeout= 0;
- if (htonl(header->request.bodylen) == 4)
- {
- timeout= ntohl(flush->message.body.expiration);
- }
-
- rval= client->root->callback->interface.v1.flush(cookie, timeout);
- if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
- header->request.opcode == PROTOCOL_BINARY_CMD_FLUSH)
- {
- /* Send a positive request */
- protocol_binary_response_no_extras response= {
- .message= {
- .header.response= {
- .magic= PROTOCOL_BINARY_RES,
- .opcode= PROTOCOL_BINARY_CMD_FLUSH,
- .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
- .opaque= header->request.opaque,
- }
- }
- };
- rval= response_handler(cookie, header, (void*)&response);
- }
- }
- else
- {
- rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
- }
-
- return rval;
-}
-
-/**
- * Callback for GET, GETK, GETQ, GETKQ
- * @param cookie the calling client
- * @param header the command
- * @param response_handler not used
- * @return the result of the operation
- */
-static protocol_binary_response_status
-get_command_handler(const void *cookie,
- protocol_binary_request_header *header,
- memcached_binary_protocol_raw_response_handler response_handler)
-{
- (void)response_handler;
- protocol_binary_response_status rval;
-
- struct memcached_binary_protocol_client_st *client= (void*)cookie;
- if (client->root->callback->interface.v1.get != NULL)
- {
- uint16_t keylen= ntohs(header->request.keylen);
- void *key= (header + 1);
- rval= client->root->callback->interface.v1.get(cookie, key, keylen,
- get_response_handler);
-
- if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT &&
- (header->request.opcode == PROTOCOL_BINARY_CMD_GETQ ||
- header->request.opcode == PROTOCOL_BINARY_CMD_GETKQ))
- {
- /* Quiet commands shouldn't respond on cache misses */
- rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
- }
- }
- else
- {
- rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
- }
-
- return rval;
-}
-
-/**
- * Callback for INCREMENT and INCREMENTQ
- * @param cookie the calling client
- * @param header the command
- * @param response_handler not used
- * @return the result of the operation
- */
-static protocol_binary_response_status
-increment_command_handler(const void *cookie,
- protocol_binary_request_header *header,
- memcached_binary_protocol_raw_response_handler response_handler)
-{
- (void)response_handler;
- protocol_binary_response_status rval;
-
- struct memcached_binary_protocol_client_st *client= (void*)cookie;
- if (client->root->callback->interface.v1.increment != NULL)
- {
- uint16_t keylen= ntohs(header->request.keylen);
- protocol_binary_request_incr *request= (void*)header;
- uint64_t init= ntohll(request->message.body.initial);
- uint64_t delta= ntohll(request->message.body.delta);
- uint32_t timeout= ntohl(request->message.body.expiration);
- void *key= request->bytes + sizeof(request->bytes);
- uint64_t cas;
- uint64_t result;
-
- rval= client->root->callback->interface.v1.increment(cookie, key, keylen,
- delta, init, timeout,
- &result, &cas);
- if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
- header->request.opcode == PROTOCOL_BINARY_CMD_INCREMENT)
- {
- /* Send a positive request */
- protocol_binary_response_incr response= {
- .message= {
- .header.response= {
- .magic= PROTOCOL_BINARY_RES,
- .opcode= PROTOCOL_BINARY_CMD_INCREMENT,
- .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
- .opaque= header->request.opaque,
- .cas= ntohll(cas),
- .bodylen= htonl(8)
- },
- .body.value = htonll(result)
- }
- };
- rval= response_handler(cookie, header, (void*)&response);
- }
- }
- else
- {
- rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
- }
-
- return rval;
-}
-
-/**
- * Callback for noop. Inform the v1 interface about the noop packet, and
- * create and send a packet back to the client