NetSurf
cache-control.c
Go to the documentation of this file.
1/*
2 * Copyright 2019 John-Mark Bell <jmb@netsurf-browser.org>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <limits.h>
20#include <stdlib.h>
21
22#include "utils/corestrings.h"
23#include "utils/http.h"
24
25#include "utils/http/generics.h"
27
28/**
29 * Representation of a Cache-Control
30 */
32 uint32_t max_age; /**< Max age (delta seconds) */
33 bool max_age_valid; /**< Whether max-age is valid */
34 bool no_cache; /**< Whether caching is forbidden */
35 bool no_store; /**< Whether persistent caching is forbidden */
36};
37
38/**
39 * Representation of a directive
40 */
41typedef struct http_directive {
43
44 lwc_string *name; /**< Parameter name */
45 lwc_string *value; /**< Parameter value (optional) */
47
48
50{
51 lwc_string_unref(self->name);
52 lwc_string_unref(self->value);
53 free(self);
54}
55
56static nserror http__parse_directive(const char **input,
58{
59 const char *pos = *input;
60 lwc_string *name;
61 lwc_string *value = NULL;
62 http_directive *directive;
63 nserror error;
64
65 /* token [ "=" ( token | quoted-string ) ] */
66
67 error = http__parse_token(&pos, &name);
68 if (error != NSERROR_OK)
69 return error;
70
71 http__skip_LWS(&pos);
72
73 if (*pos == '=') {
74 pos++;
75
76 http__skip_LWS(&pos);
77
78 if (*pos == '"')
79 error = http__parse_quoted_string(&pos, &value);
80 else
81 error = http__parse_token(&pos, &value);
82
83 if (error != NSERROR_OK) {
84 lwc_string_unref(name);
85 return error;
86 }
87 }
88
89 directive = malloc(sizeof(*directive));
90 if (directive == NULL) {
91 lwc_string_unref(value);
92 lwc_string_unref(name);
93 return NSERROR_NOMEM;
94 }
95
97 directive->name = name;
98 directive->value = value;
99
100 *result = directive;
101 *input = pos;
102
103 return NSERROR_OK;
104}
105
107{
109}
110
112 lwc_string *name, lwc_string **value)
113{
114 bool match;
115
116 while (list != NULL) {
117 if (lwc_string_caseless_isequal(name, list->name,
118 &match) == lwc_error_ok && match)
119 break;
120
121 list = (http_directive *) list->base.next;
122 }
123
124 if (list == NULL)
125 return NSERROR_NOT_FOUND;
126
127 if (list->value != NULL) {
128 *value = lwc_string_ref(list->value);
129 } else {
130 *value = NULL;
131 }
132
133 return NSERROR_OK;
134}
135
137 const http_directive *cur,
138 lwc_string **name, lwc_string **value)
139{
140 if (cur == NULL)
141 return NULL;
142
143 *name = lwc_string_ref(cur->name);
144 if (cur->value != NULL) {
145 *value = lwc_string_ref(cur->value);
146 } else {
147 *value = NULL;
148 }
149
150 return (http_directive *) cur->base.next;
151}
152
153static uint32_t count(const http_directive *list, lwc_string *key)
154{
155 uint32_t count = 0;
156 bool match;
157
158 while (list != NULL) {
159 if (lwc_string_caseless_isequal(key, list->name,
160 &match) == lwc_error_ok && match) {
161 count++;
162 }
163
164 list = (http_directive *) list->base.next;
165 }
166
167 return count;
168}
169
170static bool check_duplicates(const http_directive *directives)
171{
172 bool result = true;
173 const http_directive *key = directives;
174
175 if (key == NULL) {
176 /* No directives, so there can't be any duplicates */
177 return true;
178 }
179
180 do {
181 lwc_string *name = NULL, *value = NULL;
182
183 key = http_directive_list_iterate(key, &name, &value);
184
185 result &= (count(directives, name) == 1);
186
187 lwc_string_unref(name);
188 lwc_string_unref(value);
189 } while (key != NULL);
190
191 return result;
192}
193
194static nserror parse_max_age(lwc_string *value, uint32_t *result)
195{
196 const char *pos = lwc_string_data(value);
197 const char *end = pos + lwc_string_length(value);
198 uint32_t val = 0;
199
200 /* 1*DIGIT */
201
202 if (pos == end) {
203 /* Blank value */
204 return NSERROR_NOT_FOUND;
205 }
206
207 while (pos < end) {
208 if ('0' <= *pos && *pos <= '9') {
209 uint32_t nv = val * 10 + (*pos - '0');
210 if (nv < val) {
211 val = UINT_MAX;
212 } else {
213 val = nv;
214 }
215 } else {
216 /* Non-digit */
217 return NSERROR_NOT_FOUND;
218 }
219
220 pos++;
221 }
222
223 *result = val;
224
225 return NSERROR_OK;
226}
227
228/* See cache-control.h for documentation */
229nserror http_parse_cache_control(const char *header_value,
231{
232 const char *pos = header_value;
234 http_directive *first = NULL;
235 http_directive *directives = NULL;
236 lwc_string *value_str = NULL;
237 uint32_t max_age = 0;
238 bool max_age_valid = false;
239 bool no_cache = false;
240 bool no_store = false;
241 nserror error;
242
243 /* 1#cache-directive */
244
245 http__skip_LWS(&pos);
246
247 error = http__parse_directive(&pos, &first);
248 if (error != NSERROR_OK) {
249 return error;
250 }
251
252 http__skip_LWS(&pos);
253
254 if (*pos == ',') {
255 error = http__item_list_parse(&pos,
256 http__parse_directive, first, &directives);
257 if (error != NSERROR_OK) {
258 if (directives != NULL) {
259 http_directive_list_destroy(directives);
260 }
261 return error;
262 }
263 } else {
264 directives = first;
265 }
266
267 /* Each directive must only appear once */
268 if (check_duplicates(directives) == false) {
269 http_directive_list_destroy(directives);
270 return NSERROR_NOT_FOUND;
271 }
272
273 /* Find max-age */
274 error = http_directive_list_find_item(directives,
275 corestring_lwc_max_age, &value_str);
276 if (error == NSERROR_OK && value_str != NULL) {
277 error = parse_max_age(value_str, &max_age);
278 max_age_valid = (error == NSERROR_OK);
279 lwc_string_unref(value_str);
280 }
281
282 /* Find no-cache */
283 error = http_directive_list_find_item(directives,
284 corestring_lwc_no_cache, &value_str);
285 if (error == NSERROR_OK) {
286 no_cache = true;
287 lwc_string_unref(value_str);
288 }
289
290 /* Find no-store */
291 error = http_directive_list_find_item(directives,
292 corestring_lwc_no_store, &value_str);
293 if (error == NSERROR_OK) {
294 no_store = true;
295 lwc_string_unref(value_str);
296 }
297
298 http_directive_list_destroy(directives);
299
300 cc = malloc(sizeof(*cc));
301 if (cc == NULL) {
302 return NSERROR_NOMEM;
303 }
304
305 cc->max_age = max_age;
306 cc->max_age_valid = max_age_valid;
307 cc->no_cache = no_cache;
308 cc->no_store = no_store;
309
310 *result = cc;
311
312 return NSERROR_OK;
313}
314
315/* See cache-control.h for documentation */
317{
318 free(victim);
319}
320
321/* See cache-control.h for documentation */
323{
324 return cc->max_age_valid;
325}
326
327/* See cache-control.h for documentation */
329{
330 return cc->max_age;
331}
332
333/* See cache-control.h for documentation */
335{
336 return cc->no_cache;
337}
338
339/* See cache-control.h for documentation */
341{
342 return cc->no_store;
343}
STATIC char result[100]
Definition: arexx.c:77
struct http_directive http_directive
Representation of a directive.
static bool check_duplicates(const http_directive *directives)
static void http_destroy_directive(http_directive *self)
Definition: cache-control.c:49
uint32_t http_cache_control_max_age(http_cache_control *cc)
Get the value of a cache control's max-age.
static nserror parse_max_age(lwc_string *value, uint32_t *result)
static void http_directive_list_destroy(http_directive *list)
bool http_cache_control_has_max_age(http_cache_control *cc)
Determine if a valid max-age directive is present.
nserror http_parse_cache_control(const char *header_value, http_cache_control **result)
Parse an HTTP Cache-Control header value.
static nserror http__parse_directive(const char **input, http_directive **result)
Definition: cache-control.c:56
static uint32_t count(const http_directive *list, lwc_string *key)
static const http_directive * http_directive_list_iterate(const http_directive *cur, lwc_string **name, lwc_string **value)
void http_cache_control_destroy(http_cache_control *victim)
Destroy a cache_control object.
bool http_cache_control_no_cache(http_cache_control *cc)
Get the value of a cache control's no-cache flag.
bool http_cache_control_no_store(http_cache_control *cc)
Get the value of a cache control's no-store flag.
static nserror http_directive_list_find_item(const http_directive *list, lwc_string *name, lwc_string **value)
Useful interned string pointers (interface).
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_NOT_FOUND
Requested item not found.
Definition: errors.h:34
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_OK
No error.
Definition: errors.h:30
#define http__item_list_destroy(l)
Definition: generics.h:46
#define HTTP__ITEM_INIT(item, n, f)
Definition: generics.h:35
#define http__item_list_parse(i, p, f, r)
Definition: generics.h:52
HTTP header parsing functions.
nserror http__parse_token(const char **input, lwc_string **value)
Parse an HTTP token.
Definition: primitives.c:68
void http__skip_LWS(const char **input)
Skip past linear whitespace in input.
Definition: primitives.c:31
nserror http__parse_quoted_string(const char **input, lwc_string **value)
Parse an HTTP quoted-string.
Definition: primitives.c:102
Representation of an item.
Definition: generics.h:29
struct http__item * next
Next item in list, or NULL.
Definition: generics.h:30
Representation of a Cache-Control.
Definition: cache-control.c:31
bool no_store
Whether persistent caching is forbidden.
Definition: cache-control.c:35
bool max_age_valid
Whether max-age is valid.
Definition: cache-control.c:33
uint32_t max_age
Max age (delta seconds)
Definition: cache-control.c:32
bool no_cache
Whether caching is forbidden.
Definition: cache-control.c:34
Representation of a directive.
Definition: cache-control.c:41
lwc_string * value
Parameter value (optional)
Definition: cache-control.c:45
lwc_string * name
Parameter name.
Definition: cache-control.c:44
http__item base
Definition: cache-control.c:42