Line data Source code
1 : //
2 : // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/http_proto
8 : //
9 :
10 : #include <boost/http_proto/field.hpp>
11 : #include <boost/core/detail/string_view.hpp>
12 : #include <boost/assert.hpp>
13 : #include <algorithm>
14 : #include <array>
15 : #include <cstring>
16 : #include <ostream>
17 :
18 : namespace boost {
19 : namespace http_proto {
20 :
21 : namespace detail {
22 :
23 : struct field_table
24 : {
25 : static
26 : std::uint32_t
27 28853 : get_chars(
28 : unsigned char const* p) noexcept
29 : {
30 : // VFALCO memcpy is endian-dependent
31 : //std::memcpy(&v, p, 4);
32 : // Compiler should be smart enough to
33 : // optimize this down to one instruction.
34 : return
35 28853 : p[0] |
36 28853 : (p[1] << 8) |
37 28853 : (p[2] << 16) |
38 28853 : (p[3] << 24);
39 : }
40 :
41 : using array_type = std::array<
42 : core::string_view, 357>;
43 :
44 : // Strings are converted to lowercase
45 : static
46 : std::uint32_t
47 7835 : digest(core::string_view s)
48 : {
49 7835 : std::uint32_t r = 0;
50 7835 : std::size_t n = s.size();
51 : auto p = reinterpret_cast<
52 7835 : unsigned char const*>(s.data());
53 : // consume N characters at a time
54 : // VFALCO Can we do 8 on 64-bit systems?
55 26816 : while(n >= 4)
56 : {
57 18981 : auto const v = get_chars(p);
58 18981 : r = (r * 5 + (
59 18981 : v | 0x20202020 )); // convert to lower
60 18981 : p += 4;
61 18981 : n -= 4;
62 : }
63 : // handle remaining characters
64 19805 : while( n > 0 )
65 : {
66 11970 : r = r * 5 + ( *p | 0x20 );
67 11970 : ++p;
68 11970 : --n;
69 : }
70 7835 : return r;
71 : }
72 :
73 : // This comparison is case-insensitive, and the
74 : // strings must contain only valid http field characters.
75 : static
76 : bool
77 2269 : equals(
78 : core::string_view lhs,
79 : core::string_view rhs)
80 : {
81 : using Int = std::uint32_t; // VFALCO std::size_t?
82 2269 : auto n = lhs.size();
83 2269 : if(n != rhs.size())
84 3 : return false;
85 : auto p1 = reinterpret_cast<
86 2266 : unsigned char const*>(lhs.data());
87 : auto p2 = reinterpret_cast<
88 2266 : unsigned char const*>(rhs.data());
89 2266 : auto constexpr S = sizeof(Int);
90 2266 : auto constexpr Mask = static_cast<Int>(
91 : 0xDFDFDFDFDFDFDFDF & ~Int{0});
92 7202 : for(; n >= S; p1 += S, p2 += S, n -= S)
93 : {
94 4936 : Int const v1 = get_chars(p1);
95 4936 : Int const v2 = get_chars(p2);
96 4936 : if((v1 ^ v2) & Mask)
97 0 : return false;
98 : }
99 6430 : for(; n; ++p1, ++p2, --n)
100 4164 : if(( *p1 ^ *p2) & 0xDF)
101 0 : return false;
102 2266 : return true;
103 : }
104 :
105 : array_type by_name_;
106 :
107 : enum { N = 5155 };
108 : unsigned char map_[ N ][ 2 ] = {};
109 :
110 : /*
111 : From:
112 :
113 : https://www.iana.org/assignments/message-headers/message-headers.xhtml
114 : */
115 13 : field_table()
116 13 : : by_name_({{
117 : // string constants
118 : "<unknown-field>",
119 : "A-IM",
120 : "Accept",
121 : "Accept-Additions",
122 : "Accept-Charset",
123 : "Accept-Datetime",
124 : "Accept-Encoding",
125 : "Accept-Features",
126 : "Accept-Language",
127 : "Accept-Patch",
128 : "Accept-Post",
129 : "Accept-Ranges",
130 : "Access-Control",
131 : "Access-Control-Allow-Credentials",
132 : "Access-Control-Allow-Headers",
133 : "Access-Control-Allow-Methods",
134 : "Access-Control-Allow-Origin",
135 : "Access-Control-Expose-Headers",
136 : "Access-Control-Max-Age",
137 : "Access-Control-Request-Headers",
138 : "Access-Control-Request-Method",
139 : "Age",
140 : "Allow",
141 : "ALPN",
142 : "Also-Control",
143 : "Alt-Svc",
144 : "Alt-Used",
145 : "Alternate-Recipient",
146 : "Alternates",
147 : "Apparently-To",
148 : "Apply-To-Redirect-Ref",
149 : "Approved",
150 : "Archive",
151 : "Archived-At",
152 : "Article-Names",
153 : "Article-Updates",
154 : "Authentication-Control",
155 : "Authentication-Info",
156 : "Authentication-Results",
157 : "Authorization",
158 : "Auto-Submitted",
159 : "Autoforwarded",
160 : "Autosubmitted",
161 : "Base",
162 : "Bcc",
163 : "Body",
164 : "C-Ext",
165 : "C-Man",
166 : "C-Opt",
167 : "C-PEP",
168 : "C-PEP-Info",
169 : "Cache-Control",
170 : "CalDAV-Timezones",
171 : "Cancel-Key",
172 : "Cancel-Lock",
173 : "Cc",
174 : "Close",
175 : "Comments",
176 : "Compliance",
177 : "Connection",
178 : "Content-Alternative",
179 : "Content-Base",
180 : "Content-Description",
181 : "Content-Disposition",
182 : "Content-Duration",
183 : "Content-Encoding",
184 : "Content-features",
185 : "Content-ID",
186 : "Content-Identifier",
187 : "Content-Language",
188 : "Content-Length",
189 : "Content-Location",
190 : "Content-MD5",
191 : "Content-Range",
192 : "Content-Return",
193 : "Content-Script-Type",
194 : "Content-Style-Type",
195 : "Content-Transfer-Encoding",
196 : "Content-Type",
197 : "Content-Version",
198 : "Control",
199 : "Conversion",
200 : "Conversion-With-Loss",
201 : "Cookie",
202 : "Cookie2",
203 : "Cost",
204 : "DASL",
205 : "Date",
206 : "Date-Received",
207 : "DAV",
208 : "Default-Style",
209 : "Deferred-Delivery",
210 : "Delivery-Date",
211 : "Delta-Base",
212 : "Depth",
213 : "Derived-From",
214 : "Destination",
215 : "Differential-ID",
216 : "Digest",
217 : "Discarded-X400-IPMS-Extensions",
218 : "Discarded-X400-MTS-Extensions",
219 : "Disclose-Recipients",
220 : "Disposition-Notification-Options",
221 : "Disposition-Notification-To",
222 : "Distribution",
223 : "DKIM-Signature",
224 : "DL-Expansion-History",
225 : "Downgraded-Bcc",
226 : "Downgraded-Cc",
227 : "Downgraded-Disposition-Notification-To",
228 : "Downgraded-Final-Recipient",
229 : "Downgraded-From",
230 : "Downgraded-In-Reply-To",
231 : "Downgraded-Mail-From",
232 : "Downgraded-Message-Id",
233 : "Downgraded-Original-Recipient",
234 : "Downgraded-Rcpt-To",
235 : "Downgraded-References",
236 : "Downgraded-Reply-To",
237 : "Downgraded-Resent-Bcc",
238 : "Downgraded-Resent-Cc",
239 : "Downgraded-Resent-From",
240 : "Downgraded-Resent-Reply-To",
241 : "Downgraded-Resent-Sender",
242 : "Downgraded-Resent-To",
243 : "Downgraded-Return-Path",
244 : "Downgraded-Sender",
245 : "Downgraded-To",
246 : "EDIINT-Features",
247 : "Eesst-Version",
248 : "Encoding",
249 : "Encrypted",
250 : "Errors-To",
251 : "ETag",
252 : "Expect",
253 : "Expires",
254 : "Expiry-Date",
255 : "Ext",
256 : "Followup-To",
257 : "Forwarded",
258 : "From",
259 : "Generate-Delivery-Report",
260 : "GetProfile",
261 : "Hobareg",
262 : "Host",
263 : "HTTP2-Settings",
264 : "If",
265 : "If-Match",
266 : "If-Modified-Since",
267 : "If-None-Match",
268 : "If-Range",
269 : "If-Schedule-Tag-Match",
270 : "If-Unmodified-Since",
271 : "IM",
272 : "Importance",
273 : "In-Reply-To",
274 : "Incomplete-Copy",
275 : "Injection-Date",
276 : "Injection-Info",
277 : "Jabber-ID",
278 : "Keep-Alive",
279 : "Keywords",
280 : "Label",
281 : "Language",
282 : "Last-Modified",
283 : "Latest-Delivery-Time",
284 : "Lines",
285 : "Link",
286 : "List-Archive",
287 : "List-Help",
288 : "List-ID",
289 : "List-Owner",
290 : "List-Post",
291 : "List-Subscribe",
292 : "List-Unsubscribe",
293 : "List-Unsubscribe-Post",
294 : "Location",
295 : "Lock-Token",
296 : "Man",
297 : "Max-Forwards",
298 : "Memento-Datetime",
299 : "Message-Context",
300 : "Message-ID",
301 : "Message-Type",
302 : "Meter",
303 : "Method-Check",
304 : "Method-Check-Expires",
305 : "MIME-Version",
306 : "MMHS-Acp127-Message-Identifier",
307 : "MMHS-Authorizing-Users",
308 : "MMHS-Codress-Message-Indicator",
309 : "MMHS-Copy-Precedence",
310 : "MMHS-Exempted-Address",
311 : "MMHS-Extended-Authorisation-Info",
312 : "MMHS-Handling-Instructions",
313 : "MMHS-Message-Instructions",
314 : "MMHS-Message-Type",
315 : "MMHS-Originator-PLAD",
316 : "MMHS-Originator-Reference",
317 : "MMHS-Other-Recipients-Indicator-CC",
318 : "MMHS-Other-Recipients-Indicator-To",
319 : "MMHS-Primary-Precedence",
320 : "MMHS-Subject-Indicator-Codes",
321 : "MT-Priority",
322 : "Negotiate",
323 : "Newsgroups",
324 : "NNTP-Posting-Date",
325 : "NNTP-Posting-Host",
326 : "Non-Compliance",
327 : "Obsoletes",
328 : "Opt",
329 : "Optional",
330 : "Optional-WWW-Authenticate",
331 : "Ordering-Type",
332 : "Organization",
333 : "Origin",
334 : "Original-Encoded-Information-Types",
335 : "Original-From",
336 : "Original-Message-ID",
337 : "Original-Recipient",
338 : "Original-Sender",
339 : "Original-Subject",
340 : "Originator-Return-Address",
341 : "Overwrite",
342 : "P3P",
343 : "Path",
344 : "PEP",
345 : "Pep-Info",
346 : "PICS-Label",
347 : "Position",
348 : "Posting-Version",
349 : "Pragma",
350 : "Prefer",
351 : "Preference-Applied",
352 : "Prevent-NonDelivery-Report",
353 : "Priority",
354 : "Privicon",
355 : "ProfileObject",
356 : "Protocol",
357 : "Protocol-Info",
358 : "Protocol-Query",
359 : "Protocol-Request",
360 : "Proxy-Authenticate",
361 : "Proxy-Authentication-Info",
362 : "Proxy-Authorization",
363 : "Proxy-Connection",
364 : "Proxy-Features",
365 : "Proxy-Instruction",
366 : "Public",
367 : "Public-Key-Pins",
368 : "Public-Key-Pins-Report-Only",
369 : "Range",
370 : "Received",
371 : "Received-SPF",
372 : "Redirect-Ref",
373 : "References",
374 : "Referer",
375 : "Referer-Root",
376 : "Relay-Version",
377 : "Reply-By",
378 : "Reply-To",
379 : "Require-Recipient-Valid-Since",
380 : "Resent-Bcc",
381 : "Resent-Cc",
382 : "Resent-Date",
383 : "Resent-From",
384 : "Resent-Message-ID",
385 : "Resent-Reply-To",
386 : "Resent-Sender",
387 : "Resent-To",
388 : "Resolution-Hint",
389 : "Resolver-Location",
390 : "Retry-After",
391 : "Return-Path",
392 : "Safe",
393 : "Schedule-Reply",
394 : "Schedule-Tag",
395 : "Sec-Fetch-Dest",
396 : "Sec-Fetch-Mode",
397 : "Sec-Fetch-Site",
398 : "Sec-Fetch-User",
399 : "Sec-WebSocket-Accept",
400 : "Sec-WebSocket-Extensions",
401 : "Sec-WebSocket-Key",
402 : "Sec-WebSocket-Protocol",
403 : "Sec-WebSocket-Version",
404 : "Security-Scheme",
405 : "See-Also",
406 : "Sender",
407 : "Sensitivity",
408 : "Server",
409 : "Set-Cookie",
410 : "Set-Cookie2",
411 : "SetProfile",
412 : "SIO-Label",
413 : "SIO-Label-History",
414 : "SLUG",
415 : "SoapAction",
416 : "Solicitation",
417 : "Status-URI",
418 : "Strict-Transport-Security",
419 : "Subject",
420 : "SubOK",
421 : "Subst",
422 : "Summary",
423 : "Supersedes",
424 : "Surrogate-Capability",
425 : "Surrogate-Control",
426 : "TCN",
427 : "TE",
428 : "Timeout",
429 : "Title",
430 : "To",
431 : "Topic",
432 : "Trailer",
433 : "Transfer-Encoding",
434 : "TTL",
435 : "UA-Color",
436 : "UA-Media",
437 : "UA-Pixels",
438 : "UA-Resolution",
439 : "UA-Windowpixels",
440 : "Upgrade",
441 : "Urgency",
442 : "URI",
443 : "User-Agent",
444 : "Variant-Vary",
445 : "Vary",
446 : "VBR-Info",
447 : "Version",
448 : "Via",
449 : "Want-Digest",
450 : "Warning",
451 : "WWW-Authenticate",
452 : "X-Archived-At",
453 : "X-Device-Accept",
454 : "X-Device-Accept-Charset",
455 : "X-Device-Accept-Encoding",
456 : "X-Device-Accept-Language",
457 : "X-Device-User-Agent",
458 : "X-Frame-Options",
459 : "X-Mittente",
460 : "X-PGP-Sig",
461 : "X-Ricevuta",
462 : "X-Riferimento-Message-ID",
463 : "X-TipoRicevuta",
464 : "X-Trasporto",
465 : "X-VerificaSicurezza",
466 : "X400-Content-Identifier",
467 : "X400-Content-Return",
468 : "X400-Content-Type",
469 : "X400-MTS-Identifier",
470 : "X400-Originator",
471 : "X400-Received",
472 : "X400-Recipients",
473 : "X400-Trace",
474 : "Xref"
475 13 : }})
476 : {
477 3328 : for(std::size_t i = 1, n = 256; i < n; ++i)
478 : {
479 3315 : auto sv = by_name_[ i ];
480 3315 : auto h = digest(sv);
481 3315 : auto j = h % N;
482 3315 : BOOST_ASSERT(map_[j][0] == 0);
483 3315 : map_[j][0] = static_cast<unsigned char>(i);
484 : }
485 :
486 1326 : for(std::size_t i = 256, n = by_name_.size(); i < n; ++i)
487 : {
488 1313 : auto sv = by_name_[i];
489 1313 : auto h = digest(sv);
490 1313 : auto j = h % N;
491 1313 : BOOST_ASSERT(map_[j][1] == 0);
492 1313 : map_[j][1] = static_cast<unsigned char>(i - 255);
493 : }
494 13 : }
495 :
496 : field
497 3207 : string_to_field(
498 : core::string_view s) const noexcept
499 : {
500 3207 : auto h = digest(s);
501 3207 : auto j = h % N;
502 3207 : int i = map_[j][0];
503 3207 : core::string_view s2 = by_name_[i];
504 3207 : if(i != 0 && equals(s, s2))
505 1119 : return static_cast<field>(i);
506 2088 : i = map_[j][1];
507 2088 : if(i == 0)
508 941 : return field::unknown;
509 1147 : i += 255;
510 1147 : s2 = by_name_[i];
511 :
512 1147 : if(equals(s, s2))
513 1147 : return static_cast<field>(i);
514 0 : return field::unknown;
515 : }
516 :
517 : //
518 : // Deprecated
519 : //
520 :
521 : using const_iterator =
522 : array_type::const_iterator;
523 :
524 : std::size_t
525 543 : size() const
526 : {
527 543 : return by_name_.size();
528 : }
529 :
530 : const_iterator
531 543 : begin() const
532 : {
533 543 : return by_name_.begin();
534 : }
535 :
536 : const_iterator
537 : end() const
538 : {
539 : return by_name_.end();
540 : }
541 : };
542 :
543 : static
544 : field_table const&
545 3750 : get_field_table() noexcept
546 : {
547 3750 : static field_table const tab;
548 3750 : return tab;
549 : }
550 :
551 : } // detail
552 :
553 : core::string_view
554 543 : to_string(field f)
555 : {
556 543 : auto const& v = detail::get_field_table();
557 543 : BOOST_ASSERT(static_cast<unsigned>(f) < v.size());
558 543 : return v.begin()[static_cast<unsigned>(f)];
559 : }
560 :
561 : field
562 3207 : string_to_field(
563 : core::string_view s) noexcept
564 : {
565 3207 : return detail::get_field_table().string_to_field(s);
566 : }
567 :
568 : std::ostream&
569 0 : operator<<(std::ostream& os, field f)
570 : {
571 0 : return os << to_string(f);
572 : }
573 :
574 : } // http_proto
575 : } // boost
|