mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-12-22 20:16:54 +03:00
HTTP: add minimal parser for Cache-Control
This commit is contained in:
parent
9893b05b08
commit
04b790643b
@ -144,6 +144,8 @@ CORESTRING_LWC_STRING(_top);
|
||||
CORESTRING_LWC_VALUE(shortcut_icon, "shortcut icon");
|
||||
CORESTRING_LWC_VALUE(slash_, "/");
|
||||
CORESTRING_LWC_VALUE(max_age, "max-age");
|
||||
CORESTRING_LWC_VALUE(no_cache, "no-cache");
|
||||
CORESTRING_LWC_VALUE(no_store, "no-store");
|
||||
|
||||
/* mime types */
|
||||
CORESTRING_LWC_VALUE(multipart_form_data, "multipart/form-data");
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include "utils/errors.h"
|
||||
|
||||
#include "utils/http/cache-control.h"
|
||||
#include "utils/http/content-disposition.h"
|
||||
#include "utils/http/content-type.h"
|
||||
#include "utils/http/strict-transport-security.h"
|
||||
|
@ -1,7 +1,7 @@
|
||||
# http utils sources
|
||||
|
||||
S_HTTP := challenge.c generics.c primitives.c parameter.c \
|
||||
content-disposition.c content-type.c \
|
||||
cache-control.c content-disposition.c content-type.c \
|
||||
strict-transport-security.c www-authenticate.c
|
||||
|
||||
S_HTTP := $(addprefix utils/http/,$(S_HTTP))
|
||||
|
343
utils/http/cache-control.c
Normal file
343
utils/http/cache-control.c
Normal file
@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright 2019 John-Mark Bell <jmb@netsurf-browser.org>
|
||||
*
|
||||
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
||||
*
|
||||
* NetSurf is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* NetSurf is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "utils/corestrings.h"
|
||||
#include "utils/http.h"
|
||||
|
||||
#include "utils/http/generics.h"
|
||||
#include "utils/http/primitives.h"
|
||||
|
||||
/**
|
||||
* Representation of a Cache-Control
|
||||
*/
|
||||
struct http_cache_control {
|
||||
uint32_t max_age; /**< Max age (delta seconds) */
|
||||
bool no_cache; /**< Whether caching is forbidden */
|
||||
bool no_store; /**< Whether persistent caching is forbidden */
|
||||
};
|
||||
|
||||
/**
|
||||
* Representation of a directive
|
||||
*/
|
||||
typedef struct http_directive {
|
||||
http__item base;
|
||||
|
||||
lwc_string *name; /**< Parameter name */
|
||||
lwc_string *value; /**< Parameter value (optional) */
|
||||
} http_directive;
|
||||
|
||||
|
||||
static void http_destroy_directive(http_directive *self)
|
||||
{
|
||||
lwc_string_unref(self->name);
|
||||
if (self->value != NULL) {
|
||||
lwc_string_unref(self->value);
|
||||
}
|
||||
free(self);
|
||||
}
|
||||
|
||||
static nserror http__parse_directive(const char **input,
|
||||
http_directive **result)
|
||||
{
|
||||
const char *pos = *input;
|
||||
lwc_string *name;
|
||||
lwc_string *value = NULL;
|
||||
http_directive *directive;
|
||||
nserror error;
|
||||
|
||||
/* token [ "=" ( token | quoted-string ) ] */
|
||||
|
||||
error = http__parse_token(&pos, &name);
|
||||
if (error != NSERROR_OK)
|
||||
return error;
|
||||
|
||||
http__skip_LWS(&pos);
|
||||
|
||||
if (*pos == '=') {
|
||||
pos++;
|
||||
|
||||
http__skip_LWS(&pos);
|
||||
|
||||
if (*pos == '"')
|
||||
error = http__parse_quoted_string(&pos, &value);
|
||||
else
|
||||
error = http__parse_token(&pos, &value);
|
||||
|
||||
if (error != NSERROR_OK) {
|
||||
lwc_string_unref(name);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
directive = malloc(sizeof(*directive));
|
||||
if (directive == NULL) {
|
||||
if (value != NULL) {
|
||||
lwc_string_unref(value);
|
||||
}
|
||||
lwc_string_unref(name);
|
||||
return NSERROR_NOMEM;
|
||||
}
|
||||
|
||||
HTTP__ITEM_INIT(directive, NULL, http_destroy_directive);
|
||||
directive->name = name;
|
||||
directive->value = value;
|
||||
|
||||
*result = directive;
|
||||
*input = pos;
|
||||
|
||||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
static void http_directive_list_destroy(http_directive *list)
|
||||
{
|
||||
http__item_list_destroy(list);
|
||||
}
|
||||
|
||||
static nserror http_directive_list_find_item(const http_directive *list,
|
||||
lwc_string *name, lwc_string **value)
|
||||
{
|
||||
bool match;
|
||||
|
||||
while (list != NULL) {
|
||||
if (lwc_string_caseless_isequal(name, list->name,
|
||||
&match) == lwc_error_ok && match)
|
||||
break;
|
||||
|
||||
list = (http_directive *) list->base.next;
|
||||
}
|
||||
|
||||
if (list == NULL)
|
||||
return NSERROR_NOT_FOUND;
|
||||
|
||||
if (list->value != NULL) {
|
||||
*value = lwc_string_ref(list->value);
|
||||
} else {
|
||||
*value = NULL;
|
||||
}
|
||||
|
||||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
static const http_directive *http_directive_list_iterate(
|
||||
const http_directive *cur,
|
||||
lwc_string **name, lwc_string **value)
|
||||
{
|
||||
if (cur == NULL)
|
||||
return NULL;
|
||||
|
||||
*name = lwc_string_ref(cur->name);
|
||||
if (cur->value != NULL) {
|
||||
*value = lwc_string_ref(cur->value);
|
||||
} else {
|
||||
*value = NULL;
|
||||
}
|
||||
|
||||
return (http_directive *) cur->base.next;
|
||||
}
|
||||
|
||||
static uint32_t count(const http_directive *list, lwc_string *key)
|
||||
{
|
||||
uint32_t count = 0;
|
||||
bool match;
|
||||
|
||||
while (list != NULL) {
|
||||
if (lwc_string_caseless_isequal(key, list->name,
|
||||
&match) == lwc_error_ok && match) {
|
||||
count++;
|
||||
}
|
||||
|
||||
list = (http_directive *) list->base.next;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static bool check_duplicates(const http_directive *directives)
|
||||
{
|
||||
bool result = true;
|
||||
const http_directive *key = directives;
|
||||
|
||||
if (key == NULL) {
|
||||
/* No directives, so there can't be any duplicates */
|
||||
return true;
|
||||
}
|
||||
|
||||
do {
|
||||
lwc_string *name = NULL, *value = NULL;
|
||||
|
||||
key = http_directive_list_iterate(key, &name, &value);
|
||||
|
||||
result &= (count(directives, name) == 1);
|
||||
|
||||
lwc_string_unref(name);
|
||||
if (value != NULL) {
|
||||
lwc_string_unref(value);
|
||||
}
|
||||
} while (key != NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static nserror parse_max_age(lwc_string *value, uint32_t *result)
|
||||
{
|
||||
const char *pos = lwc_string_data(value);
|
||||
const char *end = pos + lwc_string_length(value);
|
||||
uint32_t val = 0;
|
||||
|
||||
/* 1*DIGIT */
|
||||
|
||||
if (pos == end) {
|
||||
/* Blank value */
|
||||
return NSERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
while (pos < end) {
|
||||
if ('0' <= *pos && *pos <= '9') {
|
||||
uint32_t nv = val * 10 + (*pos - '0');
|
||||
if (nv < val) {
|
||||
val = UINT_MAX;
|
||||
} else {
|
||||
val = nv;
|
||||
}
|
||||
} else {
|
||||
/* Non-digit */
|
||||
return NSERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
*result = val;
|
||||
|
||||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
/* See cache-control.h for documentation */
|
||||
nserror http_parse_cache_control(const char *header_value,
|
||||
http_cache_control **result)
|
||||
{
|
||||
const char *pos = header_value;
|
||||
http_cache_control *cc;
|
||||
http_directive *first = NULL;
|
||||
http_directive *directives = NULL;
|
||||
lwc_string *value_str = NULL;
|
||||
uint32_t max_age = 0;
|
||||
bool no_cache = false;
|
||||
bool no_store = false;
|
||||
nserror error;
|
||||
|
||||
/* 1#cache-directive */
|
||||
|
||||
http__skip_LWS(&pos);
|
||||
|
||||
error = http__parse_directive(&pos, &first);
|
||||
if (error != NSERROR_OK) {
|
||||
return error;
|
||||
}
|
||||
|
||||
http__skip_LWS(&pos);
|
||||
|
||||
if (*pos == ',') {
|
||||
error = http__item_list_parse(&pos,
|
||||
http__parse_directive, first, &directives);
|
||||
if (error != NSERROR_OK) {
|
||||
if (directives != NULL) {
|
||||
http_directive_list_destroy(directives);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
} else {
|
||||
directives = first;
|
||||
}
|
||||
|
||||
/* Each directive must only appear once */
|
||||
if (check_duplicates(directives) == false) {
|
||||
http_directive_list_destroy(directives);
|
||||
return NSERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* Find max-age */
|
||||
error = http_directive_list_find_item(directives,
|
||||
corestring_lwc_max_age, &value_str);
|
||||
if (error == NSERROR_OK && value_str != NULL) {
|
||||
error = parse_max_age(value_str, &max_age);
|
||||
lwc_string_unref(value_str);
|
||||
}
|
||||
|
||||
/* Find no-cache */
|
||||
error = http_directive_list_find_item(directives,
|
||||
corestring_lwc_no_cache, &value_str);
|
||||
if (error == NSERROR_OK) {
|
||||
no_cache = true;
|
||||
if (value_str != NULL) {
|
||||
lwc_string_unref(value_str);
|
||||
}
|
||||
}
|
||||
|
||||
/* Find no-store */
|
||||
error = http_directive_list_find_item(directives,
|
||||
corestring_lwc_no_store, &value_str);
|
||||
if (error == NSERROR_OK) {
|
||||
no_store = true;
|
||||
if (value_str != NULL) {
|
||||
lwc_string_unref(value_str);
|
||||
}
|
||||
}
|
||||
|
||||
http_directive_list_destroy(directives);
|
||||
|
||||
cc = malloc(sizeof(*cc));
|
||||
if (cc == NULL) {
|
||||
return NSERROR_NOMEM;
|
||||
}
|
||||
|
||||
cc->max_age = max_age;
|
||||
cc->no_cache = no_cache;
|
||||
cc->no_store = no_store;
|
||||
|
||||
*result = cc;
|
||||
|
||||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
/* See cache-control.h for documentation */
|
||||
void http_cache_control_destroy(http_cache_control *victim)
|
||||
{
|
||||
free(victim);
|
||||
}
|
||||
|
||||
/* See cache-control.h for documentation */
|
||||
uint32_t http_cache_control_max_age(http_cache_control *cc)
|
||||
{
|
||||
return cc->max_age;
|
||||
}
|
||||
|
||||
/* See cache-control.h for documentation */
|
||||
bool http_cache_control_no_cache(http_cache_control *cc)
|
||||
{
|
||||
return cc->no_cache;
|
||||
}
|
||||
|
||||
/* See cache-control.h for documentation */
|
||||
bool http_cache_control_no_store(http_cache_control *cc)
|
||||
{
|
||||
return cc->no_store;
|
||||
}
|
69
utils/http/cache-control.h
Normal file
69
utils/http/cache-control.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2019 John-Mark Bell <jmb@netsurf-browser.org>
|
||||
*
|
||||
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
||||
*
|
||||
* NetSurf is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* NetSurf is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NETSURF_UTILS_HTTP_CACHE_CONTROL_H_
|
||||
#define NETSURF_UTILS_HTTP_CACHE_CONTROL_H_
|
||||
|
||||
#include <libwapcaplet/libwapcaplet.h>
|
||||
|
||||
typedef struct http_cache_control http_cache_control;
|
||||
|
||||
/**
|
||||
* Parse an HTTP Cache-Control header value
|
||||
*
|
||||
* \param header_value Header value to parse
|
||||
* \param result Pointer to location to receive result
|
||||
* \return NSERROR_OK on success,
|
||||
* NSERROR_NOMEM on memory exhaustion,
|
||||
* appropriate error otherwise
|
||||
*/
|
||||
nserror http_parse_cache_control(const char *header_value,
|
||||
http_cache_control **result);
|
||||
|
||||
/**
|
||||
* Destroy a cache_control object
|
||||
*
|
||||
* \param victim Object to destroy
|
||||
*/
|
||||
void http_cache_control_destroy(http_cache_control *victim);
|
||||
|
||||
/**
|
||||
* Get the value of a cache control's max-age
|
||||
*
|
||||
* \param cc Object to inspect
|
||||
* \return Max age, in delta-seconds
|
||||
*/
|
||||
uint32_t http_cache_control_max_age(http_cache_control *cc);
|
||||
|
||||
/**
|
||||
* Get the value of a cache control's no-cache flag
|
||||
*
|
||||
* \param cc Object to inspect
|
||||
* \return Whether caching is forbidden
|
||||
*/
|
||||
bool http_cache_control_no_cache(http_cache_control *cc);
|
||||
|
||||
/**
|
||||
* Get the value of a cache control's no-store flag
|
||||
*
|
||||
* \param cc Object to inspect
|
||||
* \return Whether persistent caching is forbidden
|
||||
*/
|
||||
bool http_cache_control_no_store(http_cache_control *cc);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user