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
commit6cd5d365a877629eb9999348f277c56b386ac440 (patch)
tree5fca13f0e21b630cb1f8869e189b83775ca1d37a
parentSet only HTTP_HOST (diff)
downloadsrc-6cd5d365a877629eb9999348f277c56b386ac440.tar.gz
src-6cd5d365a877629eb9999348f277c56b386ac440.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.
Diffstat (limited to '')
-rw-r--r--bin/quick.c87
1 files changed, 39 insertions, 48 deletions
diff --git a/bin/quick.c b/bin/quick.c
index b24aef24..06810463 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]);
 	}