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