Commit Diff


commit - a6f2cfe7927f8385938f220016280629d3e8a6d9
commit + 0f7fdd21050e3795db896b99e542523c84e075d7
blob - 9452e306aa4eb3545ae87c9a4dd30836e029723a
blob + 7a141a58fe7808af08033bb33aa9643e5e19b143
--- fcgi.c
+++ fcgi.c
@@ -17,6 +17,7 @@
 #include "gmid.h"
 
 #include <assert.h>
+#include <ctype.h>
 #include <errno.h>
 #include <string.h>
 
@@ -209,6 +210,65 @@ static inline int
 reclen(struct fcgi_header *h)
 {
 	return h->content_len0 + (h->content_len1 << 8);
+}
+
+static void
+fcgi_handle_stdout(struct client *c, struct evbuffer *src, size_t len)
+{
+	struct bufferevent	*bev = c->cgibev;
+	char			*t;
+	size_t			 l;
+	int			 code;
+
+	if (c->code == 0) {
+		l = len;
+		if (l > sizeof(c->sbuf) - c->soff)
+			l = sizeof(c->sbuf) - c->soff;
+
+		memcpy(&c->sbuf[c->soff], EVBUFFER_DATA(src), l);
+		c->soff += l;
+		evbuffer_drain(src, l);
+		len -= l;
+
+		if ((t = memmem(c->sbuf, c->soff, "\r\n", 2)) == NULL) {
+			if (c->soff == sizeof(c->sbuf)) {
+				log_warnx("FastCGI application is trying to"
+				    " send a header that's too long.");
+				fcgi_error(bev, EVBUFFER_ERROR, c);
+			}
+
+			/* wait a bit */
+			return;
+		}
+		*t = '\0';
+		t += 2; /* skip CRLF */
+
+		if (!isdigit((unsigned char)c->sbuf[0]) ||
+		    !isdigit((unsigned char)c->sbuf[1]) ||
+		    c->sbuf[2] != ' ') {
+			fcgi_error(bev, EVBUFFER_ERROR, c);
+			return;
+		}
+
+		code = (c->sbuf[0] - '0') * 10 + (c->sbuf[1] - '0');
+		if (code < 10 || code >= 70) {
+			log_warnx("FastCGI application is trying to send an"
+			    " invalid reply code: %d", code);
+			fcgi_error(bev, EVBUFFER_ERROR, c);
+			return;
+		}
+
+		start_reply(c, code, c->sbuf + 3);
+		if (c->code < 20 || c->code > 29) {
+			fcgi_error(bev, EVBUFFER_EOF, c);
+			return;
+		}
+
+		bufferevent_write(c->bev, t, &c->sbuf[c->soff] - t);
+	}
+
+	bufferevent_write(c->bev, EVBUFFER_DATA(src), len);
+	evbuffer_drain(src, len);
 }
 
 void
@@ -259,8 +319,7 @@ fcgi_read(struct bufferevent *bev, void *d)
 			break;
 
 		case FCGI_STDOUT:
-			bufferevent_write(c->bev, EVBUFFER_DATA(src), len);
-			evbuffer_drain(src, len);
+			fcgi_handle_stdout(c, src, len);
 			break;
 
 		default:
@@ -290,14 +349,25 @@ fcgi_error(struct bufferevent *bev, short err, void *d
 {
 	struct client	*c = d;
 
-	if (!(err & (EVBUFFER_ERROR|EVBUFFER_EOF)))
-		log_warn("unknown event error (%x)", err);
+	/*
+	 * If we're here it means that some kind of non-recoverable
+	 * error happened.
+	 */
 
-	c->type = REQUEST_DONE;
-	if (c->code != 0)
-		client_close(c);
-	else
+	bufferevent_free(bev);
+	c->cgibev = NULL;
+
+	close(c->pfd);
+	c->pfd = -1;
+
+	/* EOF and no header */
+	if (c->code == 0) {
 		start_reply(c, CGI_ERROR, "CGI error");
+		return;
+	}
+
+	c->type = REQUEST_DONE;
+	client_write(c->bev, c);
 }
 
 void
blob - 885a7a7d669e99bb45a7818e2104d0e7beaff221
blob + 770deeae94340a541a8e2827b2574e8d371488e2
--- gmid.h
+++ gmid.h
@@ -306,7 +306,7 @@ struct client {
 
 	/* big enough to store STATUS + SPACE + META + CRLF */
 	char		 sbuf[1029];
-	ssize_t		 len, off;
+	size_t		 soff;
 
 	struct sockaddr_storage	 raddr;
 	socklen_t		 raddrlen;