#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "glog.h"
#include "gmem.h"
#include "header.h"
#define HEADER_IS_CLASS(h, v) (h->order >= v && h->order < (v+100))
#define HEADER_IS_GENERAL(h) HEADER_IS_CLASS(h, HEADER_TYPE_GENERAL)
#define HEADER_IS_REQUEST(h) HEADER_IS_CLASS(h, HEADER_TYPE_REQUEST)
#define HEADER_IS_RESPONSE(h) HEADER_IS_CLASS(h, HEADER_TYPE_RESPONSE)
#define HEADER_IS_ENTITY(h) HEADER_IS_CLASS(h, HEADER_TYPE_ENTITY)
/*
* List of all standard headers, with the correct ordering for them.
*/
static Header standard_headers[] = {
/* general headers */
{ 100, "Cache-Control" },
{ 101, "Connection" },
{ 102, "Date" },
{ 103, "Pragma" },
{ 104, "Trailer" },
{ 105, "Transfer-Encoding" },
{ 106, "Upgrade" },
{ 107, "Via" },
{ 108, "Warning" },
/* request headers */
{ 200, "Accept" },
{ 201, "Accept-Charset" },
{ 202, "Accept-Encoding" },
{ 203, "Accept-Language" },
{ 204, "Authorization" },
{ 205, "Expect" },
{ 206, "From" },
{ 207, "Host" },
{ 208, "If-Match" },
{ 209, "If-Modified-Since" },
{ 210, "If-None-Match" },
{ 211, "If-Range" },
{ 212, "If-Unmodified-Since" },
{ 213, "Max-Forwards" },
{ 214, "Proxy-Authorization" },
{ 215, "Range" },
{ 216, "Referer" },
{ 217, "TE" },
{ 218, "User-Agent" },
/* response headers */
{ 300, "Accept-Ranges" },
{ 301, "Age" },
{ 302, "ETag" },
{ 303, "Location" },
{ 304, "Proxy-Authenticate" },
{ 305, "Retry-After" },
{ 306, "Server" },
{ 307, "Vary" },
{ 308, "WWW-Authenticate" },
/* entity headers */
{ 400, "Allow" },
{ 401, "Content-Encoding" },
{ 402, "Content-Language" },
{ 403, "Content-Length" },
{ 404, "Content-Location" },
{ 405, "Content-MD5" },
{ 406, "Content-Range" },
{ 407, "Content-Type" },
{ 408, "Expires" },
{ 409, "Last-Modified" },
};
static int standard_headers_size = sizeof(standard_headers) / sizeof(standard_headers[0]);
static int normalise(char* buf, const char* str);
Header* header_create(const char* name) {
int l;
Header* h = 0;
GMEM_NEW(h, Header*, sizeof(Header));
h->order = HEADER_TYPE_NONE;
l = strlen(name) + 1;
GMEM_NEW(h->name, char*, l);
normalise(h->name, name);
GLOG(("=C= Created header [%s] => [%s]", name, h->name));
return h;
}
Header* header_clone(Header* header) {
Header *h;
if (header->order != HEADER_TYPE_NONE) {
return header;
}
h = header_create(header->name);
return h;
}
void header_destroy(Header* header) {
if (header->order != HEADER_TYPE_NONE) {
return;
}
GMEM_DELSTR(header->name, -1);
GMEM_DEL(header, Header*, sizeof(Header));
}
#define CONVERT(c) c == '_' ? '-' : isupper(c) ? tolower(c) : c
/* TODO: this could probably made faster if we precomputed the CONVERTed */
/* values instead of doing it over and over again... */
int header_compare(const char* n1, const char* n2) {
int p = 0;
char c1;
char c2;
while (1) {
if (n1[p] == '\0' || n2[p] == '\0') {
break;
}
c1 = CONVERT(n1[p]);
c2 = CONVERT(n2[p]);
if (c1 < c2) {
return -1;
}
if (c1 > c2) {
return +1;
}
++p;
}
if (n1[p] == '\0' && n2[p] != '\0') {
return -1;
}
if (n1[p] != '\0' && n2[p] == '\0') {
return +1;
}
return 0;
}
int header_matches_type_or_name(const Header* h, int type, const char* name) {
int cmp;
if (type != HEADER_TYPE_NONE && !HEADER_IS_CLASS(h, type)) {
return 0;
}
cmp = header_compare(name, h->name);
/* GLOG(("=C= compare [%s] & [%s] => %d", name, h->name, cmp)); */
return cmp == 0;
}
Header* header_lookup_standard(int type, const char* name) {
int j;
for (j = 0; j < standard_headers_size; ++j) {
Header* h = &standard_headers[j];
if (header_matches_type_or_name(h, type, name)) {
return h;
}
}
return 0;
}
void header_dump(const Header* h, FILE* fp) {
fprintf(fp, "[%p", h);
if (h) {
fprintf(fp, "|%3d|%s", h->order, h->name);
}
fprintf(fp, "]\n");
fflush(fp);
}
int header_is_entity(const Header* h) {
const char* start = "content-";
int j;
if (HEADER_IS_ENTITY(h)) {
GLOG(("=C= header [%s] is entity (QUICK)", h->name));
return 1;
}
if (HEADER_IS_GENERAL(h) ||
HEADER_IS_REQUEST(h) ||
HEADER_IS_RESPONSE(h)) {
GLOG(("=C= header [%s] is not entity (QUICK)", h->name));
return 0;
}
for (j = 0; start[j] != 0; ++j) {
if (h->name[j] == '\0') {
GLOG(("=C= header [%s] is not entity (EOS)", h->name));
return 0;
}
if (tolower(h->name[j]) != start[j]) {
GLOG(("=C= header [%s] is not entity (DIFF)", h->name));
return 0;
}
}
GLOG(("=C= header [%s] is entity (CMP)", h->name));
return 1;
}
static int normalise(char* buf, const char* str) {
int word = 0;
int j = 0;
for (j = 0; str[j] != '\0'; ++j) {
if (isalpha(str[j])) {
if (word) {
buf[j] = tolower(str[j]);
} else {
buf[j] = toupper(str[j]);
word = 1;
}
} else {
buf[j] = str[j] == '_' ? '-' : str[j];
word = 0;
}
}
buf[j] = '\0';
return j;
}