[PATCH 1/5] http.sys: Parse salient data from request headers.
Zebediah Figura
z.figura12 at gmail.com
Fri Aug 23 17:36:15 CDT 2019
Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
dlls/http.sys/http.c | 165 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 163 insertions(+), 2 deletions(-)
diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c
index 5e29db0fa7..506aefccf1 100644
--- a/dlls/http.sys/http.c
+++ b/dlls/http.sys/http.c
@@ -52,6 +52,13 @@ struct connection
char *buffer;
unsigned int len, size;
+
+ /* Things we already parsed out of the request header in parse_request(). */
+ unsigned int req_len;
+ HTTP_VERB verb;
+ HTTP_VERSION version;
+ const char *url, *host;
+ ULONG unk_verb_len, url_len, content_len;
};
static struct list connections = LIST_INIT(connections);
@@ -99,19 +106,173 @@ static void accept_connection(int socket)
static void close_connection(struct connection *conn)
{
+ heap_free(conn->buffer);
shutdown(conn->socket, SD_BOTH);
closesocket(conn->socket);
list_remove(&conn->entry);
heap_free(conn);
}
+static HTTP_VERB parse_verb(const char *verb, int len)
+{
+ static const char *const verbs[] =
+ {
+ "OPTIONS",
+ "GET",
+ "HEAD",
+ "POST",
+ "PUT",
+ "DELETE",
+ "TRACE",
+ "CONNECT",
+ "TRACK",
+ "MOVE",
+ "COPY",
+ "PROPFIND",
+ "PROPPATCH",
+ "MKCOL",
+ "LOCK",
+ "UNLOCK",
+ "SEARCH",
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(verbs); ++i)
+ {
+ if (!strncmp(verb, verbs[i], len))
+ return HttpVerbOPTIONS + i;
+ }
+ return HttpVerbUnknown;
+}
+
+/* Return the length of a token, as defined in RFC 2616 section 2.2. */
+static int parse_token(const char *str, const char *end)
+{
+ const char *p;
+ for (p = str; !end || p < end; ++p)
+ {
+ if (!isgraph(*p) || strchr("()<>@,;:\\\"/[]?={}", *p))
+ break;
+ }
+ return p - str;
+}
+
+/* Return 1 if str matches expect, 0 if str is incomplete, -1 if they don't match. */
+static int compare_exact(const char *str, const char *expect, const char *end)
+{
+ while (*expect)
+ {
+ if (str >= end) return 0;
+ if (*str++ != *expect++) return -1;
+ }
+ return 1;
+}
+
+static int parse_number(const char *str, const char **endptr, const char *end)
+{
+ int n = 0;
+ while (str < end && isdigit(*str))
+ n = n * 10 + (*str++ - '0');
+ *endptr = str;
+ return n;
+}
+
/* Upon receiving a request, parse it to ensure that it is a valid HTTP request,
* and mark down some information that we will use later. Returns 1 if we parsed
* a complete request, 0 if incomplete, -1 if invalid. */
static int parse_request(struct connection *conn)
{
- FIXME("Not implemented.\n");
- return -1;
+ const char *const req = conn->buffer, *const end = conn->buffer + conn->len;
+ const char *p = req, *q;
+ int len, ret;
+
+ if (!conn->len) return 0;
+
+ TRACE("%s\n", wine_dbgstr_an(conn->buffer, conn->len));
+
+ len = parse_token(p, end);
+ if (p + len >= end) return 0;
+ if (!len || p[len] != ' ') return -1;
+
+ /* verb */
+ if ((conn->verb = parse_verb(p, len)) == HttpVerbUnknown)
+ conn->unk_verb_len = len;
+ p += len + 1;
+
+ TRACE("Got verb %u (%s).\n", conn->verb, debugstr_an(req, len));
+
+ /* URL */
+ conn->url = p;
+ while (p < end && isgraph(*p)) ++p;
+ conn->url_len = p - conn->url;
+ if (p >= end) return 0;
+ if (!conn->url_len) return -1;
+
+ TRACE("Got URI %s.\n", debugstr_an(conn->url, conn->url_len));
+
+ /* version */
+ if ((ret = compare_exact(p, " HTTP/", end)) <= 0) return ret;
+ p += 6;
+ conn->version.MajorVersion = parse_number(p, &q, end);
+ if (q >= end) return 0;
+ if (q == p || *q != '.') return -1;
+ p = q + 1;
+ if (p >= end) return 0;
+ conn->version.MinorVersion = parse_number(p, &q, end);
+ if (q >= end) return 0;
+ if (q == p) return -1;
+ p = q;
+ if ((ret = compare_exact(p, "\r\n", end)) <= 0) return ret;
+ p += 2;
+
+ TRACE("Got version %hu.%hu.\n", conn->version.MajorVersion, conn->version.MinorVersion);
+
+ /* headers */
+ conn->host = NULL;
+ conn->content_len = 0;
+ for (;;)
+ {
+ const char *name = p;
+
+ if (!(ret = compare_exact(p, "\r\n", end))) return 0;
+ else if (ret > 0) break;
+
+ len = parse_token(p, end);
+ if (p + len >= end) return 0;
+ if (!len) return -1;
+ p += len;
+ while (p < end && (*p == ' ' || *p == '\t')) ++p;
+ if (p >= end) return 0;
+ if (*p != ':') return -1;
+ ++p;
+ while (p < end && (*p == ' ' || *p == '\t')) ++p;
+
+ TRACE("Got %s header.\n", debugstr_an(name, len));
+
+ if (!strncmp(name, "Host", len))
+ conn->host = p;
+ else if (!strncmp(name, "Content-Length", len))
+ {
+ conn->content_len = parse_number(p, &q, end);
+ if (q >= end) return 0;
+ if (q == p) return -1;
+ }
+ else if (!strncmp(name, "Transfer-Encoding", len))
+ FIXME("Unhandled Transfer-Encoding header.\n");
+ while (p < end && (isprint(*p) || *p == '\t')) ++p;
+ if ((ret = compare_exact(p, "\r\n", end)) <= 0) return ret;
+ p += 2;
+ }
+ p += 2;
+ if (conn->url[0] == '/' && !conn->host) return -1;
+
+ if (end - p < conn->content_len) return 0;
+
+ conn->req_len = (p - req) + conn->content_len;
+
+ TRACE("Received a full request, length %u bytes.\n", conn->req_len);
+
+ return 1;
}
static void receive_data(struct connection *conn)
--
2.22.0
More information about the wine-devel
mailing list