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 15863978
VTIM_mono(void)
131
{
132
#if defined(HAVE_CLOCK_GETTIME) && !defined(USE_GETHRTIME)
133
        struct timespec ts;
134
135 15863978
        AZ(clock_gettime(CLOCK_MONOTONIC, &ts));
136 15863978
        return (ts.tv_sec + 1e-9 * ts.tv_nsec);
137
#elif defined(HAVE_GETHRTIME)
138
        return (gethrtime() * 1e-9);
139
#elif defined(__MACH__)
140
        uint64_t mt = mach_absolute_time() - mt_base;
141
142
        return (mt * mt_scale);
143
#else
144
#error Varnish needs some monotonic time source
145
#endif
146
}
147
148
vtim_real
149 4482201
VTIM_real(void)
150
{
151
#ifdef HAVE_CLOCK_GETTIME
152
        struct timespec ts;
153
154 4482201
        AZ(clock_gettime(CLOCK_REALTIME, &ts));
155 4482201
        return (ts.tv_sec + 1e-9 * ts.tv_nsec);
156
#else
157
        struct timeval tv;
158
159
        AZ(gettimeofday(&tv, NULL));
160
        return (tv.tv_sec + 1e-6 * tv.tv_usec);
161
#endif
162
}
163
164
void
165 146792
VTIM_format(vtim_real t, char *p)
166
{
167
        struct tm tm;
168
        time_t tt;
169
170 146792
        AN(p);
171 146792
        *p = '\0';
172
173 146792
        if (t < (vtim_real)INTMAX_MIN || t > (vtim_real)INTMAX_MAX)
174 28
                return;
175
176 146764
        tt = (time_t)(intmax_t)t;
177 146764
        if (gmtime_r(&tt, &tm) == NULL)
178 0
                return;
179
180 146764
        AN(snprintf(p, VTIM_FORMAT_SIZE,
181
            "%s, %02d %s %4d %02d:%02d:%02d GMT",
182
            weekday_name[tm.tm_wday],
183
            tm.tm_mday, month_name[tm.tm_mon], tm.tm_year + 1900,
184
            tm.tm_hour, tm.tm_min, tm.tm_sec));
185 146792
}
186
187
#ifdef TEST_DRIVER
188
#define FAIL()  \
189
        do { printf("\nFAIL <<%d>>\n", __LINE__); return (0); } while (0)
190
#else
191
#define FAIL()  \
192
        do { return (0); } while (0)
193
#endif
194
195
#define DIGIT(mult, fld)                                        \
196
        do {                                                    \
197
                if (*p < '0' || *p > '9')                       \
198
                        FAIL();                                 \
199
                fld += (*p - '0') * mult;                       \
200
                p++;                                            \
201
        } while(0)
202
203
#define MUSTBE(chr)                                             \
204
        do {                                                    \
205
                if (*p != chr)                                  \
206
                        FAIL();                                 \
207
                p++;                                            \
208
        } while(0)
209
210
#define WEEKDAY()                                               \
211
        do {                                                    \
212
                int i;                                          \
213
                for (i = 0; i < 7; i++) {                       \
214
                        if (!memcmp(p, weekday_name[i], 3)) {   \
215
                                weekday = i;                    \
216
                                break;                          \
217
                        }                                       \
218
                }                                               \
219
                if (i == 7)                                     \
220
                        FAIL();                                 \
221
                p += 3;                                         \
222
        } while(0)
223
224
225
#define MONTH()                                                 \
226
        do {                                                    \
227
                int i;                                          \
228
                for (i = 0; i < 12; i++) {                      \
229
                        if (!memcmp(p, month_name[i], 3)) {     \
230
                                month = i + 1;                  \
231
                                break;                          \
232
                        }                                       \
233
                }                                               \
234
                if (i == 12)                                    \
235
                        FAIL();                                 \
236
                p += 3;                                         \
237
        } while(0)
238
239
#define TIMESTAMP()                                             \
240
        do {                                                    \
241
                DIGIT(10, hour);                                \
242
                DIGIT(1, hour);                                 \
243
                MUSTBE(':');                                    \
244
                DIGIT(10, min);                                 \
245
                DIGIT(1, min);                                  \
246
                MUSTBE(':');                                    \
247
                DIGIT(10, sec);                                 \
248
                DIGIT(1, sec);                                  \
249
        } while(0)
250
251
vtim_real
252 122806
VTIM_parse(const char *p)
253
{
254
        vtim_real t;
255 122806
        int month = 0, year = 0, weekday = -1, mday = 0;
256 122806
        int hour = 0, min = 0, sec = 0;
257
        int d, leap;
258
259 122806
        if (p == NULL || *p == '\0')
260 28
                FAIL();
261
262 122834
        while (*p == ' ')
263 56
                p++;
264
265 122778
        if (*p >= '0' && *p <= '9') {
266
                /* ISO8601 -- "1994-11-06T08:49:37" */
267 616
                DIGIT(1000, year);
268 616
                DIGIT(100, year);
269 616
                DIGIT(10, year);
270 616
                DIGIT(1, year);
271 616
                MUSTBE('-');
272 224
                DIGIT(10, month);
273 224
                DIGIT(1, month);
274 224
                MUSTBE('-');
275 224
                DIGIT(10, mday);
276 224
                DIGIT(1, mday);
277 224
                MUSTBE('T');
278 224
                TIMESTAMP();
279 224
        } else {
280 295962
                WEEKDAY();
281 122050
                assert(weekday >= 0 && weekday <= 6);
282 122050
                if (*p == ',') {
283
                        /* RFC822 & RFC1123 - "Sun, 06 Nov 1994 08:49:37 GMT" */
284 67954
                        p++;
285 67954
                        MUSTBE(' ');
286 67954
                        if (VTIM_postel && *p && p[1] == ' ')
287 112
                                DIGIT(1, mday);
288
                        else {
289 67842
                                DIGIT(10, mday);
290 67842
                                DIGIT(1, mday);
291
                        }
292 67954
                        MUSTBE(' ');
293 759713
                        MONTH();
294 67926
                        MUSTBE(' ');
295 67926
                        DIGIT(1000, year);
296 67926
                        DIGIT(100, year);
297 67926
                        DIGIT(10, year);
298 67926
                        DIGIT(1, year);
299 67926
                        MUSTBE(' ');
300 67926
                        TIMESTAMP();
301 67926
                        MUSTBE(' ');
302 67926
                        MUSTBE('G');
303 67898
                        MUSTBE('M');
304 67898
                        MUSTBE('T');
305 121994
                } else if (*p == ' ') {
306
                        /* ANSI-C asctime() -- "Sun Nov  6 08:49:37 1994" */
307 26936
                        p++;
308 296632
                        MONTH();
309 26824
                        MUSTBE(' ');
310 26824
                        if (*p != ' ')
311 112
                                DIGIT(10, mday);
312
                        else
313 26712
                                p++;
314 26824
                        DIGIT(1, mday);
315 26824
                        MUSTBE(' ');
316 26824
                        TIMESTAMP();
317 26824
                        MUSTBE(' ');
318 26824
                        DIGIT(1000, year);
319 26824
                        DIGIT(100, year);
320 26824
                        DIGIT(10, year);
321 26824
                        DIGIT(1, year);
322 81144
                } else if (!memcmp(p, more_weekday[weekday],
323 27160
                    strlen(more_weekday[weekday]))) {
324
                        /* RFC850 -- "Sunday, 06-Nov-94 08:49:37 GMT" */
325 27048
                        p += strlen(more_weekday[weekday]);
326 27048
                        MUSTBE(',');
327 27048
                        MUSTBE(' ');
328 27048
                        DIGIT(10, mday);
329 27048
                        DIGIT(1, mday);
330 27048
                        MUSTBE('-');
331 296744
                        MONTH();
332 27048
                        MUSTBE('-');
333 27048
                        DIGIT(10, year);
334 27048
                        DIGIT(1, year);
335 27048
                        year += 1900;
336 27048
                        if (year < 1969)
337 336
                                year += 100;
338 27048
                        MUSTBE(' ');
339 27048
                        TIMESTAMP();
340 27048
                        MUSTBE(' ');
341 27048
                        MUSTBE('G');
342 27048
                        MUSTBE('M');
343 27048
                        MUSTBE('T');
344 27048
                } else
345 112
                        FAIL();
346
        }
347
348 122106
        while (*p == ' ')
349 112
                p++;
350
351 121994
        if (*p != '\0')
352 112
                FAIL();
353
354 121882
        if (sec < 0 || sec > 60)        /* Leapseconds! */
355 112
                FAIL();
356 121770
        if (min < 0 || min > 59)
357 112
                FAIL();
358 121658
        if (hour < 0 || hour > 23)
359 112
                FAIL();
360 121546
        if (month < 1 || month > 12)
361 112
                FAIL();
362 121434
        if (mday < 1 || mday > days_in_month[month - 1])
363 112
                FAIL();
364 121322
        if (year < 1899)
365 0
                FAIL();
366
367 121322
        leap =
368 123730
            ((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0);
369
370 121322
        if (month == 2 && mday > 28 && !leap)
371 112
                FAIL();
372
373 121210
        if (sec == 60)                  /* Ignore Leapseconds */
374 112
                sec--;
375
376 121210
        t = ((hour * 60.) + min) * 60. + sec;
377
378 121210
        d = (mday - 1) + days_before_month[month - 1];
379
380 121210
        if (month > 2 && leap)
381 2268
                d++;
382
383 121210
        d += (year % 100) * 365;        /* There are 365 days in a year */
384
385 121210
        if ((year % 100) > 0)           /* And a leap day every four years */
386 121210
                d += (((year % 100) - 1) / 4);
387
388 121210
        d += ((year / 100) - 20) *      /* Days relative to y2000 */
389
            (100 * 365 + 24);           /* 24 leapdays per year in a century */
390
391 121210
        d += ((year - 1) / 400) - 4;    /* And one more every 400 years */
392
393
        /*
394
         * Now check weekday, if we have one.
395
         * 6 is because 2000-01-01 was a saturday.
396
         * 10000 is to make sure the modulus argument is always positive
397
         */
398 121210
        if (weekday != -1 && (d + 6 + 7 * 10000) % 7 != weekday)
399 224
                FAIL();
400
401 120986
        t += d * 86400.;
402
403 120986
        t += 10957. * 86400.;           /* 10957 days frm UNIX epoch to y2000 */
404
405 120986
        return (t);
406 122806
}
407
408
void
409 2536429
VTIM_sleep(vtim_dur t)
410
{
411
#ifdef HAVE_NANOSLEEP
412
        struct timespec ts;
413
414 2536429
        ts = VTIM_timespec(t);
415
416 2536429
        (void)nanosleep(&ts, NULL);
417
#else
418
        if (t >= 1.) {
419
                (void)sleep(floor(t));
420
                t -= floor(t);
421
        }
422
        /* XXX: usleep() is not mandated to be thread safe */
423
        t *= 1e6;
424
        if (t > 0)
425
                (void)usleep(floor(t));
426
#endif
427 2536429
}
428
429
/*
430
 * VTIM_timeval and VTIM_timespec may need variants with different signatures
431
 * when vtim_real / vtim_mono typedefs are changed
432
 */
433
434
struct timeval
435 142190
VTIM_timeval(vtim_dur t)
436
{
437
        struct timeval tv;
438
439 142190
        AZ(isnan(t));
440 142190
        tv.tv_sec = (time_t)trunc(t);
441 142190
        tv.tv_usec = (int)(1e6 * (t - tv.tv_sec));
442 142190
        return (tv);
443
}
444
445
struct timespec
446 3026705
VTIM_timespec(vtim_dur t)
447
{
448
        struct timespec tv;
449
450 3026705
        AZ(isnan(t));
451 3026705
        tv.tv_sec = (time_t)trunc(t);
452 3026705
        tv.tv_nsec = (int)(1e9 * (t - tv.tv_sec));
453 3026705
        return (tv);
454
}
455
456
457
#ifdef TEST_DRIVER
458
459
/**********************************************************************
460
 * Compile with:
461
 *      cc -o foo -DTEST_DRIVER -I../.. -I../../include vtim.c vas.c -lm
462
 * Test with:
463
 *      ./foo
464
 */
465
466
#include <stdint.h>
467
468
static void
469
tst(const char *s, time_t good)
470
{
471
        time_t t;
472
        char buf[BUFSIZ];
473
474
        t = VTIM_parse(s);
475
        VTIM_format(t, buf);
476
        printf("%-30s -> %12jd -> %s\n", s, (intmax_t)t, buf);
477
        if (t != good) {
478
                printf("Parse error! Got: %jd should have %jd diff %jd\n",
479
                    (intmax_t)t, (intmax_t)good, (intmax_t)(t - good));
480
                exit(4);
481
        }
482
}
483
484
/* XXX keep as double for the time being */
485
static int
486
tst_delta_check(const char *name, double begin, double end, vtim_dur ref)
487
{
488
        const double tol_max = 1.1;
489
        const double tol_min = 1;
490
491
        printf("%s delta for %fs sleep: %f\n", name, ref, (end - begin));
492
493
        if ((end - begin) > tol_max * ref) {
494
                printf("%s delta above tolerance: ((%f - %f) = %f) > %f\n",
495
                    name, end, begin, (end - begin), tol_max);
496
                return (1);
497
        } else if ((end - begin) < tol_min * ref) {
498
                printf("%s delta below tolerance: ((%f - %f) = %f) < %f\n",
499
                    name, end, begin, (end - begin), tol_min);
500
                return (1);
501
        }
502
        return (0);
503
}
504
505
static void
506
tst_delta()
507
{
508
        vtim_mono m_begin, m_end;
509
        vtim_real r_begin, r_end;
510
        const vtim_dur ref = 1;
511
        int err = 0;
512
513
        r_begin = VTIM_real();
514
        m_begin = VTIM_mono();
515
        VTIM_sleep(ref);
516
        r_end = VTIM_real();
517
        m_end = VTIM_mono();
518
519
        err += tst_delta_check("VTIM_mono", m_begin, m_end, ref);
520
        err += tst_delta_check("VTIM_real", r_begin, r_end, ref);
521
522
        if (err) {
523
                printf("%d time delta test errors\n", err);
524
                exit(4);
525
        }
526
}
527
528
static void
529
bench()
530
{
531
        vtim_mono s, e;
532
        vtim_mono t_m;
533
        vtim_real t_r;
534
        unsigned long t_i;
535
        int i;
536
        char buf[64];
537
538
        t_r = 0;
539
        s = VTIM_mono();
540
        for (i=0; i<100000; i++)
541
                t_r += VTIM_real();
542
        e = VTIM_mono();
543
        printf("real: %fs / %d = %fns - tst val %f\n",
544
            e - s, i, 1e9 * (e - s) / i, t_r);
545
546
        t_i = 0;
547
        s = VTIM_mono();
548
        for (i=0; i<100000; i++)
549
                t_m += VTIM_mono();
550
        e = VTIM_mono();
551
        printf("mono: %fs / %d = %fns - tst val %f\n",
552
            e - s, i, 1e9 * (e - s) / i, t_m);
553
554
        t_i = 0;
555
        s = VTIM_mono();
556
        for (i=0; i<100000; i++) {
557
                snprintf(buf, sizeof(buf), "%.6f", s);
558
                t_i += buf[4];
559
        }
560
        e = VTIM_mono();
561
        printf("printf %%.6f: %fs / %d = %fns - tst val %lu %s\n",
562
            e - s, i, 1e9 * (e - s) / i, t_i, buf);
563
564
        t_i = 0;
565
        s = VTIM_mono();
566
        for (i=0; i<100000; i++) {
567
                snprintf(buf, sizeof(buf), "%ju.%06ju",
568
                    (uint64_t)floor(s),
569
                    (uint64_t)floor((s * 1e6)) % 1000000UL);
570
                t_i += buf[4];
571
        }
572
        e = VTIM_mono();
573
        printf("printf %%ju.%%06ju: %fs / %d = %fns - tst val %lu %s\n",
574
            e - s, i, 1e9 * (e - s) / i, t_i, buf);
575
}
576
577
void
578
parse_check(time_t t, const char *s)
579
{
580
        vtim_real tt;
581
        char buf[BUFSIZ];
582
583
        tt = VTIM_parse(s);
584
        if (tt != t) {
585
                VTIM_format(tt, buf);
586
                printf("  fm: %12jd <%s>\n", (intmax_t)t, s);
587
                printf("  to: %12.0f <%s>\n", tt, buf);
588
                exit(2);
589
        }
590
}
591
592
int
593
main(int argc, char **argv)
594
{
595
        time_t t;
596
        struct tm tm;
597
        char buf[BUFSIZ];
598
        char buf1[BUFSIZ];
599
600
        AZ(setenv("TZ", "UTC", 1));
601
        assert(sizeof t >= 8);
602
603
        bench();
604
605
        /* Brute force test against libc version */
606
        for (t = -2209852800; t < 20000000000; t += 3599) {
607
                gmtime_r(&t, &tm);
608
                strftime(buf1, sizeof buf1, "%a, %d %b %Y %T GMT", &tm);
609
                VTIM_format(t, buf);
610
                if (strcmp(buf, buf1)) {
611
                        printf("libc: <%s> Vtim <%s> %jd\n",
612
                            buf1, buf, (intmax_t)t);
613
                        exit(2);
614
                }
615
                parse_check(t, buf1);
616
617
                strftime(buf1, sizeof buf1, "%a %b %e %T %Y", &tm);
618
                parse_check(t, buf1);
619
620
                strftime(buf1, sizeof buf1, "%Y-%m-%dT%T", &tm);
621
                parse_check(t, buf1);
622
623
                if (tm.tm_year >= 69 && tm.tm_year < 169) {
624
                        strftime(buf1, sizeof buf1, "%A, %d-%b-%y %T GMT", &tm);
625
                        parse_check(t, buf1);
626
                }
627
        }
628
629
        /* Examples from RFC2616 section 3.3.1 */
630
        tst("Sun, 06 Nov 1994 08:49:37 GMT", 784111777);
631
        tst("Sunday, 06-Nov-94 08:49:37 GMT", 784111777);
632
        tst("Sun Nov  6 08:49:37 1994", 784111777);
633
634
        tst("1994-11-06T08:49:37", 784111777);
635
636
        tst_delta();
637
638
        return (0);
639
}
640
#endif