Skip to content

Commit 9093689

Browse files
authored
Fix custom verb issue (#50)
* Fix custom verb issue Signed-off-by: Wayne Zhang <[email protected]> * remove unused include Signed-off-by: Wayne Zhang <[email protected]> * Add more lookup pattern in the new test Signed-off-by: Wayne Zhang <[email protected]> * add a comment Signed-off-by: Wayne Zhang <[email protected]>
1 parent 3a471aa commit 9093689

File tree

4 files changed

+59
-24
lines changed

4 files changed

+59
-24
lines changed

src/http_template.cc

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -335,12 +335,6 @@ class Parser {
335335
// this will allow the matcher code to reconstruct the variable
336336
// value based on the url segments.
337337
var.end_segment = (var.end_segment - segments_.size() - 1);
338-
339-
if (!verb_.empty()) {
340-
// a custom verb will add an additional segment, so
341-
// the end_postion needs a -1
342-
--var.end_segment;
343-
}
344338
}
345339
}
346340
}

src/include/grpc_transcoding/path_matcher.h

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,8 @@ void ExtractBindingsFromQueryParameters(
369369
// - Strips off query string: "/a?foo=bar" --> "/a"
370370
// - Collapses extra slashes: "///" --> "/"
371371
std::vector<std::string> ExtractRequestParts(
372-
std::string path, const std::unordered_set<std::string>& custom_verbs) {
372+
std::string path, const std::unordered_set<std::string>& custom_verbs,
373+
std::string& verb) {
373374
// Remove query parameters.
374375
path = path.substr(0, path.find_first_of('?'));
375376

@@ -378,11 +379,11 @@ std::vector<std::string> ExtractRequestParts(
378379
std::size_t last_colon_pos = path.find_last_of(':');
379380
std::size_t last_slash_pos = path.find_last_of('/');
380381
if (last_colon_pos != std::string::npos && last_colon_pos > last_slash_pos) {
381-
std::string verb = path.substr(last_colon_pos + 1);
382+
std::string tmp_verb = path.substr(last_colon_pos + 1);
382383
// only verb in the configured custom verbs, treat it as verb
383-
// replace ":" with / as a separate segment.
384-
if (custom_verbs.find(verb) != custom_verbs.end()) {
385-
path[last_colon_pos] = '/';
384+
if (custom_verbs.find(tmp_verb) != custom_verbs.end()) {
385+
verb = tmp_verb;
386+
path = path.substr(0, last_colon_pos);
386387
}
387388
}
388389

@@ -412,9 +413,6 @@ PathMatcherNode::PathInfo TransformHttpTemplate(const HttpTemplate& ht) {
412413
for (const std::string& part : ht.segments()) {
413414
builder.AppendLiteralNode(part);
414415
}
415-
if (!ht.verb().empty()) {
416-
builder.AppendLiteralNode(ht.verb());
417-
}
418416

419417
return builder.Build();
420418
}
@@ -443,8 +441,9 @@ Method PathMatcher<Method>::Lookup(
443441
const std::string& query_params,
444442
std::vector<VariableBinding>* variable_bindings,
445443
std::string* body_field_path) const {
444+
std::string verb;
446445
const std::vector<std::string> parts =
447-
ExtractRequestParts(path, custom_verbs_);
446+
ExtractRequestParts(path, custom_verbs_, verb);
448447

449448
// If service_name has not been registered to ESP and strict_service_matching_
450449
// is set to false, tries to lookup the method in all registered services.
@@ -453,7 +452,7 @@ Method PathMatcher<Method>::Lookup(
453452
}
454453

455454
PathMatcherLookupResult lookup_result =
456-
LookupInPathMatcherNode(*root_ptr_, parts, http_method);
455+
LookupInPathMatcherNode(*root_ptr_, parts, http_method + verb);
457456
// Return nullptr if nothing is found or the result is marked for duplication.
458457
if (lookup_result.data == nullptr || lookup_result.is_multiple) {
459458
return nullptr;
@@ -477,8 +476,9 @@ Method PathMatcher<Method>::Lookup(
477476
template <class Method>
478477
Method PathMatcher<Method>::Lookup(const std::string& http_method,
479478
const std::string& path) const {
479+
std::string verb;
480480
const std::vector<std::string> parts =
481-
ExtractRequestParts(path, custom_verbs_);
481+
ExtractRequestParts(path, custom_verbs_, verb);
482482

483483
// If service_name has not been registered to ESP and strict_service_matching_
484484
// is set to false, tries to lookup the method in all registered services.
@@ -487,7 +487,7 @@ Method PathMatcher<Method>::Lookup(const std::string& http_method,
487487
}
488488

489489
PathMatcherLookupResult lookup_result =
490-
LookupInPathMatcherNode(*root_ptr_, parts, http_method);
490+
LookupInPathMatcherNode(*root_ptr_, parts, http_method + verb);
491491
// Return nullptr if nothing is found or the result is marked for duplication.
492492
if (lookup_result.data == nullptr || lookup_result.is_multiple) {
493493
return nullptr;
@@ -540,7 +540,7 @@ bool PathMatcherBuilder<Method>::Register(
540540
method_data->body_field_path = body_field_path;
541541
method_data->system_query_parameter_names = system_query_parameter_names;
542542

543-
InsertPathToNode(path_info, method_data.get(), http_method, true,
543+
InsertPathToNode(path_info, method_data.get(), http_method + ht->verb(), true,
544544
root_ptr_.get());
545545
// Add the method_data to the methods_ vector for cleanup
546546
methods_.emplace_back(std::move(method_data));

test/http_template_test.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ TEST(HttpTemplate, ParseTest16) {
233233
ASSERT_EQ(Segments({"a", "c", "**", "d", "e"}), ht->segments());
234234
ASSERT_EQ("verb", ht->verb());
235235
ASSERT_EQ(Variables({
236-
Variable{1, -3, FieldPath{"b"}, true},
236+
Variable{1, -2, FieldPath{"b"}, true},
237237
}),
238238
ht->Variables());
239239
}
@@ -399,7 +399,7 @@ TEST(HttpTemplate, VariableAndCustomVerbTests) {
399399
ASSERT_EQ(Segments({"**"}), ht->segments());
400400
ASSERT_EQ("myverb", ht->verb());
401401
ASSERT_EQ(Variables({
402-
Variable{0, -2, FieldPath{"x"}, true},
402+
Variable{0, -1, FieldPath{"x"}, true},
403403
}),
404404
ht->Variables());
405405

@@ -409,7 +409,7 @@ TEST(HttpTemplate, VariableAndCustomVerbTests) {
409409
ASSERT_EQ(Segments({"**"}), ht->segments());
410410
ASSERT_EQ("myverb", ht->verb());
411411
ASSERT_EQ(Variables({
412-
Variable{0, -2, FieldPath{"x", "y", "z"}, true},
412+
Variable{0, -1, FieldPath{"x", "y", "z"}, true},
413413
}),
414414
ht->Variables());
415415

@@ -419,7 +419,7 @@ TEST(HttpTemplate, VariableAndCustomVerbTests) {
419419
ASSERT_EQ(Segments({"a", "**", "b"}), ht->segments());
420420
ASSERT_EQ("custom", ht->verb());
421421
ASSERT_EQ(Variables({
422-
Variable{0, -2, FieldPath{"x", "y", "z"}, true},
422+
Variable{0, -1, FieldPath{"x", "y", "z"}, true},
423423
}),
424424
ht->Variables());
425425

@@ -429,7 +429,7 @@ TEST(HttpTemplate, VariableAndCustomVerbTests) {
429429
ASSERT_EQ(Segments({"a", "**", "b", "c", "d"}), ht->segments());
430430
ASSERT_EQ("custom", ht->verb());
431431
ASSERT_EQ(Variables({
432-
Variable{0, -4, FieldPath{"x", "y", "z"}, true},
432+
Variable{0, -3, FieldPath{"x", "y", "z"}, true},
433433
}),
434434
ht->Variables());
435435
}

test/path_matcher_test.cc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ bool operator==(const Binding& b1, const Binding& b2) {
5353
return b1.field_path == b2.field_path && b1.value == b2.value;
5454
}
5555

56+
// These comment out code will be useful when debugging. It can be added as:
57+
// std::cerr << Bingings;
58+
#if 0
5659
std::string FieldPathToString(const FieldPath& fp) {
5760
std::string s;
5861
for (const auto& f : fp) {
@@ -74,6 +77,7 @@ std::ostream& operator<<(std::ostream& os, const Bindings& bindings) {
7477
}
7578
return os;
7679
}
80+
#endif
7781

7882
} // namespace
7983

@@ -418,6 +422,43 @@ TEST_F(PathMatcherTest,
418422
MultiSegmentMatchWithReservedCharactersBase("!#$&'()*+,/:;=?@[]");
419423
}
420424

425+
TEST_F(PathMatcherTest, CustomVerbIssue) {
426+
MethodInfo* list_person = AddGetPath("/person");
427+
MethodInfo* get_person = AddGetPath("/person/{id=*}");
428+
MethodInfo* verb = AddGetPath("/{x=**}:verb");
429+
Build();
430+
431+
EXPECT_NE(nullptr, list_person);
432+
EXPECT_NE(nullptr, get_person);
433+
EXPECT_NE(nullptr, verb);
434+
435+
Bindings bindings;
436+
// with the verb
437+
EXPECT_EQ(Lookup("GET", "/person:verb", &bindings), verb);
438+
EXPECT_EQ(Bindings({Binding{FieldPath{"x"}, "person"}}), bindings);
439+
EXPECT_EQ(Lookup("GET", "/person/jason:verb", &bindings), verb);
440+
EXPECT_EQ(Bindings({Binding{FieldPath{"x"}, "person/jason"}}), bindings);
441+
442+
// with the verb but with a different prefix
443+
EXPECT_EQ(Lookup("GET", "/animal:verb", &bindings), verb);
444+
EXPECT_EQ(Bindings({Binding{FieldPath{"x"}, "animal"}}), bindings);
445+
EXPECT_EQ(Lookup("GET", "/animal/cat:verb", &bindings), verb);
446+
EXPECT_EQ(Bindings({Binding{FieldPath{"x"}, "animal/cat"}}), bindings);
447+
448+
// without a verb
449+
EXPECT_EQ(Lookup("GET", "/person", &bindings), list_person);
450+
EXPECT_EQ(Lookup("GET", "/person/jason", &bindings), get_person);
451+
EXPECT_EQ(Lookup("GET", "/animal", &bindings), nullptr);
452+
EXPECT_EQ(Lookup("GET", "/animal/cat", &bindings), nullptr);
453+
454+
// with a non-verb
455+
EXPECT_EQ(Lookup("GET", "/person:other", &bindings), nullptr);
456+
EXPECT_EQ(Lookup("GET", "/person/jason:other", &bindings), get_person);
457+
EXPECT_EQ(Bindings({Binding{FieldPath{"id"}, "jason:other"}}), bindings);
458+
EXPECT_EQ(Lookup("GET", "/animal:other", &bindings), nullptr);
459+
EXPECT_EQ(Lookup("GET", "/animal/cat:other", &bindings), nullptr);
460+
}
461+
421462
TEST_F(PathMatcherTest, VariableBindingsWithCustomVerb) {
422463
MethodInfo* a_verb = AddGetPath("/a/{y=*}:verb");
423464
MethodInfo* ad__verb = AddGetPath("/a/{y=d/**}:verb");

0 commit comments

Comments
 (0)