summaryrefslogtreecommitdiff
path: root/fcgiwrap.c
diff options
context:
space:
mode:
Diffstat (limited to 'fcgiwrap.c')
-rw-r--r--fcgiwrap.c124
1 files changed, 71 insertions, 53 deletions
diff --git a/fcgiwrap.c b/fcgiwrap.c
index 1755af9..72037c1 100644
--- a/fcgiwrap.c
+++ b/fcgiwrap.c
@@ -80,7 +80,56 @@ enum reply_state_t {
REPLY_STATE_LF,
REPLY_STATE_2CR,
REPLY_STATE_2LF,
- REPLY_STATE_BODY
+ REPLY_STATE_BODY,
+ REPLY_STATE_MAX
+};
+
+enum char_class_t {
+ CC_NORMAL,
+ CC_CR,
+ CC_LF,
+ CC_MAX
+};
+
+#define ACTION_MASK (15 << 4)
+#define ACTION_EMIT 0
+#define ACTION_ERROR (1 << 4)
+#define ACTION_END (2 << 4)
+#define ACTION_SKIP (3 << 4)
+#define ACTION_EXTRA_CR (4 << 4)
+#define ACTION_EXTRA_LF (5 << 4)
+
+static const unsigned char header_state_machine[REPLY_STATE_MAX][CC_MAX] = {
+ [REPLY_STATE_INIT] = {
+ [CC_NORMAL] = REPLY_STATE_HEADER,
+ [CC_CR] = ACTION_ERROR,
+ [CC_LF] = ACTION_ERROR,
+ },
+ [REPLY_STATE_HEADER] = {
+ [CC_NORMAL] = REPLY_STATE_HEADER,
+ [CC_CR] = REPLY_STATE_CR,
+ [CC_LF] = REPLY_STATE_LF | ACTION_EXTRA_CR,
+ },
+ [REPLY_STATE_CR] = {
+ [CC_NORMAL] = REPLY_STATE_HEADER | ACTION_EXTRA_LF,
+ [CC_CR] = REPLY_STATE_CR | ACTION_SKIP,
+ [CC_LF] = REPLY_STATE_LF,
+ },
+ [REPLY_STATE_LF] = {
+ [CC_NORMAL] = REPLY_STATE_HEADER,
+ [CC_CR] = REPLY_STATE_2CR,
+ [CC_LF] = REPLY_STATE_2LF | ACTION_EXTRA_CR,
+ },
+ [REPLY_STATE_2CR] = {
+ [CC_NORMAL] = REPLY_STATE_BODY | ACTION_EXTRA_LF,
+ [CC_CR] = REPLY_STATE_CR | ACTION_SKIP,
+ [CC_LF] = REPLY_STATE_2LF,
+ },
+ [REPLY_STATE_2LF] = {
+ [CC_NORMAL] = REPLY_STATE_BODY | ACTION_END,
+ [CC_CR] = REPLY_STATE_2LF | ACTION_SKIP,
+ [CC_LF] = REPLY_STATE_2LF | ACTION_SKIP,
+ },
};
struct fcgi_context {
@@ -110,69 +159,38 @@ static const char * fcgi_pass_fd(struct fcgi_context *fc, int *fdp, FCGI_FILE *f
{
ssize_t nread;
char *p = buf;
+ unsigned char cclass, next_state;
nread = read(*fdp, buf, bufsize);
if (nread > 0) {
while (p <= buf + nread) {
- switch(fc->reply_state) {
- case REPLY_STATE_INIT:
- if (*p == '\r' || *p == '\n') return "parsing CGI reply (CR or LF as first character)";
- fc->reply_state = REPLY_STATE_HEADER;
- break;
+ if (*p == '\r') {
+ cclass = CC_CR;
+ } else if (*p == '\n') {
+ cclass = CC_LF;
+ } else {
+ cclass = CC_NORMAL;
+ }
+ next_state = header_state_machine[fc->reply_state][cclass];
+ fc->reply_state = next_state & ~ACTION_MASK;
+ switch(next_state & ACTION_MASK) {
+ case ACTION_ERROR:
+ return "parsing CGI reply";
- case REPLY_STATE_HEADER:
- if (*p == '\r') {
- fc->reply_state = REPLY_STATE_CR;
- } else if (*p == '\n') {
- if (FCGI_fputc('\r', ffp) == EOF) return "writing CGI reply";
- fc->reply_state = REPLY_STATE_LF;
- }
- break;
+ case ACTION_END:
+ goto out_of_loop;
- case REPLY_STATE_CR:
- if (*p == '\r') {
- goto next_char;
- } else if (*p == '\n') {
- fc->reply_state = REPLY_STATE_LF;
- } else {
- if (FCGI_fputc('\n', ffp) == EOF) return "writing CGI reply";
- fc->reply_state = REPLY_STATE_HEADER;
- }
- break;
+ case ACTION_SKIP:
+ goto next_char;
- case REPLY_STATE_LF:
- if (*p == '\r') {
- fc->reply_state = REPLY_STATE_2CR;
- } else if (*p == '\n') {
- if (FCGI_fputc('\r', ffp) == EOF) return "writing CGI reply";
- fc->reply_state = REPLY_STATE_2LF;
- } else {
- fc->reply_state = REPLY_STATE_HEADER;
- }
+ case ACTION_EXTRA_CR:
+ if (FCGI_fputc('\r', ffp) == EOF) return "writing CGI reply";
break;
- case REPLY_STATE_2CR:
- if (*p == '\r') {
- goto next_char;
- } else if (*p == '\n') {
- fc->reply_state = REPLY_STATE_2LF;
- } else {
- if (FCGI_fputc('\n', ffp) == EOF) return "writing CGI reply";
- fc->reply_state = REPLY_STATE_BODY;
- }
+ case ACTION_EXTRA_LF:
+ if (FCGI_fputc('\n', ffp) == EOF) return "writing CGI reply";
break;
-
- case REPLY_STATE_2LF:
- if (*p == '\r' || *p == '\n')
- goto next_char;
- fc->reply_state = REPLY_STATE_BODY;
- /* FALLTHROUGH */
-
- case REPLY_STATE_BODY:
- goto out_of_loop;
}
-
-
if (FCGI_fputc(*p, ffp) == EOF) {
return "writing CGI reply";
}