void memcache_parse_req(struct msg *r) { struct mbuf *b; uint8_t *p, *m; uint8_t ch; enum { SW_START, SW_REQ_TYPE, SW_SPACES_BEFORE_KEY, SW_KEY, SW_SPACES_BEFORE_KEYS, SW_SPACES_BEFORE_FLAGS, SW_FLAGS, SW_SPACES_BEFORE_EXPIRY, SW_EXPIRY, SW_SPACES_BEFORE_VLEN, SW_VLEN, SW_SPACES_BEFORE_CAS, SW_CAS, SW_RUNTO_VAL, SW_VAL, SW_SPACES_BEFORE_NUM, SW_NUM, SW_RUNTO_CRLF, SW_CRLF, SW_NOREPLY, SW_AFTER_NOREPLY, SW_ALMOST_DONE, SW_SENTINEL } state; state = r->state; b = STAILQ_LAST(&r->mhdr, mbuf, next); ASSERT(r->request); ASSERT(state >= SW_START && state < SW_SENTINEL); ASSERT(b != NULL); ASSERT(b->pos <= b->last); /* validate the parsing maker */ ASSERT(r->pos != NULL); ASSERT(r->pos >= b->pos && r->pos <= b->last); for (p = r->pos; p < b->last; p++) { ch = *p; switch (state) { case SW_START: if (ch == ' ') { break; } if (!islower(ch)) { goto error; } /* req_start <- p; type_start <- p */ r->token = p; state = SW_REQ_TYPE; break; case SW_REQ_TYPE: if (ch == ' ' || ch == CR) { /* type_end = p - 1 */ m = r->token; r->token = NULL; r->type = MSG_UNKNOWN; switch (p - m) { case 3: if (str4cmp(m, 'g', 'e', 't', ' ')) { r->type = MSG_REQ_GET; break; } if (str4cmp(m, 's', 'e', 't', ' ')) { r->type = MSG_REQ_SET; break; } if (str4cmp(m, 'a', 'd', 'd', ' ')) { r->type = MSG_REQ_ADD; break; } if (str4cmp(m, 'c', 'a', 's', ' ')) { r->type = MSG_REQ_CAS; break; } break; case 4: if (str4cmp(m, 'g', 'e', 't', 's')) { r->type = MSG_REQ_GETS; break; } if (str4cmp(m, 'i', 'n', 'c', 'r')) { r->type = MSG_REQ_INCR; break; } if (str4cmp(m, 'd', 'e', 'c', 'r')) { r->type = MSG_REQ_DECR; break; } if (str4cmp(m, 'q', 'u', 'i', 't')) { r->type = MSG_REQ_QUIT; r->quit = 1; break; } break; case 6: if (str6cmp(m, 'a', 'p', 'p', 'e', 'n', 'd')) { r->type = MSG_REQ_APPEND; break; } if (str6cmp(m, 'd', 'e', 'l', 'e', 't', 'e')) { r->type = MSG_REQ_DELETE; break; } break; case 7: if (str7cmp(m, 'p', 'r', 'e', 'p', 'e', 'n', 'd')) { r->type = MSG_REQ_PREPEND; break; } if (str7cmp(m, 'r', 'e', 'p', 'l', 'a', 'c', 'e')) { r->type = MSG_REQ_REPLACE; break; } if (str7cmp(m, 'v', 'e', 'r', 's', 'i', 'o', 'n')) { r->type = MSG_REQ_VERSION; break; } break; } if (memcache_key(r)) { if (ch == CR) { goto error; } state = SW_SPACES_BEFORE_KEY; } else if (memcache_quit(r) || memcache_version(r)) { p = p - 1; /* go back by 1 byte */ state = SW_CRLF; } else { goto error; } } else if (!islower(ch)) { goto error; } break; case SW_SPACES_BEFORE_KEY: if (ch != ' ') { p = p - 1; /* go back by 1 byte */ state = SW_KEY; } break; case SW_KEY: if (r->token == NULL) { r->token = p; r->key_start = p; } if (ch == ' ' || ch == CR) { if ((p - r->key_start) > MEMCACHE_MAX_KEY_LENGTH) { log_error("parsed bad req %"PRIu64" of type %d with key " "prefix '%.*s...' and length %d that exceeds " "maximum key length", r->id, r->type, 16, r->key_start, p - r->key_start); goto error; } r->key_end = p; r->token = NULL; /* get next state */ if (memcache_storage(r)) { state = SW_SPACES_BEFORE_FLAGS; } else if (memcache_arithmetic(r)) { state = SW_SPACES_BEFORE_NUM; } else if (memcache_delete(r)) { state = SW_RUNTO_CRLF; } else if (memcache_retrieval(r)) { state = SW_SPACES_BEFORE_KEYS; } else { state = SW_RUNTO_CRLF; } if (ch == CR) { if (memcache_storage(r) || memcache_arithmetic(r)) { goto error; } p = p - 1; /* go back by 1 byte */ } } break; case SW_SPACES_BEFORE_KEYS: ASSERT(memcache_retrieval(r)); switch (ch) { case ' ': break; case CR: state = SW_ALMOST_DONE; break; default: r->token = p; goto fragment; } break; case SW_SPACES_BEFORE_FLAGS: if (ch != ' ') { if (!isdigit(ch)) { goto error; } p = p - 1; /* go back by 1 byte */ state = SW_FLAGS; } break; case SW_FLAGS: if (r->token == NULL) { /* flags_start <- p */ r->token = p; r->flags = 0; } if (isdigit(ch)) { r->flags = r->flags * 10 + (uint32_t)(ch - '0'); } else if (ch == ' ') { /* flags_end <- p - 1 */ r->token = NULL; state = SW_SPACES_BEFORE_EXPIRY; } else { goto error; } break; case SW_SPACES_BEFORE_EXPIRY: if (ch != ' ') { if (!isdigit(ch)) { goto error; } p = p - 1; /* go back by 1 byte */ state = SW_EXPIRY; } break; case SW_EXPIRY: if (r->token == NULL) { /* expiry_start <- p */ r->token = p; r->expiry = 0; } if (isdigit(ch)) { r->expiry = r->expiry * 10 + (uint32_t)(ch - '0'); } else if (ch == ' ') { /* expiry_end <- p - 1 */ r->token = NULL; state = SW_SPACES_BEFORE_VLEN; } else { goto error; } break; case SW_SPACES_BEFORE_VLEN: if (ch != ' ') { if (!isdigit(ch)) { goto error; } p = p - 1; /* go back by 1 byte */ state = SW_VLEN; } break; case SW_VLEN: if (r->token == NULL) { /* vlen_start <- p */ r->token = p; r->vlen = 0; } if (isdigit(ch)) { r->vlen = r->vlen * 10 + (uint32_t)(ch - '0'); } else if (memcache_cas(r)) { if (ch != ' ') { goto error; } /* vlen_end <- p - 1 */ r->rvlen = r->vlen; p = p - 1; /* go back by 1 byte */ r->token = NULL; state = SW_SPACES_BEFORE_CAS; } else if (ch == ' ' || ch == CR) { /* vlen_end <- p - 1 */ r->rvlen = r->vlen; p = p - 1; /* go back by 1 byte */ r->token = NULL; state = SW_RUNTO_CRLF; } else { goto error; } break; case SW_SPACES_BEFORE_CAS: if (ch != ' ') { if (!isdigit(ch)) { goto error; } p = p - 1; /* go back by 1 byte */ state = SW_CAS; } break; case SW_CAS: if (r->token == NULL) { /* cas_start <- p */ r->token = p; r->cas = 0; } if (isdigit(ch)) { r->cas = r->cas * 10ULL + (uint64_t)(ch - '0'); } else if (ch == ' ' || ch == CR) { /* cas_end <- p - 1 */ p = p - 1; /* go back by 1 byte */ r->token = NULL; state = SW_RUNTO_CRLF; } else { goto error; } break; case SW_RUNTO_VAL: switch (ch) { case LF: /* val_start <- p + 1 */ state = SW_VAL; break; default: goto error; } break; case SW_VAL: if (r->value == NULL) { r->value = p; } m = p + r->rvlen; if (m >= b->last) { ASSERT(r->rvlen >= (uint32_t)(b->last - p)); r->rvlen -= (uint32_t)(b->last - p); m = b->last - 1; p = m; /* move forward by vlen bytes */ break; } switch (*m) { case CR: /* val_end <- p - 1 */ p = m; /* move forward by vlen bytes */ state = SW_ALMOST_DONE; break; default: goto error; } break; case SW_SPACES_BEFORE_NUM: if (ch != ' ') { if (!isdigit(ch)) { goto error; } p = p - 1; /* go back by 1 byte */ state = SW_NUM; } break; case SW_NUM: if (r->token == NULL) { /* num_start <- p */ r->token = p; r->num = 0; } if (isdigit(ch)) { r->num = r->num * 10ULL + (uint64_t)(ch - '0'); } else if (ch == ' ' || ch == CR) { r->token = NULL; /* num_end <- p - 1 */ p = p - 1; /* go back by 1 byte */ state = SW_RUNTO_CRLF; } else { goto error; } break; case SW_RUNTO_CRLF: switch (ch) { case ' ': break; case 'n': if (memcache_storage(r) || memcache_arithmetic(r) || memcache_delete(r)) { p = p - 1; /* go back by 1 byte */ state = SW_NOREPLY; } else { goto error; } break; case CR: if (memcache_storage(r)) { state = SW_RUNTO_VAL; } else { state = SW_ALMOST_DONE; } break; default: goto error; } break; case SW_NOREPLY: if (r->token == NULL) { /* noreply_start <- p */ r->token = p; } switch (ch) { case ' ': case CR: m = r->token; if (((p - m) == 7) && str7cmp(m, 'n', 'o', 'r', 'e', 'p', 'l', 'y')) { ASSERT(memcache_storage(r) || memcache_arithmetic(r) || memcache_delete(r)); r->token = NULL; /* noreply_end <- p - 1 */ r->noreply = 1; state = SW_AFTER_NOREPLY; p = p - 1; /* go back by 1 byte */ } else { goto error; } } break; case SW_AFTER_NOREPLY: switch (ch) { case ' ': break; case CR: if (memcache_storage(r)) { state = SW_RUNTO_VAL; } else { state = SW_ALMOST_DONE; } break; default: goto error; } break; case SW_CRLF: switch (ch) { case ' ': break; case CR: state = SW_ALMOST_DONE; break; default: goto error; } break; case SW_ALMOST_DONE: switch (ch) { case LF: /* req_end <- p */ goto done; default: goto error; } break; case SW_SENTINEL: default: NOT_REACHED(); break; } } /* * At this point, buffer from b->pos to b->last has been parsed completely * but we haven't been able to reach to any conclusion. Normally, this * means that we have to parse again starting from the state we are in * after more data has been read. The newly read data is either read into * a new mbuf, if existing mbuf is full (b->last == b->end) or into the * existing mbuf. * * The only exception to this is when the existing mbuf is full (b->last * is at b->end) and token marker is set, which means that we have to * copy the partial token into a new mbuf and parse again with more data * read into new mbuf. */ ASSERT(p == b->last); r->pos = p; r->state = state; if (b->last == b->end && r->token != NULL) { r->pos = r->token; r->token = NULL; r->result = MSG_PARSE_REPAIR; } else { r->result = MSG_PARSE_AGAIN; } log_hexdump(LOG_VERB, b->pos, mbuf_length(b), "parsed req %"PRIu64" res %d " "type %d state %d rpos %d of %d", r->id, r->result, r->type, r->state, r->pos - b->pos, b->last - b->pos); return; fragment: ASSERT(p != b->last); ASSERT(r->token != NULL); r->pos = r->token; r->token = NULL; r->state = state; r->result = MSG_PARSE_FRAGMENT; log_hexdump(LOG_VERB, b->pos, mbuf_length(b), "parsed req %"PRIu64" res %d " "type %d state %d rpos %d of %d", r->id, r->result, r->type, r->state, r->pos - b->pos, b->last - b->pos); return; done: ASSERT(r->type > MSG_UNKNOWN && r->type < MSG_SENTINEL); r->pos = p + 1; ASSERT(r->pos <= b->last); r->state = SW_START; r->result = MSG_PARSE_OK; log_hexdump(LOG_VERB, b->pos, mbuf_length(b), "parsed req %"PRIu64" res %d " "type %d state %d rpos %d of %d", r->id, r->result, r->type, r->state, r->pos - b->pos, b->last - b->pos); return; error: r->result = MSG_PARSE_ERROR; r->state = state; errno = EINVAL; log_hexdump(LOG_INFO, b->pos, mbuf_length(b), "parsed bad req %"PRIu64" " "res %d type %d state %d", r->id, r->result, r->type, r->state); }
static ssize_t memcacheParseReq(struct memcacheProcData *r, struct slice *s) { char *m; char ch; size_t command_len; size_t pos = 0; uint64_t left = 0; struct slice val_s; enum reqStage state = r->stage; r->token_pos = 0; while (pos < s->len) { ch = s->data[pos]; switch (state) { case SW_START: if (!islower(ch)) { goto error; } r->token_pos = pos; state = SW_REQ_TYPE; break; case SW_REQ_TYPE: if (ch == ' ' || ch == CR) { r->type = UNKNOWN; if (wstrlen(r->command)) { r->command = wstrCatLen(r->command, (char *)&s->data[r->token_pos], pos); command_len = wstrlen(r->command); m = (char*)&r->command; } else { command_len = pos - r->token_pos; m = (char*)&s->data[r->token_pos]; } switch (command_len) { case 3: if (str4icmp(m, 'g', 'e', 't', ' ')) { r->type = REQ_MC_GET; break; } if (str4icmp(m, 's', 'e', 't', ' ')) { r->type = REQ_MC_SET; break; } if (str4icmp(m, 'a', 'd', 'd', ' ')) { r->type = REQ_MC_ADD; break; } if (str4icmp(m, 'c', 'a', 's', ' ')) { r->type = REQ_MC_CAS; break; } break; case 4: if (str4icmp(m, 'g', 'e', 't', 's')) { r->type = REQ_MC_GETS; break; } if (str4icmp(m, 'i', 'n', 'c', 'r')) { r->type = REQ_MC_INCR; break; } if (str4icmp(m, 'd', 'e', 'c', 'r')) { r->type = REQ_MC_DECR; break; } if (str4icmp(m, 'q', 'u', 'i', 't')) { r->type = REQ_MC_QUIT; r->quit = 1; break; } break; case 6: if (str6icmp(m, 'a', 'p', 'p', 'e', 'n', 'd')) { r->type = REQ_MC_APPEND; break; } if (str6icmp(m, 'd', 'e', 'l', 'e', 't', 'e')) { r->type = REQ_MC_DELETE; break; } break; case 7: if (str7icmp(m, 'p', 'r', 'e', 'p', 'e', 'n', 'd')) { r->type = REQ_MC_PREPEND; break; } if (str7icmp(m, 'r', 'e', 'p', 'l', 'a', 'c', 'e')) { r->type = REQ_MC_REPLACE; break; } break; } switch (r->type) { case REQ_MC_GET: case REQ_MC_GETS: case REQ_MC_DELETE: case REQ_MC_CAS: case REQ_MC_SET: case REQ_MC_ADD: case REQ_MC_REPLACE: case REQ_MC_APPEND: case REQ_MC_PREPEND: case REQ_MC_INCR: case REQ_MC_DECR: if (ch == CR) { goto error; } state = SW_SPACES_BEFORE_KEY; break; case REQ_MC_QUIT: pos--; /* go back by 1 byte */ state = SW_CRLF; break; case UNKNOWN: goto error; default: ASSERT(0); } } else if (!islower(ch)) { goto error; } break; case SW_SPACES_BEFORE_KEY: if (ch != ' ') { pos--; /* go back by 1 byte */ r->token_pos = -1; state = SW_KEY; } break; case SW_KEY: if (r->token_pos == -1) { r->token_pos = pos; } if (ch == ' ' || ch == CR) { if (wstrlen(r->key) + (pos - r->token_pos) > MEMCACHE_MAX_KEY_LENGTH) { wheatLog(WHEAT_WARNING, "parsed bad type %d with key prefix '%d' and length %d that exceeds " "maximum key length", r->type, r->token_pos, pos); goto error; } r->key = wstrCatLen(r->key, (char*)&s->data[r->token_pos], pos - r->token_pos); arrayPush(r->keys, &r->key); r->key = wstrNewLen(NULL, 64); r->token_pos = -1; /* get next state */ if (memcache_storage(r)) { state = SW_SPACES_BEFORE_FLAGS; } else if (memcache_arithmetic(r)) { state = SW_SPACES_BEFORE_NUM; } else if (memcache_delete(r)) { // whether exist delay time if (ch == ' ') state = SW_SPACES_BEFORE_NUM; else state = SW_RUNTO_CRLF; } else if (memcache_retrieval(r)) { state = SW_SPACES_BEFORE_KEYS; } else { state = SW_RUNTO_CRLF; } if (ch == CR) { if (memcache_storage(r) || memcache_arithmetic(r)) { goto error; } pos--; /* go back by 1 byte */ } } break; case SW_SPACES_BEFORE_KEYS: ASSERT(memcache_retrieval(r)); switch (ch) { case ' ': break; case CR: state = SW_ALMOST_DONE; break; default: r->token_pos = -1; pos--; /* go back by 1 byte */ state = SW_KEY; } break; case SW_SPACES_BEFORE_FLAGS: if (ch != ' ') { if (!isdigit(ch)) { goto error; } r->flag = ch - '0'; state = SW_FLAGS; } break; case SW_FLAGS: if (isdigit(ch)) { r->flag = r->flag * 10 + (ch - '0'); } else if (ch == ' ') { state = SW_SPACES_BEFORE_EXPIRY; } else { goto error; } break; case SW_SPACES_BEFORE_EXPIRY: if (ch != ' ') { if (!isdigit(ch)) { goto error; } /* expiry_start <- p; expiry <- ch - '0' */ r->expire = ch - '0'; state = SW_EXPIRY; } break; case SW_EXPIRY: if (isdigit(ch)) { r->expire = r->expire * 10 + (ch - '0'); } else if (ch == ' ') { state = SW_SPACES_BEFORE_VLEN; } else { goto error; } break; case SW_SPACES_BEFORE_VLEN: if (ch != ' ') { if (!isdigit(ch)) { goto error; } /* vlen_start <- p */ r->vlen = ch - '0'; state = SW_VLEN; } break; case SW_VLEN: if (isdigit(ch)) { r->vlen = r->vlen * 10 + (uint32_t)(ch - '0'); } else if (memcache_cas(r)) { if (ch != ' ') { goto error; } pos--; /* go back by 1 byte */ state = SW_SPACES_BEFORE_CAS; } else if (ch == ' ' || ch == CR) { pos--; /* go back by 1 byte */ state = SW_RUNTO_CRLF; } else { goto error; } break; case SW_SPACES_BEFORE_CAS: if (ch != ' ') { if (!isdigit(ch)) { goto error; } /* cas_start <- p; cas <- ch - '0' */ r->cas = ch - '0'; state = SW_CAS; } break; case SW_CAS: if (isdigit(ch)) { r->cas = r->cas * 10 + (ch - '0'); } else if (ch == ' ' || ch == CR) { pos--; /* go back by 1 byte */ state = SW_RUNTO_CRLF; } else { goto error; } break; case SW_RUNTO_VAL: switch (ch) { case LF: /* val_start <- p + 1 */ state = SW_VAL; r->vlen_left = r->vlen; break; default: goto error; } break; case SW_VAL: if (r->vlen_left) { left = (s->len - pos) >= r->vlen ? (r->vlen) : (s->len-pos); val_s.len = left; val_s.data = &s->data[pos]; arrayPush(r->vals, &val_s); r->vlen_left -= left; pos += left; } switch (s->data[pos]) { case CR: state = SW_ALMOST_DONE; break; default: goto error; } break; case SW_SPACES_BEFORE_NUM: if (ch != ' ') { if (!isdigit(ch)) { goto error; } r->num = ch - '0'; state = SW_NUM; } break; case SW_NUM: if (isdigit(ch)) { r->num = r->num * 10 + ch - '0'; } else if (ch == ' ' || ch == CR) { pos--; /* go back by 1 byte */ state = SW_RUNTO_CRLF; } else { goto error; } break; case SW_RUNTO_CRLF: switch (ch) { case ' ': break; case 'n': if (memcache_storage(r) || memcache_arithmetic(r) || memcache_delete(r)) { state = SW_NOREPLY; r->noreply_banner = wstrNewLen(NULL, 8); r->token_pos = pos; } else { goto error; } break; case CR: if (memcache_storage(r)) { state = SW_RUNTO_VAL; } else { state = SW_ALMOST_DONE; } break; default: goto error; } break; case SW_NOREPLY: switch (ch) { case ' ': case CR: r->noreply_banner = wstrCatLen(r->noreply_banner, (char*)&s->data[r->token_pos], pos - r->token_pos); if (str7icmp(r->noreply_banner, 'n', 'o', 'r', 'e', 'p', 'l', 'y')) { ASSERT(memcache_storage(r) || memcache_arithmetic(r) || memcache_delete(r)); /* noreply_end <- p - 1 */ r->noreply = 1; state = SW_AFTER_NOREPLY; pos--; /* go back by 1 byte */ } else { goto error; } } break; case SW_AFTER_NOREPLY: switch (ch) { case ' ': break; case CR: if (memcache_storage(r)) { state = SW_RUNTO_VAL; } else { state = SW_ALMOST_DONE; } break; default: goto error; } break; case SW_CRLF: switch (ch) { case ' ': break; case CR: state = SW_ALMOST_DONE; break; default: goto error; } break; case SW_ALMOST_DONE: switch (ch) { case LF: /* req_end <- p */ pos++; goto done; default: goto error; } break; case SW_SENTINEL: default: ASSERT(0); break; } pos++; } if (state == SW_REQ_TYPE) { r->command = wstrCatLen(r->command, (char*)&s->data[r->token_pos], pos - r->token_pos); } else if (state == SW_KEY) { r->key = wstrCatLen(r->key, (char*)&s->data[r->token_pos], pos - r->token_pos); } else if (state == SW_NOREPLY) { r->noreply_banner = wstrCatLen(r->noreply_banner, (char*)&s->data[r->token_pos], pos - r->token_pos); } done: r->stage = state; wheatLog(WHEAT_DEBUG, "parsed successfully type %d state %d", r->type, r->stage); return pos; error: wheatLog(WHEAT_DEBUG, "parsed failed type %d state %d: %s", r->type, r->stage, s->data); return -1; }