1
#include "source/extensions/path/uri_template_lib/uri_template.h"
2

            
3
#include <map>
4
#include <string>
5
#include <utility>
6
#include <vector>
7

            
8
#include "source/common/http/path_utility.h"
9
#include "source/extensions/path/uri_template_lib/uri_template_internal.h"
10

            
11
#include "absl/status/statusor.h"
12
#include "absl/strings/escaping.h"
13
#include "absl/strings/str_split.h"
14
#include "absl/strings/string_view.h"
15
#include "re2/re2.h"
16

            
17
namespace Envoy {
18
namespace Extensions {
19
namespace UriTemplate {
20

            
21
using Internal::ParsedPathPattern;
22

            
23
#ifndef SWIG
24
// Silence warnings about missing initializers for members of LazyRE2.
25
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
26
#endif
27

            
28
20
std::ostream& operator<<(std::ostream& os, const ParsedSegment& parsed_segment) {
29
20
  os << "{kind = ";
30
20
  switch (parsed_segment.kind_) {
31
10
  case RewriteStringKind::Variable:
32
10
    os << "Variable";
33
10
    break;
34
10
  case RewriteStringKind::Literal:
35
10
    os << "Literal";
36
10
    break;
37
20
  }
38
20
  return os << ", value = \"" << absl::CEscape(parsed_segment.value_) << "\"}";
39
20
}
40

            
41
253
absl::StatusOr<std::string> convertPathPatternSyntaxToRegex(absl::string_view path_pattern) {
42
253
  absl::StatusOr<ParsedPathPattern> status = Internal::parsePathPatternSyntax(path_pattern);
43
253
  if (!status.ok()) {
44
41
    return status.status();
45
41
  }
46
212
  return Internal::toRegexPattern(*status);
47
253
}
48

            
49
174
absl::StatusOr<std::vector<ParsedSegment>> parseRewritePattern(absl::string_view path_pattern) {
50
174
  std::vector<ParsedSegment> result;
51

            
52
  // The pattern should start with a '/' and thus the first segment should
53
  // always be a literal.
54
174
  if (path_pattern.empty() || path_pattern[0] != '/') {
55
6
    return absl::InvalidArgumentError("Invalid rewrite variable placement");
56
6
  }
57

            
58
  // Don't allow contiguous '/' patterns.
59
168
  static const LazyRE2 invalid_regex = {"^.*//.*$"};
60
168
  if (RE2::FullMatch(path_pattern, *invalid_regex)) {
61
10
    return absl::InvalidArgumentError("Invalid rewrite literal");
62
10
  }
63

            
64
419
  while (!path_pattern.empty()) {
65
289
    std::vector<absl::string_view> segments1 =
66
289
        absl::StrSplit(path_pattern, absl::MaxSplits('{', 1));
67
289
    if (!segments1[0].empty()) {
68
287
      if (!Internal::isValidRewriteLiteral(segments1[0])) {
69
9
        return absl::InvalidArgumentError("Invalid rewrite literal pattern");
70
9
      }
71
278
      result.emplace_back(segments1[0], RewriteStringKind::Literal);
72
278
    }
73

            
74
280
    if (segments1.size() < 2) {
75
      // No more variable replacement, done.
76
12
      break;
77
12
    }
78

            
79
268
    std::vector<absl::string_view> segments2 =
80
268
        absl::StrSplit(segments1[1], absl::MaxSplits('}', 1));
81
268
    if (segments2.size() < 2) {
82
1
      return absl::InvalidArgumentError("Unmatched variable bracket");
83
1
    }
84
267
    path_pattern = segments2[1];
85

            
86
267
    if (!Internal::isValidVariableName(segments2[0])) {
87
6
      return absl::InvalidArgumentError("Invalid variable name");
88
6
    }
89
261
    result.emplace_back(segments2[0], RewriteStringKind::Variable);
90
261
  }
91
142
  return result;
92
158
}
93

            
94
absl::StatusOr<RewriteSegments> parseRewritePattern(absl::string_view pattern,
95
99
                                                    absl::string_view capture_regex) {
96
99
  RewriteSegments parsed_pattern;
97
99
  RE2 regex = RE2(capture_regex);
98
99
  if (!regex.ok()) {
99
1
    return absl::InternalError(regex.error());
100
1
  }
101

            
102
98
  absl::StatusOr<std::vector<ParsedSegment>> status = parseRewritePattern(pattern);
103
98
  if (!status.ok()) {
104
9
    return status.status();
105
9
  }
106
89
  std::vector<ParsedSegment> processed_pattern = *std::move(status);
107

            
108
89
  const std::map<std::string, int>& capture_index_map = regex.NamedCapturingGroups();
109

            
110
313
  for (const auto& [str, kind] : processed_pattern) {
111
313
    switch (kind) {
112
160
    case RewriteStringKind::Literal:
113
160
      parsed_pattern.push_back(RewriteSegment(std::string(str)));
114
160
      break;
115
153
    case RewriteStringKind::Variable:
116
153
      auto it = capture_index_map.find(std::string(str));
117
153
      if (it == capture_index_map.end()) {
118
15
        return absl::InvalidArgumentError("Nonexisting variable name");
119
15
      }
120
138
      parsed_pattern.push_back(RewriteSegment(it->second));
121
138
      break;
122
313
    }
123
313
  }
124

            
125
74
  return parsed_pattern;
126
89
}
127

            
128
121
absl::Status isValidMatchPattern(absl::string_view path_template_match) {
129
121
  return convertPathPatternSyntaxToRegex(path_template_match).status();
130
121
}
131

            
132
61
absl::Status isValidRewritePattern(absl::string_view path_template_rewrite) {
133
61
  return parseRewritePattern(path_template_rewrite).status();
134
61
}
135

            
136
47
absl::Status isValidSharedVariableSet(absl::string_view pattern, absl::string_view capture_regex) {
137
47
  absl::StatusOr<std::string> status = convertPathPatternSyntaxToRegex(capture_regex);
138
47
  if (!status.ok()) {
139
    return status.status();
140
  }
141
47
  return parseRewritePattern(pattern, *std::move(status)).status();
142
47
}
143

            
144
} // namespace UriTemplate
145
} // namespace Extensions
146
} // namespace Envoy