Coverage Report

Created: 2025-02-02 06:38

/proc/self/cwd/src/http_template.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2016 Google Inc. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//    http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
//
15
////////////////////////////////////////////////////////////////////////////////
16
//
17
#include "grpc_transcoding/http_template.h"
18
19
#include <string>
20
#include <vector>
21
22
namespace google {
23
namespace grpc {
24
namespace transcoding {
25
26
namespace {
27
28
// TODO: implement an error sink.
29
30
// HTTP Template Grammar:
31
// Questions:
32
//   - what are the constraints on LITERAL and IDENT?
33
//   - what is the character set for the grammar?
34
//
35
// Template = "/" | "/" Segments [ Verb ] ;
36
// Segments = Segment { "/" Segment } ;
37
// Segment  = "*" | "**" | LITERAL | Variable ;
38
// Variable = "{" FieldPath [ "=" Segments ] "}" ;
39
// FieldPath = IDENT { "." IDENT } ;
40
// Verb     = ":" LITERAL ;
41
class Parser {
42
 public:
43
  Parser(const std::string &input)
44
1.01k
      : input_(input), tb_(0), te_(0), in_variable_(false) {}
45
46
1.01k
  bool Parse() {
47
1.01k
    if (!ParseTemplate() || !ConsumedAllInput()) {
48
508
      return false;
49
508
    }
50
509
    PostProcessVariables();
51
509
    return true;
52
1.01k
  }
53
54
441
  std::vector<std::string> &segments() { return segments_; }
55
441
  std::string &verb() { return verb_; }
56
441
  std::vector<HttpTemplate::Variable> &variables() { return variables_; }
57
58
  // only constant path segments are allowed after '**'.
59
509
  bool ValidateParts() {
60
509
    bool found_wild_card = false;
61
3.74M
    for (size_t i = 0; i < segments_.size(); i++) {
62
3.74M
      if (!found_wild_card) {
63
3.73M
        if (segments_[i] == HttpTemplate::kWildCardPathKey) {
64
216
          found_wild_card = true;
65
216
        }
66
3.73M
      } else if (segments_[i] == HttpTemplate::kSingleParameterKey ||
67
4.17k
                 segments_[i] == HttpTemplate::kWildCardPathPartKey ||
68
4.17k
                 segments_[i] == HttpTemplate::kWildCardPathKey) {
69
68
        return false;
70
68
      }
71
3.74M
    }
72
441
    return true;
73
509
  }
74
75
 private:
76
  // Template = "/" Segments [ Verb ] ;
77
1.01k
  bool ParseTemplate() {
78
1.01k
    if (!Consume('/')) {
79
      // Expected '/'
80
26
      return false;
81
26
    }
82
991
    if (!ParseSegments()) {
83
368
      return false;
84
368
    }
85
86
623
    if (EnsureCurrent() && current_char() == ':') {
87
57
      if (!ParseVerb()) {
88
30
        return false;
89
30
      }
90
57
    }
91
593
    return true;
92
623
  }
93
94
  // Segments = Segment { "/" Segment } ;
95
17.3k
  bool ParseSegments() {
96
17.3k
    if (!ParseSegment()) {
97
128
      return false;
98
128
    }
99
100
5.64M
    for (;;) {
101
5.64M
      if (!Consume('/')) break;
102
5.62M
      if (!ParseSegment()) {
103
268
        return false;
104
268
      }
105
5.62M
    }
106
107
16.9k
    return true;
108
17.2k
  }
109
110
  // Segment  = "*" | "**" | LITERAL | Variable ;
111
5.64M
  bool ParseSegment() {
112
5.64M
    if (!EnsureCurrent()) {
113
49
      return false;
114
49
    }
115
5.64M
    switch (current_char()) {
116
2.85M
      case '*': {
117
2.85M
        Consume('*');
118
2.85M
        if (Consume('*')) {
119
          // **
120
42.3k
          segments_.push_back("**");
121
42.3k
          if (in_variable_) {
122
17.2k
            return MarkVariableHasWildCardPath();
123
17.2k
          }
124
25.0k
          return true;
125
2.81M
        } else {
126
2.81M
          segments_.push_back("*");
127
2.81M
          return true;
128
2.81M
        }
129
2.85M
      }
130
131
441k
      case '{':
132
441k
        return ParseVariable();
133
2.34M
      default:
134
2.34M
        return ParseLiteralSegment();
135
5.64M
    }
136
5.64M
  }
137
138
  // Variable = "{" FieldPath [ "=" Segments ] "}" ;
139
441k
  bool ParseVariable() {
140
441k
    if (!Consume('{')) {
141
0
      return false;
142
0
    }
143
441k
    if (!StartVariable()) {
144
8
      return false;
145
8
    }
146
441k
    if (!ParseFieldPath()) {
147
101
      return false;
148
101
    }
149
440k
    if (Consume('=')) {
150
16.3k
      if (!ParseSegments()) {
151
28
        return false;
152
28
      }
153
424k
    } else {
154
      // {field_path} is equivalent to {field_path=*}
155
424k
      segments_.push_back("*");
156
424k
    }
157
440k
    if (!EndVariable()) {
158
0
      return false;
159
0
    }
160
440k
    if (!Consume('}')) {
161
185
      return false;
162
185
    }
163
440k
    return true;
164
440k
  }
165
166
2.34M
  bool ParseLiteralSegment() {
167
2.34M
    std::string ls;
168
2.34M
    if (!ParseLiteral(&ls)) {
169
25
      return false;
170
25
    }
171
2.34M
    segments_.push_back(ls);
172
2.34M
    return true;
173
2.34M
  }
174
175
  // FieldPath = IDENT { "." IDENT } ;
176
441k
  bool ParseFieldPath() {
177
441k
    if (!ParseIdentifier()) {
178
66
      return false;
179
66
    }
180
1.08M
    while (Consume('.')) {
181
645k
      if (!ParseIdentifier()) {
182
35
        return false;
183
35
      }
184
645k
    }
185
440k
    return true;
186
440k
  }
187
188
  // Verb     = ":" LITERAL ;
189
57
  bool ParseVerb() {
190
57
    if (!Consume(':')) return false;
191
57
    if (!ParseLiteral(&verb_)) return false;
192
27
    return true;
193
57
  }
194
195
1.08M
  bool ParseIdentifier() {
196
1.08M
    std::string idf;
197
198
    // Initialize to false to handle empty literal.
199
1.08M
    bool result = false;
200
201
9.52M
    while (NextChar()) {
202
9.52M
      char c;
203
9.52M
      switch (c = current_char()) {
204
645k
        case '.':
205
1.07M
        case '}':
206
1.08M
        case '=':
207
1.08M
          return result && AddFieldIdentifier(std::move(idf));
208
8.43M
        default:
209
8.43M
          Consume(c);
210
8.43M
          idf.push_back(c);
211
8.43M
          break;
212
9.52M
      }
213
8.43M
      result = true;
214
8.43M
    }
215
211
    return result && AddFieldIdentifier(std::move(idf));
216
1.08M
  }
217
218
2.34M
  bool ParseLiteral(std::string *lit) {
219
2.34M
    if (!EnsureCurrent()) {
220
29
      return false;
221
29
    }
222
223
    // Initialize to false in case we encounter an empty literal.
224
2.34M
    bool result = false;
225
226
29.6M
    for (;;) {
227
29.6M
      char c;
228
29.6M
      switch (c = current_char()) {
229
2.34M
        case '/':
230
2.34M
        case ':':
231
2.34M
        case '}':
232
2.34M
          return result;
233
27.3M
        default:
234
27.3M
          Consume(c);
235
27.3M
          lit->push_back(c);
236
27.3M
          break;
237
29.6M
      }
238
239
27.3M
      result = true;
240
241
27.3M
      if (!NextChar()) {
242
381
        break;
243
381
      }
244
27.3M
    }
245
381
    return result;
246
2.34M
  }
247
248
49.5M
  bool Consume(char c) {
249
49.5M
    if (tb_ >= te_ && !NextChar()) {
250
1.01k
      return false;
251
1.01k
    }
252
49.5M
    if (current_char() != c) {
253
3.69M
      return false;
254
3.69M
    }
255
45.8M
    tb_++;
256
45.8M
    return true;
257
49.5M
  }
258
259
593
  bool ConsumedAllInput() { return tb_ >= input_.size(); }
260
261
7.99M
  bool EnsureCurrent() { return tb_ < te_ || NextChar(); }
262
263
45.8M
  bool NextChar() {
264
45.8M
    if (te_ < input_.size()) {
265
45.8M
      te_++;
266
45.8M
      return true;
267
45.8M
    } else {
268
2.16k
      return false;
269
2.16k
    }
270
45.8M
  }
271
272
  // Returns the character looked at.
273
94.3M
  char current_char() const {
274
94.3M
    return tb_ < te_ && te_ <= input_.size() ? input_[te_ - 1] : -1;
275
94.3M
  }
276
277
2.86M
  HttpTemplate::Variable &CurrentVariable() { return variables_.back(); }
278
279
441k
  bool StartVariable() {
280
441k
    if (!in_variable_) {
281
441k
      variables_.push_back(HttpTemplate::Variable{});
282
441k
      CurrentVariable().start_segment = segments_.size();
283
441k
      CurrentVariable().has_wildcard_path = false;
284
441k
      in_variable_ = true;
285
441k
      return true;
286
441k
    } else {
287
      // nested variables are not allowed
288
8
      return false;
289
8
    }
290
441k
  }
291
292
440k
  bool EndVariable() {
293
440k
    if (in_variable_ && !variables_.empty()) {
294
440k
      CurrentVariable().end_segment = segments_.size();
295
440k
      in_variable_ = false;
296
440k
      return ValidateVariable(CurrentVariable());
297
440k
    } else {
298
      // something's wrong we're not in a variable
299
0
      return false;
300
0
    }
301
440k
  }
302
303
1.08M
  bool AddFieldIdentifier(std::string id) {
304
1.08M
    if (in_variable_ && !variables_.empty()) {
305
1.08M
      CurrentVariable().field_path.emplace_back(std::move(id));
306
1.08M
      return true;
307
1.08M
    } else {
308
      // something's wrong we're not in a variable
309
0
      return false;
310
0
    }
311
1.08M
  }
312
313
17.2k
  bool MarkVariableHasWildCardPath() {
314
17.2k
    if (in_variable_ && !variables_.empty()) {
315
17.2k
      CurrentVariable().has_wildcard_path = true;
316
17.2k
      return true;
317
17.2k
    } else {
318
      // something's wrong we're not in a variable
319
0
      return false;
320
0
    }
321
17.2k
  }
322
323
440k
  bool ValidateVariable(const HttpTemplate::Variable &var) {
324
440k
    return !var.field_path.empty() && (var.start_segment < var.end_segment) &&
325
440k
           (var.end_segment <= static_cast<int>(segments_.size()));
326
440k
  }
327
328
509
  void PostProcessVariables() {
329
218k
    for (auto &var : variables_) {
330
218k
      if (var.has_wildcard_path) {
331
        // if the variable contains a '**', store the end_positon
332
        // relative to the end, such that -1 corresponds to the end
333
        // of the path. As we only support fixed path after '**',
334
        // this will allow the matcher code to reconstruct the variable
335
        // value based on the url segments.
336
5.11k
        var.end_segment = (var.end_segment - segments_.size() - 1);
337
5.11k
      }
338
218k
    }
339
509
  }
340
341
  const std::string &input_;
342
343
  // Token delimiter indexes
344
  size_t tb_;
345
  size_t te_;
346
347
  // are we in nested Segments of a variable?
348
  bool in_variable_;
349
350
  std::vector<std::string> segments_;
351
  std::string verb_;
352
  std::vector<HttpTemplate::Variable> variables_;
353
};
354
355
}  // namespace
356
357
const char HttpTemplate::kSingleParameterKey[] = "/.";
358
359
const char HttpTemplate::kWildCardPathPartKey[] = "*";
360
361
const char HttpTemplate::kWildCardPathKey[] = "**";
362
363
1.01k
std::unique_ptr<HttpTemplate> HttpTemplate::Parse(const std::string &ht) {
364
1.01k
  if (ht == "/") {
365
1
    return std::unique_ptr<HttpTemplate>(new HttpTemplate({}, {}, {}));
366
1
  }
367
368
1.01k
  Parser p(ht);
369
1.01k
  if (!p.Parse() || !p.ValidateParts()) {
370
576
    return nullptr;
371
576
  }
372
373
441
  return std::unique_ptr<HttpTemplate>(new HttpTemplate(
374
441
      std::move(p.segments()), std::move(p.verb()), std::move(p.variables())));
375
1.01k
}
376
377
}  // namespace transcoding
378
}  // namespace grpc
379
}  // namespace google