varnish-cache/lib/libvarnish/vtim.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2011 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 * Semi-trivial functions to handle HTTP header timestamps according to
31
 * RFC 2616 section 3.3.
32
 *
33
 * We must parse four different formats:
34
 *       000000000011111111112222222222
35
 *       012345678901234567890123456789
36
 *       ------------------------------
37
 *      "Sun, 06 Nov 1994 08:49:37 GMT"         RFC822 & RFC1123
38
 *      "Sunday, 06-Nov-94 08:49:37 GMT"        RFC850
39
 *      "Sun Nov  6 08:49:37 1994"              ANSI-C asctime()
40
 *      "1994-11-06T08:49:37"                   ISO 8601
41
 *
42
 * And always output the RFC1123 format.
43
 *
44
 * So why are these functions hand-built ?
45
 *
46
 * Because the people behind POSIX were short-sighted morons who didn't think
47
 * anybody would ever need to deal with timestamps in multiple different
48
 * timezones at the same time -- for that matter, convert timestamps to
49
 * broken down UTC/GMT time.
50
 *
51
 * We could, and used to, get by by smashing our TZ variable to "UTC" but
52
 * that ruins the LOCALE for VMODs.
53
 *
54
 */
55
56
#include "config.h"
57
58
#include <time.h>
59
#include <sys/time.h>
60
61
#include <math.h>
62
#include <stdint.h>
63
#include <stdio.h>
64
#include <stdlib.h>
65
#include <string.h>
66
#ifdef __MACH__
67
#include <mach/mach_time.h>
68
#endif
69
70
#include "vdef.h"
71
72
#include "vas.h"
73
#include "vtim.h"
74
75
/* relax vtim parsing */
76
unsigned VTIM_postel = 0;
77
78
static const char * const weekday_name[] = {
79
        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
80
};
81
82
static const char * const more_weekday[] = {
83
        "day", "day", "sday", "nesday", "rsday", "day", "urday"
84
};
85
86
static const char * const month_name[] = {
87
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
88
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
89
};
90
91
static const int days_in_month[] = {
92
        31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
93
};
94
95
static const int days_before_month[] = {
96
        0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
97
};
98
99
#ifdef __MACH__
100
// http://stackoverflow.com/a/21352348
101
static uint64_t mt_base;
102
static double   mt_scale;
103
104
static void
105
mach_time_init(void)
106
{
107
        mach_timebase_info_data_t timebase;
108
109
        mt_base = mach_absolute_time();
110
111
        AZ(mach_timebase_info(&timebase));
112
        mt_scale = (double)timebase.numer / (double)timebase.denom * 1e-9;
113
}
114
115
static __attribute__((constructor)) void
116
init(void)
117
{
118
        mach_time_init();
119
}
120
#endif
121
122
/*
123
 * On older Solaris-incarnations, gethrtime() was faster than
124
 * clock_gettime(CLOCK_MONOTONIC). Our configure script prefers
125
 * clock_gettime if it is consistently at least twice as fast as
126
 * gethrtime(), which is the case on modern Solaris descendents.
127
 */
128
129
vtim_mono
130 743978
VTIM_mono(void)
131
{
132
#if defined(HAVE_GETHRTIME) && defined(USE_GETHRTIME)
133
        return (gethrtime() * 1e-9);
134
#else
135
        struct timespec ts;
136
137 743978
        AZ(clock_gettime(CLOCK_MONOTONIC, &ts));
138 743978
        return (ts.tv_sec + 1e-9 * ts.tv_nsec);
139
#endif
140
}
141
142
vtim_real
143 278746
VTIM_real(void)
144
{
145
#ifdef HAVE_CLOCK_GETTIME
146
        struct timespec ts;
147
148 278746
        AZ(clock_gettime(CLOCK_REALTIME, &ts));
149 278746
        return (ts.tv_sec + 1e-9 * ts.tv_nsec);
150
#else
151
        struct timeval tv;
152
153
        AZ(gettimeofday(&tv, NULL));
154
        return (tv.tv_sec + 1e-6 * tv.tv_usec);
155
#endif
156
}
157
158
void
159 6176701
VTIM_format(vtim_real t, char *p)
160
{
161
        struct tm tm;
162
        time_t tt;
163
164 6176701
        AN(p);
165 6176701
        *p = '\0';
166
167 6176701
        if (t < (vtim_real)INTMAX_MIN || t > (vtim_real)INTMAX_MAX)
168 1
                return;
169
170 6176700
        tt = (time_t)(intmax_t)t;
171 6176700
        if (gmtime_r(&tt, &tm) == NULL)
172 0
                return;
173
174 6176700
        AN(snprintf(p, VTIM_FORMAT_SIZE,
175
            "%s, %02d %s %4d %02d:%02d:%02d GMT",
176
            weekday_name[tm.tm_wday],
177
            tm.tm_mday, month_name[tm.tm_mon], tm.tm_year + 1900,
178
            tm.tm_hour, tm.tm_min, tm.tm_sec));
179 6176701
}
180
181
#ifdef TEST_DRIVER
182
#define FAIL()  \
183
        do { printf("\nFAIL <<%d>>\n", __LINE__); return (0); } while (0)
184
#else
185
#define FAIL()  \
186
        do { return (0); } while (0)
187
#endif
188
189
#define DIGIT(mult, fld)                                        \
190
        do {                                                    \
191
                if (*p < '0' || *p > '9')                       \
192
                        FAIL();                                 \
193
                fld += (*p - '0') * mult;                       \
194
                p++;                                            \
195
        } while(0)
196
197
#define MUSTBE(chr)                                             \
198
        do {                                                    \
199
                if (*p != chr)                                  \
200
                        FAIL();                                 \
201
                p++;                                            \
202
        } while(0)
203
204
#define WEEKDAY()                                               \
205
        do {                                                    \
206
                int i;                                          \
207
                for (i = 0; i < 7; i++) {                       \
208
                        if (!memcmp(p, weekday_name[i], 3)) {   \
209
                                weekday = i;                    \
210
                                break;                          \
211
                        }                                       \
212
                }                                               \
213
                if (i == 7)                                     \
214
                        FAIL();                                 \
215
                p += 3;                                         \
216
        } while(0)
217
218
219
#define MONTH()                                                 \
220
        do {                                                    \
221
                int i;                                          \
222
                for (i = 0; i < 12; i++) {                      \
223
                        if (!memcmp(p, month_name[i], 3)) {     \
224
                                month = i + 1;                  \
225
                                break;                          \
226
                        }                                       \
227
                }                                               \
228
                if (i == 12)                                    \
229
                        FAIL();                                 \
230
                p += 3;                                         \
231
        } while(0)
232
233
#define TIMESTAMP()                                             \
234
        do {                                                    \
235
                DIGIT(10, hour);                                \
236
                DIGIT(1, hour);                                 \
237
                MUSTBE(':');                                    \
238
                DIGIT(10, min);                                 \
239
                DIGIT(1, min);                                  \
240
                MUSTBE(':');                                    \
241
                DIGIT(10, sec);                                 \
242
                DIGIT(1, sec);                                  \
243
        } while(0)
244
245
vtim_real
246 19394893
VTIM_parse(const char *p)
247
{
248
        vtim_real t;
249 19394893
        int month = 0, year = 0, weekday = -1, mday = 0;
250 19394893
        int hour = 0, min = 0, sec = 0;
251
        int d, leap;
252
253 19394893
        if (p == NULL || *p == '\0')
254 1
                FAIL();
255
256 19394894
        while (*p == ' ')
257 2
                p++;
258
259 19394892
        if (*p >= '0' && *p <= '9') {
260
                /* ISO8601 -- "1994-11-06T08:49:37" */
261 6171141
                DIGIT(1000, year);
262 6171141
                DIGIT(100, year);
263 6171141
                DIGIT(10, year);
264 6171141
                DIGIT(1, year);
265 6171141
                MUSTBE('-');
266 6171127
                DIGIT(10, month);
267 6171127
                DIGIT(1, month);
268 6171127
                MUSTBE('-');
269 6171127
                DIGIT(10, mday);
270 6171127
                DIGIT(1, mday);
271 6171127
                MUSTBE('T');
272 6171127
                TIMESTAMP();
273 6171127
        } else {
274 52885738
                WEEKDAY();
275 13223747
                assert(weekday >= 0 && weekday <= 6);
276 13223747
                if (*p == ',') {
277
                        /* RFC822 & RFC1123 - "Sun, 06 Nov 1994 08:49:37 GMT" */
278 6173702
                        p++;
279 6173702
                        MUSTBE(' ');
280 6173702
                        if (VTIM_postel && *p && p[1] == ' ')
281 4
                                DIGIT(1, mday);
282
                        else {
283 6173698
                                DIGIT(10, mday);
284 6173698
                                DIGIT(1, mday);
285
                        }
286 6173702
                        MUSTBE(' ');
287 40263484
                        MONTH();
288 6173701
                        MUSTBE(' ');
289 6173701
                        DIGIT(1000, year);
290 6173701
                        DIGIT(100, year);
291 6173701
                        DIGIT(10, year);
292 6173701
                        DIGIT(1, year);
293 6173701
                        MUSTBE(' ');
294 6173701
                        TIMESTAMP();
295 6173701
                        MUSTBE(' ');
296 6173701
                        MUSTBE('G');
297 6173700
                        MUSTBE('M');
298 6173700
                        MUSTBE('T');
299 13223745
                } else if (*p == ' ') {
300
                        /* ANSI-C asctime() -- "Sun Nov  6 08:49:37 1994" */
301 6172156
                        p++;
302 40258139
                        MONTH();
303 6172152
                        MUSTBE(' ');
304 6172152
                        if (*p != ' ')
305 4346282
                                DIGIT(10, mday);
306
                        else
307 1825870
                                p++;
308 6172152
                        DIGIT(1, mday);
309 6172152
                        MUSTBE(' ');
310 6172152
                        TIMESTAMP();
311 6172152
                        MUSTBE(' ');
312 6172152
                        DIGIT(1000, year);
313 6172152
                        DIGIT(100, year);
314 6172152
                        DIGIT(10, year);
315 6172152
                        DIGIT(1, year);
316 7927930
                } else if (!memcmp(p, more_weekday[weekday],
317 877889
                    strlen(more_weekday[weekday]))) {
318
                        /* RFC850 -- "Sunday, 06-Nov-94 08:49:37 GMT" */
319 877885
                        p += strlen(more_weekday[weekday]);
320 877885
                        MUSTBE(',');
321 877885
                        MUSTBE(' ');
322 877885
                        DIGIT(10, mday);
323 877885
                        DIGIT(1, mday);
324 877885
                        MUSTBE('-');
325 5731016
                        MONTH();
326 877885
                        MUSTBE('-');
327 877885
                        DIGIT(10, year);
328 877885
                        DIGIT(1, year);
329 877885
                        year += 1900;
330 877885
                        if (year < 1969)
331 605052
                                year += 100;
332 877885
                        MUSTBE(' ');
333 877885
                        TIMESTAMP();
334 877885
                        MUSTBE(' ');
335 877885
                        MUSTBE('G');
336 877885
                        MUSTBE('M');
337 877885
                        MUSTBE('T');
338 877885
                } else
339 4
                        FAIL();
340
        }
341
342 19394868
        while (*p == ' ')
343 4
                p++;
344
345 19394864
        if (*p != '\0')
346 4
                FAIL();
347
348 19394860
        if (sec < 0 || sec > 60)        /* Leapseconds! */
349 4
                FAIL();
350 19394856
        if (min < 0 || min > 59)
351 4
                FAIL();
352 19394852
        if (hour < 0 || hour > 23)
353 4
                FAIL();
354 19394848
        if (month < 1 || month > 12)
355 4
                FAIL();
356 19394844
        if (mday < 1 || mday > days_in_month[month - 1])
357 4
                FAIL();
358 19394840
        if (year < 1899)
359 0
                FAIL();
360
361 19394840
        leap =
362 24253390
            ((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0);
363
364 19394840
        if (month == 2 && mday > 28 && !leap)
365 4
                FAIL();
366
367 19394836
        if (sec == 60)                  /* Ignore Leapseconds */
368 4
                sec--;
369
370 19394836
        t = ((hour * 60.) + min) * 60. + sec;
371
372 19394836
        d = (mday - 1) + days_before_month[month - 1];
373
374 19394836
        if (month > 2 && leap)
375 3930214
                d++;
376
377 19394836
        d += (year % 100) * 365;        /* There are 365 days in a year */
378
379 19394836
        if ((year % 100) > 0)           /* And a leap day every four years */
380 19175608
                d += (((year % 100) - 1) / 4);
381
382 19394836
        d += ((year / 100) - 20) *      /* Days relative to y2000 */
383
            (100 * 365 + 24);           /* 24 leapdays per year in a century */
384
385 19394836
        d += ((year - 1) / 400) - 4;    /* And one more every 400 years */
386
387
        /*
388
         * Now check weekday, if we have one.
389
         * 6 is because 2000-01-01 was a saturday.
390
         * 10000 is to make sure the modulus argument is always positive
391
         */
392 19394836
        if (weekday != -1 && (d + 6 + 7 * 10000) % 7 != weekday)
393 8
                FAIL();
394
395 19394828
        t += d * 86400.;
396
397 19394828
        t += 10957. * 86400.;           /* 10957 days frm UNIX epoch to y2000 */
398
399 19394828
        return (t);
400 19394893
}
401
402
void
403 107391
VTIM_sleep(vtim_dur t)
404
{
405
        struct timespec ts;
406
407 107391
        ts = VTIM_timespec(t);
408
409 107391
        (void)nanosleep(&ts, NULL);
410 107391
}
411
412
/*
413
 * VTIM_timeval and VTIM_timespec may need variants with different signatures
414
 * when vtim_real / vtim_mono typedefs are changed
415
 */
416
417
struct timeval
418 9020
VTIM_timeval(vtim_dur t)
419
{
420
        struct timeval tv;
421
422 9020
        AZ(isnan(t));
423 9020
        tv.tv_sec = (time_t)trunc(t);
424 9020
        tv.tv_usec = (int)(1e6 * (t - tv.tv_sec));
425 9020
        return (tv);
426
}
427
428
struct timespec
429 127128
VTIM_timespec(vtim_dur t)
430
{
431
        struct timespec tv;
432
433 127128
        AZ(isnan(t));
434 127128
        tv.tv_sec = (time_t)trunc(t);
435 127128
        tv.tv_nsec = (int)(1e9 * (t - tv.tv_sec));
436 127128
        return (tv);
437
}
438
439
struct timeval
440 9020
VTIM_timeval_sock(vtim_dur t)
441
{
442
443 9020
        return (VTIM_timeval(isinf(t) ? 0. : vmax(t, 1e-3)));
444
}
445
446
int
447 54796
VTIM_poll_tmo(vtim_dur tmo)
448
{
449
450 54796
        if (isinf(tmo))
451 3
                return (-1);
452 54793
        assert(!isnan(tmo));
453 54793
        return (vmax_t(int, 0, ((int)(tmo * 1e3))));
454 54796
}
455
456
#ifdef TEST_DRIVER
457
458
#pragma GCC diagnostic ignored "-Wformat-y2k"
459
460
#include <stdint.h>
461
462
static void
463 4
tst(const char *s, time_t good)
464
{
465
        time_t t;
466
        char buf[BUFSIZ];
467
468 4
        t = VTIM_parse(s);
469 4
        VTIM_format(t, buf);
470 4
        printf("%-30s -> %12jd -> %s\n", s, (intmax_t)t, buf);
471 4
        if (t != good) {
472 0
                printf("Parse error! Got: %jd should have %jd diff %jd\n",
473 0
                    (intmax_t)t, (intmax_t)good, (intmax_t)(t - good));
474 0
                exit(4);
475
        }
476 4
}
477
478
/* XXX keep as double for the time being */
479
static int
480 2
tst_delta_check(const char *name, double begin, double end, vtim_dur ref)
481
{
482 2
        const double tol_max = 1.1;
483 2
        const double tol_min = 1;
484
485 2
        printf("%s delta for %fs sleep: %f\n", name, ref, (end - begin));
486
487 2
        if ((end - begin) > tol_max * ref) {
488 0
                printf("%s delta above tolerance: ((%f - %f) = %f) > %f\n",
489 0
                    name, end, begin, (end - begin), tol_max);
490 0
                return (1);
491 2
        } else if ((end - begin) < tol_min * ref) {
492 0
                printf("%s delta below tolerance: ((%f - %f) = %f) < %f\n",
493 0
                    name, end, begin, (end - begin), tol_min);
494 0
                return (1);
495
        }
496 2
        return (0);
497 2
}
498
499
static void
500 1
tst_delta(void)
501
{
502
        vtim_mono m_begin, m_end;
503
        vtim_real r_begin, r_end;
504 1
        const vtim_dur ref = 1;
505 1
        int err = 0;
506
507 1
        r_begin = VTIM_real();
508 1
        m_begin = VTIM_mono();
509 1
        VTIM_sleep(ref);
510 1
        r_end = VTIM_real();
511 1
        m_end = VTIM_mono();
512
513 1
        err += tst_delta_check("VTIM_mono", m_begin, m_end, ref);
514 1
        err += tst_delta_check("VTIM_real", r_begin, r_end, ref);
515
516 1
        if (err) {
517 0
                printf("%d time delta test errors\n", err);
518 0
                exit(4);
519
        }
520 1
}
521
522
static void
523 1
bench(void)
524
{
525
        vtim_mono s, e;
526
        vtim_mono t_m;
527
        vtim_real t_r;
528
        unsigned long t_i;
529
        int i;
530
        char buf[64];
531
532 1
        t_m = 0;
533 1
        t_r = 0;
534 1
        s = VTIM_mono();
535 100001
        for (i=0; i<100000; i++)
536 100000
                t_r += VTIM_real();
537 1
        e = VTIM_mono();
538 1
        printf("real: %fs / %d = %fns - tst val %f\n",
539 1
            e - s, i, 1e9 * (e - s) / i, t_r);
540
541 1
        t_i = 0;
542 1
        s = VTIM_mono();
543 100001
        for (i=0; i<100000; i++)
544 100000
                t_m += VTIM_mono();
545 1
        e = VTIM_mono();
546 1
        printf("mono: %fs / %d = %fns - tst val %f\n",
547 1
            e - s, i, 1e9 * (e - s) / i, t_m);
548
549 1
        t_i = 0;
550 1
        s = VTIM_mono();
551 100001
        for (i=0; i<100000; i++) {
552 100000
                snprintf(buf, sizeof(buf), "%.6f", s);
553 100000
                t_i += buf[4];
554 100000
        }
555 1
        e = VTIM_mono();
556 1
        printf("printf %%.6f: %fs / %d = %fns - tst val %lu %s\n",
557 1
            e - s, i, 1e9 * (e - s) / i, t_i, buf);
558
559 1
        t_i = 0;
560 1
        s = VTIM_mono();
561 100001
        for (i=0; i<100000; i++) {
562 200000
                snprintf(buf, sizeof(buf), "%ju.%06ju",
563 100000
                    (uintmax_t)floor(s),
564 100000
                    (uintmax_t)floor((s * 1e6)) % 1000000UL);
565 100000
                t_i += buf[4];
566 100000
        }
567 1
        e = VTIM_mono();
568 1
        printf("printf %%ju.%%06ju: %fs / %d = %fns - tst val %lu %s\n",
569 1
            e - s, i, 1e9 * (e - s) / i, t_i, buf);
570 1
}
571
572
static void
573 19390197
parse_check(time_t t, const char *s)
574
{
575
        vtim_real tt;
576
        char buf[BUFSIZ];
577
578 19390197
        tt = VTIM_parse(s);
579 19390197
        if (tt != t) {
580 0
                VTIM_format(tt, buf);
581 0
                printf("  fm: %12jd <%s>\n", (intmax_t)t, s);
582 0
                printf("  to: %12.0f <%s>\n", tt, buf);
583 0
                exit(2);
584
        }
585 19390197
}
586
587
#define TTEST_MIN (sizeof(time_t) >= 8 ? -2209852800LL : INT32_MIN)
588
#define TTEST_MAX (sizeof(time_t) >= 8 ? 20000000000LL : INT32_MAX)
589
590
int
591 1
main(int argc, char **argv)
592
{
593
        time_t t;
594
        intmax_t iter;
595
        struct tm tm;
596
        char buf[BUFSIZ];
597
        char buf1[BUFSIZ];
598
599 1
        (void)argc;
600 1
        (void)argv;
601
602 1
        AZ(setenv("TZ", "UTC", 1));
603
604 1
        bench();
605
606
        /* Brute force test against libc version */
607 6171119
        for (iter = TTEST_MIN; iter < TTEST_MAX; iter += 3599) {
608 6171118
                t = (time_t)iter;
609 6171118
                gmtime_r(&t, &tm);
610 6171118
                strftime(buf1, sizeof buf1, "%a, %d %b %Y %T GMT", &tm);
611 6171118
                VTIM_format(t, buf);
612 6171118
                if (strcmp(buf, buf1)) {
613 0
                        printf("libc: <%s> Vtim <%s> %jd\n",
614 0
                            buf1, buf, (intmax_t)t);
615 0
                        exit(2);
616
                }
617 6171118
                parse_check(t, buf1);
618
619 6171118
                strftime(buf1, sizeof buf1, "%a %b %e %T %Y", &tm);
620 6171118
                parse_check(t, buf1);
621
622 6171118
                strftime(buf1, sizeof buf1, "%Y-%m-%dT%T", &tm);
623 6171118
                parse_check(t, buf1);
624
625 6171118
                if (tm.tm_year >= 69 && tm.tm_year < 169) {
626 876843
                        strftime(buf1, sizeof buf1, "%A, %d-%b-%y %T GMT", &tm);
627 876843
                        parse_check(t, buf1);
628 876843
                }
629 6171118
        }
630
631
        /* Examples from RFC2616 section 3.3.1 */
632 1
        tst("Sun, 06 Nov 1994 08:49:37 GMT", 784111777);
633 1
        tst("Sunday, 06-Nov-94 08:49:37 GMT", 784111777);
634 1
        tst("Sun Nov  6 08:49:37 1994", 784111777);
635
636 1
        tst("1994-11-06T08:49:37", 784111777);
637
638 1
        tst_delta();
639
640 1
        return (0);
641
}
642
643
#endif