[multiple] etag.[ch] -> http_etag.[ch]; better imp
more efficient implementation of HTTP ETag generation and comparison modify dekhash() to take hash value to allow for incremental hashingmaster
parent
91e94f4875
commit
b700a8ca09
@ -1,183 +0,0 @@
|
||||
#include "first.h"
|
||||
|
||||
#include "algo_md.h"
|
||||
#include "buffer.h"
|
||||
#include "etag.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
int etag_is_equal(const buffer *etag, const char *line, int weak_ok) {
|
||||
enum {
|
||||
START = 0,
|
||||
CHECK,
|
||||
CHECK_QUOTED,
|
||||
SKIP,
|
||||
SKIP_QUOTED,
|
||||
TAIL
|
||||
} state = START;
|
||||
|
||||
const char *current;
|
||||
const char *tok_start;
|
||||
const char *tok = NULL;
|
||||
int matched;
|
||||
|
||||
if ('*' == line[0] && '\0' == line[1]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!etag || buffer_string_is_empty(etag)) return 0;
|
||||
tok_start = etag->ptr;
|
||||
|
||||
if ('W' == tok_start[0]) {
|
||||
if (!weak_ok || '/' != tok_start[1]) return 0; /* bad etag */
|
||||
tok_start = tok_start + 2;
|
||||
}
|
||||
|
||||
if ('"' != tok_start[0]) return 0; /* bad etag */
|
||||
/* we start comparing after the first '"' */
|
||||
++tok_start;
|
||||
|
||||
for (current = line; *current; ++current) {
|
||||
switch (state) {
|
||||
case START:
|
||||
/* wait for etag to start; ignore whitespace and ',' */
|
||||
switch (*current) {
|
||||
case 'W':
|
||||
/* weak etag always starts with 'W/"' */
|
||||
if ('/' != *++current) return 0; /* bad etag list */
|
||||
if ('"' != *++current) return 0; /* bad etag list */
|
||||
if (!weak_ok) {
|
||||
state = SKIP;
|
||||
} else {
|
||||
state = CHECK;
|
||||
tok = tok_start;
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
/* strong etag starts with '"' */
|
||||
state = CHECK;
|
||||
tok = tok_start;
|
||||
break;
|
||||
case ' ':
|
||||
case ',':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
default:
|
||||
return 0; /* bad etag list */
|
||||
}
|
||||
break;
|
||||
case CHECK:
|
||||
/* compare etags (after the beginning '"')
|
||||
* quoted-pairs must match too (i.e. quoted in both strings):
|
||||
* > (RFC 2616:) both validators MUST be identical in every way
|
||||
*/
|
||||
matched = *tok && *tok == *current;
|
||||
++tok;
|
||||
switch (*current) {
|
||||
case '\\':
|
||||
state = matched ? CHECK_QUOTED : SKIP_QUOTED;
|
||||
break;
|
||||
case '"':
|
||||
if (*tok) {
|
||||
/* bad etag - string should end after '"' */
|
||||
return 0;
|
||||
}
|
||||
if (matched) {
|
||||
/* matching etag: strings were equal */
|
||||
return 1;
|
||||
}
|
||||
|
||||
state = TAIL;
|
||||
break;
|
||||
default:
|
||||
if (!matched) {
|
||||
/* strings not matching, skip remainder of etag */
|
||||
state = SKIP;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CHECK_QUOTED:
|
||||
if (!*tok || *tok != *current) {
|
||||
/* strings not matching, skip remainder of etag */
|
||||
state = SKIP;
|
||||
break;
|
||||
}
|
||||
++tok;
|
||||
state = CHECK;
|
||||
break;
|
||||
case SKIP:
|
||||
/* wait for final (not quoted) '"' */
|
||||
switch (*current) {
|
||||
case '\\':
|
||||
state = SKIP_QUOTED;
|
||||
break;
|
||||
case '"':
|
||||
state = TAIL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SKIP_QUOTED:
|
||||
state = SKIP;
|
||||
break;
|
||||
case TAIL:
|
||||
/* search for ',', ignore white space */
|
||||
switch (*current) {
|
||||
case ',':
|
||||
state = START;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
default:
|
||||
return 0; /* bad etag list */
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* no matching etag found */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int etag_create(buffer *etag, const struct stat *st, int flags) {
|
||||
if (0 == flags) return 0;
|
||||
|
||||
buffer_clear(etag);
|
||||
|
||||
if (flags & ETAG_USE_INODE) {
|
||||
buffer_append_int(etag, st->st_ino);
|
||||
buffer_append_string_len(etag, CONST_STR_LEN("-"));
|
||||
}
|
||||
|
||||
if (flags & ETAG_USE_SIZE) {
|
||||
buffer_append_int(etag, st->st_size);
|
||||
buffer_append_string_len(etag, CONST_STR_LEN("-"));
|
||||
}
|
||||
|
||||
if (flags & ETAG_USE_MTIME) {
|
||||
buffer_append_int(etag, st->st_mtime);
|
||||
#ifdef st_mtime /* use high-precision timestamp if available */
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
buffer_append_int(etag, st->st_mtimespec.tv_nsec);
|
||||
#else
|
||||
buffer_append_int(etag, st->st_mtim.tv_nsec);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
etag_mutate (buffer * const mut, const buffer * const etag) {
|
||||
/* mut and etag may be the same, so calculate hash before modifying mut */
|
||||
const uint32_t h = dekhash(CONST_BUF_LEN(etag));
|
||||
buffer_copy_string_len(mut, CONST_STR_LEN("\""));
|
||||
buffer_append_int(mut, h);
|
||||
buffer_append_string_len(mut, CONST_STR_LEN("\""));
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#ifndef ETAG_H
|
||||
#define ETAG_H
|
||||
#include "first.h"
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
#ifdef _AIX
|
||||
#include <sys/stat.h>
|
||||
#else
|
||||
struct stat; /* declaration */
|
||||
#endif
|
||||
|
||||
typedef enum { ETAG_USE_INODE = 1, ETAG_USE_MTIME = 2, ETAG_USE_SIZE = 4 } etag_flags_t;
|
||||
|
||||
int etag_is_equal(const buffer *etag, const char *matches, int weak_ok);
|
||||
int etag_create(buffer *etag, const struct stat *st, int flags);
|
||||
void etag_mutate(buffer *mut, const buffer *etag);
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* http_etag - HTTP ETag manipulation
|
||||
*
|
||||
* Copyright(c) 2015,2020 Glenn Strauss gstrauss()gluelogic.com All rights reserved
|
||||
* License: BSD 3-clause (same as lighttpd)
|
||||
*/
|
||||
#include "first.h"
|
||||
|
||||
#include "http_etag.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "algo_md.h"
|
||||
#include "buffer.h"
|
||||
|
||||
int
|
||||
http_etag_matches (const buffer * const etag, const char *s, const int weak_ok)
|
||||
{
|
||||
if ('*' == s[0] && '\0' == s[1]) return 1;
|
||||
if (buffer_string_is_empty(etag)) return 0;
|
||||
|
||||
uint32_t etag_sz = buffer_string_length(etag);
|
||||
const char *etag_ptr = etag->ptr;
|
||||
|
||||
if (etag_ptr[0] == 'W' && etag_ptr[1] == '/') {
|
||||
if (!weak_ok) return 0;
|
||||
etag_ptr += 2;
|
||||
etag_sz -= 2;
|
||||
}
|
||||
|
||||
while (*s) {
|
||||
while (*s == ' ' || *s == '\t' || *s == ',') ++s;
|
||||
if (s[0] == 'W' && s[1] == '/' ? (s+=2, weak_ok) : 1) {
|
||||
if (0 == strncmp(s, etag_ptr, etag_sz) || *s == '*') {
|
||||
s += (*s != '*' ? etag_sz : 1);
|
||||
if (*s == '\0' || *s == ' ' || *s == '\t' || *s == ',')
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
while (*s != '\0' && *s != ',') ++s;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
http_etag_remix (buffer * const etag, const char * const str, const uint32_t len)
|
||||
{
|
||||
uint32_t h = dekhash(str, len, len); /*(pass len as initial hash value)*/
|
||||
uint32_t elen = buffer_string_length(etag);
|
||||
if (elen > 2) /*(expect "..." if set)*/
|
||||
h = dekhash(etag->ptr+1, elen-2, h);
|
||||
buffer_string_set_length(etag, 1);
|
||||
etag->ptr[0] = '\"';
|
||||
buffer_append_int(etag, h);
|
||||
buffer_append_string_len(etag, CONST_STR_LEN("\""));
|
||||
}
|
||||
|
||||
void
|
||||
http_etag_create (buffer * const etag, const struct stat * const st, const int flags)
|
||||
{
|
||||
if (0 == flags) return;
|
||||
|
||||
uint64_t x[4];
|
||||
uint32_t len = 0;
|
||||
|
||||
if (flags & ETAG_USE_INODE)
|
||||
x[len++] = (uint64_t)st->st_ino;
|
||||
|
||||
if (flags & ETAG_USE_SIZE)
|
||||
x[len++] = (uint64_t)st->st_size;
|
||||
|
||||
if (flags & ETAG_USE_MTIME) {
|
||||
x[len++] = (uint64_t)st->st_mtime;
|
||||
#ifdef st_mtime /* use high-precision timestamp if available */
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
x[len++] = (uint64_t)st->st_mtimespec.tv_nsec;
|
||||
#else
|
||||
x[len++] = (uint64_t)st->st_mtim.tv_nsec;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
buffer_clear(etag);
|
||||
http_etag_remix(etag, (char *)x, len << 3);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* http_etag - HTTP ETag manipulation
|
||||
*
|
||||
* Copyright(c) 2015,2020 Glenn Strauss gstrauss()gluelogic.com All rights reserved
|
||||
* License: BSD 3-clause (same as lighttpd)
|
||||
*/
|
||||
#ifndef INCLUDED_HTTP_ETAG_H
|
||||
#define INCLUDED_HTTP_ETAG_H
|
||||
#include "first.h"
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
#ifdef _AIX
|
||||
#include <sys/stat.h>
|
||||
#else
|
||||
struct stat; /* declaration */
|
||||
#endif
|
||||
|
||||
typedef enum { ETAG_USE_INODE = 1, ETAG_USE_MTIME = 2, ETAG_USE_SIZE = 4 } etag_flags_t;
|
||||
|
||||
__attribute_pure__
|
||||
int http_etag_matches (const buffer *etag, const char *matches, int weak_ok);
|
||||
|
||||
void http_etag_create (buffer *etag, const struct stat *st, int flags);
|
||||
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue