Line data Source code
1 : // Snap Websites Server -- handle Cache-Control settings
2 : // Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // This program is free software; you can redistribute it and/or modify
5 : // it under the terms of the GNU General Public License as published by
6 : // the Free Software Foundation; either version 2 of the License, or
7 : // (at your option) any later version.
8 : //
9 : // This program is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : // GNU General Public License for more details.
13 : //
14 : // You should have received a copy of the GNU General Public License
15 : // along with this program; if not, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 :
18 : // self
19 : //
20 : #include "snapwebsites/cache_control.h"
21 :
22 : // snapwebsites lib
23 : //
24 : #include "snapwebsites/http_strings.h"
25 : #include "snapwebsites/log.h"
26 :
27 : // snapdev lib
28 : //
29 : #include <snapdev/not_reached.h>
30 :
31 : // boost lib
32 : //
33 : #include <boost/algorithm/string.hpp>
34 :
35 :
36 : // last include
37 : //
38 : #include <snapdev/poison.h>
39 :
40 :
41 : namespace snap
42 : {
43 :
44 :
45 : /** \brief Initialize a cache control object with defaults.
46 : *
47 : * This function initialize this cache control object with the
48 : * defaults and returns.
49 : *
50 : * You may later apply various changes to the cache control data
51 : * using the set_...() functions and the set_cache_info() if you
52 : * have cache control data in the form of a standard HTTP string.
53 : */
54 0 : cache_control_settings::cache_control_settings()
55 : {
56 0 : }
57 :
58 :
59 : /** \brief Initializes a cache control object with the specified info.
60 : *
61 : * This function initialize this cache control object with the defaults
62 : * and then applies the \p info parameters to the controls if \p info is
63 : * not an empty string.
64 : *
65 : * \param[in] info The new cache control information to assign to this object.
66 : * \param[in] internal_setup Whether to allow our special '!' syntax.
67 : *
68 : * \sa set_cache_info()
69 : */
70 0 : cache_control_settings::cache_control_settings(QString const & info, bool const internal_setup)
71 : {
72 0 : set_cache_info(info, internal_setup);
73 0 : }
74 :
75 :
76 : /** \brief Reset all the cache information to their defaults.
77 : *
78 : * This function resets all the cache information to their defaults so
79 : * the object looks as if it just got initialized.
80 : */
81 0 : void cache_control_settings::reset_cache_info()
82 : {
83 0 : f_max_age = 0;
84 0 : f_max_stale = IGNORE_VALUE;
85 0 : f_min_fresh = IGNORE_VALUE;
86 0 : f_must_revalidate = true;
87 0 : f_no_cache = false;
88 0 : f_no_store = true;
89 0 : f_no_transform = false;
90 0 : f_only_if_cached = false;
91 0 : f_private = false;
92 0 : f_public = false;
93 0 : f_s_maxage = IGNORE_VALUE;
94 0 : }
95 :
96 :
97 : /** \brief Set the cache information parsed from the \p info parameter.
98 : *
99 : * This function parses the \p info string for new cache definitions.
100 : * The \p info string may be empty in which case nothing is modified.
101 : * It is expected to be the string found in a Cache-Control header
102 : * sent by the client.
103 : *
104 : * If you want to start from scratch, you may call reset_cache_info()
105 : * first. You can also use a brand new object and then copy it since
106 : * the copy operator is available.
107 : *
108 : * The 'must-revalidate' and 'no-store' are set by default. Unfortunately
109 : * that would mean the page setup capability would not be able to ever
110 : * clear those two flags. That would mean you could never use a full
111 : * permanent cache definition. Instead we offer an extension to the
112 : * flags and allow one to add a '!' in front of the names as in:
113 : * '!no-cache'. This way you can force the 'no-cache' flag to false
114 : * instead of the default of true.
115 : *
116 : * \todo
117 : * Determine whether any error in the field should be considered fatal and
118 : * thus react by ignoring the entire \p info parameter. It seems that the
119 : * HTTP specification asks us to do so... (i.e. ignore all when any one
120 : * flag is not understood.) However, it seems that most browsers implement
121 : * such things the other way around: try to retrieve the maximum amount of
122 : * information as possible and use whatever they understand from that.
123 : *
124 : * \todo
125 : * Determine whether we should accept certain parameters only once.
126 : * Especially those that include values (i.e. max-age=123) because
127 : * the current implementation only takes the last one in account when
128 : * we probably should remember the first one (within one \p info string).
129 : *
130 : * \todo
131 : * Add support for private and no-cache parameters (server side only).
132 : *
133 : * \param[in] info The new cache control information to assign to this object.
134 : * \param[in] internal_setup Whether to allow our special '!' syntax.
135 : */
136 0 : void cache_control_settings::set_cache_info(QString const & info, bool const internal_setup)
137 : {
138 : // TODO: we want the weighted HTTP strings to understand
139 : // parameters with values assigned and more than the
140 : // q=xxx then we can update this code to better test
141 : // what we are dealing with...
142 :
143 : // parse the data with the weighted HTTP string implementation
144 : //
145 0 : http_strings::WeightedHttpString client_cache_control(info);
146 :
147 : // no go through the list of parts and handle them appropriately
148 : //
149 0 : http_strings::WeightedHttpString::part_t::vector_t const & cache_control_parts(client_cache_control.get_parts());
150 0 : for(auto const & c : cache_control_parts)
151 : {
152 : // get the part name
153 : //
154 0 : QString name(c.get_name());
155 :
156 0 : bool negate(false);
157 0 : if(internal_setup)
158 : {
159 0 : negate = name[0].unicode() == '!';
160 0 : if(negate)
161 : {
162 : // remove the negate flag
163 : //
164 0 : name = name.mid(1);
165 : }
166 : }
167 :
168 0 : if(name.isEmpty())
169 : {
170 0 : continue;
171 : }
172 :
173 : // TODO: add code to check whether 'negate' (!) was used with
174 : // an item that does not support it
175 :
176 : // do a switch first to save a tad bit of time
177 0 : switch(name[0].unicode())
178 : {
179 0 : case 'i':
180 0 : if(name == "immutable")
181 : {
182 0 : set_immutable(!negate);
183 : }
184 0 : break;
185 :
186 0 : case 'm':
187 0 : if(name == "max-age")
188 : {
189 0 : set_max_age(c.get_value());
190 : }
191 0 : else if(name == "max-stale")
192 : {
193 0 : QString const & value(c.get_value());
194 0 : if(value.isEmpty())
195 : {
196 : // any stale data can be returned
197 : //
198 0 : set_max_stale(0);
199 : }
200 : else
201 : {
202 0 : set_max_stale(value);
203 : }
204 : }
205 0 : else if(name == "min-fresh")
206 : {
207 0 : set_min_fresh(c.get_value());
208 : }
209 0 : else if(name == "must-revalidate")
210 : {
211 0 : set_must_revalidate(!negate);
212 : }
213 0 : break;
214 :
215 0 : case 'n':
216 0 : if(name == "no-cache")
217 : {
218 0 : QString const & value(c.get_value());
219 0 : if(value.isEmpty())
220 : {
221 0 : set_no_cache(!negate);
222 : }
223 : else
224 : {
225 : // list of fields that require revalidation
226 : //
227 0 : snap_string_list const list(value.split(","));
228 0 : for(auto const & l : list)
229 : {
230 0 : add_revalidate_field_name(l);
231 : }
232 : }
233 : }
234 0 : else if(name == "no-store")
235 : {
236 0 : set_no_store(!negate);
237 : }
238 0 : else if(name == "no-transform")
239 : {
240 0 : set_no_transform(!negate);
241 : }
242 0 : break;
243 :
244 0 : case 'o':
245 0 : if(name == "only-if-cached")
246 : {
247 0 : set_only_if_cached(!negate);
248 : }
249 0 : break;
250 :
251 0 : case 'p':
252 0 : if(name == "private")
253 : {
254 0 : QString const & value(c.get_value());
255 0 : if(value.isEmpty())
256 : {
257 0 : set_private(!negate);
258 : }
259 : else
260 : {
261 : // list of fields that require revalidation
262 : //
263 0 : snap_string_list list(value.split(","));
264 0 : for(auto l : list)
265 : {
266 0 : add_private_field_name(l);
267 : }
268 : }
269 : }
270 0 : else if(name == "proxy-revalidate")
271 : {
272 0 : set_proxy_revalidate(!negate);
273 : }
274 0 : else if(name == "public")
275 : {
276 0 : set_public(!negate);
277 : }
278 0 : break;
279 :
280 0 : case 's':
281 0 : if(name == "s-maxage")
282 : {
283 0 : set_s_maxage(c.get_value());
284 : }
285 0 : break;
286 :
287 : }
288 : }
289 0 : }
290 :
291 :
292 : /** \brief Set the must-revalidate to true or false.
293 : *
294 : * This function should only be called with 'true' to request
295 : * that the client revalidate the data each time it wants to
296 : * access it.
297 : *
298 : * However, the flag is set to 'true' by default, so really it
299 : * is only useful if you want to set the parameter to 'false'.
300 : *
301 : * \note
302 : * This flag may appear in the server response.
303 : *
304 : * \param[in] must_revalidate Whether the client should revalidate that
305 : * specific content on each page load.
306 : *
307 : * \sa get_must_revalidate()
308 : */
309 0 : void cache_control_settings::set_must_revalidate(bool const must_revalidate)
310 : {
311 0 : f_must_revalidate = must_revalidate;
312 0 : }
313 :
314 :
315 : /** \brief Get the current value of the must-revalidate flag.
316 : *
317 : * This function returns the current value of the must-revalidate
318 : * flag.
319 : *
320 : * \note
321 : * This flag may appear in the server response.
322 : *
323 : * \return true if the must-revalidate flag is to be specified in the
324 : * Cache-Control field.
325 : *
326 : * \sa set_must_revalidate()
327 : */
328 0 : bool cache_control_settings::get_must_revalidate() const
329 : {
330 0 : return f_must_revalidate;
331 : }
332 :
333 :
334 : /** \brief Set the 'private' flag to true or false.
335 : *
336 : * Any page that is private, and thus should not be saved in a
337 : * shared cache (i.e. proxies), must be assigned the private
338 : * flag, so this function must be called with true.
339 : *
340 : * Note that does not encrypt the data in any way. It just adds
341 : * the private flag to the Cache-Control header. If you need to
342 : * encrypt the data, make sure to enforce HTTPS before returning
343 : * a reply with secret data.
344 : *
345 : * \note
346 : * This flag may appear in the server response.
347 : *
348 : * \param[in] private_cache Whether intermediate, shared, proxy caches
349 : * are allowed to cache this data.
350 : *
351 : * \sa get_private()
352 : */
353 0 : void cache_control_settings::set_private(bool const private_cache)
354 : {
355 0 : f_private = private_cache;
356 0 : }
357 :
358 :
359 : /** \brief Get the current value of the 'private' flag.
360 : *
361 : * This function returns the current value of the 'private'
362 : * flag.
363 : *
364 : * Note that 'private' has priority over 'public'. So if 'private'
365 : * is true, 'public' is ignored. For this reason you should only
366 : * set those flags to true and never attempt to reset them to
367 : * false. Similarly, the 'no-cache' and 'no-store' have priority
368 : * over the 'private' flag.
369 : *
370 : * \note
371 : * This flag may appear in the server response.
372 : *
373 : * \return true if the 'private' flag is to be specified in the
374 : * Cache-Control field.
375 : *
376 : * \sa set_private()
377 : */
378 0 : bool cache_control_settings::get_private() const
379 : {
380 0 : return f_private;
381 : }
382 :
383 :
384 : /** \brief Set 'proxy-revalidate' to true or false.
385 : *
386 : * This function should only be called with 'true' to request
387 : * that proxy caches revalidate the data each time a client
388 : * asks for the data.
389 : *
390 : * You may instead want to use the 's-maxage' field.
391 : *
392 : * \note
393 : * This flag may appear in the server response.
394 : *
395 : * \param[in] proxy_revalidate Whether proxy caches should revalidate that
396 : * specific content.
397 : *
398 : * \sa get_proxy_revalidate()
399 : */
400 0 : void cache_control_settings::set_proxy_revalidate(bool const proxy_revalidate)
401 : {
402 0 : f_proxy_revalidate = proxy_revalidate;
403 0 : }
404 :
405 :
406 : /** \brief Set whether the resource is immutable or not.
407 : *
408 : * This function sets whether the resource is immutable. Browsers that
409 : * understand this flag will never check the server again for that
410 : * specific resource as long as they keep it in their caches.
411 : *
412 : * Immutable means that it will never change. This is true for all
413 : * our CSS and JS files because these are versioned and any changes
414 : * to those files require a change in their version.
415 : *
416 : * \param[in] immutable Whether the file is immutable or not.
417 : */
418 0 : void cache_control_settings::set_immutable(bool const immutable)
419 : {
420 0 : f_immutable = immutable;
421 0 : }
422 :
423 :
424 : /** \brief Get whether the data was marked immutable.
425 : *
426 : * This function returns true if the data attached to that resource
427 : * is considered immutable.
428 : *
429 : * For example, JS and CSS files are always considered immutable. This
430 : * is because we have a version and if you want to modify those files,
431 : * you must increase the version accordingly.
432 : *
433 : * Browsers that support the immutable flag never check the server
434 : * again because the file will be saved permanently in their caches.
435 : *
436 : * \return true if the resource is considered immutable.
437 : */
438 0 : bool cache_control_settings::get_immutable() const
439 : {
440 0 : return f_immutable;
441 : }
442 :
443 :
444 : /** \brief Get the current value of the 'proxy-revalidate' flag.
445 : *
446 : * This function returns the current value of the 'proxy-revalidate'
447 : * flag.
448 : *
449 : * Note that 'must-revalidate' has priority and if specified, the
450 : * 'proxy-revalidate' is ignored since the proxy-cache should
451 : * honor the 'must-revalidate' anyway. However, this function
452 : * directly returns the value of the 'proxy-revalidate' flag.
453 : *
454 : * \note
455 : * This flag may appear in the server response.
456 : *
457 : * \return true if the 'proxy-revalidate' flag is to be specified in the
458 : * Cache-Control field.
459 : *
460 : * \sa set_proxy_revalidate()
461 : */
462 0 : bool cache_control_settings::get_proxy_revalidate() const
463 : {
464 0 : return f_proxy_revalidate;
465 : }
466 :
467 :
468 : /** \brief Set the 'public' flag to true or false.
469 : *
470 : * Any page that is public, and thus can be saved in a public shared
471 : * cache (i.e. proxy caches).
472 : *
473 : * Snap! detects whether a page is accessible by a visitor, if so
474 : * it sets the public flag automatically. So you should not have
475 : * to set this flag unless somehow your page is public and the Snap!
476 : * test could fail or you know that your pages are always public
477 : * and thus you could avoid having to check the permissions.
478 : *
479 : * Note that if the 'private' flag is set to true, then the 'public'
480 : * flag is ignored. Further, if the 'no-cache' or 'no-store' flags
481 : * are set, then 'public' and 'private' are ignored.
482 : *
483 : * \note
484 : * This flag may appear in the server response.
485 : *
486 : * \param[in] public_cache Whether proxy caches can cache this content.
487 : *
488 : * \sa get_public()
489 : */
490 0 : void cache_control_settings::set_public(bool const public_cache)
491 : {
492 0 : f_public = public_cache;
493 0 : }
494 :
495 :
496 : /** \brief Get the current value of the 'public' flag.
497 : *
498 : * This function returns the current value of the 'public'
499 : * flag.
500 : *
501 : * Note that 'private' has priority over 'public'. So if 'private'
502 : * is true, 'public' is ignored. Similarly, the 'no-cache' and
503 : * 'no-store' have priority over the 'private' flag. However, this
504 : * function directly returns the 'public' flag.
505 : *
506 : * \note
507 : * This flag may appear in the server response.
508 : *
509 : * \return true if the 'public' flag is to be specified in the
510 : * Cache-Control field.
511 : *
512 : * \sa set_public()
513 : */
514 0 : bool cache_control_settings::get_public() const
515 : {
516 0 : return f_public;
517 : }
518 :
519 :
520 : /** \brief Add a field name that needs to get revalidated by proxy caches.
521 : *
522 : * The Cache-Control header can include a no-cache parameter that includes
523 : * a list of field names that should not be cached and be revalidated
524 : * whenever a new client request is received.
525 : *
526 : * Note that means there are three possibilities with the no-cache parameters:
527 : *
528 : * \li the parameter is not present, caching may still not be allowed
529 : * (i.e. max-age=0)
530 : * \li the parameter is present on its own, absolutely no caching is possible
531 : * \li the parameter is set to a list of field names, in which case caching
532 : * is allowed, but the specified fields must be revalidated from the
533 : * server (and should probably not be saved in the cache)
534 : *
535 : * \note
536 : * If the string is empty after left and right trimming the request to
537 : * add a field name is ignored.
538 : *
539 : * References:
540 : *
541 : * https://tools.ietf.org/html/rfc7234#section-5.2.2
542 : *
543 : * \param[in] field_name The name of the HTTP header field which needs to
544 : * never be cached.
545 : *
546 : * \sa add_private_field_name()
547 : */
548 0 : void cache_control_settings::add_revalidate_field_name(std::string const & field_name)
549 : {
550 : // we just have to insert, it will be inserted just once
551 : //
552 0 : std::string name(boost::trim_copy(field_name));
553 0 : if(!name.empty())
554 : {
555 0 : f_revalidate_field_names.insert(name);
556 : }
557 0 : }
558 :
559 :
560 : /** \brief Add a field name that needs to never be cached.
561 : *
562 : * This function is an overload of the add_never_cache_field_name() with
563 : * an std::string parameter.
564 : *
565 : * \param[in] field_name The name of the HTTP header field which needs to
566 : * never be cached.
567 : */
568 0 : void cache_control_settings::add_revalidate_field_name(QString const & field_name)
569 : {
570 0 : add_revalidate_field_name(std::string(field_name.toUtf8().data()));
571 0 : }
572 :
573 :
574 : /** \brief Add a field name that needs to never be cached.
575 : *
576 : * This function is an overload of the add_never_cache_field_name() with
577 : * an std::string parameter.
578 : *
579 : * \note
580 : * The function accepts a nullptr and ignores the request in that situation.
581 : *
582 : * \param[in] field_name The name of the HTTP header field which needs to
583 : * never be cached.
584 : */
585 0 : void cache_control_settings::add_revalidate_field_name(char const * field_name)
586 : {
587 0 : if(field_name != nullptr)
588 : {
589 0 : add_revalidate_field_name(std::string(field_name));
590 : }
591 0 : }
592 :
593 :
594 : /** \brief Retrieve the existing list of field names to never cache.
595 : *
596 : * The Cache-Control field can include a no-cache="<list of field names>".
597 : * This is that list.
598 : *
599 : * By default this list is empty (Contrary to the private list which
600 : * includes the "Set-Cookie".)
601 : *
602 : * For certain pages that require the "Set-Cookie" or some other user
603 : * fields, such should be added to this list. That way the cache will
604 : * make sure to revalidate the page conditionally.
605 : *
606 : * Other fields that are specific to a user need to be added to this
607 : * list with one of the add_revalidate_field_name() functions.
608 : *
609 : * \return A list of field names.
610 : */
611 0 : cache_control_settings::tags_t const & cache_control_settings::get_revalidate_field_names() const
612 : {
613 0 : return f_revalidate_field_names;
614 : }
615 :
616 :
617 : /** \brief Add a field name that needs to remain private.
618 : *
619 : * The Cache-Control header can include a private parameter that includes
620 : * a list of field names that need to not be cached, except by private
621 : * caches (i.e. client browser).
622 : *
623 : * Note that means there are three possibilities with the private parameters:
624 : *
625 : * \li the parameter is not present, the data is considered private anyway
626 : * \li the parameter is present on its own, all data is considered private
627 : * \li the parameter is set to a list of field names, in which case caching
628 : * is allowed, but the specified fields must be removed from the HTTP
629 : * header before caching the header
630 : *
631 : * Note that this is different from the no-cache parameter which requires
632 : * a hit to the server to revalidate the header. In case of the private
633 : * parameter, no revalidation is required. We can simply send the cache
634 : * without the private fields (which with Snap! C++ is fine for pretty
635 : * much all our attachments.)
636 : *
637 : * \note
638 : * If the string is empty after left and right trimming the request to
639 : * add a field name is ignored.
640 : *
641 : * References:
642 : *
643 : * https://tools.ietf.org/html/rfc7234#section-5.2.2
644 : *
645 : * \param[in] field_name The name of the HTTP header field which needs to
646 : * never be cached.
647 : *
648 : * \sa add_revalidate_field_name()
649 : */
650 0 : void cache_control_settings::add_private_field_name(std::string const & field_name)
651 : {
652 : // we just have to insert, it will be inserted just once
653 : //
654 0 : std::string name(boost::trim_copy(field_name));
655 0 : if(!name.empty())
656 : {
657 0 : f_private_field_names.insert(name);
658 : }
659 0 : }
660 :
661 :
662 : /** \brief Add a field name that needs to never be cached.
663 : *
664 : * This function is an overload of the add_private_field_name() with
665 : * an std::string parameter.
666 : *
667 : * \param[in] field_name The name of the HTTP header field which needs to
668 : * never be cached.
669 : */
670 0 : void cache_control_settings::add_private_field_name(QString const & field_name)
671 : {
672 0 : add_private_field_name(std::string(field_name.toUtf8().data()));
673 0 : }
674 :
675 :
676 : /** \brief Add a field name that needs to never be cached.
677 : *
678 : * This function is an overload of the add_private_field_name() with
679 : * an std::string parameter.
680 : *
681 : * \note
682 : * The function accepts a nullptr and ignores the request in that situation.
683 : *
684 : * \param[in] field_name The name of the HTTP header field which needs to
685 : * never be cached.
686 : */
687 0 : void cache_control_settings::add_private_field_name(char const * field_name)
688 : {
689 0 : if(field_name != nullptr)
690 : {
691 0 : add_private_field_name(std::string(field_name));
692 : }
693 0 : }
694 :
695 :
696 : /** \brief Retrieve the existing list of field names to never cache.
697 : *
698 : * The Cache-Control field can include a private="<list of field names>".
699 : * This is that list.
700 : *
701 : * By default, this list is set to "Set-Cookie" which is more than likely
702 : * a field that include many user specific data such as a session
703 : * identifier.
704 : *
705 : * Other fields that are specific to a user need to be added to this
706 : * list with one of the add_private_field_name() functions.
707 : *
708 : * \return A list of field names.
709 : */
710 0 : cache_control_settings::tags_t const & cache_control_settings::get_private_field_names() const
711 : {
712 0 : return f_private_field_names;
713 : }
714 :
715 :
716 : /** \brief Add a tag to the existing list of tags.
717 : *
718 : * This function adds the specified tag_name parameter to the list of
719 : * tags to send to the client. This is used by CDN systems to allow
720 : * for segregation of files to be cached.
721 : *
722 : * Note that a page which cache is turned off (i.e. Cache-Control has
723 : * the no-cache parameter set) does not get tagged. These tags will be
724 : * ignored in that situation.
725 : *
726 : * By default we use the "Cache-Tag" HTTP header as defined by CloudFlare.
727 : * The name can be changed in the snapserver.conf file. You may also
728 : * add multiple names. Drupal uses "X-Drupal-Cache-Tags". Akamai uses
729 : * "Edge-Cache-Tag".
730 : *
731 : * References:
732 : *
733 : * https://support.cloudflare.com/hc/en-us/articles/206596608-How-to-Purge-Cache-Using-Cache-Tags-Enterprise-only-
734 : *
735 : * https://www.drupal.org/docs/8/api/cache-api/cache-tags
736 : *
737 : * https://learn.akamai.com/en-us/webhelp/fast-purge/fast-purge/GUID-9AEF6978-697F-410C-A347-8155FDB535C8.html
738 : *
739 : * \param[in] tag_name The name of the tag to add to the list.
740 : */
741 0 : void cache_control_settings::add_tag(std::string const & tag_name)
742 : {
743 : // we just have to insert, it will be inserted just once
744 : //
745 0 : f_tags.insert(tag_name);
746 0 : }
747 :
748 :
749 : /** \brief Add a tag to the existing list of tags.
750 : *
751 : * This function adds the specified tag_name parameter to the list
752 : * of tags to send to proxy caches.
753 : *
754 : * This is an overload version of the add_tag() with a std::string.
755 : *
756 : * \param[in] tag_name The name of the tag to add to the list.
757 : */
758 0 : void cache_control_settings::add_tag(QString const & tag_name)
759 : {
760 0 : add_tag(std::string(tag_name.toUtf8().data()));
761 0 : }
762 :
763 :
764 : /** \brief Add a tag to the existing list of tags.
765 : *
766 : * This function adds the specified tag_name parameter to the list
767 : * of tags to send to proxy caches.
768 : *
769 : * This is an overload version of the add_tag() with a std::string.
770 : *
771 : * \param[in] tag_name The name of the tag to add to the list.
772 : */
773 0 : void cache_control_settings::add_tag(char const * tag_name)
774 : {
775 0 : add_tag(std::string(tag_name));
776 0 : }
777 :
778 :
779 : /** \brief Retrieve the existing list of cache tags.
780 : *
781 : * Various CDN systems accept a cache tag. This function returns the
782 : * list of tags that will be used with that HTTP header.
783 : *
784 : * By default, this is expected to be empty and no tag will be added
785 : * anywhere.
786 : *
787 : * \return A list of tag names.
788 : */
789 0 : cache_control_settings::tags_t const & cache_control_settings::get_tags() const
790 : {
791 0 : return f_tags;
792 : }
793 :
794 :
795 : /** \brief Set the maximum number of seconds to cache this data.
796 : *
797 : * This function requests for the specified data to be cached
798 : * for that many seconds.
799 : *
800 : * By default the value of max-age is set to 0, meaning that the
801 : * data will not be cached.
802 : *
803 : * In order to create a cache on the client's side (and within proxies,)
804 : * the value can be set to a number of seconds between 1 and AGE_MAXIMUM.
805 : * Any value under 60 is probably not going to be very useful. Any
806 : * value larger than AGE_MAXIMUM (which is one year, as per HTTP/1.1)
807 : * is clamped to AGE_MAXIMUM.
808 : *
809 : * You may also set \p max_age to -1 in order for the system to ignore
810 : * the 'max-age' cache control parameter.
811 : *
812 : * \note
813 : * This flag may appear in the client request or the server response.
814 : *
815 : * \param[in] max_age The maximum age the data can have before checking
816 : * the data again.
817 : *
818 : * \sa update_max_age()
819 : * \sa get_max_age()
820 : */
821 0 : void cache_control_settings::set_max_age(int64_t const max_age)
822 : {
823 0 : if(max_age >= AGE_MAXIMUM)
824 : {
825 : // 1 year in seconds
826 0 : f_max_age = AGE_MAXIMUM;
827 : }
828 0 : else if(max_age < 0)
829 : {
830 0 : f_max_age = IGNORE_VALUE;
831 : }
832 : else
833 : {
834 0 : f_max_age = max_age;
835 : }
836 0 : }
837 :
838 :
839 : /** \brief Set the 'max-age' field value from a string.
840 : *
841 : * This function accepts a string as input to setup the 'max-age'
842 : * field.
843 : *
844 : * The value may be set to IGNORE_VALUE if the string does not represent
845 : * a valid decimal number (no signs allowed.) It is also clamped to a
846 : * maximum of AGE_MAXIMUM.
847 : *
848 : * \param[in] max_age The maximum age as a string.
849 : *
850 : * \sa update_max_age()
851 : * \sa get_max_age()
852 : */
853 0 : void cache_control_settings::set_max_age(QString const & max_age)
854 : {
855 : // in this case -1 is what we want in case of an error
856 : // and not 1 year in seconds... no other negative values
857 : // are possible so we are fine
858 0 : f_max_age = string_to_seconds(max_age);
859 0 : }
860 :
861 :
862 : /** \brief Update the maximum number of seconds to cache this data.
863 : *
864 : * This function keeps the smaller 'max-age' of the existing setup and
865 : * the new value specified to this function.
866 : *
867 : * Note that if the current value is IGNORE_VALUE, then the new maximum
868 : * is automatically used, whatever it is.
869 : *
870 : * Negative values are ignored.
871 : *
872 : * \param[in] max_age The maximum age the data can have before the client
873 : * is expected to check the data again.
874 : *
875 : * \sa set_max_age()
876 : * \sa get_max_age()
877 : */
878 0 : void cache_control_settings::update_max_age(int64_t max_age)
879 : {
880 0 : if(max_age > AGE_MAXIMUM)
881 : {
882 : // 1 year in seconds
883 : //
884 0 : max_age = AGE_MAXIMUM;
885 : }
886 0 : f_max_age = minimum(f_max_age, max_age);
887 0 : }
888 :
889 :
890 : /** \brief Retrieve the current max-age field.
891 : *
892 : * The Cache-Control can specify how long the data being returned
893 : * can be cached for. The 'max-age' field defines that duration
894 : * in seconds.
895 : *
896 : * By default the data is marked as 'do not cache' (i.e. max-age
897 : * is set to zero.)
898 : *
899 : * This function may return IGNORE_VALUE in which case the
900 : * 'max-age' field is ignored.
901 : *
902 : * \note
903 : * This flag may appear in the client request or the server response.
904 : *
905 : * \return The number of seconds to cache the data or IGNORE_VALUE.
906 : *
907 : * \sa update_max_age()
908 : * \sa get_max_age()
909 : */
910 0 : int64_t cache_control_settings::get_max_age() const
911 : {
912 0 : return f_max_age;
913 : }
914 :
915 :
916 : /** \brief Set the 'no-cache' flag to true or false.
917 : *
918 : * This function should only be called with 'true' to request
919 : * that the client and intermediate caches do not cache any
920 : * of the data. This does not prevent the client to store
921 : * the data.
922 : *
923 : * When the client sets this field to true, it means that we
924 : * should regenerate the specified page data.
925 : *
926 : * \note
927 : * This flag may appear in the client request or the server response.
928 : *
929 : * \param[in] no_cache Whether the client and intermediate caches
930 : * must not cache the data.
931 : *
932 : * \sa get_no_cache()
933 : */
934 0 : void cache_control_settings::set_no_cache(bool const no_cache)
935 : {
936 0 : f_no_cache = no_cache;
937 0 : }
938 :
939 :
940 : /** \brief Retrieve the 'no-cache' flag.
941 : *
942 : * This function returns the current value of the 'no-cache' flag.
943 : *
944 : * The system ignores the 'public' and 'private' flags when the
945 : * 'no-cache' flag is true.
946 : *
947 : * \note
948 : * This flag may appear in the client request or the server response.
949 : *
950 : * \return true if the 'no-cache' flag is to be specified in the
951 : * Cache-Control field.
952 : *
953 : * \sa set_no_cache()
954 : */
955 0 : bool cache_control_settings::get_no_cache() const
956 : {
957 0 : return f_no_cache;
958 : }
959 :
960 :
961 : /** \brief Set the 'no-store' flag to true or false.
962 : *
963 : * This function sets the 'no-store' flag to true or false. This
964 : * flag means that any of the data in that request needs to be
965 : * transferred only and not stored anywhere except in 100%
966 : * temporary buffers on the client's machine.
967 : *
968 : * Further, shared/proxy caches should clear all the data bufferized
969 : * to process this request as soon as it is done with it.
970 : *
971 : * \note
972 : * This flag may appear in the client request or the server response.
973 : *
974 : * \param[in] no_store Whether the data can be stored or not.
975 : *
976 : * \sa get_no_store()
977 : */
978 0 : void cache_control_settings::set_no_store(bool const no_store)
979 : {
980 0 : f_no_store = no_store;
981 0 : }
982 :
983 :
984 : /** \brief Retrieve the 'no-store' flag.
985 : *
986 : * This function returns the current 'no-store' flag status.
987 : *
988 : * In most cases, this flag is not required. It should be true
989 : * only on pages that include extremely secure content such
990 : * as a page recording the settings of an electronic payment
991 : * (i.e. the e-payment Paypal page allows you to enter your
992 : * Paypal identifiers and those should not be stored anywhere.)
993 : *
994 : * Since most of our HTML pages are already marked as 'no-cache',
995 : * the 'no-store' is generally not required.
996 : *
997 : * \note
998 : * This flag may appear in the client request or the server response.
999 : *
1000 : * \return true if the data in this request should not be stored
1001 : * anywhere.
1002 : *
1003 : * \sa set_no_store()
1004 : */
1005 0 : bool cache_control_settings::get_no_store() const
1006 : {
1007 0 : return f_no_store;
1008 : }
1009 :
1010 :
1011 : /** \brief Set whether the data can be transformed.
1012 : *
1013 : * The 'no-transform' flag can be used to make sure that caches do
1014 : * not transform the data. This can also appear in the request from
1015 : * the client in which case an exact original is required.
1016 : *
1017 : * This is generally important only for document files that may be
1018 : * converted to a lousy format such as images that could be saved
1019 : * as JPEG images although we enforce it when the client sends us
1020 : * an AJAX request.
1021 : *
1022 : * \note
1023 : * This flag may appear in the client request or the server response.
1024 : *
1025 : * \param[in] no_transform Set to true to retrieve the original document as is.
1026 : *
1027 : * \sa get_no_transform()
1028 : */
1029 0 : void cache_control_settings::set_no_transform(bool const no_transform)
1030 : {
1031 0 : f_no_transform = no_transform;
1032 0 : }
1033 :
1034 :
1035 : /** \brief Retrieve whether the data can be transformed.
1036 : *
1037 : * Check whether the client or the server are requesting that the data
1038 : * not be transformed. If true, then the original data should be
1039 : * transferred as is.
1040 : *
1041 : * \note
1042 : * This flag may appear in the client request or the server response.
1043 : *
1044 : * \return true if the 'no-transform' flag is set.
1045 : *
1046 : * \sa set_no_transform()
1047 : */
1048 0 : bool cache_control_settings::get_no_transform() const
1049 : {
1050 0 : return f_no_transform;
1051 : }
1052 :
1053 :
1054 : /** \brief Set the number of seconds to cache this data in shared caches.
1055 : *
1056 : * This function requests for the specified data to be cached
1057 : * for that many seconds in any shared caches between the client
1058 : * and the server. The client ignores that information.
1059 : *
1060 : * To use the maximum, call this function with AGE_MAXIMUM.
1061 : *
1062 : * To ignore this value, call this function with IGNORE_VALUE. This is
1063 : * the default value for this field.
1064 : *
1065 : * \note
1066 : * This flag may appear in the client request or the server response.
1067 : *
1068 : * \param[in] s_maxage The maximum age the data can have before checking
1069 : * the source again.
1070 : *
1071 : * \sa update_s_maxage()
1072 : * \sa get_s_maxage()
1073 : */
1074 0 : void cache_control_settings::set_s_maxage(int64_t const s_maxage)
1075 : {
1076 0 : if(s_maxage >= AGE_MAXIMUM)
1077 : {
1078 0 : f_s_maxage = AGE_MAXIMUM;
1079 : }
1080 0 : else if(s_maxage < 0)
1081 : {
1082 0 : f_s_maxage = IGNORE_VALUE;
1083 : }
1084 : else
1085 : {
1086 0 : f_s_maxage = s_maxage;
1087 : }
1088 0 : }
1089 :
1090 :
1091 : /** \brief Set the 's-maxage' field value from a string.
1092 : *
1093 : * This function accepts a string as input to setup the 's-maxage'
1094 : * field.
1095 : *
1096 : * The value may be set to -1 if the string does not represent a valid
1097 : * decimal number (no signs allowed.) It will be clamped to a maximum
1098 : * of AGE_MAXIMUM.
1099 : *
1100 : * \param[in] s_maxage The new maximum age defined as a string.
1101 : *
1102 : * \sa update_s_maxage()
1103 : * \sa get_s_maxage()
1104 : */
1105 0 : void cache_control_settings::set_s_maxage(QString const & s_maxage)
1106 : {
1107 : // in this case -1 is what we want in case of an error
1108 : // and not 1 year in seconds... no other negative values
1109 : // are possible so we are fine
1110 0 : f_s_maxage = string_to_seconds(s_maxage);
1111 0 : }
1112 :
1113 :
1114 : /** \brief Update the maximum number of seconds to cache this data on proxies.
1115 : *
1116 : * This function keeps the smaller maximum of the existing setup and
1117 : * the new value specified to this function.
1118 : *
1119 : * Note that if the current value is IGNORE_VALUE, then the new maximum
1120 : * is always used.
1121 : *
1122 : * Negative values are ignored.
1123 : *
1124 : * \param[in] s_maxage The maximum age the data can have before checking
1125 : * the data again from shared proxy cache.
1126 : *
1127 : * \sa set_s_maxage()
1128 : * \sa get_s_maxage()
1129 : */
1130 0 : void cache_control_settings::update_s_maxage(int64_t s_maxage)
1131 : {
1132 0 : if(s_maxage >= AGE_MAXIMUM)
1133 : {
1134 : // 1 year in seconds
1135 : //
1136 0 : s_maxage = AGE_MAXIMUM;
1137 : }
1138 0 : f_s_maxage = minimum(f_s_maxage, s_maxage);
1139 0 : }
1140 :
1141 :
1142 : /** \brief Retrieve the current 's-maxage' field.
1143 : *
1144 : * The 'Cache-Control' HTTP header can specify how long the data
1145 : * being returned can be cached for in a shared cache. The
1146 : * 's-maxage' field defines that duration in seconds.
1147 : *
1148 : * By default shared caches are expected to use the 'max-age' parameter
1149 : * when the 's-maxage' parameter is not defined. So if the value is
1150 : * the same, you do not have to specified 's-maxage'.
1151 : *
1152 : * The value of 0 means that shared caches will not cache anything.
1153 : *
1154 : * \note
1155 : * This field may appear in the client request or the server response.
1156 : *
1157 : * \return The number of seconds to cache the data or IGNORE_VALUE.
1158 : *
1159 : * \sa set_s_maxage()
1160 : * \sa update_s_maxage()
1161 : */
1162 0 : int64_t cache_control_settings::get_s_maxage() const
1163 : {
1164 0 : return f_s_maxage;
1165 : }
1166 :
1167 :
1168 : /** \brief How long of a 'stale' is accepted by the client.
1169 : *
1170 : * The client may asks for data that is stale. Assuming that
1171 : * a cache may keep data after it became stale, the client may
1172 : * retrieve that data if they specified the stale parameter.
1173 : *
1174 : * A value of zero means that any stale data is acceptable.
1175 : * A greater value specifies the number of seconds after the
1176 : * normal cache threshold the data can be to be considered
1177 : * okay to be returned to the client.
1178 : *
1179 : * In general, this is for cache systems and not the server
1180 : * so our server generally ignores that data.
1181 : *
1182 : * You may use the AGE_MAXIMUM parameter to set the 'stale'
1183 : * parameter to the maximum possible.
1184 : *
1185 : * You may set the 'stale' parameter to IGNORE_VALUE in which
1186 : * case any possible user of the parameter has to ignore the
1187 : * value (do as if it was not specified).
1188 : *
1189 : * \note
1190 : * This flag may appear in the client request.
1191 : *
1192 : * \param[in] max_stale The maximum number of seconds for the stale
1193 : * data to be before the request is forwarded
1194 : * to the server.
1195 : *
1196 : * \sa get_max_stale()
1197 : */
1198 0 : void cache_control_settings::set_max_stale(int64_t const max_stale)
1199 : {
1200 0 : if(max_stale >= AGE_MAXIMUM)
1201 : {
1202 0 : f_max_stale = AGE_MAXIMUM;
1203 : }
1204 0 : else if(max_stale < 0)
1205 : {
1206 0 : f_max_stale = IGNORE_VALUE;
1207 : }
1208 : else
1209 : {
1210 0 : f_max_stale = max_stale;
1211 : }
1212 0 : }
1213 :
1214 :
1215 : /** \brief Set the 'max-stale' field value from a string.
1216 : *
1217 : * This function accepts a string as input to setup the 'max-stale'
1218 : * field.
1219 : *
1220 : * The value may be set to IGNORE_VALUE if the string does not
1221 : * represent a valid decimal number (no signs allowed.)
1222 : *
1223 : * The value is clamped to a maximum of AGE_MAXIMUM.
1224 : *
1225 : * \param[in] max_stale The 'max-stale' parameter as a string.
1226 : *
1227 : * \sa get_max_stale()
1228 : */
1229 0 : void cache_control_settings::set_max_stale(QString const & max_stale)
1230 : {
1231 0 : set_max_stale(string_to_seconds(max_stale));
1232 0 : }
1233 :
1234 :
1235 : /** \brief Retrieve the current maximum 'stale' value.
1236 : *
1237 : * This function returns the maximum number of seconds the client is
1238 : * willing to accept after the cache expiration date.
1239 : *
1240 : * So if your cache expires at 14:30:00 and the user makes a new
1241 : * request on 14:32:50 with a 'max-stale' value of 3600, then you
1242 : * may return the stale cache instead of regenerating it.
1243 : *
1244 : * Note that only shared caches are expected to return stale data.
1245 : * The server itself currently always regenerate the data as
1246 : * required.
1247 : *
1248 : * The stale value may be set to zero in which case the cache data
1249 : * is always returned if available.
1250 : *
1251 : * \note
1252 : * This flag may appear in the client request.
1253 : *
1254 : * \return The number of seconds the cache may be stale by before
1255 : * regenerating it.
1256 : *
1257 : * \sa set_max_stale()
1258 : */
1259 0 : int64_t cache_control_settings::get_max_stale() const
1260 : {
1261 0 : return f_max_stale;
1262 : }
1263 :
1264 :
1265 : /** \brief Set the number of seconds of freshness required by the client.
1266 : *
1267 : * The freshness is the amount of seconds before the cache goes stale.
1268 : * So if the cache goes stale in 60 seconds and the freshness query
1269 : * is 3600, then the cache is ignored.
1270 : *
1271 : * Note that freshness cannot always be satisfied since a page cache
1272 : * duration ('max-age') may always be smaller than the specified
1273 : * freshness amount.
1274 : *
1275 : * You may set this parameter to its maximum using the AGE_MAXIMUM
1276 : * value.
1277 : *
1278 : * You may set this parameter to IGNORE_VALUE to mark it as being
1279 : * ignored (not set by client.)
1280 : *
1281 : * \note
1282 : * This flag may appear in the client request.
1283 : *
1284 : * \param[in] min_fresh The minimum freshness in seconds.
1285 : *
1286 : * \sa get_min_fresh()
1287 : */
1288 0 : void cache_control_settings::set_min_fresh(int64_t const min_fresh)
1289 : {
1290 0 : if(min_fresh >= AGE_MAXIMUM)
1291 : {
1292 0 : f_min_fresh = AGE_MAXIMUM;
1293 : }
1294 0 : else if(min_fresh < 0)
1295 : {
1296 0 : f_min_fresh = IGNORE_VALUE;
1297 : }
1298 : else
1299 : {
1300 0 : f_min_fresh = min_fresh;
1301 : }
1302 0 : }
1303 :
1304 :
1305 : /** \brief Set the 'min-fresh' field value from a string.
1306 : *
1307 : * This function accepts a string as input to setup the 'min-fresh'
1308 : * field.
1309 : *
1310 : * The value may be set to INGORE_VALUE if the string does not represent
1311 : * a valid decimal number (no signs allowed.) It will be clamped to
1312 : * a maximum of AGE_MAXIMUM.
1313 : *
1314 : * \param[in] min_fresh The minimum freshness required by the client.
1315 : *
1316 : * \sa get_min_fresh()
1317 : */
1318 0 : void cache_control_settings::set_min_fresh(QString const & min_fresh)
1319 : {
1320 0 : set_min_fresh(string_to_seconds(min_fresh));
1321 0 : }
1322 :
1323 :
1324 : /** \brief Retrieve the 'min-fresh' value from the Cache-Control.
1325 : *
1326 : * If the cache is to get stale within less than 'min-fresh' then
1327 : * the server is expected to recalculate the page.
1328 : *
1329 : * Pages that are given a 'max-age' of less than what 'min-fresh'
1330 : * is set at will react as fully dynamic pages (i.e. as if no
1331 : * caches were available.)
1332 : *
1333 : * \note
1334 : * This flag may appear in the client request.
1335 : *
1336 : * \return The number of seconds of freshness that are required.
1337 : *
1338 : * \sa set_min_fresh()
1339 : */
1340 0 : int64_t cache_control_settings::get_min_fresh() const
1341 : {
1342 0 : return f_min_fresh;
1343 : }
1344 :
1345 :
1346 : /** \brief Set the 'only-if-cached' flag.
1347 : *
1348 : * The 'only-if-cached' flag is used by clients with poor network
1349 : * connectivity to request any available data from any cache instead
1350 : * of gettings newer data.
1351 : *
1352 : * The server ignores that flag since the user connected to the server
1353 : * it would not make sense to not return a valid response from that
1354 : * point.
1355 : *
1356 : * \note
1357 : * This flag may appear in the client request.
1358 : *
1359 : * \param[in] only_if_cached The new flag value.
1360 : *
1361 : * \sa get_only_if_cached()
1362 : */
1363 0 : void cache_control_settings::set_only_if_cached(bool const only_if_cached)
1364 : {
1365 0 : f_only_if_cached = only_if_cached;
1366 0 : }
1367 :
1368 :
1369 : /** \brief Retrieve the 'only-if-cached' flag.
1370 : *
1371 : * This function returns the current status of the 'only-if-cached' flag.
1372 : *
1373 : * The server ignores this flag since it is generally used so a client
1374 : * can request one of the in between caches to return any data they have
1375 : * available, instead of trying to reconnect to the server. However,
1376 : * the server may still check the flag and if true change the behavior.
1377 : * Yet, that would mean the cache behavior would change for all clients.
1378 : *
1379 : * Note that caches still do not return stale data unless the client
1380 : * also specifies the max-stale parameter.
1381 : *
1382 : * \code
1383 : * Cache-Control: max-stale=0,only-if-cached
1384 : * \endcode
1385 : *
1386 : * \return Return true if the only-if-cached flag was specified in the
1387 : * request.
1388 : *
1389 : * \sa set_only_if_cached()
1390 : */
1391 0 : bool cache_control_settings::get_only_if_cached() const
1392 : {
1393 0 : return f_only_if_cached;
1394 : }
1395 :
1396 :
1397 : /** \brief Convert a string to a number.
1398 : *
1399 : * This function returns a number as defined in a string. The input
1400 : * string must exclusively be composed of decimal digits.
1401 : *
1402 : * No plus or minus signs are allowed. If any character is not valid,
1403 : * then the function returns IGNORE_VALUE.
1404 : *
1405 : * If the value is larger than AGE_MAXIMUM, it will be clamped at
1406 : * AGE_MAXIMUM.
1407 : *
1408 : * \note
1409 : * The function only accepts decimal numbers.
1410 : *
1411 : * \param[in] max_age The maximum age decimal number defined as a string.
1412 : *
1413 : * \return The number of seconds the string represents
1414 : * or IGNORE_VALUE on errors.
1415 : */
1416 0 : int64_t cache_control_settings::string_to_seconds(QString const & max_age)
1417 : {
1418 0 : if(max_age.isEmpty())
1419 : {
1420 : // undefined / invalid (not 0)
1421 : //
1422 0 : return IGNORE_VALUE;
1423 : }
1424 :
1425 0 : QByteArray const utf8(max_age.toUtf8());
1426 0 : char const * s(utf8.data());
1427 0 : char const * const start(s);
1428 0 : int64_t result(0);
1429 0 : for(; *s != '\0'; ++s)
1430 : {
1431 0 : if(*s < '0'
1432 0 : || *s > '9'
1433 0 : || s - start > 9) // too large! (for 1 year in seconds we need 8 digits maximum)
1434 : {
1435 : // invalid
1436 : //
1437 0 : return IGNORE_VALUE;
1438 : }
1439 0 : result = result * 10 + *s - '0';
1440 : }
1441 :
1442 0 : return result > AGE_MAXIMUM ? AGE_MAXIMUM : result;
1443 : }
1444 :
1445 :
1446 : /** \brief Retrieve the smallest value of two.
1447 : *
1448 : * This special minimum function returns the smallest of two values,
1449 : * only if one of those values is IGNORE_VALUE, then it is ignored
1450 : * and the other is returned. Of course, if both are IGNORE_VALUE,
1451 : * you get IGNORE_VALUE as a result.
1452 : *
1453 : * \note
1454 : * This function is expected to be used with the 'max-age' and
1455 : * 's-maxage' numbers. These numbers are expected to be defined
1456 : * between 0 and AGE_MAXIMUM, or set to IGNORE_VALUE.
1457 : *
1458 : * \param[in] a The left hand side value.
1459 : * \param[in] b The right hand side value.
1460 : *
1461 : * \return The smallest of a and b, ignoring either if set to IGNORE_VALUE.
1462 : */
1463 0 : int64_t cache_control_settings::minimum(int64_t const a, int64_t const b)
1464 : {
1465 : // if a or b are -1, then return the other
1466 : //
1467 0 : if(a == IGNORE_VALUE)
1468 : {
1469 : // in this case the value may return -1
1470 : //
1471 0 : return b;
1472 : }
1473 0 : if(b == IGNORE_VALUE)
1474 : {
1475 0 : return a;
1476 : }
1477 :
1478 : // normal std::min() otherwise
1479 : //
1480 0 : return std::min(a, b);
1481 : }
1482 :
1483 :
1484 6 : } // namespace snap
1485 :
1486 : // vim: ts=4 sw=4 et
|