Introduction
Introduction Statistics Contact Development Disclaimer Help
Split up http_get_request() - quark - quark web server
git clone git://git.suckless.org/quark
Log
Files
Refs
LICENSE
---
commit a5163d08135b81b271b1d7ba36b24e342b57961f
parent c1b242e405d40067c282e8116d21c6f2641e4eee
Author: Laslo Hunhold <[email protected]>
Date: Sat, 22 Aug 2020 11:05:20 +0200
Split up http_get_request()
The function has become too long and basically did two things: Receiving
the header and parsing it. To better reflect this, we split it up into
the two functions http_recv_header() and http_parse_header(). This way,
we also obtain a better separation of concerns and can further reduce
the scope of each parameter-list.
http_recv_header() has been written in such a way that it can be
reentered and fill up the header-buffer bit by bit using a pointer to
an offset value.
The error handling was improved by only returning the immediate error
status codes and letting the caller do the error-handling with
http_send_status().
Signed-off-by: Laslo Hunhold <[email protected]>
Diffstat:
M http.c | 120 +++++++++++++++++------------…
M http.h | 3 ++-
M main.c | 6 +++++-
3 files changed, 71 insertions(+), 58 deletions(-)
---
diff --git a/http.c b/http.c
@@ -143,43 +143,51 @@ decode(const char src[PATH_MAX], char dest[PATH_MAX])
dest[i] = '\0';
}
-int
-http_get_request(int fd, struct request *req)
+enum status
+http_recv_header(int fd, char *h, size_t hsiz, size_t *off)
{
- struct in6_addr addr;
- size_t hlen, i, mlen;
- ssize_t off;
- char *h = req->header, *p, *q;
+ ssize_t r;
- /* empty all fields */
- memset(req, 0, sizeof(*req));
+ if (h == NULL || off == NULL || *off > hsiz) {
+ return S_INTERNAL_SERVER_ERROR;
+ }
- /*
- * receive header
- */
- for (hlen = 0; ;) {
- if ((off = read(fd, h + hlen, sizeof(h) - hlen)) < 0) {
- return http_send_status(fd, S_REQUEST_TIMEOUT);
- } else if (off == 0) {
- break;
+ while (1) {
+ if ((r = read(fd, h + *off, hsiz - *off)) <= 0) {
+ return S_REQUEST_TIMEOUT;
}
- hlen += off;
- if (hlen >= 4 && !memcmp(h + hlen - 4, "\r\n\r\n", 4)) {
+ *off += r;
+
+ /* check if we are done (header terminated) */
+ if (*off >= 4 && !memcmp(h + *off - 4, "\r\n\r\n", 4)) {
break;
}
- if (hlen == sizeof(h)) {
- return http_send_status(fd, S_REQUEST_TOO_LARGE);
+
+ /* buffer is full or read over, but header is not terminated */
+ if (r == 0 || *off == hsiz) {
+ return S_REQUEST_TOO_LARGE;
}
}
- /* remove terminating empty line */
- if (hlen < 2) {
- return http_send_status(fd, S_BAD_REQUEST);
- }
- hlen -= 2;
+ /* header is complete, remove last \r\n and null-terminate */
+ h[*off - 2] = '\0';
+
+ /* set *off to 0 to indicate we are finished */
+ *off = 0;
- /* null-terminate the header */
- h[hlen] = '\0';
+ return 0;
+}
+
+enum status
+http_parse_header(const char *h, struct request *req)
+{
+ struct in6_addr addr;
+ size_t i, mlen;
+ const char *p, *q;
+ char *m, *n;
+
+ /* empty all fields */
+ memset(req, 0, sizeof(*req));
/*
* parse request line
@@ -194,12 +202,12 @@ http_get_request(int fd, struct request *req)
}
}
if (i == NUM_REQ_METHODS) {
- return http_send_status(fd, S_METHOD_NOT_ALLOWED);
+ return S_METHOD_NOT_ALLOWED;
}
/* a single space must follow the method */
if (h[mlen] != ' ') {
- return http_send_status(fd, S_BAD_REQUEST);
+ return S_BAD_REQUEST;
}
/* basis for next step */
@@ -207,13 +215,13 @@ http_get_request(int fd, struct request *req)
/* TARGET */
if (!(q = strchr(p, ' '))) {
- return http_send_status(fd, S_BAD_REQUEST);
+ return S_BAD_REQUEST;
}
- *q = '\0';
if (q - p + 1 > PATH_MAX) {
- return http_send_status(fd, S_REQUEST_TOO_LARGE);
+ return S_REQUEST_TOO_LARGE;
}
- memcpy(req->target, p, q - p + 1);
+ memcpy(req->target, p, q - p);
+ req->target[q - p] = '\0';
decode(req->target, req->target);
/* basis for next step */
@@ -221,18 +229,18 @@ http_get_request(int fd, struct request *req)
/* HTTP-VERSION */
if (strncmp(p, "HTTP/", sizeof("HTTP/") - 1)) {
- return http_send_status(fd, S_BAD_REQUEST);
+ return S_BAD_REQUEST;
}
p += sizeof("HTTP/") - 1;
if (strncmp(p, "1.0", sizeof("1.0") - 1) &&
strncmp(p, "1.1", sizeof("1.1") - 1)) {
- return http_send_status(fd, S_VERSION_NOT_SUPPORTED);
+ return S_VERSION_NOT_SUPPORTED;
}
p += sizeof("1.*") - 1;
/* check terminator */
if (strncmp(p, "\r\n", sizeof("\r\n") - 1)) {
- return http_send_status(fd, S_BAD_REQUEST);
+ return S_BAD_REQUEST;
}
/* basis for next step */
@@ -253,7 +261,7 @@ http_get_request(int fd, struct request *req)
if (i == NUM_REQ_FIELDS) {
/* unmatched field, skip this line */
if (!(q = strstr(p, "\r\n"))) {
- return http_send_status(fd, S_BAD_REQUEST);
+ return S_BAD_REQUEST;
}
p = q + (sizeof("\r\n") - 1);
continue;
@@ -263,7 +271,7 @@ http_get_request(int fd, struct request *req)
/* a single colon must follow the field name */
if (*p != ':') {
- return http_send_status(fd, S_BAD_REQUEST);
+ return S_BAD_REQUEST;
}
/* skip whitespace */
@@ -272,13 +280,13 @@ http_get_request(int fd, struct request *req)
/* extract field content */
if (!(q = strstr(p, "\r\n"))) {
- return http_send_status(fd, S_BAD_REQUEST);
+ return S_BAD_REQUEST;
}
- *q = '\0';
if (q - p + 1 > FIELD_MAX) {
- return http_send_status(fd, S_REQUEST_TOO_LARGE);
+ return S_REQUEST_TOO_LARGE;
}
- memcpy(req->field[i], p, q - p + 1);
+ memcpy(req->field[i], p, q - p);
+ req->field[i][q - p] = '\0';
/* go to next line */
p = q + (sizeof("\r\n") - 1);
@@ -288,37 +296,37 @@ http_get_request(int fd, struct request *req)
* clean up host
*/
- p = strrchr(req->field[REQ_HOST], ':');
- q = strrchr(req->field[REQ_HOST], ']');
+ m = strrchr(req->field[REQ_HOST], ':');
+ n = strrchr(req->field[REQ_HOST], ']');
/* strip port suffix but don't interfere with IPv6 bracket notation
* as per RFC 2732 */
- if (p && (!q || p > q)) {
+ if (m && (!n || m > n)) {
/* port suffix must not be empty */
- if (*(p + 1) == '\0') {
- return http_send_status(fd, S_BAD_REQUEST);
+ if (*(m + 1) == '\0') {
+ return S_BAD_REQUEST;
}
- *p = '\0';
+ *m = '\0';
}
/* strip the brackets from the IPv6 notation and validate the address …
- if (q) {
+ if (n) {
/* brackets must be on the outside */
- if (req->field[REQ_HOST][0] != '[' || *(q + 1) != '\0') {
- return http_send_status(fd, S_BAD_REQUEST);
+ if (req->field[REQ_HOST][0] != '[' || *(n + 1) != '\0') {
+ return S_BAD_REQUEST;
}
/* remove the right bracket */
- *q = '\0';
- p = req->field[REQ_HOST] + 1;
+ *n = '\0';
+ m = req->field[REQ_HOST] + 1;
/* validate the contained IPv6 address */
- if (inet_pton(AF_INET6, p, &addr) != 1) {
- return http_send_status(fd, S_BAD_REQUEST);
+ if (inet_pton(AF_INET6, m, &addr) != 1) {
+ return S_BAD_REQUEST;
}
/* copy it into the host field */
- memmove(req->field[REQ_HOST], p, q - p + 1);
+ memmove(req->field[REQ_HOST], m, n - m + 1);
}
return 0;
diff --git a/http.h b/http.h
@@ -104,7 +104,8 @@ struct connection {
enum status http_send_header(int, const struct response *);
enum status http_send_status(int, enum status);
-int http_get_request(int fd, struct request *);
+enum status http_recv_header(int, char *, size_t, size_t *);
+enum status http_parse_header(const char *, struct request *);
enum status http_send_response(int fd, const struct request *,
const struct server *);
diff --git a/main.c b/main.c
@@ -37,7 +37,11 @@ serve(int infd, const struct sockaddr_storage *in_sa, const …
}
/* handle request */
- if (!(status = http_get_request(c.fd, &c.req))) {
+ if ((status = http_recv_header(c.fd, c.header, LEN(c.header), &c.off))…
+ status = http_send_status(c.fd, status);
+ } else if ((status = http_parse_header(c.header, &c.req))) {
+ status = http_send_status(c.fd, status);
+ } else {
status = http_send_response(c.fd, &c.req, s);
}
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.