MEDIUM: h1: implement the request parser as well

The original H1 request parsing code was reintroduced into the generic
H1 parser so that it can be used regardless of the direction. If the
parser is interrupted and restarts, it makes use of the H1_MF_RESP
flag to decide whether to re-parse a request or a response. While
parsing the request, the method is decoded and set into the start line
structure.
diff --git a/src/h1.c b/src/h1.c
index 1172e05..db71789 100644
--- a/src/h1.c
+++ b/src/h1.c
@@ -735,7 +735,7 @@
 	end   = stop;
 	state = h1m->state;
 
-	if (state != H1_MSG_RPBEFORE)
+	if (state != H1_MSG_RQBEFORE && state != H1_MSG_RPBEFORE)
 		restarting = 1;
 
 	if (unlikely(ptr >= end))
@@ -746,6 +746,200 @@
 		skip_update = 1;
 
 	switch (state)	{
+	case H1_MSG_RQBEFORE:
+	http_msg_rqbefore:
+		if (likely(HTTP_IS_TOKEN(*ptr))) {
+			/* we have a start of message, we may have skipped some
+			 * heading CRLF. Skip them now.
+			 */
+			skip += ptr - start;
+			start = ptr;
+
+			sol = 0;
+			sl.rq.m = skip;
+			hdr_count = 0;
+			state = H1_MSG_RQMETH;
+			goto http_msg_rqmeth;
+		}
+
+		if (unlikely(!HTTP_IS_CRLF(*ptr))) {
+			state = H1_MSG_RQBEFORE;
+			goto http_msg_invalid;
+		}
+
+		if (unlikely(*ptr == '\n'))
+			EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqbefore, http_msg_ood, state, H1_MSG_RQBEFORE);
+		EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqbefore_cr, http_msg_ood, state, H1_MSG_RQBEFORE_CR);
+		/* stop here */
+
+	case H1_MSG_RQBEFORE_CR:
+	http_msg_rqbefore_cr:
+		EXPECT_LF_HERE(ptr, http_msg_invalid, state, H1_MSG_RQBEFORE_CR);
+		EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqbefore, http_msg_ood, state, H1_MSG_RQBEFORE);
+		/* stop here */
+
+	case H1_MSG_RQMETH:
+	http_msg_rqmeth:
+		if (likely(HTTP_IS_TOKEN(*ptr)))
+			EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqmeth, http_msg_ood, state, H1_MSG_RQMETH);
+
+		if (likely(HTTP_IS_SPHT(*ptr))) {
+			sl.rq.m_l = ptr - start;
+			sl.rq.meth = find_http_meth(start, sl.rq.m_l);
+			EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqmeth_sp, http_msg_ood, state, H1_MSG_RQMETH_SP);
+		}
+
+		if (likely(HTTP_IS_CRLF(*ptr))) {
+			/* HTTP 0.9 request */
+			sl.rq.m_l = ptr - start;
+			sl.rq.meth = find_http_meth(start, sl.rq.m_l);
+		http_msg_req09_uri:
+			sl.rq.u = ptr - start + skip;
+		http_msg_req09_uri_e:
+			sl.rq.u_l = ptr - start + skip - sl.rq.u;
+		http_msg_req09_ver:
+			sl.rq.v = ptr - start + skip;
+			sl.rq.v_l = 0;
+			goto http_msg_rqline_eol;
+		}
+		state = H1_MSG_RQMETH;
+		goto http_msg_invalid;
+
+	case H1_MSG_RQMETH_SP:
+	http_msg_rqmeth_sp:
+		if (likely(!HTTP_IS_LWS(*ptr))) {
+			sl.rq.u = ptr - start + skip;
+			goto http_msg_rquri;
+		}
+		if (likely(HTTP_IS_SPHT(*ptr)))
+			EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqmeth_sp, http_msg_ood, state, H1_MSG_RQMETH_SP);
+		/* so it's a CR/LF, meaning an HTTP 0.9 request */
+		goto http_msg_req09_uri;
+
+	case H1_MSG_RQURI:
+	http_msg_rquri:
+#if defined(__x86_64__) ||						\
+    defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || \
+    defined(__ARM_ARCH_7A__)
+		/* speedup: skip bytes not between 0x21 and 0x7e inclusive */
+		while (ptr <= end - sizeof(int)) {
+			int x = *(int *)ptr - 0x21212121;
+			if (x & 0x80808080)
+				break;
+
+			x -= 0x5e5e5e5e;
+			if (!(x & 0x80808080))
+				break;
+
+			ptr += sizeof(int);
+		}
+#endif
+		if (ptr >= end) {
+			state = H1_MSG_RQURI;
+			goto http_msg_ood;
+		}
+	http_msg_rquri2:
+		if (likely((unsigned char)(*ptr - 33) <= 93)) /* 33 to 126 included */
+			EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rquri2, http_msg_ood, state, H1_MSG_RQURI);
+
+		if (likely(HTTP_IS_SPHT(*ptr))) {
+			sl.rq.u_l = ptr - start + skip - sl.rq.u;
+			EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rquri_sp, http_msg_ood, state, H1_MSG_RQURI_SP);
+		}
+		if (likely((unsigned char)*ptr >= 128)) {
+			/* non-ASCII chars are forbidden unless option
+			 * accept-invalid-http-request is enabled in the frontend.
+			 * In any case, we capture the faulty char.
+			 */
+			if (h1m->err_pos < -1)
+				goto invalid_char;
+			if (h1m->err_pos == -1)
+				h1m->err_pos = ptr - start + skip;
+			EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rquri, http_msg_ood, state, H1_MSG_RQURI);
+		}
+
+		if (likely(HTTP_IS_CRLF(*ptr))) {
+			/* so it's a CR/LF, meaning an HTTP 0.9 request */
+			goto http_msg_req09_uri_e;
+		}
+
+		/* OK forbidden chars, 0..31 or 127 */
+	invalid_char:
+		state = H1_MSG_RQURI;
+		goto http_msg_invalid;
+
+	case H1_MSG_RQURI_SP:
+	http_msg_rquri_sp:
+		if (likely(!HTTP_IS_LWS(*ptr))) {
+			sl.rq.v = ptr - start + skip;
+			goto http_msg_rqver;
+		}
+		if (likely(HTTP_IS_SPHT(*ptr)))
+			EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rquri_sp, http_msg_ood, state, H1_MSG_RQURI_SP);
+		/* so it's a CR/LF, meaning an HTTP 0.9 request */
+		goto http_msg_req09_ver;
+
+
+	case H1_MSG_RQVER:
+	http_msg_rqver:
+		if (likely(HTTP_IS_VER_TOKEN(*ptr)))
+			EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqver, http_msg_ood, state, H1_MSG_RQVER);
+
+		if (likely(HTTP_IS_CRLF(*ptr))) {
+			sl.rq.v_l = ptr - start + skip - sl.rq.v;
+		http_msg_rqline_eol:
+			/* We have seen the end of line. Note that we do not
+			 * necessarily have the \n yet, but at least we know that we
+			 * have EITHER \r OR \n, otherwise the request would not be
+			 * complete. We can then record the request length and return
+			 * to the caller which will be able to register it.
+			 */
+
+			if (likely(!skip_update)) {
+				if (unlikely(hdr_count >= hdr_num)) {
+					state = H1_MSG_RQVER;
+					goto http_output_full;
+				}
+				http_set_hdr(&hdr[hdr_count++], ist(":method"), ist2(start + sl.rq.m, sl.rq.m_l));
+
+				if (unlikely(hdr_count >= hdr_num)) {
+					state = H1_MSG_RQVER;
+					goto http_output_full;
+				}
+				http_set_hdr(&hdr[hdr_count++], ist(":path"), ist2(start + sl.rq.u, sl.rq.u_l));
+			}
+
+			sol = ptr - start;
+			if (likely(*ptr == '\r'))
+				EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqline_end, http_msg_ood, state, H1_MSG_RQLINE_END);
+			goto http_msg_rqline_end;
+		}
+
+		/* neither an HTTP_VER token nor a CRLF */
+		state = H1_MSG_RQVER;
+		goto http_msg_invalid;
+
+	case H1_MSG_RQLINE_END:
+	http_msg_rqline_end:
+		/* check for HTTP/0.9 request : no version information
+		 * available. sol must point to the first of CR or LF. However
+		 * since we don't save these elements between calls, if we come
+		 * here from a restart, we don't necessarily know. Thus in this
+		 * case we simply start over.
+		 */
+		if (restarting)
+			goto restart;
+
+		if (unlikely(sl.rq.v_l == 0))
+			goto http_msg_last_lf;
+
+		EXPECT_LF_HERE(ptr, http_msg_invalid, state, H1_MSG_RQLINE_END);
+		EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_first, http_msg_ood, state, H1_MSG_HDR_FIRST);
+		/* stop here */
+
+	/*
+	 * Common states below
+	 */
 	case H1_MSG_RPBEFORE:
 	http_msg_rpbefore:
 		if (likely(HTTP_IS_TOKEN(*ptr))) {
@@ -1133,7 +1327,10 @@
 
  restart:
 	h1m->next  = 0;
-	h1m->state = H1_MSG_RPBEFORE;
+	if (h1m->flags & H1_MF_RESP)
+		h1m->state = H1_MSG_RPBEFORE;
+	else
+		h1m->state = H1_MSG_RQBEFORE;
 	goto try_again;
 }