summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2021-09-24 10:35:38 -0400
committerJune McEnroe <june@causal.agency>2021-09-24 10:35:38 -0400
commit67836ce3d7fb9062053f39fd7f75bb88af2a863d (patch)
tree837037ab4eaecd4202c906d0ab7379fcf31c2bee
parentSet only HTTP_HOST (diff)
downloadsrc-67836ce3d7fb9062053f39fd7f75bb88af2a863d.tar.gz
src-67836ce3d7fb9062053f39fd7f75bb88af2a863d.zip
Consume request headers using MSG_PEEK
Use MSG_PEEK to determine where the request headers end and consume
only up to there, leaving the CGI process to read any request body
directly from the socket.
-rw-r--r--bin/quick.c87
1 files changed, 39 insertions, 48 deletions
diff --git a/bin/quick.c b/bin/quick.c
index 9e916ff0..064b81d8 100644
--- a/bin/quick.c
+++ b/bin/quick.c
@@ -19,7 +19,7 @@
 #include <fcntl.h>
 #include <netdb.h>
 #include <netinet/in.h>
-#include <signal.h>
+#include <poll.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -30,78 +30,74 @@
 #include <unistd.h>
 
 static void request(int sock, char *argv[]) {
-	FILE *req = fdopen(dup(sock), "r");
-	if (!req) err(EX_OSERR, "fdopen");
-	fcntl(fileno(req), F_SETFD, FD_CLOEXEC);
-
-	size_t cap = 0;
-	char *buf = NULL;
-	ssize_t len = getline(&buf, &cap, req);
-	if (len < 0) goto close;
+	struct pollfd pfd = { .fd = sock, .events = POLLIN };
+	int nfds = poll(&pfd, 1, -1);
+	if (nfds < 0) err(EX_OSERR, "poll");
+
+	char buf[4096];
+	ssize_t len = recv(sock, buf, sizeof(buf)-1, MSG_PEEK);
+	if (len < 0) {
+		warn("recv");
+		return;
+	}
+	char *blank = memmem(buf, len, "\r\n\r\n", 4);
+	if (!blank) {
+		warnx("can't find end of request headers in peek");
+		return;
+	}
+	len = recv(sock, buf, &blank[4] - buf, 0);
+	if (len < 0) {
+		warn("recv");
+		return;
+	}
+	buf[len] = '\0';
 
 	char *ptr = buf;
-	char *method = strsep(&ptr, " ");
-	char *query = strsep(&ptr, " ");
+	char *req = strsep(&ptr, "\r\n");
+	char *method = strsep(&req, " ");
+	char *query = strsep(&req, " ");
 	char *path = strsep(&query, "?");
-	char *proto = strsep(&ptr, "\r\n");
-	if (!method || !path || !proto) goto close;
-
+	char *proto = strsep(&req, " ");
+	if (!method || !path || !proto) {
+		warnx("invalid request line");
+		return;
+	}
 	setenv("REQUEST_METHOD", method, 1);
 	setenv("PATH_INFO", path, 1);
 	setenv("QUERY_STRING", (query ? query : ""), 1);
 	setenv("SERVER_PROTOCOL", proto, 1);
+
 	unsetenv("CONTENT_TYPE");
 	unsetenv("CONTENT_LENGTH");
 	unsetenv("HTTP_HOST");
-
-	size_t bodyLen = 0;
-	while (0 <= (len = getline(&buf, &cap, req))) {
-		if (len && buf[len-1] == '\n') buf[--len] = '\0';
-		if (len && buf[len-1] == '\r') buf[--len] = '\0';
-		if (!len) break;
-
-		char *value = buf;
+	while (ptr) {
+		char *value = strsep(&ptr, "\r\n");
+		if (!value[0]) continue;
 		char *header = strsep(&value, ":");
-		if (!header || !value++) goto close;
-
+		if (!header || !value++) {
+			warnx("invalid header");
+			return;
+		}
 		if (!strcasecmp(header, "Content-Type")) {
 			setenv("CONTENT_TYPE", value, 1);
 		} else if (!strcasecmp(header, "Content-Length")) {
-			bodyLen = strtoull(value, NULL, 10);
 			setenv("CONTENT_LENGTH", value, 1);
 		} else if (!strcasecmp(header, "Host")) {
 			setenv("HTTP_HOST", value, 1);
 		}
 	}
 
-	int rw[2];
-	int error = pipe(rw);
-	if (error) err(EX_OSERR, "pipe");
-	fcntl(rw[0], F_SETFD, FD_CLOEXEC);
-	fcntl(rw[1], F_SETFD, FD_CLOEXEC);
-
 	dprintf(sock, "HTTP/1.1 200 OK\nConnection: close\n");
 	pid_t pid = fork();
 	if (pid < 0) err(EX_OSERR, "fork");
 	if (!pid) {
-		dup2(rw[0], STDIN_FILENO);
+		dup2(sock, STDIN_FILENO);
 		dup2(sock, STDOUT_FILENO);
 		execv(argv[0], argv);
 		warn("%s", argv[0]);
 		_exit(127);
 	}
 
-	close(rw[0]);
-	char body[4096];
-	while (bodyLen) {
-		size_t cap = (bodyLen < sizeof(body) ? bodyLen : sizeof(body));
-		size_t len = fread(&body, 1, cap, req);
-		if (!len) break;
-		write(rw[1], body, len);
-		bodyLen -= len;
-	}
-	close(rw[1]);
-
 	int status;
 	pid = wait(&status);
 	if (pid < 0) err(EX_OSERR, "wait");
@@ -110,10 +106,6 @@ static void request(int sock, char *argv[]) {
 	} else if (WIFSIGNALED(status)) {
 		warnx("%s killed %d", argv[0], WTERMSIG(status));
 	}
-
-close:
-	fclose(req);
-	free(buf);
 }
 
 int main(int argc, char *argv[]) {
@@ -160,7 +152,6 @@ int main(int argc, char *argv[]) {
 	setenv("REMOTE_HOST", host, 1);
 	setenv("SCRIPT_NAME", "/", 1);
 
-	signal(SIGPIPE, SIG_IGN);
 	for (int sock; 0 <= (sock = accept(server, NULL, NULL)); close(sock)) {
 		request(sock, &argv[optind]);
 	}