diff options
author | rofl0r <rofl0r@users.noreply.github.com> | 2020-09-07 04:32:13 +0100 |
---|---|---|
committer | rofl0r <rofl0r@users.noreply.github.com> | 2020-09-07 04:32:13 +0100 |
commit | a8848d4bd898a15e02a8f4210917166bca9f50b1 (patch) | |
tree | 77f3b6399fe928f456264bccdf31e5d9c2f6bb59 /src/html-error.c | |
parent | 17ae1b512c386970d0f5b84b385b89749a89b1e5 (diff) |
html-error: substitute template variables via a regex
previously, in order to detect and insert {variables} into error/stats
templates, tinyproxy iterated char-by-char over the input file, and would
try to parse anything inside {} pairs and treat it like a variable name.
this breaks CSS, and additionally it's dog slow as tinyproxy wrote every
single character to the client via a write syscall.
now we process line-by-line, and inspect all matches of the regex
\{[a-z]{1,32}\}. if the contents of the regex are a known variable name,
substitution is taking place. if not, the contents are passed as-is to
the client. also the chunks before and after matches are written in
a single syscall.
closes #108
Diffstat (limited to 'src/html-error.c')
-rw-r--r-- | src/html-error.c | 93 |
1 files changed, 33 insertions, 60 deletions
diff --git a/src/html-error.c b/src/html-error.c index f04943e..c94dbd7 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -83,76 +83,49 @@ static char *get_html_file (unsigned int errornum) return (val); } +static void varsubst_sendline(struct conn_s *connptr, regex_t *re, char *p) { + int fd = connptr->client_fd; + while(*p) { + regmatch_t match; + char varname[32+1], *varval; + size_t l; + int st = regexec(re, p, 1, &match, 0); + if(st == 0) { + if(match.rm_so > 0) safe_write(fd, p, match.rm_so); + l = match.rm_eo - match.rm_so; + assert(l>2 && l-2 < sizeof(varname)); + p += match.rm_so; + memcpy(varname, p+1, l-2); + varname[l-2] = 0; + varval = lookup_variable(connptr->error_variables, varname); + if(varval) write_message(fd, "%s", varval); + else if(varval && !*varval) write_message(fd, "(unknown)"); + else safe_write(fd, p, l); + p += l; + } else { + write_message(fd, "%s", p); + break; + } + } +} + /* * Send an already-opened file to the client with variable substitution. */ int send_html_file (FILE *infile, struct conn_s *connptr) { - char *inbuf; - char *varstart = NULL; - char *p; - const char *varval; - int in_variable = 0; - int r = 0; - - inbuf = (char *) safemalloc (4096); - - while (fgets (inbuf, 4096, infile) != NULL) { - for (p = inbuf; *p; p++) { - switch (*p) { - case '}': - if (in_variable) { - *p = '\0'; - varval = (const char *) - lookup_variable (connptr->error_variables, - varstart); - if (!varval || !varval[0]) - varval = "(unknown)"; - r = write_message (connptr->client_fd, - "%s", varval); - in_variable = 0; - } else { - r = write_message (connptr->client_fd, - "%c", *p); - } - - break; - - case '{': - /* a {{ will print a single {. If we are NOT - * already in a { variable, then proceed with - * setup. If we ARE already in a { variable, - * this code will fallthrough to the code that - * just dumps a character to the client fd. - */ - if (!in_variable) { - varstart = p + 1; - in_variable++; - } else - in_variable = 0; - - /* FALL THROUGH */ - default: - if (!in_variable) { - r = write_message (connptr->client_fd, - "%c", *p); - } - } - - if (r) - break; - } + regex_t re; + char *inbuf = safemalloc (4096); + (void) regcomp(&re, "{[a-z]\\{1,32\\}}", 0); - if (r) - break; - - in_variable = 0; + while (fgets (inbuf, 4096, infile)) { + varsubst_sendline(connptr, &re, inbuf); } + regfree (&re); safefree (inbuf); - - return r; + return 1; } int send_http_headers (struct conn_s *connptr, int code, const char *message) |