static req_type_t asc_parse_type(struct conn *c, struct token *token, int ntoken) { char *tval; /* token value */ size_t tlen; /* token length */ req_type_t type; /* request type */ if (ntoken < 2) { return REQ_UNKNOWN; } tval = token[TOKEN_COMMAND].val; tlen = token[TOKEN_COMMAND].len; type = REQ_UNKNOWN; switch (tlen) { case 3: if (str4cmp(tval, 'g', 'e', 't', ' ')) { type = REQ_GET; } else if (str4cmp(tval, 's', 'e', 't', ' ')) { type = REQ_SET; } else if (str4cmp(tval, 'a', 'd', 'd', ' ')) { type = REQ_ADD; } else if (str4cmp(tval, 'c', 'a', 's', ' ')) { type = REQ_CAS; } break; case 4: if (str4cmp(tval, 'g', 'e', 't', 's')) { type = REQ_GETS; } else if (str4cmp(tval, 'i', 'n', 'c', 'r')) { type = REQ_INCR; } else if (str4cmp(tval, 'd', 'e', 'c', 'r')) { type = REQ_DECR; } else if (str4cmp(tval, 'q', 'u', 'i', 't')) { type = REQ_QUIT; } break; case 5: if (str5cmp(tval, 's', 't', 'a', 't', 's')) { type = REQ_STATS; } break; case 6: if (str6cmp(tval, 'a', 'p', 'p', 'e', 'n', 'd')) { type = REQ_APPEND; } else if (str6cmp(tval, 'd', 'e', 'l', 'e', 't', 'e')) { type = REQ_DELETE; } else if (str6cmp(tval, 'c', 'o', 'n', 'f', 'i', 'g')) { type = REQ_CONFIG; } break; case 7: if (str8cmp(tval, 'r', 'e', 'p', 'l', 'a', 'c', 'e', ' ')) { type = REQ_REPLACE; } else if (str8cmp(tval, 'p', 'r', 'e', 'p', 'e', 'n', 'd', ' ')) { type = REQ_PREPEND; } else if (str7cmp(tval, 'v', 'e', 'r', 's', 'i', 'o', 'n')) { type = REQ_VERSION; } break; case 9: if (str9cmp(tval, 'f', 'l', 'u', 's', 'h', '_', 'a', 'l', 'l')) { type = REQ_FLUSHALL; } else if (str9cmp(tval, 'v', 'e', 'r', 'b', 'o', 's', 'i', 't', 'y')) { type = REQ_VERBOSITY; } break; default: type = REQ_UNKNOWN; break; } return type; }
void memcache_parse_rsp(struct msg *r) { struct mbuf *b; uint8_t *p, *m; uint8_t ch; enum { SW_START, SW_RSP_NUM, SW_RSP_STR, SW_SPACES_BEFORE_KEY, SW_KEY, SW_SPACES_BEFORE_FLAGS, /* 5 */ SW_FLAGS, SW_SPACES_BEFORE_VLEN, SW_VLEN, SW_RUNTO_VAL, SW_VAL, /* 10 */ SW_VAL_LF, SW_END, SW_RUNTO_CRLF, SW_CRLF, SW_ALMOST_DONE, /* 15 */ SW_SENTINEL } state; state = r->state; b = STAILQ_LAST(&r->mhdr, mbuf, next); ASSERT(!r->request); ASSERT(!r->redis); ASSERT(state >= SW_START && state < SW_SENTINEL); ASSERT(b != NULL); ASSERT(b->pos <= b->last); /* validate the parsing marker */ 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 (isdigit(ch)) { state = SW_RSP_NUM; } else { state = SW_RSP_STR; } p = p - 1; /* go back by 1 byte */ break; case SW_RSP_NUM: if (r->token == NULL) { /* rsp_start <- p; type_start <- p */ r->token = p; } if (isdigit(ch)) { /* num <- num * 10 + (ch - '0') */ ; } else if (ch == ' ' || ch == CR) { /* type_end <- p - 1 */ r->token = NULL; r->type = MSG_RSP_MC_NUM; p = p - 1; /* go back by 1 byte */ state = SW_CRLF; } else { goto error; } break; case SW_RSP_STR: if (r->token == NULL) { /* rsp_start <- p; type_start <- p */ r->token = p; } 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, 'E', 'N', 'D', '\r')) { r->type = MSG_RSP_MC_END; /* end_start <- m; end_end <- p - 1 */ r->end = m; break; } break; case 5: if (str5cmp(m, 'V', 'A', 'L', 'U', 'E')) { /* * Encompasses responses for 'get', 'gets' and * 'cas' command. */ r->type = MSG_RSP_MC_VALUE; break; } if (str5cmp(m, 'E', 'R', 'R', 'O', 'R')) { r->type = MSG_RSP_MC_ERROR; break; } break; case 6: if (str6cmp(m, 'S', 'T', 'O', 'R', 'E', 'D')) { r->type = MSG_RSP_MC_STORED; break; } if (str6cmp(m, 'E', 'X', 'I', 'S', 'T', 'S')) { r->type = MSG_RSP_MC_EXISTS; break; } break; case 7: if (str7cmp(m, 'D', 'E', 'L', 'E', 'T', 'E', 'D')) { r->type = MSG_RSP_MC_DELETED; break; } break; case 9: if (str9cmp(m, 'N', 'O', 'T', '_', 'F', 'O', 'U', 'N', 'D')) { r->type = MSG_RSP_MC_NOT_FOUND; break; } break; case 10: if (str10cmp(m, 'N', 'O', 'T', '_', 'S', 'T', 'O', 'R', 'E', 'D')) { r->type = MSG_RSP_MC_NOT_STORED; break; } break; case 12: if (str12cmp(m, 'C', 'L', 'I', 'E', 'N', 'T', '_', 'E', 'R', 'R', 'O', 'R')) { r->type = MSG_RSP_MC_CLIENT_ERROR; break; } if (str12cmp(m, 'S', 'E', 'R', 'V', 'E', 'R', '_', 'E', 'R', 'R', 'O', 'R')) { r->type = MSG_RSP_MC_SERVER_ERROR; break; } break; } switch (r->type) { case MSG_UNKNOWN: goto error; case MSG_RSP_MC_STORED: case MSG_RSP_MC_NOT_STORED: case MSG_RSP_MC_EXISTS: case MSG_RSP_MC_NOT_FOUND: case MSG_RSP_MC_DELETED: state = SW_CRLF; break; case MSG_RSP_MC_END: state = SW_CRLF; break; case MSG_RSP_MC_VALUE: state = SW_SPACES_BEFORE_KEY; break; case MSG_RSP_MC_ERROR: state = SW_CRLF; break; case MSG_RSP_MC_CLIENT_ERROR: case MSG_RSP_MC_SERVER_ERROR: state = SW_RUNTO_CRLF; break; default: NOT_REACHED(); } p = p - 1; /* go back by 1 byte */ } break; case SW_SPACES_BEFORE_KEY: if (ch != ' ') { state = SW_KEY; p = p - 1; /* go back by 1 byte */ } break; case SW_KEY: if (ch == ' ') { /* r->token = NULL; */ state = SW_SPACES_BEFORE_FLAGS; } break; case SW_SPACES_BEFORE_FLAGS: if (ch != ' ') { if (!isdigit(ch)) { goto error; } state = SW_FLAGS; p = p - 1; /* go back by 1 byte */ } break; case SW_FLAGS: if (r->token == NULL) { /* flags_start <- p */ /* r->token = p; */ } if (isdigit(ch)) { /* flags <- flags * 10 + (ch - '0') */ ; } else if (ch == ' ') { /* flags_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; r->vlen = 0; } break; case SW_VLEN: if (isdigit(ch)) { r->vlen = r->vlen * 10 + (uint32_t)(ch - '0'); } else if (ch == ' ' || ch == CR) { /* vlen_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; r->token = NULL; break; default: goto error; } break; case SW_VAL: m = p + r->vlen; if (m >= b->last) { ASSERT(r->vlen >= (uint32_t)(b->last - p)); r->vlen -= (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_VAL_LF; break; default: goto error; } break; case SW_VAL_LF: switch (ch) { case LF: /* state = SW_END; */ state = SW_RSP_STR; break; default: goto error; } break; case SW_END: if (r->token == NULL) { if (ch != 'E') { goto error; } /* end_start <- p */ r->token = p; } else if (ch == CR) { /* end_end <- p */ m = r->token; r->token = NULL; switch (p - m) { case 3: if (str4cmp(m, 'E', 'N', 'D', '\r')) { r->end = m; state = SW_ALMOST_DONE; } break; default: goto error; } } break; case SW_RUNTO_CRLF: switch (ch) { case CR: if (r->type == MSG_RSP_MC_VALUE) { state = SW_RUNTO_VAL; } else { state = SW_ALMOST_DONE; } break; default: break; } 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: /* rsp_end <- p */ goto done; default: goto error; } break; case SW_SENTINEL: default: NOT_REACHED(); break; } } ASSERT(p == b->last); r->pos = p; r->state = state; if (b->last == b->end && r->token != NULL) { if (state <= SW_RUNTO_VAL || state == SW_CRLF || state == SW_ALMOST_DONE) { r->state = SW_START; } 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 rsp %"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->token = NULL; r->result = MSG_PARSE_OK; log_hexdump(LOG_VERB, b->pos, mbuf_length(b), "parsed rsp %"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 rsp %"PRIu64" " "res %d type %d state %d", r->id, r->result, r->type, r->state); }