]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpUpgradeProtocolAccess.h
Allow upgrading from HTTP/1.1 to other protocols (#481)
[thirdparty/squid.git] / src / HttpUpgradeProtocolAccess.h
1 /*
2 * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 #ifndef SQUID_HTTP_UPGRADE_H
10 #define SQUID_HTTP_UPGRADE_H
11
12 #include "acl/forward.h"
13 #include "sbuf/SBuf.h"
14
15 #include <map>
16
17 /// a reference to a protocol name[/version] string; no 0-termination is assumed
18 class ProtocolView
19 {
20 public:
21 ProtocolView(const char * const start, const size_t len);
22 explicit ProtocolView(const SBuf &proto);
23
24 SBuf name; ///< everything up to (but excluding) the first slash('/')
25 SBuf version; ///< everything after the name, including the slash('/')
26 };
27
28 std::ostream &operator <<(std::ostream &, const ProtocolView &);
29
30 // HTTP is not explicit about case sensitivity of Upgrade protocol strings, but
31 // there are bug reports showing different case variants used for WebSocket. We
32 // conservatively preserve the received case and compare case-sensitively.
33
34 /// Either b has no version restrictions or both have the same version.
35 /// For example, "ws/1" is in "ws" but "ws" is not in "ws/1".
36 inline bool
37 vAinB(const ProtocolView &a, const ProtocolView &b)
38 {
39 // Optimization: Do not assert(a.name == b.name).
40 return b.version.isEmpty() || (a.version == b.version);
41 }
42
43 /// Allows or blocks HTTP Upgrade protocols (see http_upgrade_request_protocols)
44 class HttpUpgradeProtocolAccess
45 {
46 public:
47 HttpUpgradeProtocolAccess() = default;
48 ~HttpUpgradeProtocolAccess();
49 HttpUpgradeProtocolAccess(HttpUpgradeProtocolAccess &&) = delete; // no copying of any kind
50
51 /// \returns the ACLs matching the given "name[/version]" protocol (or nil)
52 const acl_access *findGuard(const SBuf &proto) const;
53
54 /// parses a single allow/deny rule
55 void configureGuard(ConfigParser&);
56
57 /// iterates over all configured rules, calling the given visitor
58 template <typename Visitor> inline void forEach(const Visitor &) const;
59
60 /// iterates over rules applicable to the given protocol, calling visitor;
61 /// breaks iteration if the visitor returns true
62 template <typename Visitor> inline void forApplicable(const ProtocolView &, const Visitor &) const;
63
64 private:
65 /// a single configured access rule for an explicitly named protocol
66 class NamedGuard
67 {
68 public:
69 NamedGuard(const char *rawProtocol, acl_access*);
70 NamedGuard(const NamedGuard &&) = delete; // no copying of any kind
71 ~NamedGuard();
72
73 const SBuf protocol; ///< configured protocol name (and version)
74 const ProtocolView proto; ///< optimization: compiled this->protocol
75 acl_access *guard = nullptr; ///< configured access rule; never nil
76 };
77
78 /// maps HTTP Upgrade protocol name/version to the ACLs guarding its usage
79 typedef std::deque<NamedGuard> NamedGuards;
80
81 /// pseudonym to specify rules for "all other protocols"
82 static const SBuf ProtoOther;
83
84 /// rules governing upgrades to explicitly named protocols
85 NamedGuards namedGuards;
86
87 /// OTHER rules governing unnamed protocols
88 acl_access *other = nullptr;
89 };
90
91 template <typename Visitor>
92 inline void
93 HttpUpgradeProtocolAccess::forEach(const Visitor &visitor) const
94 {
95 for (const auto &namedGuard: namedGuards)
96 visitor(namedGuard.protocol, namedGuard.guard);
97 if (other)
98 visitor(ProtoOther, other);
99 }
100
101 template <typename Visitor>
102 inline void
103 HttpUpgradeProtocolAccess::forApplicable(const ProtocolView &offer, const Visitor &visitor) const
104 {
105 auto seenApplicable = false;
106 for (const auto &namedGuard: namedGuards) {
107 if (offer.name != namedGuard.proto.name)
108 continue;
109 if (vAinB(offer, namedGuard.proto) && visitor(namedGuard.protocol, namedGuard.guard))
110 return;
111 seenApplicable = true; // may already be true
112 }
113 if (!seenApplicable && other) // OTHER is applicable if named rules were not
114 (void)visitor(ProtoOther, other);
115 }
116
117 #endif /* SQUID_HTTP_UPGRADE_H */
118