|
19 | 19 | #include <string.h>
|
20 | 20 | #include <stdlib.h>
|
21 | 21 |
|
| 22 | +#define _DO_CAS true |
| 23 | +#define _NO_CAS false |
| 24 | +#define _DO_TOUCH true |
| 25 | +#define _NO_TOUCH false |
| 26 | + |
22 | 27 | #define META_SPACE(p) { \
|
23 | 28 | *p = ' '; \
|
24 | 29 | p++; \
|
@@ -493,208 +498,81 @@ static inline bool set_noreply_maybe(conn *c, token_t *tokens, size_t ntokens)
|
493 | 498 | return c->resp->noreply;
|
494 | 499 | }
|
495 | 500 |
|
496 |
| -/* client flags == 0 means use no storage for client flags */ |
497 |
| -static inline int make_ascii_get_suffix(char *suffix, item *it, bool return_cas, int nbytes) { |
498 |
| - char *p = suffix; |
499 |
| - *p = ' '; |
500 |
| - p++; |
501 |
| - if (FLAGS_SIZE(it) == 0) { |
502 |
| - *p = '0'; |
503 |
| - p++; |
| 501 | +static void process_get_command_err(conn *c, const char *errstr) { |
| 502 | + // Use passed in error or rescue the last error while processing. |
| 503 | + char wbuf[WRITE_BUFFER_SIZE]; |
| 504 | + if (errstr) { |
| 505 | + memcpy(wbuf, errstr, strlen(errstr)); |
504 | 506 | } else {
|
505 |
| - p = itoa_u64(*((client_flags_t *) ITEM_suffix(it)), p); |
| 507 | + size_t l = c->resp->iov[0].iov_len; |
| 508 | + memcpy(wbuf, c->resp->wbuf, l); |
| 509 | + wbuf[l] = '\0'; |
506 | 510 | }
|
507 |
| - *p = ' '; |
508 |
| - p = itoa_u32(nbytes-2, p+1); |
509 |
| - |
510 |
| - if (return_cas) { |
511 |
| - *p = ' '; |
512 |
| - p = itoa_u64(ITEM_get_cas(it), p+1); |
| 511 | + conn_release_items(c); |
| 512 | + if (!resp_start(c)) { |
| 513 | + conn_set_state(c, conn_closing); |
| 514 | + return; |
513 | 515 | }
|
514 |
| - |
515 |
| - *p = '\r'; |
516 |
| - *(p+1) = '\n'; |
517 |
| - *(p+2) = '\0'; |
518 |
| - return (p - suffix) + 2; |
| 516 | + out_string(c, wbuf); |
519 | 517 | }
|
520 | 518 |
|
521 |
| -/* ntokens is overwritten here... shrug.. */ |
522 |
| -static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, bool return_cas, bool should_touch) { |
523 |
| - char *key; |
524 |
| - size_t nkey; |
525 |
| - item *it; |
526 |
| - token_t *key_token = &tokens[KEY_TOKEN]; |
527 |
| - int32_t exptime_int = 0; |
| 519 | +static void process_get_command(conn *c, LIBEVENT_THREAD *t, mcp_parser_t *pr, parser_storage_get_cb storage_cb, bool return_cas, bool should_touch) { |
| 520 | + uint32_t keyoff = pr->tok.tokens[pr->keytoken]; |
| 521 | + const char *curkey = pr->request + keyoff; |
| 522 | + int klen = pr->klen; |
| 523 | + const char *kend = NULL; |
528 | 524 | rel_time_t exptime = 0;
|
529 |
| - bool fail_length = false; |
530 |
| - assert(c != NULL); |
531 |
| - mc_resp *resp = c->resp; |
532 | 525 |
|
533 | 526 | if (should_touch) {
|
534 |
| - // For get and touch commands, use first token as exptime |
535 |
| - if (!safe_strtol(tokens[1].value, &exptime_int)) { |
| 527 | + int32_t exptime_int = 0; |
| 528 | + if (!safe_strtol(&pr->request[pr->tok.tokens[1]], &exptime_int)) { |
536 | 529 | out_string(c, "CLIENT_ERROR invalid exptime argument");
|
537 | 530 | return;
|
538 | 531 | }
|
539 |
| - key_token++; |
540 | 532 | exptime = realtime(EXPTIME_TO_POSITIVE_TIME(exptime_int));
|
541 | 533 | }
|
542 | 534 |
|
543 |
| - do { |
544 |
| - while(key_token->length != 0) { |
545 |
| - bool overflow; // not used here. |
546 |
| - key = key_token->value; |
547 |
| - nkey = key_token->length; |
548 |
| - |
549 |
| - if (nkey > KEY_MAX_LENGTH) { |
550 |
| - fail_length = true; |
551 |
| - goto stop; |
552 |
| - } |
553 |
| - |
554 |
| - it = limited_get(key, nkey, c->thread, exptime, should_touch, DO_UPDATE, &overflow); |
555 |
| - if (settings.detail_enabled) { |
556 |
| - stats_prefix_record_get(key, nkey, NULL != it); |
557 |
| - } |
558 |
| - if (it) { |
559 |
| - /* |
560 |
| - * Construct the response. Each hit adds three elements to the |
561 |
| - * outgoing data list: |
562 |
| - * "VALUE " |
563 |
| - * key |
564 |
| - * " " + flags + " " + data length + "\r\n" + data (with \r\n) |
565 |
| - */ |
566 |
| - |
567 |
| - { |
568 |
| - MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey, |
569 |
| - it->nbytes, ITEM_get_cas(it)); |
570 |
| - int nbytes = it->nbytes; |
571 |
| - char *p = resp->wbuf; |
572 |
| - memcpy(p, "VALUE ", 6); |
573 |
| - p += 6; |
574 |
| - memcpy(p, ITEM_key(it), it->nkey); |
575 |
| - p += it->nkey; |
576 |
| - p += make_ascii_get_suffix(p, it, return_cas, nbytes); |
577 |
| - resp_add_iov(resp, resp->wbuf, p - resp->wbuf); |
578 |
| - |
579 |
| -#ifdef EXTSTORE |
580 |
| - if (it->it_flags & ITEM_HDR) { |
581 |
| - if (storage_get_item(c->thread, it, resp) != 0) { |
582 |
| - pthread_mutex_lock(&c->thread->stats.mutex); |
583 |
| - c->thread->stats.get_oom_extstore++; |
584 |
| - pthread_mutex_unlock(&c->thread->stats.mutex); |
585 |
| - |
586 |
| - item_remove(it); |
587 |
| - goto stop; |
588 |
| - } else { |
589 |
| - assert(resp->io_pending != NULL); |
590 |
| - resp->io_pending->c = c; |
591 |
| - conn_resp_suspend(c, resp); |
592 |
| - } |
593 |
| - } else if ((it->it_flags & ITEM_CHUNKED) == 0) { |
594 |
| - resp_add_iov(resp, ITEM_data(it), it->nbytes); |
595 |
| - } else { |
596 |
| - resp_add_chunked_iov(resp, it, it->nbytes); |
597 |
| - } |
598 |
| -#else |
599 |
| - if ((it->it_flags & ITEM_CHUNKED) == 0) { |
600 |
| - resp_add_iov(resp, ITEM_data(it), it->nbytes); |
601 |
| - } else { |
602 |
| - resp_add_chunked_iov(resp, it, it->nbytes); |
603 |
| - } |
604 |
| -#endif |
605 |
| - } |
606 |
| - |
607 |
| - if (settings.verbose > 1) { |
608 |
| - int ii; |
609 |
| - fprintf(stderr, ">%d sending key ", c->sfd); |
610 |
| - for (ii = 0; ii < it->nkey; ++ii) { |
611 |
| - fprintf(stderr, "%c", key[ii]); |
612 |
| - } |
613 |
| - fprintf(stderr, "\n"); |
614 |
| - } |
| 535 | + if (pr->request[pr->reqlen-2] == '\r') { |
| 536 | + kend = pr->request + pr->reqlen - 2; |
| 537 | + } else { |
| 538 | + kend = pr->request + pr->reqlen - 1; |
| 539 | + } |
615 | 540 |
|
616 |
| - /* item_get() has incremented it->refcount for us */ |
617 |
| - pthread_mutex_lock(&c->thread->stats.mutex); |
618 |
| - if (should_touch) { |
619 |
| - c->thread->stats.touch_cmds++; |
620 |
| - c->thread->stats.slab_stats[ITEM_clsid(it)].touch_hits++; |
621 |
| - } else { |
622 |
| - c->thread->stats.lru_hits[it->slabs_clsid]++; |
623 |
| - c->thread->stats.get_cmds++; |
624 |
| - } |
625 |
| - pthread_mutex_unlock(&c->thread->stats.mutex); |
626 |
| -#ifdef EXTSTORE |
627 |
| - /* If ITEM_HDR, an io_wrap owns the reference. */ |
628 |
| - if ((it->it_flags & ITEM_HDR) == 0) { |
629 |
| - resp->item = it; |
630 |
| - } |
631 |
| -#else |
632 |
| - resp->item = it; |
633 |
| -#endif |
| 541 | + while (klen != 0) { |
| 542 | + mc_resp *resp = c->resp; |
| 543 | + if (process_get_cmd(t, curkey, klen, c->resp, storage_get_item, exptime, return_cas, should_touch) != 0) { |
| 544 | + process_get_command_err(c, NULL); |
| 545 | + return; |
| 546 | + } |
| 547 | + if (resp->io_pending) { |
| 548 | + resp->io_pending->c = c; |
| 549 | + conn_resp_suspend(c, resp); |
| 550 | + } |
| 551 | + curkey += klen; |
| 552 | + klen = 0; |
| 553 | + while (curkey != kend) { |
| 554 | + if (*curkey == ' ') { |
| 555 | + curkey++; |
634 | 556 | } else {
|
635 |
| - pthread_mutex_lock(&c->thread->stats.mutex); |
636 |
| - if (should_touch) { |
637 |
| - c->thread->stats.touch_cmds++; |
638 |
| - c->thread->stats.touch_misses++; |
| 557 | + const char *s = memchr(curkey, ' ', kend - curkey); |
| 558 | + if (s != NULL) { |
| 559 | + klen = s - curkey; |
639 | 560 | } else {
|
640 |
| - c->thread->stats.get_misses++; |
641 |
| - c->thread->stats.get_cmds++; |
642 |
| - } |
643 |
| - MEMCACHED_COMMAND_GET(c->sfd, key, nkey, -1, 0); |
644 |
| - pthread_mutex_unlock(&c->thread->stats.mutex); |
645 |
| - } |
646 |
| - |
647 |
| - key_token++; |
648 |
| - if (key_token->length != 0) { |
649 |
| - if (!resp_start(c)) { |
650 |
| - goto stop; |
| 561 | + klen = kend - curkey; |
651 | 562 | }
|
652 |
| - resp = c->resp; |
653 |
| - } |
654 |
| - } |
655 |
| - |
656 |
| - /* |
657 |
| - * If the command string hasn't been fully processed, get the next set |
658 |
| - * of tokens. |
659 |
| - */ |
660 |
| - if (key_token->value != NULL) { |
661 |
| - ntokens = tokenize_command(key_token->value, tokens, MAX_TOKENS); |
662 |
| - key_token = tokens; |
663 |
| - if (!resp_start(c)) { |
664 |
| - goto stop; |
| 563 | + break; |
665 | 564 | }
|
666 |
| - resp = c->resp; |
667 | 565 | }
|
668 |
| - } while(key_token->value != NULL); |
669 |
| -stop: |
670 |
| - |
671 |
| - if (settings.verbose > 1) |
672 |
| - fprintf(stderr, ">%d END\n", c->sfd); |
673 | 566 |
|
674 |
| - /* |
675 |
| - If the loop was terminated because of out-of-memory, it is not |
676 |
| - reliable to add END\r\n to the buffer, because it might not end |
677 |
| - in \r\n. So we send SERVER_ERROR instead. |
678 |
| - */ |
679 |
| - if (key_token->value != NULL) { |
680 |
| - // Kill any stacked responses we had. |
681 |
| - conn_release_items(c); |
682 |
| - // Start a new response object for the error message. |
683 |
| - if (!resp_start(c)) { |
684 |
| - // severe out of memory error. |
685 |
| - conn_set_state(c, conn_closing); |
| 567 | + if (klen && !resp_start(c)) { |
| 568 | + // This may succeed because it first frees existing resp objects. |
| 569 | + process_get_command_err(c, "SERVER_ERROR out of memory writing get response"); |
686 | 570 | return;
|
687 | 571 | }
|
688 |
| - if (fail_length) { |
689 |
| - out_string(c, "CLIENT_ERROR bad command line format"); |
690 |
| - } else { |
691 |
| - out_of_memory(c, "SERVER_ERROR out of memory writing get response"); |
692 |
| - } |
693 |
| - } else { |
694 |
| - // Tag the end token onto the most recent response object. |
695 |
| - resp_add_iov(resp, "END\r\n", 5); |
696 |
| - conn_set_state(c, conn_new_cmd); |
697 | 572 | }
|
| 573 | + resp_add_iov(c->resp, "END\r\n", 5); |
| 574 | + conn_set_state(c, conn_new_cmd); |
| 575 | + return; |
698 | 576 | }
|
699 | 577 |
|
700 | 578 | inline static void process_stats_detail(conn *c, const char *command) {
|
@@ -1664,24 +1542,6 @@ static void _process_command_ascii(conn *c, char *command) {
|
1664 | 1542 | out_string(c, "ERROR");
|
1665 | 1543 | break;
|
1666 | 1544 | }
|
1667 |
| - } else if (first == 'g') { |
1668 |
| - // Various get commands are very common. |
1669 |
| - WANT_TOKENS_MIN(ntokens, 3); |
1670 |
| - if (strcmp(tokens[COMMAND_TOKEN].value, "get") == 0) { |
1671 |
| - |
1672 |
| - process_get_command(c, tokens, ntokens, false, false); |
1673 |
| - } else if (strcmp(tokens[COMMAND_TOKEN].value, "gets") == 0) { |
1674 |
| - |
1675 |
| - process_get_command(c, tokens, ntokens, true, false); |
1676 |
| - } else if (strcmp(tokens[COMMAND_TOKEN].value, "gat") == 0) { |
1677 |
| - |
1678 |
| - process_get_command(c, tokens, ntokens, false, true); |
1679 |
| - } else if (strcmp(tokens[COMMAND_TOKEN].value, "gats") == 0) { |
1680 |
| - |
1681 |
| - process_get_command(c, tokens, ntokens, true, true); |
1682 |
| - } else { |
1683 |
| - out_string(c, "ERROR"); |
1684 |
| - } |
1685 | 1545 | } else if (first == 's') {
|
1686 | 1546 | if (strcmp(tokens[COMMAND_TOKEN].value, "stats") == 0) {
|
1687 | 1547 |
|
@@ -1813,6 +1673,22 @@ void process_command_ascii(conn *c, char *command, char *el) {
|
1813 | 1673 | process_mset_command(c, &pr, resp);
|
1814 | 1674 | handled = true;
|
1815 | 1675 | break;
|
| 1676 | + case CMD_GET: |
| 1677 | + process_get_command(c, t, &pr, storage_get_item, _NO_CAS, _NO_TOUCH); |
| 1678 | + handled = true; |
| 1679 | + break; |
| 1680 | + case CMD_GETS: |
| 1681 | + process_get_command(c, t, &pr, storage_get_item, _DO_CAS, _NO_TOUCH); |
| 1682 | + handled = true; |
| 1683 | + break; |
| 1684 | + case CMD_GAT: |
| 1685 | + process_get_command(c, t, &pr, storage_get_item, _NO_CAS, _DO_TOUCH); |
| 1686 | + handled = true; |
| 1687 | + break; |
| 1688 | + case CMD_GATS: |
| 1689 | + process_get_command(c, t, &pr, storage_get_item, _DO_CAS, _DO_TOUCH); |
| 1690 | + handled = true; |
| 1691 | + break; |
1816 | 1692 | case CMD_DELETE:
|
1817 | 1693 | process_delete_cmd(t, &pr, resp);
|
1818 | 1694 | conn_set_state(c, conn_new_cmd);
|
|
0 commit comments