Coverage Report

Created: 2026-06-30 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pistache/src/common/http_defs.cc
Line
Count
Source
1
/*
2
 * SPDX-FileCopyrightText: 2015 Mathieu Stefani
3
 *
4
 * SPDX-License-Identifier: Apache-2.0
5
 */
6
7
/* http_defs.cc
8
   Mathieu Stefani, 01 September 2015
9
10
   Implementation of http definitions
11
*/
12
13
#include <iomanip>
14
#include <iostream>
15
16
#include <pistache/common.h>
17
#ifdef __GNUC__
18
#pragma GCC diagnostic push
19
#pragma GCC diagnostic ignored "-Wconversion"
20
#endif
21
#include <pistache/date_wrapper.h>
22
#ifdef __GNUC__
23
#pragma GCC diagnostic pop
24
#endif
25
#include <pistache/http_defs.h>
26
27
#include PST_CLOCK_GETTIME_HDR
28
29
namespace Pistache::Http
30
{
31
32
    namespace
33
    {
34
        using time_point = FullDate::time_point;
35
36
        bool parse_RFC_1123(const std::string& s, time_point& tp)
37
10.4k
        {
38
10.4k
            std::istringstream in { s };
39
10.4k
            in >> date::parse("%a, %d %b %Y %T %Z", tp);
40
10.4k
            if (in.fail())
41
9.82k
            {
42
                // Google seems to use this 1123 variant, like this:
43
                // from www.google.com: expires=Mon, 26-May-2025 18:38:48 GMT
44
9.82k
                std::istringstream in2 { s };
45
9.82k
                in2 >> date::parse("%a, %d-%b-%Y %T %Z", tp);
46
9.82k
                return !in2.fail();
47
9.82k
            }
48
618
            return true;
49
10.4k
        }
50
51
        bool parse_RFC_850(const std::string& s, time_point& tp)
52
9.65k
        {
53
9.65k
            std::istringstream in { s };
54
9.65k
            in >> date::parse("%A, %d-%b-%y %T %Z", tp);
55
9.65k
            return !in.fail();
56
9.65k
        }
57
58
        bool parse_asctime(const std::string& s, time_point& tp)
59
7.88k
        {
60
7.88k
            std::istringstream in { s };
61
7.88k
            in >> date::parse("%a %b %d %T %Y", tp);
62
7.88k
            return !in.fail();
63
7.88k
        }
64
65
        bool parse_epoch(const std::string& s, time_point& tp)
66
5.25k
        {
67
14.8k
            for (unsigned int i = 0; i < s.size(); ++i)
68
13.5k
            {
69
13.5k
                if (!std::isdigit(s[i]))
70
3.93k
                    return false;
71
13.5k
            }
72
73
1.32k
            try
74
1.32k
            {
75
1.32k
                tp = time_point(std::chrono::seconds(std::stoull(s)));
76
1.32k
            }
77
1.32k
            catch (std::out_of_range& e)
78
1.32k
            {
79
6
                return false;
80
6
            }
81
82
1.30k
            return true;
83
1.32k
        }
84
85
    } // anonymous namespace
86
87
    CacheDirective::CacheDirective(Directive directive)
88
        : directive_()
89
2.42k
        , data()
90
2.42k
    {
91
2.42k
        init(directive, std::chrono::seconds(0));
92
2.42k
    }
93
94
    CacheDirective::CacheDirective(Directive directive, std::chrono::seconds delta)
95
        : directive_()
96
17.0k
        , data()
97
17.0k
    {
98
17.0k
        init(directive, delta);
99
17.0k
    }
100
101
    std::chrono::seconds CacheDirective::delta() const
102
17.0k
    {
103
17.0k
        switch (directive_)
104
17.0k
        {
105
15.3k
        case MaxAge:
106
15.3k
            return std::chrono::seconds(data.maxAge);
107
891
        case SMaxAge:
108
891
            return std::chrono::seconds(data.sMaxAge);
109
826
        case MaxStale:
110
826
            return std::chrono::seconds(data.maxStale);
111
23
        case MinFresh:
112
23
            return std::chrono::seconds(data.minFresh);
113
0
        default:
114
0
            throw std::domain_error("Invalid operation on cache directive");
115
17.0k
        }
116
17.0k
    }
117
118
    void CacheDirective::init(Directive directive, std::chrono::seconds delta)
119
19.5k
    {
120
19.5k
        directive_ = directive;
121
19.5k
        switch (directive)
122
19.5k
        {
123
15.3k
        case MaxAge:
124
15.3k
            data.maxAge = delta.count();
125
15.3k
            break;
126
891
        case SMaxAge:
127
891
            data.sMaxAge = delta.count();
128
891
            break;
129
826
        case MaxStale:
130
826
            data.maxStale = delta.count();
131
826
            break;
132
23
        case MinFresh:
133
23
            data.minFresh = delta.count();
134
23
            break;
135
2.42k
        default:
136
2.42k
            break;
137
19.5k
        }
138
19.5k
    }
139
140
    FullDate FullDate::fromString(const std::string& str)
141
10.4k
    {
142
143
10.4k
        FullDate::time_point tp;
144
10.4k
        if (parse_RFC_1123(str, tp))
145
782
            return FullDate(tp);
146
9.65k
        else if (parse_RFC_850(str, tp))
147
1.77k
            return FullDate(tp);
148
7.88k
        else if (parse_asctime(str, tp))
149
2.62k
            return FullDate(tp);
150
5.25k
        else if (parse_epoch(str, tp))
151
1.30k
            return FullDate(tp);
152
153
3.95k
        PS_LOG_DEBUG_ARGS("Failed parsing date: %s", str.c_str());
154
3.95k
        throw std::runtime_error("Invalid Date format");
155
10.4k
    }
156
157
    void FullDate::write(std::ostream& os, Type type) const
158
4.06k
    {
159
4.06k
        switch (type)
160
4.06k
        {
161
4.06k
        case Type::RFC1123:
162
4.06k
            date::to_stream(os, "%a, %d %b %Y %T %Z", date_);
163
4.06k
            break;
164
0
        case Type::RFC1123GMT: {
165
            // Requires GMT so we must use std::gmtime to convert to GMT.  We
166
            // cannot use "%Z" since may refer to the local time zone name,
167
            // not the name associated with the std::tm object (since std::tm
168
            // isn't guaranteed to have a tm_zone field - it only does on
169
            // POSIX.1-2024 systems; this issue seen on NetBSD 10.0).
170
0
            time_t t = std::chrono::system_clock::to_time_t(date_);
171
172
0
            struct tm gmtm;
173
0
            PST_GMTIME_R(&t, &gmtm);
174
0
            os << std::put_time(&gmtm, "%a, %d %b %Y %T GMT");
175
0
        }
176
0
        break;
177
0
        case Type::RFC850:
178
0
            date::to_stream(os, "%a, %d-%b-%y %T %Z", date_);
179
0
            break;
180
0
        case Type::AscTime:
181
0
            date::to_stream(os, "%a %b %d %T %Y", date_);
182
0
            break;
183
0
        default:
184
0
            throw std::runtime_error("Invalid use of FullDate::write");
185
4.06k
        }
186
4.06k
    }
187
188
    const char* versionString(Version version)
189
0
    {
190
0
        switch (version)
191
0
        {
192
0
        case Version::Http10:
193
0
            return "HTTP/1.0";
194
0
        case Version::Http11:
195
0
            return "HTTP/1.1";
196
0
        }
197
198
0
        Pistache::details::unreachable();
199
0
    }
200
201
    const char* methodString(Method method)
202
0
    {
203
0
        switch (method)
204
0
        {
205
0
#define METHOD(name, str) \
206
0
    case Method::name:    \
207
0
        return str;
208
0
            HTTP_METHODS
209
0
#undef METHOD
210
0
        }
211
212
0
        Pistache::details::unreachable();
213
0
    }
214
215
    const char* codeString(Code code)
216
0
    {
217
0
        switch (code)
218
0
        {
219
0
#define CODE(_, name, str) \
220
0
    case Code::name:       \
221
0
        return str;
222
0
            STATUS_CODES
223
0
#undef CODE
224
0
        }
225
226
0
        return "";
227
0
    }
228
229
    std::ostream& operator<<(std::ostream& os, Version version)
230
0
    {
231
0
        os << versionString(version);
232
0
        return os;
233
0
    }
234
235
    std::ostream& operator<<(std::ostream& os, Method method)
236
0
    {
237
0
        os << methodString(method);
238
0
        return os;
239
0
    }
240
241
    std::ostream& operator<<(std::ostream& os, Code code)
242
0
    {
243
0
        os << codeString(code);
244
0
        return os;
245
0
    }
246
247
    HttpError::HttpError(Code code, std::string reason)
248
7.71k
        : code_(static_cast<int>(code))
249
7.71k
        , reason_(std::move(reason))
250
7.71k
    { }
251
252
    HttpError::HttpError(int code, std::string reason)
253
0
        : code_(code)
254
0
        , reason_(std::move(reason))
255
0
    { }
256
257
} // namespace Pistache::Http