1 /** @file
2
3 Configuration of Forwarded HTTP header option.
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include <bitset>
25 #include <string>
26 #include <cctype>
27
28 #include <string_view>
29 #include "tscpp/util/TextView.h"
30
31 #include <HttpConfig.h>
32
33 namespace
34 {
35 class BadOptionsErrMsg
36 {
37 public:
38 // Construct with reference to string that will contain error message.
39 //
BadOptionsErrMsg(ts::FixedBufferWriter & err)40 BadOptionsErrMsg(ts::FixedBufferWriter &err) : _err(err), _count(0) {}
41
42 // Add a bad option.
43 //
44 void
add(ts::TextView badOpt)45 add(ts::TextView badOpt)
46 {
47 if (_count == 0) {
48 _err << "\"Forwarded\" configuration: ";
49 _addQuoted(badOpt);
50 _count = 1;
51 } else if (_count == 1) {
52 _saveLast = badOpt;
53 _count = 2;
54 } else {
55 _err << ", ";
56 _addQuoted(_saveLast);
57 _saveLast = badOpt;
58 ++_count;
59 }
60 }
61
62 // Returns true it error seen.
63 //
64 bool
done()65 done()
66 {
67 if (_count == 0) {
68 return false;
69 }
70
71 if (_count == 1) {
72 _err << " is a bad option.";
73
74 } else if (_count != 0) {
75 _err << " and ";
76 _addQuoted(_saveLast);
77 _err << " are bad options.";
78 }
79 return true;
80 }
81
82 private:
83 void
_addQuoted(ts::TextView sv)84 _addQuoted(ts::TextView sv)
85 {
86 _err << '\"' << sv << '\"';
87 }
88
89 ts::FixedBufferWriter &_err;
90
91 ts::TextView _saveLast;
92
93 int _count;
94 };
95
96 // Compare a TextView to a nul-termimated string, converting the TextView to lower case and ignoring whitespace in it.
97 //
98 bool
eqIgnoreCaseWs(ts::TextView sv,const char * target)99 eqIgnoreCaseWs(ts::TextView sv, const char *target)
100 {
101 const char *s = sv.data();
102
103 std::size_t skip = 0;
104 std::size_t i = 0;
105
106 while ((i + skip) < sv.size()) {
107 if (std::isspace(s[i + skip])) {
108 ++skip;
109 } else if (std::tolower(s[i + skip]) != target[i]) {
110 return false;
111 } else {
112 ++i;
113 }
114 }
115
116 return target[i] == '\0';
117 }
118
119 } // end anonymous namespace
120
121 namespace HttpForwarded
122 {
123 OptionBitSet
optStrToBitset(std::string_view optConfigStr,ts::FixedBufferWriter & error)124 optStrToBitset(std::string_view optConfigStr, ts::FixedBufferWriter &error)
125 {
126 const ts::TextView Delimiters(":|");
127
128 OptionBitSet optBS;
129
130 // Convert to TS TextView to be able to use parsing members.
131 //
132 ts::TextView oCS{optConfigStr};
133
134 if (eqIgnoreCaseWs(oCS, "none")) {
135 return OptionBitSet();
136 }
137
138 BadOptionsErrMsg em(error);
139
140 do {
141 ts::TextView optStr = oCS.take_prefix_at(Delimiters);
142
143 if (eqIgnoreCaseWs(optStr, "for")) {
144 optBS.set(FOR);
145
146 } else if (eqIgnoreCaseWs(optStr, "by=ip")) {
147 optBS.set(BY_IP);
148
149 } else if (eqIgnoreCaseWs(optStr, "by=unknown")) {
150 optBS.set(BY_UNKNOWN);
151
152 } else if (eqIgnoreCaseWs(optStr, "by=servername")) {
153 optBS.set(BY_SERVER_NAME);
154
155 } else if (eqIgnoreCaseWs(optStr, "by=uuid")) {
156 optBS.set(BY_UUID);
157
158 } else if (eqIgnoreCaseWs(optStr, "proto")) {
159 optBS.set(PROTO);
160
161 } else if (eqIgnoreCaseWs(optStr, "host")) {
162 optBS.set(HOST);
163
164 } else if (eqIgnoreCaseWs(optStr, "connection=compact")) {
165 optBS.set(CONNECTION_COMPACT);
166
167 } else if (eqIgnoreCaseWs(optStr, "connection=std")) {
168 optBS.set(CONNECTION_STD);
169
170 } else if (eqIgnoreCaseWs(optStr, "connection=standard")) {
171 optBS.set(CONNECTION_STD);
172
173 } else if (eqIgnoreCaseWs(optStr, "connection=full")) {
174 optBS.set(CONNECTION_FULL);
175
176 } else {
177 em.add(optStr);
178 }
179 } while (oCS);
180
181 if (em.done()) {
182 return OptionBitSet();
183 }
184
185 return optBS;
186
187 } // end optStrToBitset()
188
189 } // end namespace HttpForwarded
190