summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <rmk@armlinux.org.uk>2021-09-14 23:15:40 +0100
committerRussell King <rmk@armlinux.org.uk>2021-09-15 00:10:00 +0100
commit6e0f5e73810814afcc0420eca4afdd44af6cc942 (patch)
tree4915756b9dc2e0ef7d3597eb563834afe57e4ce2
parent760874afe4d70abc1c6779a6b8163d62fb8d347b (diff)
event-httpd: parse request-line when received
Parse the request line when it is received and save any errors encountered which will be returned to the client after the headers have been received. Signed-off-by: Russell King <rmk@armlinux.org.uk>
-rw-r--r--event-httpd.c95
-rw-r--r--event-httpd.h4
2 files changed, 58 insertions, 41 deletions
diff --git a/event-httpd.c b/event-httpd.c
index c080107..1b5df6a 100644
--- a/event-httpd.c
+++ b/event-httpd.c
@@ -22,8 +22,10 @@ static GHashTable *resource_hash;
void close_client(struct client *c)
{
- if (c->request)
- g_free(c->request);
+ g_free(c->method);
+ g_free(c->uri);
+ g_free(c->query);
+ g_free(c->version);
g_object_unref(c->data);
g_object_unref(c->conn);
g_free(c);
@@ -123,13 +125,51 @@ static void update(GObject *source, GAsyncResult *res, gpointer user_data)
g_data_input_stream_read_line_async(c->data, 0, NULL, update, c);
}
-static bool parse_request(struct client *c, const char *line)
+static void parse_request_line(struct client *c, const char *line)
{
+ char **parts, **uri;
+
// In the interest of robustness, servers SHOULD ignore any empty
// line(s) received where a Request-Line is expected.
- if (!c->request) {
- if (line[0])
- c->request = g_strdup(line);
+ if (!line[0])
+ return;
+
+ // Split the request line: Method SP Request-URI SP HTTP-Version
+ // We do not support early HTTP.
+ parts = g_strsplit(line, " ", 3);
+ if (!parts || !parts[0] || !parts[1] || !parts[2]) {
+ c->error = 400;
+ c->errstr = "Bad Request";
+ g_strfreev(parts);
+ return;
+ }
+
+ c->method = g_strdup(parts[0]);
+ c->version = g_strdup(parts[2]);
+
+ // Split the URI and query string
+ uri = g_strsplit(parts[1], "?", 2);
+ g_strfreev(parts);
+ if (!uri || !uri[0]) {
+ c->error = 400;
+ c->errstr = "Bad Request";
+ g_strfreev(uri);
+ return;
+ }
+
+ // Unescape the URI
+ c->uri = g_uri_unescape_string(uri[0], NULL);
+ if (uri[1])
+ c->query = g_strdup(uri[1]);
+
+ g_strfreev(uri);
+}
+
+static bool parse_request(struct client *c, const char *line)
+{
+ // Parse the request line
+ if (!c->method && !c->error) {
+ parse_request_line(c, line);
return true;
}
@@ -157,7 +197,7 @@ static void receive(GObject *source, GAsyncResult *res, gpointer user_data)
enum method method;
GError *error = NULL;
gsize len;
- char *line, *uri, *query, *unescaped, *version;
+ char *line;
bool more;
line = g_data_input_stream_read_line_finish(c->data, res, &len, &error);
@@ -178,50 +218,28 @@ static void receive(GObject *source, GAsyncResult *res, gpointer user_data)
return;
}
- // End of request. Parse it.
- // Find the URI
- uri = strchr(c->request, ' ');
- if (!uri) {
- respond_error(c, 400, "Invalid request");
+ // Did we encounter an error?
+ if (c->error) {
+ respond_error(c, c->error, c->errstr);
return;
}
- *uri++ = '\0';
-
- // Find the version
- version = strchr(uri, ' ');
- if (!version) {
- respond_error(c, 400, "Invalid request");
- return;
- }
-
- *version++ = '\0';
-
// Check that the version is HTTP/1.x. We probably ought to
// parse the major version better, as leading zeros should be
// accepted.
- if (!g_str_has_prefix(version, "HTTP/1.")) {
+ if (!g_str_has_prefix(c->version, "HTTP/1.")) {
respond_error(c, 505, "HTTP Version Not Supported");
return;
}
- // Split the query string
- query = strchr(uri, '?');
- if (query)
- *query++ = '\0';
-
- c->uri = uri;
- c->query = query;
- c->version = version;
-
// HTTP/1.1 and later can use chunked mode. Note that http 1.1
// allows leading zeros.
- c->can_chunk = atoi(version + 7) != '0';
+ c->can_chunk = atoi(c->version + 7) != '0';
// Check the method
- if (!strcmp(c->request, "GET")) {
+ if (!strcmp(c->method, "GET")) {
method = GET;
- } else if (!strcmp(c->request, "UPDATE") && !c->forwarded) {
+ } else if (!strcmp(c->method, "UPDATE") && !c->forwarded) {
// Update is only permitted if not forwarded through a
// proxy. NOTE: this is the only way we control access.
method = UPDATE;
@@ -230,11 +248,8 @@ static void receive(GObject *source, GAsyncResult *res, gpointer user_data)
return;
}
- // Unescape the URI
- unescaped = g_uri_unescape_string(uri, NULL);
-
// Lookup the resource handler
- resource = g_hash_table_lookup(resource_hash, unescaped);
+ resource = g_hash_table_lookup(resource_hash, c->uri);
if (!resource) {
respond_error(c, 404, "Not Found");
return;
diff --git a/event-httpd.h b/event-httpd.h
index 9af8432..cb8779d 100644
--- a/event-httpd.h
+++ b/event-httpd.h
@@ -11,7 +11,9 @@ struct client {
gboolean forwarded;
gboolean can_chunk;
- char *request;
+ int error;
+ const char *errstr;
+ char *method;
char *uri;
char *query;
char *version;