diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | fcgiwrap.c | 124 |
2 files changed, 72 insertions, 54 deletions
@@ -5,7 +5,7 @@ install: all rm /usr/local/bin/fcgiwrap~ fcgiwrap: fcgiwrap.c - gcc -Wall -Wextra -Werror -pedantic -O2 -g3 fcgiwrap.c -o fcgiwrap -lfcgi + gcc -std=gnu99 -Wall -Wextra -Werror -pedantic -O2 -g3 fcgiwrap.c -o fcgiwrap -lfcgi clean: -rm fcgiwrap @@ -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"; } |