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 if (self->value != NULL) {
53 lwc_string_unref(self->value);
54 }
55 free(self);
56}
57
58static nserror http__parse_directive(const char **input,
60{
61 const char *pos = *input;
62 lwc_string *name;
63 lwc_string *value = NULL;
64 http_directive *directive;
65 nserror error;
66
67 /* token [ "=" ( token | quoted-string ) ] */
68
69 error = http__parse_token(&pos, &name);
70 if (error != NSERROR_OK)
71 return error;
72
73 http__skip_LWS(&pos);
74
75 if (*pos == '=') {
76 pos++;
77
78 http__skip_LWS(&pos);
79
80 if (*pos == '"')
81 error = http__parse_quoted_string(&pos, &value);
82 else
83 error = http__parse_token(&pos, &value);
84
85 if (error != NSERROR_OK) {
86 lwc_string_unref(name);
87 return error;
88 }
89 }
90
91 directive = malloc(sizeof(*directive));
92 if (directive == NULL) {
93 if (value != NULL) {
94 lwc_string_unref(value);
95 }
96 lwc_string_unref(name);
97 return NSERROR_NOMEM;
98 }
99
100 HTTP__ITEM_INIT(directive, NULL, http_destroy_directive);
101 directive->name = name;
102 directive->value = value;
103
104 *result = directive;
105 *input = pos;
106
107 return NSERROR_OK;
108}
109
111{
113}
114
116 lwc_string *name, lwc_string **value)
117{
118 bool match;
119
120 while (list != NULL) {
121 if (lwc_string_caseless_isequal(name, list->name,
122 &match) == lwc_error_ok && match)
123 break;
124
125 list = (http_directive *) list->base.next;
126 }
127
128 if (list == NULL)
129 return NSERROR_NOT_FOUND;
130
131 if (list->value != NULL) {
132 *value = lwc_string_ref(list->value);
133 } else {
134 *value = NULL;
135 }
136
137 return NSERROR_OK;
138}
139
141 const http_directive *cur,
142 lwc_string **name, lwc_string **value)
143{
144 if (cur == NULL)
145 return NULL;
146
147 *name = lwc_string_ref(cur->name);
148 if (cur->value != NULL) {
149 *value = lwc_string_ref(cur->value);
150 } else {
151 *value = NULL;
152 }
153
154 return (http_directive *) cur->base.next;
155}
156
157static uint32_t count(const http_directive *list, lwc_string *key)
158{
159 uint32_t count = 0;
160 bool match;
161
162 while (list != NULL) {
163 if (lwc_string_caseless_isequal(key, list->name,
164 &match) == lwc_error_ok && match) {
165 count++;
166 }
167
168 list = (http_directive *) list->base.next;
169 }
170
171 return count;
172}
173
174static bool check_duplicates(const http_directive *directives)
175{
176 bool result = true;
177 const http_directive *key = directives;
178
179 if (key == NULL) {
180 /* No directives, so there can't be any duplicates */
181 return true;
182 }
183
184 do {
185 lwc_string *name = NULL, *value = NULL;
186
187 key = http_directive_list_iterate(key, &name, &value);
188
189 result &= (count(directives, name) == 1);
190
191 lwc_string_unref(name);
192 if (value != NULL) {
193 lwc_string_unref(value);
194 }
195 } while (key != NULL);
196
197 return result;
198}
199
200static nserror parse_max_age(lwc_string *value, uint32_t *result)
201{
202 const char *pos = lwc_string_data(value);
203 const char *end = pos + lwc_string_length(value);
204 uint32_t val = 0;
205
206 /* 1*DIGIT */
207
208 if (pos == end) {
209 /* Blank value */
210 return NSERROR_NOT_FOUND;
211 }
212
213 while (pos < end) {
214 if ('0' <= *pos && *pos <= '9') {
215 uint32_t nv = val * 10 + (*pos - '0');
216 if (nv < val) {
217 val = UINT_MAX;
218 } else {
219 val = nv;
220 }
221 } else {
222 /* Non-digit */
223 return NSERROR_NOT_FOUND;
224 }
225
226 pos++;
227 }
228
229 *result = val;
230
231 return NSERROR_OK;
232}
233
234/* See cache-control.h for documentation */
235nserror http_parse_cache_control(const char *header_value,
237{
238 const char *pos = header_value;
240 http_directive *first = NULL;
241 http_directive *directives = NULL;
242 lwc_string *value_str = NULL;
243 uint32_t max_age = 0;
244 bool max_age_valid = false;
245 bool no_cache = false;
246 bool no_store = false;
247 nserror error;
248
249 /* 1#cache-directive */
250
251 http__skip_LWS(&pos);
252
253 error = http__parse_directive(&pos, &first);
254 if (error != NSERROR_OK) {
255 return error;
256 }
257
258 http__skip_LWS(&pos);
259
260 if (*pos == ',') {
261 error = http__item_list_parse(&pos,
262 http__parse_directive, first, &directives);
263 if (error != NSERROR_OK) {
264 if (directives != NULL) {
265 http_directive_list_destroy(directives);
266 }
267 return error;
268 }
269 } else {
270 directives = first;
271 }
272
273 /* Each directive must only appear once */
274 if (check_duplicates(directives) == false) {
275 http_directive_list_destroy(directives);
276 return NSERROR_NOT_FOUND;
277 }
278
279 /* Find max-age */
280 error = http_directive_list_find_item(directives,
281 corestring_lwc_max_age, &value_str);
282 if (error == NSERROR_OK && value_str != NULL) {
283 error = parse_max_age(value_str, &max_age);
284 max_age_valid = (error == NSERROR_OK);
285 lwc_string_unref(value_str);
286 }
287
288 /* Find no-cache */
289 error = http_directive_list_find_item(directives,
290 corestring_lwc_no_cache, &value_str);
291 if (error == NSERROR_OK) {
292 no_cache = true;
293 if (value_str != NULL) {
294 lwc_string_unref(value_str);
295 }
296 }
297
298 /* Find no-store */
299 error = http_directive_list_find_item(directives,
300 corestring_lwc_no_store, &value_str);
301 if (error == NSERROR_OK) {
302 no_store = true;
303 if (value_str != NULL) {
304 lwc_string_unref(value_str);
305 }
306 }
307
308 http_directive_list_destroy(directives);
309
310 cc = malloc(sizeof(*cc));
311 if (cc == NULL) {
312 return NSERROR_NOMEM;
313 }
314
315 cc->max_age = max_age;
316 cc->max_age_valid = max_age_valid;
317 cc->no_cache = no_cache;
318 cc->no_store = no_store;
319
320 *result = cc;
321
322 return NSERROR_OK;
323}
324
325/* See cache-control.h for documentation */
327{
328 free(victim);
329}
330
331/* See cache-control.h for documentation */
333{
334 return cc->max_age_valid;
335}
336
337/* See cache-control.h for documentation */
339{
340 return cc->max_age;
341}
342
343/* See cache-control.h for documentation */
345{
346 return cc->no_cache;
347}
348
349/* See cache-control.h for documentation */
351{
352 return cc->no_store;
353}
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:58
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