[core] buffer_append_bs_escaped()

move accesslog_append_escaped() to buffer_append_bs_escaped()
replace buffer_append_string_encoded_json()
master
Glenn Strauss 7 months ago
parent 04f2fcf8f0
commit d22e88b7d9
  1. 145
      src/buffer.c
  2. 10
      src/buffer.h
  3. 61
      src/mod_accesslog.c
  4. 2
      src/mod_dirlisting.c

@ -658,85 +658,6 @@ void buffer_append_string_encoded(buffer * const restrict b, const char * const
}
}
void buffer_append_string_encoded_json(buffer * const restrict b, const char * const restrict s, const size_t len) {
const unsigned char * const restrict ds = (unsigned char *)s;
size_t dlen = 0;
/* calculate space needed for string including encodings */
for (size_t i = 0; i < len; ++i) {
int c = ds[i];
if (c == '"' || c == '\\' || c < 0x20 || c == 0x7f) {
switch (c) {
case '\b':
case '\t':
case '\n':
case '\f':
case '\r':
case '"':
case '\\':
dlen += 2;
break;
default:
dlen += 6; /* \uCCCC */
break;
}
}
else {
++dlen;
}
}
unsigned char * const d = (unsigned char *)buffer_extend(b, dlen);
if (__builtin_expect( (dlen == len), 1)) {/*(short-circuit; nothing to encode)*/
memcpy(d, ds, len);
return;
}
dlen = 0;
for (size_t i = 0; i < len; ++i) {
int c = ds[i];
if (c == '"' || c == '\\' || c < 0x20 || c == 0x7f) {
d[dlen++] = '\\';
switch (c) {
case '\b':
d[dlen++] = 'b';
break;
case '\t':
d[dlen++] = 't';
break;
case '\n':
d[dlen++] = 'n';
break;
case '\f':
d[dlen++] = 'f';
break;
case '\r':
d[dlen++] = 'r';
break;
case '"':
d[dlen++] = '"';
break;
case '\\':
d[dlen++] = '\\';
break;
default:
d[dlen ] = 'u';
d[dlen+1] = '0';
d[dlen+2] = '0';
d[dlen+3] = hex_chars_lc[(c >> 4) & 0x0F];
d[dlen+4] = hex_chars_lc[c & 0x0F];
dlen += 5;
break;
}
}
else {
d[dlen++] = c;
}
}
}
void buffer_append_string_c_escaped(buffer * const restrict b, const char * const restrict s, size_t s_len) {
unsigned char *ds, *d;
size_t d_len, ndx;
@ -794,6 +715,72 @@ void buffer_append_string_c_escaped(buffer * const restrict b, const char * cons
}
void
buffer_append_bs_escaped (buffer * const restrict b,
const char * restrict s, const size_t len,
const buffer_bs_escape_t esc)
{
/* replaces non-printable chars with escaped string
* default: \xHH where HH is the hex representation of the byte
* json: \u00HH where HH is the hex representation of the byte
* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
/* Intended for use escaping string to be surrounded by double-quotes */
/* Performs single pass over string and is optimized for ASCII;
* non-ASCII escaping might be slightly sped up by walking input twice,
* first to calculate escaped length and extend the destination b, and
* second to do the escaping. (This non-ASCII optim is not done here) */
buffer_string_prepare_append(b, len);
for (const char * const end = s+len; s < end; ++s) {
unsigned int c;
const char * const ptr = s;
do {
c = *(const unsigned char *)s;
} while (c >= ' ' && c <= '~' && c != '"' && c != '\\' && ++s < end);
if (s - ptr) buffer_append_string_len(b, ptr, s - ptr);
if (s == end)
return;
/* ('\a', '\v' shortcuts are technically not json-escaping) */
/* ('\0' is also omitted due to the possibility of string corruption if
* the receiver supports decoding octal escapes (\000) and the escaped
* string contains \0 followed by two digits not part of escaping)*/
char *d;
switch (c) {
case '\a':case '\b':case '\t':case '\n':case '\v':case '\f':case '\r':
c = "0000000abtnvfr"[c];
__attribute_fallthrough__
case '"': case '\\':
d = buffer_extend(b, 2);
d[0] = '\\';
d[1] = c;
break;
default:
if (0 == esc) { /* BS_ESCAPE_DEFAULT */
/* non printable char => \xHH */
d = buffer_extend(b, 4);
d[0] = '\\';
d[1] = 'x';
d += 2;
}
else { /* BS_ESCAPE_JSON */
/*(technically do not have to escape DEL (\127) or higher)*/
d = buffer_extend(b, 6);
d[0] = '\\';
d[1] = 'u';
d[2] = '0';
d[3] = '0';
d += 4;
}
d[0] = hex_chars_uc[c >> 4];
d[1] = hex_chars_uc[c & 0xF];
break;
}
}
}
/* decodes url-special-chars inplace.
* replaces non-printable characters with '_'
* (If this is used on a portion of query string, then query string should be

@ -179,12 +179,18 @@ typedef enum {
void buffer_append_string_encoded(buffer * restrict b, const char * restrict s, size_t s_len, buffer_encoding_t encoding);
void buffer_append_string_encoded_json(buffer * restrict b, const char * restrict s, size_t len);
/* escape non-printable characters; simple escapes for \t, \r, \n; fallback to \xCC */
__attribute_nonnull__()
void buffer_append_string_c_escaped(buffer * restrict b, const char * restrict s, size_t s_len);
typedef enum {
BS_ESCAPE_DEFAULT
,BS_ESCAPE_JSON
} buffer_bs_escape_t;
/* escape non-printable chars, '"', '\\', and chars which high bit set */
void buffer_append_bs_escaped (buffer * restrict b, const char * restrict s, size_t len, buffer_bs_escape_t esc);
__attribute_nonnull__()
void buffer_urldecode_path(buffer *b);

@ -422,7 +422,9 @@ SETDEFAULTS_FUNC(mod_accesslog_set_defaults) {
break;
case 4: /* accesslog.escaping */
/* quick parse: 0 == "default", 1 == "json" */
cpv->v.u = (0 == strcmp(cpv->v.b->ptr, "json"));
cpv->v.u = (0 == strcmp(cpv->v.b->ptr, "json"))
? BS_ESCAPE_JSON
: BS_ESCAPE_DEFAULT;
cpv->vtype = T_CONFIG_LOCAL;
break;
default:/* should not happen */
@ -582,60 +584,7 @@ TRIGGER_FUNC(log_access_periodic_flush) {
return HANDLER_GO_ON;
}
static void
accesslog_append_escaped (buffer * const restrict dest,
const char * restrict str,
const uint32_t len, const int esc)
{
/* replaces non-printable chars with escaped string
* default: \xHH where HH is the hex representation of the byte
* json: \u00HH where HH is the hex representation of the byte */
/* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
buffer_string_prepare_append(dest, len);
for (const char * const end = str+len; str < end; ++str) {
unsigned int c;
const char * const ptr = str;
do {
c = *(const unsigned char *)str;
} while (c >= ' ' && c <= '~' && c != '"' && c != '\\' && ++str < end);
if (str - ptr) buffer_append_string_len(dest, ptr, str - ptr);
if (str == end)
return;
char *s;
switch (c) {
case '\a':case '\b':case '\t':case '\n':case '\v':case '\f':case '\r':
c = "0000000abtnvfr"[c];
__attribute_fallthrough__
case '"': case '\\':
s = buffer_extend(dest, 2);
s[0] = '\\';
s[1] = c;
break;
default:
if (!esc) {
/* non printable char => \xHH */
s = buffer_extend(dest, 4);
s[0] = '\\';
s[1] = 'x';
s += 2;
}
else { /* json */
/*(technically do not have to escape DEL (\127) or higher)*/
s = buffer_extend(dest, 6);
s[0] = '\\';
s[1] = 'u';
s[2] = '0';
s[3] = '0';
s += 4;
}
s[0] = "0123456789ABCDEF"[c >> 4];
s[1] = "0123456789ABCDEF"[c & 0xF];
break;
}
}
}
#define accesslog_append_escaped buffer_append_bs_escaped
static void
accesslog_append_buffer (buffer * const restrict dest,
@ -880,7 +829,7 @@ log_access_record_cold (buffer * const b, const request_st * const r,
}
}
static int log_access_record (const request_st * const r, buffer * const b, format_fields * const parsed_format, const int esc) {
static int log_access_record (const request_st * const r, buffer * const b, format_fields * const parsed_format, const buffer_bs_escape_t esc) {
const buffer *vb;
unix_timespec64_t ts = { 0, 0 };
int flush = 0;

@ -1098,7 +1098,7 @@ static int http_read_directory(handler_ctx * const p) {
p->jcomma = 1;
buffer_append_string_len(p->jb, CONST_STR_LEN( "{\"name\":\""));
}
buffer_append_string_encoded_json(p->jb, d_name, dsz);
buffer_append_bs_escaped(p->jb, d_name, dsz, BS_ESCAPE_JSON);
const char *t;
size_t tlen;

Loading…
Cancel
Save