/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 |