diff options
author | dormando <dormando@rydia.net> | 2020-05-30 21:53:11 -0700 |
---|---|---|
committer | dormando <dormando@rydia.net> | 2020-07-02 15:37:57 -0700 |
commit | e1bb1db06081d61e16ee42c791da22003357427b (patch) | |
tree | e8354a1f5fb4b1126c76d0574531919af907e439 /memcached.c | |
parent | e190205e239b0338f1b539dcd2372bdcdd67d5b3 (diff) | |
download | memcached-e1bb1db06081d61e16ee42c791da22003357427b.tar.gz |
main: split binary protocol into proto_bin.c
also some missed text bits.
Diffstat (limited to 'memcached.c')
-rw-r--r-- | memcached.c | 1446 |
1 files changed, 1 insertions, 1445 deletions
diff --git a/memcached.c b/memcached.c index c0f1ed8..a21b4aa 100644 --- a/memcached.c +++ b/memcached.c @@ -57,6 +57,7 @@ #endif #include "proto_text.h" +#include "proto_bin.h" #if defined(__FreeBSD__) #include <sys/sysctl.h> @@ -80,7 +81,6 @@ enum try_read_result { static int try_read_command_negotiate(conn *c); static int try_read_command_udp(conn *c); -static int try_read_command_binary(conn *c); static enum try_read_result try_read_network(conn *c); static enum try_read_result try_read_udp(conn *c); @@ -101,25 +101,12 @@ static void conn_close(conn *c); static void conn_init(void); static bool update_event(conn *c, const int new_flags); static void complete_nread(conn *c); -static void write_bin_error(conn *c, protocol_binary_response_status err, - const char *errstr, int swallow); -static void write_bin_miss_response(conn *c, char *key, size_t nkey); #ifdef EXTSTORE static void _get_extstore_cb(void *e, obj_io *io, int ret); #endif static void conn_free(conn *c); -/** binprot handlers **/ -static void process_bin_flush(conn *c, char *extbuf); -static void process_bin_append_prepend(conn *c); -static void process_bin_update(conn *c, char *extbuf); -static void process_bin_get_or_touch(conn *c, char *extbuf); -static void process_bin_delete(conn *c); -static void complete_incr_bin(conn *c, char *extbuf); -static void process_bin_stat(conn *c); -static void process_bin_sasl_auth(conn *c); - /** exported globals **/ struct stats stats; struct stats_state stats_state; @@ -1262,608 +1249,6 @@ void out_of_memory(conn *c, char *ascii_error) { } } -/* - * we get here after reading the value in set/add/replace commands. The command - * has been stored in c->cmd, and the item is ready in c->item. - */ -static void complete_nread_ascii(conn *c) { - assert(c != NULL); - - item *it = c->item; - int comm = c->cmd; - enum store_item_type ret; - bool is_valid = false; - - pthread_mutex_lock(&c->thread->stats.mutex); - c->thread->stats.slab_stats[ITEM_clsid(it)].set_cmds++; - pthread_mutex_unlock(&c->thread->stats.mutex); - - if ((it->it_flags & ITEM_CHUNKED) == 0) { - if (strncmp(ITEM_data(it) + it->nbytes - 2, "\r\n", 2) == 0) { - is_valid = true; - } - } else { - char buf[2]; - /* should point to the final item chunk */ - item_chunk *ch = (item_chunk *) c->ritem; - assert(ch->used != 0); - /* :( We need to look at the last two bytes. This could span two - * chunks. - */ - if (ch->used > 1) { - buf[0] = ch->data[ch->used - 2]; - buf[1] = ch->data[ch->used - 1]; - } else { - assert(ch->prev); - assert(ch->used == 1); - buf[0] = ch->prev->data[ch->prev->used - 1]; - buf[1] = ch->data[ch->used - 1]; - } - if (strncmp(buf, "\r\n", 2) == 0) { - is_valid = true; - } else { - assert(1 == 0); - } - } - - if (!is_valid) { - // metaset mode always returns errors. - if (c->mset_res) { - c->noreply = false; - } - out_string(c, "CLIENT_ERROR bad data chunk"); - } else { - ret = store_item(it, comm, c); - -#ifdef ENABLE_DTRACE - uint64_t cas = ITEM_get_cas(it); - switch (c->cmd) { - case NREAD_ADD: - MEMCACHED_COMMAND_ADD(c->sfd, ITEM_key(it), it->nkey, - (ret == 1) ? it->nbytes : -1, cas); - break; - case NREAD_REPLACE: - MEMCACHED_COMMAND_REPLACE(c->sfd, ITEM_key(it), it->nkey, - (ret == 1) ? it->nbytes : -1, cas); - break; - case NREAD_APPEND: - MEMCACHED_COMMAND_APPEND(c->sfd, ITEM_key(it), it->nkey, - (ret == 1) ? it->nbytes : -1, cas); - break; - case NREAD_PREPEND: - MEMCACHED_COMMAND_PREPEND(c->sfd, ITEM_key(it), it->nkey, - (ret == 1) ? it->nbytes : -1, cas); - break; - case NREAD_SET: - MEMCACHED_COMMAND_SET(c->sfd, ITEM_key(it), it->nkey, - (ret == 1) ? it->nbytes : -1, cas); - break; - case NREAD_CAS: - MEMCACHED_COMMAND_CAS(c->sfd, ITEM_key(it), it->nkey, it->nbytes, - cas); - break; - } -#endif - - if (c->mset_res) { - // Replace the status code in the response. - // Rest was prepared during mset parsing. - mc_resp *resp = c->resp; - conn_set_state(c, conn_new_cmd); - switch (ret) { - case STORED: - memcpy(resp->wbuf, "OK ", 3); - // Only place noreply is used for meta cmds is a nominal response. - if (c->noreply) { - resp->skip = true; - } - break; - case EXISTS: - memcpy(resp->wbuf, "EX ", 3); - break; - case NOT_FOUND: - memcpy(resp->wbuf, "NF ", 3); - break; - case NOT_STORED: - memcpy(resp->wbuf, "NS ", 3); - break; - default: - c->noreply = false; - out_string(c, "SERVER_ERROR Unhandled storage type."); - } - } else { - switch (ret) { - case STORED: - out_string(c, "STORED"); - break; - case EXISTS: - out_string(c, "EXISTS"); - break; - case NOT_FOUND: - out_string(c, "NOT_FOUND"); - break; - case NOT_STORED: - out_string(c, "NOT_STORED"); - break; - default: - out_string(c, "SERVER_ERROR Unhandled storage type."); - } - } - - } - - c->set_stale = false; /* force flag to be off just in case */ - c->mset_res = false; - item_remove(c->item); /* release the c->item reference */ - c->item = 0; -} - -/** - * get a pointer to the key in this request - */ -static char* binary_get_key(conn *c) { - return c->rcurr - (c->binary_header.request.keylen); -} - -static void add_bin_header(conn *c, uint16_t err, uint8_t hdr_len, uint16_t key_len, uint32_t body_len) { - protocol_binary_response_header* header; - mc_resp *resp = c->resp; - - assert(c); - - resp_reset(resp); - - header = (protocol_binary_response_header *)resp->wbuf; - - header->response.magic = (uint8_t)PROTOCOL_BINARY_RES; - header->response.opcode = c->binary_header.request.opcode; - header->response.keylen = (uint16_t)htons(key_len); - - header->response.extlen = (uint8_t)hdr_len; - header->response.datatype = (uint8_t)PROTOCOL_BINARY_RAW_BYTES; - header->response.status = (uint16_t)htons(err); - - header->response.bodylen = htonl(body_len); - header->response.opaque = c->opaque; - header->response.cas = htonll(c->cas); - - if (settings.verbose > 1) { - int ii; - fprintf(stderr, ">%d Writing bin response:", c->sfd); - for (ii = 0; ii < sizeof(header->bytes); ++ii) { - if (ii % 4 == 0) { - fprintf(stderr, "\n>%d ", c->sfd); - } - fprintf(stderr, " 0x%02x", header->bytes[ii]); - } - fprintf(stderr, "\n"); - } - - resp->wbytes = sizeof(header->response); - resp_add_iov(resp, resp->wbuf, resp->wbytes); -} - -/** - * Writes a binary error response. If errstr is supplied, it is used as the - * error text; otherwise a generic description of the error status code is - * included. - */ -static void write_bin_error(conn *c, protocol_binary_response_status err, - const char *errstr, int swallow) { - size_t len; - - if (!errstr) { - switch (err) { - case PROTOCOL_BINARY_RESPONSE_ENOMEM: - errstr = "Out of memory"; - break; - case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND: - errstr = "Unknown command"; - break; - case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT: - errstr = "Not found"; - break; - case PROTOCOL_BINARY_RESPONSE_EINVAL: - errstr = "Invalid arguments"; - break; - case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS: - errstr = "Data exists for key."; - break; - case PROTOCOL_BINARY_RESPONSE_E2BIG: - errstr = "Too large."; - break; - case PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL: - errstr = "Non-numeric server-side value for incr or decr"; - break; - case PROTOCOL_BINARY_RESPONSE_NOT_STORED: - errstr = "Not stored."; - break; - case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR: - errstr = "Auth failure."; - break; - default: - assert(false); - errstr = "UNHANDLED ERROR"; - fprintf(stderr, ">%d UNHANDLED ERROR: %d\n", c->sfd, err); - } - } - - if (settings.verbose > 1) { - fprintf(stderr, ">%d Writing an error: %s\n", c->sfd, errstr); - } - - len = strlen(errstr); - add_bin_header(c, err, 0, 0, len); - if (len > 0) { - resp_add_iov(c->resp, errstr, len); - } - if (swallow > 0) { - c->sbytes = swallow; - conn_set_state(c, conn_swallow); - } else { - conn_set_state(c, conn_mwrite); - } -} - -/* Form and send a response to a command over the binary protocol */ -static void write_bin_response(conn *c, void *d, int hlen, int keylen, int dlen) { - if (!c->noreply || c->cmd == PROTOCOL_BINARY_CMD_GET || - c->cmd == PROTOCOL_BINARY_CMD_GETK) { - add_bin_header(c, 0, hlen, keylen, dlen); - mc_resp *resp = c->resp; - if (dlen > 0) { - resp_add_iov(resp, d, dlen); - } - } - - conn_set_state(c, conn_new_cmd); -} - -static void complete_incr_bin(conn *c, char *extbuf) { - item *it; - char *key; - size_t nkey; - /* Weird magic in add_delta forces me to pad here */ - char tmpbuf[INCR_MAX_STORAGE_LEN]; - uint64_t cas = 0; - - protocol_binary_response_incr* rsp = (protocol_binary_response_incr*)c->resp->wbuf; - protocol_binary_request_incr* req = (void *)extbuf; - - assert(c != NULL); - //assert(c->wsize >= sizeof(*rsp)); - - /* fix byteorder in the request */ - req->message.body.delta = ntohll(req->message.body.delta); - req->message.body.initial = ntohll(req->message.body.initial); - req->message.body.expiration = ntohl(req->message.body.expiration); - key = binary_get_key(c); - nkey = c->binary_header.request.keylen; - - if (settings.verbose > 1) { - int i; - fprintf(stderr, "incr "); - - for (i = 0; i < nkey; i++) { - fprintf(stderr, "%c", key[i]); - } - fprintf(stderr, " %lld, %llu, %d\n", - (long long)req->message.body.delta, - (long long)req->message.body.initial, - req->message.body.expiration); - } - - if (c->binary_header.request.cas != 0) { - cas = c->binary_header.request.cas; - } - switch(add_delta(c, key, nkey, c->cmd == PROTOCOL_BINARY_CMD_INCREMENT, - req->message.body.delta, tmpbuf, - &cas)) { - case OK: - rsp->message.body.value = htonll(strtoull(tmpbuf, NULL, 10)); - if (cas) { - c->cas = cas; - } - write_bin_response(c, &rsp->message.body, 0, 0, - sizeof(rsp->message.body.value)); - break; - case NON_NUMERIC: - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL, NULL, 0); - break; - case EOM: - out_of_memory(c, "SERVER_ERROR Out of memory incrementing value"); - break; - case DELTA_ITEM_NOT_FOUND: - if (req->message.body.expiration != 0xffffffff) { - /* Save some room for the response */ - rsp->message.body.value = htonll(req->message.body.initial); - - snprintf(tmpbuf, INCR_MAX_STORAGE_LEN, "%llu", - (unsigned long long)req->message.body.initial); - int res = strlen(tmpbuf); - it = item_alloc(key, nkey, 0, realtime(req->message.body.expiration), - res + 2); - - if (it != NULL) { - memcpy(ITEM_data(it), tmpbuf, res); - memcpy(ITEM_data(it) + res, "\r\n", 2); - - if (store_item(it, NREAD_ADD, c)) { - c->cas = ITEM_get_cas(it); - write_bin_response(c, &rsp->message.body, 0, 0, sizeof(rsp->message.body.value)); - } else { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_NOT_STORED, - NULL, 0); - } - item_remove(it); /* release our reference */ - } else { - out_of_memory(c, - "SERVER_ERROR Out of memory allocating new item"); - } - } else { - pthread_mutex_lock(&c->thread->stats.mutex); - if (c->cmd == PROTOCOL_BINARY_CMD_INCREMENT) { - c->thread->stats.incr_misses++; - } else { - c->thread->stats.decr_misses++; - } - pthread_mutex_unlock(&c->thread->stats.mutex); - - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0); - } - break; - case DELTA_ITEM_CAS_MISMATCH: - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, NULL, 0); - break; - } -} - -static void complete_update_bin(conn *c) { - protocol_binary_response_status eno = PROTOCOL_BINARY_RESPONSE_EINVAL; - enum store_item_type ret = NOT_STORED; - assert(c != NULL); - - item *it = c->item; - pthread_mutex_lock(&c->thread->stats.mutex); - c->thread->stats.slab_stats[ITEM_clsid(it)].set_cmds++; - pthread_mutex_unlock(&c->thread->stats.mutex); - - /* We don't actually receive the trailing two characters in the bin - * protocol, so we're going to just set them here */ - if ((it->it_flags & ITEM_CHUNKED) == 0) { - *(ITEM_data(it) + it->nbytes - 2) = '\r'; - *(ITEM_data(it) + it->nbytes - 1) = '\n'; - } else { - assert(c->ritem); - item_chunk *ch = (item_chunk *) c->ritem; - if (ch->size == ch->used) - ch = ch->next; - assert(ch->size - ch->used >= 2); - ch->data[ch->used] = '\r'; - ch->data[ch->used + 1] = '\n'; - ch->used += 2; - } - - ret = store_item(it, c->cmd, c); - -#ifdef ENABLE_DTRACE - uint64_t cas = ITEM_get_cas(it); - switch (c->cmd) { - case NREAD_ADD: - MEMCACHED_COMMAND_ADD(c->sfd, ITEM_key(it), it->nkey, - (ret == STORED) ? it->nbytes : -1, cas); - break; - case NREAD_REPLACE: - MEMCACHED_COMMAND_REPLACE(c->sfd, ITEM_key(it), it->nkey, - (ret == STORED) ? it->nbytes : -1, cas); - break; - case NREAD_APPEND: - MEMCACHED_COMMAND_APPEND(c->sfd, ITEM_key(it), it->nkey, - (ret == STORED) ? it->nbytes : -1, cas); - break; - case NREAD_PREPEND: - MEMCACHED_COMMAND_PREPEND(c->sfd, ITEM_key(it), it->nkey, - (ret == STORED) ? it->nbytes : -1, cas); - break; - case NREAD_SET: - MEMCACHED_COMMAND_SET(c->sfd, ITEM_key(it), it->nkey, - (ret == STORED) ? it->nbytes : -1, cas); - break; - } -#endif - - switch (ret) { - case STORED: - /* Stored */ - write_bin_response(c, NULL, 0, 0, 0); - break; - case EXISTS: - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, NULL, 0); - break; - case NOT_FOUND: - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0); - break; - case NOT_STORED: - case TOO_LARGE: - case NO_MEMORY: - if (c->cmd == NREAD_ADD) { - eno = PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS; - } else if(c->cmd == NREAD_REPLACE) { - eno = PROTOCOL_BINARY_RESPONSE_KEY_ENOENT; - } else { - eno = PROTOCOL_BINARY_RESPONSE_NOT_STORED; - } - write_bin_error(c, eno, NULL, 0); - } - - item_remove(c->item); /* release the c->item reference */ - c->item = 0; -} - -static void write_bin_miss_response(conn *c, char *key, size_t nkey) { - if (nkey) { - add_bin_header(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, - 0, nkey, nkey); - char *ofs = c->resp->wbuf + sizeof(protocol_binary_response_header); - memcpy(ofs, key, nkey); - resp_add_iov(c->resp, ofs, nkey); - conn_set_state(c, conn_new_cmd); - } else { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, - NULL, 0); - } -} - -static void process_bin_get_or_touch(conn *c, char *extbuf) { - item *it; - - protocol_binary_response_get* rsp = (protocol_binary_response_get*)c->resp->wbuf; - char* key = binary_get_key(c); - size_t nkey = c->binary_header.request.keylen; - int should_touch = (c->cmd == PROTOCOL_BINARY_CMD_TOUCH || - c->cmd == PROTOCOL_BINARY_CMD_GAT || - c->cmd == PROTOCOL_BINARY_CMD_GATK); - int should_return_key = (c->cmd == PROTOCOL_BINARY_CMD_GETK || - c->cmd == PROTOCOL_BINARY_CMD_GATK); - int should_return_value = (c->cmd != PROTOCOL_BINARY_CMD_TOUCH); - bool failed = false; - - if (settings.verbose > 1) { - fprintf(stderr, "<%d %s ", c->sfd, should_touch ? "TOUCH" : "GET"); - if (fwrite(key, 1, nkey, stderr)) {} - fputc('\n', stderr); - } - - if (should_touch) { - protocol_binary_request_touch *t = (void *)extbuf; - time_t exptime = ntohl(t->message.body.expiration); - - it = item_touch(key, nkey, realtime(exptime), c); - } else { - it = item_get(key, nkey, c, DO_UPDATE); - } - - if (it) { - /* the length has two unnecessary bytes ("\r\n") */ - uint16_t keylen = 0; - uint32_t bodylen = sizeof(rsp->message.body) + (it->nbytes - 2); - - pthread_mutex_lock(&c->thread->stats.mutex); - if (should_touch) { - c->thread->stats.touch_cmds++; - c->thread->stats.slab_stats[ITEM_clsid(it)].touch_hits++; - } else { - c->thread->stats.get_cmds++; - c->thread->stats.lru_hits[it->slabs_clsid]++; - } - pthread_mutex_unlock(&c->thread->stats.mutex); - - if (should_touch) { - MEMCACHED_COMMAND_TOUCH(c->sfd, ITEM_key(it), it->nkey, - it->nbytes, ITEM_get_cas(it)); - } else { - MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey, - it->nbytes, ITEM_get_cas(it)); - } - - if (c->cmd == PROTOCOL_BINARY_CMD_TOUCH) { - bodylen -= it->nbytes - 2; - } else if (should_return_key) { - bodylen += nkey; - keylen = nkey; - } - - add_bin_header(c, 0, sizeof(rsp->message.body), keylen, bodylen); - rsp->message.header.response.cas = htonll(ITEM_get_cas(it)); - - // add the flags - FLAGS_CONV(it, rsp->message.body.flags); - rsp->message.body.flags = htonl(rsp->message.body.flags); - resp_add_iov(c->resp, &rsp->message.body, sizeof(rsp->message.body)); - - if (should_return_key) { - resp_add_iov(c->resp, ITEM_key(it), nkey); - } - - if (should_return_value) { - /* Add the data minus the CRLF */ -#ifdef EXTSTORE - if (it->it_flags & ITEM_HDR) { - if (_get_extstore(c, it, c->resp) != 0) { - pthread_mutex_lock(&c->thread->stats.mutex); - c->thread->stats.get_oom_extstore++; - pthread_mutex_unlock(&c->thread->stats.mutex); - - failed = true; - } - } else if ((it->it_flags & ITEM_CHUNKED) == 0) { - resp_add_iov(c->resp, ITEM_data(it), it->nbytes - 2); - } else { - // Allow transmit handler to find the item and expand iov's - resp_add_chunked_iov(c->resp, it, it->nbytes - 2); - } -#else - if ((it->it_flags & ITEM_CHUNKED) == 0) { - resp_add_iov(c->resp, ITEM_data(it), it->nbytes - 2); - } else { - resp_add_chunked_iov(c->resp, it, it->nbytes - 2); - } -#endif - } - - if (!failed) { - conn_set_state(c, conn_new_cmd); - /* Remember this command so we can garbage collect it later */ -#ifdef EXTSTORE - if ((it->it_flags & ITEM_HDR) != 0 && should_return_value) { - // Only have extstore clean if header and returning value. - c->resp->item = NULL; - } else { - c->resp->item = it; - } -#else - c->resp->item = it; -#endif - } else { - item_remove(it); - } - } else { - failed = true; - } - - if (failed) { - pthread_mutex_lock(&c->thread->stats.mutex); - if (should_touch) { - c->thread->stats.touch_cmds++; - c->thread->stats.touch_misses++; - } else { - c->thread->stats.get_cmds++; - c->thread->stats.get_misses++; - } - pthread_mutex_unlock(&c->thread->stats.mutex); - - if (should_touch) { - MEMCACHED_COMMAND_TOUCH(c->sfd, key, nkey, -1, 0); - } else { - MEMCACHED_COMMAND_GET(c->sfd, key, nkey, -1, 0); - } - - if (c->noreply) { - conn_set_state(c, conn_new_cmd); - } else { - if (should_return_key) { - write_bin_miss_response(c, key, nkey); - } else { - write_bin_miss_response(c, NULL, 0); - } - } - } - - if (settings.detail_enabled) { - stats_prefix_record_get(key, nkey, NULL != it); - } -} - static void append_bin_stats(const char *key, const uint16_t klen, const char *val, const uint32_t vlen, conn *c) { @@ -1973,762 +1358,6 @@ void append_stats(const char *key, const uint16_t klen, assert(c->stats.offset <= c->stats.size); } -static void process_bin_stat(conn *c) { - char *subcommand = binary_get_key(c); - size_t nkey = c->binary_header.request.keylen; - - if (settings.verbose > 1) { - int ii; - fprintf(stderr, "<%d STATS ", c->sfd); - for (ii = 0; ii < nkey; ++ii) { - fprintf(stderr, "%c", subcommand[ii]); - } - fprintf(stderr, "\n"); - } - - if (nkey == 0) { - /* request all statistics */ - server_stats(&append_stats, c); - (void)get_stats(NULL, 0, &append_stats, c); - } else if (strncmp(subcommand, "reset", 5) == 0) { - stats_reset(); - } else if (strncmp(subcommand, "settings", 8) == 0) { - process_stat_settings(&append_stats, c); - } else if (strncmp(subcommand, "detail", 6) == 0) { - char *subcmd_pos = subcommand + 6; - if (strncmp(subcmd_pos, " dump", 5) == 0) { - int len; - char *dump_buf = stats_prefix_dump(&len); - if (dump_buf == NULL || len <= 0) { - out_of_memory(c, "SERVER_ERROR Out of memory generating stats"); - if (dump_buf != NULL) - free(dump_buf); - return; - } else { - append_stats("detailed", strlen("detailed"), dump_buf, len, c); - free(dump_buf); - } - } else if (strncmp(subcmd_pos, " on", 3) == 0) { - settings.detail_enabled = 1; - } else if (strncmp(subcmd_pos, " off", 4) == 0) { - settings.detail_enabled = 0; - } else { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0); - return; - } - } else { - if (get_stats(subcommand, nkey, &append_stats, c)) { - if (c->stats.buffer == NULL) { - out_of_memory(c, "SERVER_ERROR Out of memory generating stats"); - } else { - write_and_free(c, c->stats.buffer, c->stats.offset); - c->stats.buffer = NULL; - } - } else { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0); - } - - return; - } - - /* Append termination package and start the transfer */ - append_stats(NULL, 0, NULL, 0, c); - if (c->stats.buffer == NULL) { - out_of_memory(c, "SERVER_ERROR Out of memory preparing to send stats"); - } else { - write_and_free(c, c->stats.buffer, c->stats.offset); - c->stats.buffer = NULL; - } -} - -/* Just write an error message and disconnect the client */ -static void handle_binary_protocol_error(conn *c) { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, 0); - if (settings.verbose) { - fprintf(stderr, "Protocol error (opcode %02x), close connection %d\n", - c->binary_header.request.opcode, c->sfd); - } - c->close_after_write = true; -} - -static void init_sasl_conn(conn *c) { - assert(c); - /* should something else be returned? */ - if (!settings.sasl) - return; - - c->authenticated = false; - - if (!c->sasl_conn) { - int result=sasl_server_new("memcached", - NULL, - my_sasl_hostname[0] ? my_sasl_hostname : NULL, - NULL, NULL, - NULL, 0, &c->sasl_conn); - if (result != SASL_OK) { - if (settings.verbose) { - fprintf(stderr, "Failed to initialize SASL conn.\n"); - } - c->sasl_conn = NULL; - } - } -} - -static void bin_list_sasl_mechs(conn *c) { - // Guard against a disabled SASL. - if (!settings.sasl) { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL, - c->binary_header.request.bodylen - - c->binary_header.request.keylen); - return; - } - - init_sasl_conn(c); - const char *result_string = NULL; - unsigned int string_length = 0; - int result=sasl_listmech(c->sasl_conn, NULL, - "", /* What to prepend the string with */ - " ", /* What to separate mechanisms with */ - "", /* What to append to the string */ - &result_string, &string_length, - NULL); - if (result != SASL_OK) { - /* Perhaps there's a better error for this... */ - if (settings.verbose) { - fprintf(stderr, "Failed to list SASL mechanisms.\n"); - } - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0); - return; - } - write_bin_response(c, (char*)result_string, 0, 0, string_length); -} - -static void process_bin_sasl_auth(conn *c) { - // Guard for handling disabled SASL on the server. - if (!settings.sasl) { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL, - c->binary_header.request.bodylen - - c->binary_header.request.keylen); - return; - } - - assert(c->binary_header.request.extlen == 0); - - int nkey = c->binary_header.request.keylen; - int vlen = c->binary_header.request.bodylen - nkey; - - if (nkey > MAX_SASL_MECH_LEN) { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, vlen); - conn_set_state(c, conn_swallow); - return; - } - - char *key = binary_get_key(c); - assert(key); - - item *it = item_alloc(key, nkey, 0, 0, vlen+2); - - /* Can't use a chunked item for SASL authentication. */ - if (it == 0 || (it->it_flags & ITEM_CHUNKED)) { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, NULL, vlen); - conn_set_state(c, conn_swallow); - if (it) { - do_item_remove(it); - } - return; - } - - c->item = it; - c->ritem = ITEM_data(it); - c->rlbytes = vlen; - conn_set_state(c, conn_nread); - c->substate = bin_reading_sasl_auth_data; -} - -static void process_bin_complete_sasl_auth(conn *c) { - assert(settings.sasl); - const char *out = NULL; - unsigned int outlen = 0; - - assert(c->item); - init_sasl_conn(c); - - int nkey = c->binary_header.request.keylen; - int vlen = c->binary_header.request.bodylen - nkey; - - if (nkey > ((item*) c->item)->nkey) { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, vlen); - conn_set_state(c, conn_swallow); - return; - } - - char mech[nkey+1]; - memcpy(mech, ITEM_key((item*)c->item), nkey); - mech[nkey] = 0x00; - - if (settings.verbose) - fprintf(stderr, "mech: ``%s'' with %d bytes of data\n", mech, vlen); - - const char *challenge = vlen == 0 ? NULL : ITEM_data((item*) c->item); - - if (vlen > ((item*) c->item)->nbytes) { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, vlen); - conn_set_state(c, conn_swallow); - return; - } - - int result=-1; - - switch (c->cmd) { - case PROTOCOL_BINARY_CMD_SASL_AUTH: - result = sasl_server_start(c->sasl_conn, mech, - challenge, vlen, - &out, &outlen); - c->sasl_started = (result == SASL_OK || result == SASL_CONTINUE); - break; - case PROTOCOL_BINARY_CMD_SASL_STEP: - if (!c->sasl_started) { - if (settings.verbose) { - fprintf(stderr, "%d: SASL_STEP called but sasl_server_start " - "not called for this connection!\n", c->sfd); - } - break; - } - result = sasl_server_step(c->sasl_conn, - challenge, vlen, - &out, &outlen); - break; - default: - assert(false); /* CMD should be one of the above */ - /* This code is pretty much impossible, but makes the compiler - happier */ - if (settings.verbose) { - fprintf(stderr, "Unhandled command %d with challenge %s\n", - c->cmd, challenge); - } - break; - } - - if (settings.verbose) { - fprintf(stderr, "sasl result code: %d\n", result); - } - - switch(result) { - case SASL_OK: - c->authenticated = true; - write_bin_response(c, "Authenticated", 0, 0, strlen("Authenticated")); - pthread_mutex_lock(&c->thread->stats.mutex); - c->thread->stats.auth_cmds++; - pthread_mutex_unlock(&c->thread->stats.mutex); - break; - case SASL_CONTINUE: - add_bin_header(c, PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE, 0, 0, outlen); - if (outlen > 0) { - resp_add_iov(c->resp, out, outlen); - } - // Immediately flush our write. - conn_set_state(c, conn_mwrite); - break; - default: - if (settings.verbose) - fprintf(stderr, "Unknown sasl response: %d\n", result); - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0); - pthread_mutex_lock(&c->thread->stats.mutex); - c->thread->stats.auth_cmds++; - c->thread->stats.auth_errors++; - pthread_mutex_unlock(&c->thread->stats.mutex); - } -} - -static bool authenticated(conn *c) { - assert(settings.sasl); - bool rv = false; - - switch (c->cmd) { - case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: /* FALLTHROUGH */ - case PROTOCOL_BINARY_CMD_SASL_AUTH: /* FALLTHROUGH */ - case PROTOCOL_BINARY_CMD_SASL_STEP: /* FALLTHROUGH */ - case PROTOCOL_BINARY_CMD_VERSION: /* FALLTHROUGH */ - rv = true; - break; - default: - rv = c->authenticated; - } - - if (settings.verbose > 1) { - fprintf(stderr, "authenticated() in cmd 0x%02x is %s\n", - c->cmd, rv ? "true" : "false"); - } - - return rv; -} - -static void dispatch_bin_command(conn *c, char *extbuf) { - int protocol_error = 0; - - uint8_t extlen = c->binary_header.request.extlen; - uint16_t keylen = c->binary_header.request.keylen; - uint32_t bodylen = c->binary_header.request.bodylen; - - if (keylen > bodylen || keylen + extlen > bodylen) { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL, 0); - c->close_after_write = true; - return; - } - - if (settings.sasl && !authenticated(c)) { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0); - c->close_after_write = true; - return; - } - - MEMCACHED_PROCESS_COMMAND_START(c->sfd, c->rcurr, c->rbytes); - c->noreply = true; - - /* binprot supports 16bit keys, but internals are still 8bit */ - if (keylen > KEY_MAX_LENGTH) { - handle_binary_protocol_error(c); - return; - } - - switch (c->cmd) { - case PROTOCOL_BINARY_CMD_SETQ: - c->cmd = PROTOCOL_BINARY_CMD_SET; - break; - case PROTOCOL_BINARY_CMD_ADDQ: - c->cmd = PROTOCOL_BINARY_CMD_ADD; - break; - case PROTOCOL_BINARY_CMD_REPLACEQ: - c->cmd = PROTOCOL_BINARY_CMD_REPLACE; - break; - case PROTOCOL_BINARY_CMD_DELETEQ: - c->cmd = PROTOCOL_BINARY_CMD_DELETE; - break; - case PROTOCOL_BINARY_CMD_INCREMENTQ: - c->cmd = PROTOCOL_BINARY_CMD_INCREMENT; - break; - case PROTOCOL_BINARY_CMD_DECREMENTQ: - c->cmd = PROTOCOL_BINARY_CMD_DECREMENT; - break; - case PROTOCOL_BINARY_CMD_QUITQ: - c->cmd = PROTOCOL_BINARY_CMD_QUIT; - break; - case PROTOCOL_BINARY_CMD_FLUSHQ: - c->cmd = PROTOCOL_BINARY_CMD_FLUSH; - break; - case PROTOCOL_BINARY_CMD_APPENDQ: - c->cmd = PROTOCOL_BINARY_CMD_APPEND; - break; - case PROTOCOL_BINARY_CMD_PREPENDQ: - c->cmd = PROTOCOL_BINARY_CMD_PREPEND; - break; - case PROTOCOL_BINARY_CMD_GETQ: - c->cmd = PROTOCOL_BINARY_CMD_GET; - break; - case PROTOCOL_BINARY_CMD_GETKQ: - c->cmd = PROTOCOL_BINARY_CMD_GETK; - break; - case PROTOCOL_BINARY_CMD_GATQ: - c->cmd = PROTOCOL_BINARY_CMD_GAT; - break; - case PROTOCOL_BINARY_CMD_GATKQ: - c->cmd = PROTOCOL_BINARY_CMD_GATK; - break; - default: - c->noreply = false; - } - - switch (c->cmd) { - case PROTOCOL_BINARY_CMD_VERSION: - if (extlen == 0 && keylen == 0 && bodylen == 0) { - write_bin_response(c, VERSION, 0, 0, strlen(VERSION)); - } else { - protocol_error = 1; - } - break; - case PROTOCOL_BINARY_CMD_FLUSH: - if (keylen == 0 && bodylen == extlen && (extlen == 0 || extlen == 4)) { - process_bin_flush(c, extbuf); - } else { - protocol_error = 1; - } - break; - case PROTOCOL_BINARY_CMD_NOOP: - if (extlen == 0 && keylen == 0 && bodylen == 0) { - write_bin_response(c, NULL, 0, 0, 0); - // NOOP forces pipeline flush. - conn_set_state(c, conn_mwrite); - } else { - protocol_error = 1; - } - break; - case PROTOCOL_BINARY_CMD_SET: /* FALLTHROUGH */ - case PROTOCOL_BINARY_CMD_ADD: /* FALLTHROUGH */ - case PROTOCOL_BINARY_CMD_REPLACE: - if (extlen == 8 && keylen != 0 && bodylen >= (keylen + 8)) { - process_bin_update(c, extbuf); - } else { - protocol_error = 1; - } - break; - case PROTOCOL_BINARY_CMD_GETQ: /* FALLTHROUGH */ - case PROTOCOL_BINARY_CMD_GET: /* FALLTHROUGH */ - case PROTOCOL_BINARY_CMD_GETKQ: /* FALLTHROUGH */ - case PROTOCOL_BINARY_CMD_GETK: - if (extlen == 0 && bodylen == keylen && keylen > 0) { - process_bin_get_or_touch(c, extbuf); - } else { - protocol_error = 1; - } - break; - case PROTOCOL_BINARY_CMD_DELETE: - if (keylen > 0 && extlen == 0 && bodylen == keylen) { - process_bin_delete(c); - } else { - protocol_error = 1; - } - break; - case PROTOCOL_BINARY_CMD_INCREMENT: - case PROTOCOL_BINARY_CMD_DECREMENT: - if (keylen > 0 && extlen == 20 && bodylen == (keylen + extlen)) { - complete_incr_bin(c, extbuf); - } else { - protocol_error = 1; - } - break; - case PROTOCOL_BINARY_CMD_APPEND: - case PROTOCOL_BINARY_CMD_PREPEND: - if (keylen > 0 && extlen == 0) { - process_bin_append_prepend(c); - } else { - protocol_error = 1; - } - break; - case PROTOCOL_BINARY_CMD_STAT: - if (extlen == 0) { - process_bin_stat(c); - } else { - protocol_error = 1; - } - break; - case PROTOCOL_BINARY_CMD_QUIT: - if (keylen == 0 && extlen == 0 && bodylen == 0) { - write_bin_response(c, NULL, 0, 0, 0); - conn_set_state(c, conn_mwrite); - c->close_after_write = true; - } else { - protocol_error = 1; - } - break; - case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: - if (extlen == 0 && keylen == 0 && bodylen == 0) { - bin_list_sasl_mechs(c); - } else { - protocol_error = 1; - } - break; - case PROTOCOL_BINARY_CMD_SASL_AUTH: - case PROTOCOL_BINARY_CMD_SASL_STEP: - if (extlen == 0 && keylen != 0) { - process_bin_sasl_auth(c); - } else { - protocol_error = 1; - } - break; - case PROTOCOL_BINARY_CMD_TOUCH: - case PROTOCOL_BINARY_CMD_GAT: - case PROTOCOL_BINARY_CMD_GATQ: - case PROTOCOL_BINARY_CMD_GATK: - case PROTOCOL_BINARY_CMD_GATKQ: - if (extlen == 4 && keylen != 0) { - process_bin_get_or_touch(c, extbuf); - } else { - protocol_error = 1; - } - break; - default: - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL, - bodylen); - } - - if (protocol_error) - handle_binary_protocol_error(c); -} - -static void process_bin_update(conn *c, char *extbuf) { - char *key; - int nkey; - int vlen; - item *it; - protocol_binary_request_set* req = (void *)extbuf; - - assert(c != NULL); - - key = binary_get_key(c); - nkey = c->binary_header.request.keylen; - - /* fix byteorder in the request */ - req->message.body.flags = ntohl(req->message.body.flags); - req->message.body.expiration = ntohl(req->message.body.expiration); - - vlen = c->binary_header.request.bodylen - (nkey + c->binary_header.request.extlen); - - if (settings.verbose > 1) { - int ii; - if (c->cmd == PROTOCOL_BINARY_CMD_ADD) { - fprintf(stderr, "<%d ADD ", c->sfd); - } else if (c->cmd == PROTOCOL_BINARY_CMD_SET) { - fprintf(stderr, "<%d SET ", c->sfd); - } else { - fprintf(stderr, "<%d REPLACE ", c->sfd); - } - for (ii = 0; ii < nkey; ++ii) { - fprintf(stderr, "%c", key[ii]); - } - - fprintf(stderr, " Value len is %d", vlen); - fprintf(stderr, "\n"); - } - - if (settings.detail_enabled) { - stats_prefix_record_set(key, nkey); - } - - it = item_alloc(key, nkey, req->message.body.flags, - realtime(req->message.body.expiration), vlen+2); - - if (it == 0) { - enum store_item_type status; - if (! item_size_ok(nkey, req->message.body.flags, vlen + 2)) { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_E2BIG, NULL, vlen); - status = TOO_LARGE; - } else { - out_of_memory(c, "SERVER_ERROR Out of memory allocating item"); - /* This error generating method eats the swallow value. Add here. */ - c->sbytes = vlen; - status = NO_MEMORY; - } - /* FIXME: losing c->cmd since it's translated below. refactor? */ - LOGGER_LOG(c->thread->l, LOG_MUTATIONS, LOGGER_ITEM_STORE, - NULL, status, 0, key, nkey, req->message.body.expiration, - ITEM_clsid(it), c->sfd); - - /* Avoid stale data persisting in cache because we failed alloc. - * Unacceptable for SET. Anywhere else too? */ - if (c->cmd == PROTOCOL_BINARY_CMD_SET) { - it = item_get(key, nkey, c, DONT_UPDATE); - if (it) { - item_unlink(it); - STORAGE_delete(c->thread->storage, it); - item_remove(it); - } - } - - /* swallow the data line */ - conn_set_state(c, conn_swallow); - return; - } - - ITEM_set_cas(it, c->binary_header.request.cas); - - switch (c->cmd) { - case PROTOCOL_BINARY_CMD_ADD: - c->cmd = NREAD_ADD; - break; - case PROTOCOL_BINARY_CMD_SET: - c->cmd = NREAD_SET; - break; - case PROTOCOL_BINARY_CMD_REPLACE: - c->cmd = NREAD_REPLACE; - break; - default: - assert(0); - } - - if (ITEM_get_cas(it) != 0) { - c->cmd = NREAD_CAS; - } - - c->item = it; -#ifdef NEED_ALIGN - if (it->it_flags & ITEM_CHUNKED) { - c->ritem = ITEM_schunk(it); - } else { - c->ritem = ITEM_data(it); - } -#else - c->ritem = ITEM_data(it); -#endif - c->rlbytes = vlen; - conn_set_state(c, conn_nread); - c->substate = bin_read_set_value; -} - -static void process_bin_append_prepend(conn *c) { - char *key; - int nkey; - int vlen; - item *it; - - assert(c != NULL); - - key = binary_get_key(c); - nkey = c->binary_header.request.keylen; - vlen = c->binary_header.request.bodylen - nkey; - - if (settings.verbose > 1) { - fprintf(stderr, "Value len is %d\n", vlen); - } - - if (settings.detail_enabled) { - stats_prefix_record_set(key, nkey); - } - - it = item_alloc(key, nkey, 0, 0, vlen+2); - - if (it == 0) { - if (! item_size_ok(nkey, 0, vlen + 2)) { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_E2BIG, NULL, vlen); - } else { - out_of_memory(c, "SERVER_ERROR Out of memory allocating item"); - /* OOM calls eat the swallow value. Add here. */ - c->sbytes = vlen; - } - /* swallow the data line */ - conn_set_state(c, conn_swallow); - return; - } - - ITEM_set_cas(it, c->binary_header.request.cas); - - switch (c->cmd) { - case PROTOCOL_BINARY_CMD_APPEND: - c->cmd = NREAD_APPEND; - break; - case PROTOCOL_BINARY_CMD_PREPEND: - c->cmd = NREAD_PREPEND; - break; - default: - assert(0); - } - - c->item = it; -#ifdef NEED_ALIGN - if (it->it_flags & ITEM_CHUNKED) { - c->ritem = ITEM_schunk(it); - } else { - c->ritem = ITEM_data(it); - } -#else - c->ritem = ITEM_data(it); -#endif - c->rlbytes = vlen; - conn_set_state(c, conn_nread); - c->substate = bin_read_set_value; -} - -static void process_bin_flush(conn *c, char *extbuf) { - time_t exptime = 0; - protocol_binary_request_flush* req = (void *)extbuf; - rel_time_t new_oldest = 0; - - if (!settings.flush_enabled) { - // flush_all is not allowed but we log it on stats - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0); - return; - } - - if (c->binary_header.request.extlen == sizeof(req->message.body)) { - exptime = ntohl(req->message.body.expiration); - } - - if (exptime > 0) { - new_oldest = realtime(exptime); - } else { - new_oldest = current_time; - } - if (settings.use_cas) { - settings.oldest_live = new_oldest - 1; - if (settings.oldest_live <= current_time) - settings.oldest_cas = get_cas_id(); - } else { - settings.oldest_live = new_oldest; - } - - pthread_mutex_lock(&c->thread->stats.mutex); - c->thread->stats.flush_cmds++; - pthread_mutex_unlock(&c->thread->stats.mutex); - - write_bin_response(c, NULL, 0, 0, 0); -} - -static void process_bin_delete(conn *c) { - item *it; - uint32_t hv; - - char* key = binary_get_key(c); - size_t nkey = c->binary_header.request.keylen; - - assert(c != NULL); - - if (settings.verbose > 1) { - int ii; - fprintf(stderr, "Deleting "); - for (ii = 0; ii < nkey; ++ii) { - fprintf(stderr, "%c", key[ii]); - } - fprintf(stderr, "\n"); - } - - if (settings.detail_enabled) { - stats_prefix_record_delete(key, nkey); - } - - it = item_get_locked(key, nkey, c, DONT_UPDATE, &hv); - if (it) { - uint64_t cas = c->binary_header.request.cas; - if (cas == 0 || cas == ITEM_get_cas(it)) { - MEMCACHED_COMMAND_DELETE(c->sfd, ITEM_key(it), it->nkey); - pthread_mutex_lock(&c->thread->stats.mutex); - c->thread->stats.slab_stats[ITEM_clsid(it)].delete_hits++; - pthread_mutex_unlock(&c->thread->stats.mutex); - do_item_unlink(it, hv); - STORAGE_delete(c->thread->storage, it); - write_bin_response(c, NULL, 0, 0, 0); - } else { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, NULL, 0); - } - do_item_remove(it); /* release our reference */ - } else { - write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0); - pthread_mutex_lock(&c->thread->stats.mutex); - c->thread->stats.delete_misses++; - pthread_mutex_unlock(&c->thread->stats.mutex); - } - item_unlock(hv); -} - -static void complete_nread_binary(conn *c) { - assert(c != NULL); - assert(c->cmd >= 0); - - switch(c->substate) { - case bin_read_set_value: - complete_update_bin(c); - break; - case bin_reading_sasl_auth_data: - process_bin_complete_sasl_auth(c); - if (c->item) { - do_item_remove(c->item); - c->item = NULL; - } - break; - default: - fprintf(stderr, "Not handling substate %d\n", c->substate); - assert(0); - } -} - static void reset_cmd_handler(conn *c) { c->cmd = -1; c->substate = bin_no_state; @@ -3952,79 +2581,6 @@ static int try_read_command_udp(conn *c) { } } -static int try_read_command_binary(conn *c) { - /* Do we have the complete packet header? */ - if (c->rbytes < sizeof(c->binary_header)) { - /* need more data! */ - return 0; - } else { - memcpy(&c->binary_header, c->rcurr, sizeof(c->binary_header)); - protocol_binary_request_header* req; - req = &c->binary_header; - - if (settings.verbose > 1) { - /* Dump the packet before we convert it to host order */ - int ii; - fprintf(stderr, "<%d Read binary protocol data:", c->sfd); - for (ii = 0; ii < sizeof(req->bytes); ++ii) { - if (ii % 4 == 0) { - fprintf(stderr, "\n<%d ", c->sfd); - } - fprintf(stderr, " 0x%02x", req->bytes[ii]); - } - fprintf(stderr, "\n"); - } - - c->binary_header = *req; - c->binary_header.request.keylen = ntohs(req->request.keylen); - c->binary_header.request.bodylen = ntohl(req->request.bodylen); - c->binary_header.request.cas = ntohll(req->request.cas); - - if (c->binary_header.request.magic != PROTOCOL_BINARY_REQ) { - if (settings.verbose) { - fprintf(stderr, "Invalid magic: %x\n", - c->binary_header.request.magic); - } - conn_set_state(c, conn_closing); - return -1; - } - - uint8_t extlen = c->binary_header.request.extlen; - uint16_t keylen = c->binary_header.request.keylen; - if (c->rbytes < keylen + extlen + sizeof(c->binary_header)) { - // Still need more bytes. Let try_read_network() realign the - // read-buffer and fetch more data as necessary. - return 0; - } - - if (!resp_start(c)) { - conn_set_state(c, conn_closing); - return -1; - } - - c->cmd = c->binary_header.request.opcode; - c->keylen = c->binary_header.request.keylen; - c->opaque = c->binary_header.request.opaque; - /* clear the returned cas value */ - c->cas = 0; - - c->last_cmd_time = current_time; - // sigh. binprot has no "largest possible extlen" define, and I don't - // want to refactor a ton of code either. Header is only ever used out - // of c->binary_header, but the extlen stuff is used for the latter - // bytes. Just wastes 24 bytes on the stack this way. - char extbuf[sizeof(c->binary_header) + BIN_MAX_EXTLEN+1]; - memcpy(extbuf + sizeof(c->binary_header), c->rcurr + sizeof(c->binary_header), - extlen > BIN_MAX_EXTLEN ? BIN_MAX_EXTLEN : extlen); - c->rbytes -= sizeof(c->binary_header) + extlen + keylen; - c->rcurr += sizeof(c->binary_header) + extlen + keylen; - - dispatch_bin_command(c, extbuf); - } - - return 1; -} - /* * read a UDP request. */ |