void Lookup_Test::test_char_lookup_1 () { const char *str = 0; char ch = 0; ch = char_lookup (&str, "bac", 3); CPPUNIT_ASSERT (ch == 0); str = (char *) ""; ch = char_lookup (&str, "bac", 3); CPPUNIT_ASSERT (ch == 0); str = (char *) "abc"; ch = char_lookup (&str, "bca", 3); CPPUNIT_ASSERT (ch == 'a'); const char *strv[] = { (char *) "abc", (char *) "bca", (char *) "cab", (char *) " abc", (char *) "bca", (char *) " cab" }; const char cv[] = {'a', 'b', 'c'}; for (uint idx = 0; idx < 3; idx++) { ch = char_lookup (&strv[idx], "bca", 3); ASSERT_IDX (ch == *strv[idx]); } for (uint idx = 3; idx < 6; idx++) { ch = char_lookup (&strv[idx], "bca", 3, true); ASSERT_IDX (ch == cv[idx - 3]); } }
/* * Parse the protocol and point relevant fields, don't take logic decisions * based on this, just parse to locate things. */ int mk_http_parser(struct mk_http_request *req, struct mk_http_parser *p, char *buffer, int buf_len, struct mk_server *server) { int s; int tmp; int ret; int len; /* lazy test printf("p->i=%i buf_len=%i\n", p->i, buf_len); for (s = p->i; s < buf_len; s++) { if (buffer[s] == '\r') { printf("CR"); } else if (buffer[s] == '\n') { printf("LF"); } else { printf("%c", buffer[s]); } } printf("\n"); */ len = buf_len; for (; p->i < len; p->i++, p->chars++) { /* FIRST LINE LEVEL: Method, URI & Protocol */ if (p->level == REQ_LEVEL_FIRST) { switch (p->status) { case MK_ST_REQ_METHOD: /* HTTP Method */ if (p->chars == -1) { switch (buffer[p->i]) { case 'G': p->method = MK_METHOD_GET; break; case 'P': p->method = MK_METHOD_POST; break; case 'H': p->method = MK_METHOD_HEAD; break; case 'D': p->method = MK_METHOD_DELETE; break; case 'O': p->method = MK_METHOD_OPTIONS; break; } continue; } if (buffer[p->i] == ' ') { mark_end(); p->status = MK_ST_REQ_URI; if (p->end < 2) { return MK_HTTP_PARSER_ERROR; } method_lookup(req, p, buffer); start_next(); } else { if ((p->i - p->start) > 10) { return MK_HTTP_PARSER_ERROR; } } break; case MK_ST_REQ_URI: /* URI */ if (buffer[p->i] == ' ') { mark_end(); p->status = MK_ST_REQ_PROT_VERSION; if (field_len() < 1) { return MK_HTTP_PARSER_ERROR; } request_set(&req->uri, p, buffer); start_next(); } else if (buffer[p->i] == '?') { mark_end(); request_set(&req->uri, p, buffer); p->status = MK_ST_REQ_QUERY_STRING; start_next(); } else if (buffer[p->i] == '\r' || buffer[p->i] == '\n') { mk_http_error(MK_CLIENT_BAD_REQUEST, req->session, req, server); return MK_HTTP_PARSER_ERROR; } break; case MK_ST_REQ_QUERY_STRING: /* Query string */ char_lookup(buffer, ' ', len, p); if (buffer[p->i] == ' ') { mark_end(); request_set(&req->query_string, p, buffer); p->status = MK_ST_REQ_PROT_VERSION; start_next(); } else if (buffer[p->i] == '\r' || buffer[p->i] == '\n') { mk_http_error(MK_CLIENT_BAD_REQUEST, req->session, req, server); return MK_HTTP_PARSER_ERROR; } break; case MK_ST_REQ_PROT_VERSION: /* Protocol Version */ /* * Most of the time we already have the string version in our * buffer, for that case try to match the version and avoid * loop rounds. */ if (p->start + 6 >= p->i) { continue; } tmp = p->start; if (buffer[tmp] == 'H' && buffer[tmp + 1] == 'T' && buffer[tmp + 2] == 'T' && buffer[tmp + 3] == 'P' && buffer[tmp + 4] == '/' && buffer[tmp + 5] == '1' && buffer[tmp + 6] == '.') { request_set(&req->protocol_p, p, buffer); req->protocol_p.len = 8; mk_http_set_minor_version(buffer[tmp + 7]); } else { mk_http_error(MK_SERVER_HTTP_VERSION_UNSUP, req->session, req, server); return MK_HTTP_PARSER_ERROR; } p->status = MK_ST_FIRST_CONTINUE; break; case MK_ST_FIRST_CONTINUE: if (buffer[p->i] == '\r') { p->status = MK_ST_FIRST_FINALIZING; } else { return MK_HTTP_PARSER_ERROR; } break; case MK_ST_FIRST_FINALIZING: /* New Line */ if (buffer[p->i] == '\n') { p->level = REQ_LEVEL_CONTINUE; start_next(); } else { return MK_HTTP_PARSER_ERROR; } break; case MK_ST_BLOCK_END: if (buffer[p->i] == '\n') { return mk_http_parser_ok(req, p, server); } else { return MK_HTTP_PARSER_ERROR; } break; }; } else if (p->level == REQ_LEVEL_CONTINUE) { if (buffer[p->i] == '\r') { p->level = REQ_LEVEL_FIRST; p->status = MK_ST_BLOCK_END; } else { p->level = REQ_LEVEL_HEADERS; p->status = MK_ST_HEADER_KEY; p->chars = 0; } } /* HEADERS: all headers stuff */ if (p->level == REQ_LEVEL_HEADERS) { /* Expect a Header key */ if (p->status == MK_ST_HEADER_KEY) { if (buffer[p->i] == '\r') { if (p->chars == 0) { p->level = REQ_LEVEL_END; start_next(); } else { return MK_HTTP_PARSER_ERROR; } } if (p->chars == 0) { /* * We reach the start of a Header row, lets catch the most * probable header. * * The goal of this 'first row character lookup', is to define a * small range set of probable headers comparison once we catch * a header end. */ s = tolower(buffer[p->i]); switch (s) { case 'a': p->header_min = MK_HEADER_ACCEPT; p->header_max = MK_HEADER_AUTHORIZATION; break; case 'c': p->header_min = MK_HEADER_CACHE_CONTROL; p->header_max = MK_HEADER_CONTENT_TYPE; break; case 'h': p->header_min = MK_HEADER_HOST; p->header_max = MK_HEADER_HTTP2_SETTINGS; break; case 'i': header_scope_eq(p, MK_HEADER_IF_MODIFIED_SINCE); break; case 'l': p->header_min = MK_HEADER_LAST_MODIFIED; p->header_max = MK_HEADER_LAST_MODIFIED_SINCE; break; case 'r': p->header_min = MK_HEADER_RANGE; p->header_max = MK_HEADER_REFERER; break; case 'u': p->header_min = MK_HEADER_UPGRADE; p->header_max = MK_HEADER_USER_AGENT; break; default: p->header_key = -1; p->header_sep = -1; p->header_min = -1; p->header_max = -1; }; p->header_key = p->i; continue; } /* Found key/value separator */ char_lookup(buffer, ':', len, p); if (buffer[p->i] == ':') { /* Set the key/value middle point */ p->header_sep = p->i; /* validate length */ mark_end(); if (field_len() < 1) { return MK_HTTP_PARSER_ERROR; } /* Wait for a value */ p->status = MK_ST_HEADER_VALUE; start_next(); } } /* Parsing the header value */ else if (p->status == MK_ST_HEADER_VALUE) { /* Trim left, set starts only when found something != ' ' */ if (buffer[p->i] == '\r' || buffer[p->i] == '\n') { return MK_HTTP_PARSER_ERROR; } else if (buffer[p->i] != ' ') { p->status = MK_ST_HEADER_VAL_STARTS; p->start = p->header_val = p->i; } continue; } /* New header row starts */ else if (p->status == MK_ST_HEADER_VAL_STARTS) { /* Maybe there is no more headers and we reach the end ? */ if (buffer[p->i] == '\r') { mark_end(); if (field_len() <= 0) { return MK_HTTP_PARSER_ERROR; } /* * A header row has ended, lets lookup the header and populate * our headers table index. */ ret = header_lookup(p, buffer); if (ret != 0) { if (ret < -1) { mk_http_error(-ret, req->session, req, server); } return MK_HTTP_PARSER_ERROR; } /* Try to catch next LF */ if (p->i + 1 < len) { if (buffer[p->i + 1] == '\n') { p->i++; p->status = MK_ST_HEADER_KEY; p->chars = -1; start_next(); } } p->status = MK_ST_HEADER_END; start_next(); } else if (buffer[p->i] == '\n' && buffer[p->i - 1] != '\r') { return MK_HTTP_PARSER_ERROR; } } else if (p->status == MK_ST_HEADER_END) { if (buffer[p->i] == '\n') { p->status = MK_ST_HEADER_KEY; p->chars = -1; start_next(); } else { return MK_HTTP_PARSER_ERROR; } } } else if (p->level == REQ_LEVEL_END) { if (buffer[p->i] == '\n') { if (p->header_content_length > 0) { p->level = REQ_LEVEL_BODY; p->chars = -1; start_next(); } else { return mk_http_parser_ok(req, p, server); } } else { return MK_HTTP_PARSER_ERROR; } } else if (p->level == REQ_LEVEL_BODY) { /* * Reaching this level can means two things: * * - A Pipeline Request * - A Body content (POST/PUT methods) */ if (p->header_content_length > 0) { p->body_received = len - p->start; if ((len - p->start) < p->header_content_length) { return MK_HTTP_PARSER_PENDING; } /* Cut off */ p->i += p->body_received; req->data.len = p->body_received; req->data.data = (buffer + p->start); } return mk_http_parser_ok(req, p, server); } } return MK_HTTP_PARSER_PENDING; }