Skip to content
This repository has been archived by the owner on Jun 30, 2021. It is now read-only.

Commit

Permalink
Served multiplexer now allows handler overrides.
Browse files Browse the repository at this point in the history
The served multiplexer now allows you to override a specific handler
pattern. Previous behaviour was to simply append the new matching
handler onto the end of the handler stack, and therefore it would never
be called since any match would already be dispatched to the first
definition.

The new behaviour is that any handler definition whose pattern matches
an existing handler will remove the previous handler from the stack
before being appended to the end.

This means for resolving the order of precendence of an overriding
handler you simply follow the sames rules as if the original overriden
handler had never existed, rather than assuming that the new handler
takes the position of the original.

For example, given the following handler definitions:

handle("/foo").get(foo);
handle("/"   ).get(bar);
handle("/foo").get(baz); // Overrides the previous by removing it

And the following request:

GET /foo HTTP/1.1

The handler 'bar' is called since it matches and comes before the
new '/foo' handler.
  • Loading branch information
Jeffail committed Dec 10, 2014
1 parent 61a81ba commit 9699b8c
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 9 deletions.
4 changes: 2 additions & 2 deletions src/served/methods_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ typedef std::map<std::string, served_method_list> served_endpoint_list;
*/
class methods_handler
{
const std::string _path;
const std::string _info;
std::string _path;
std::string _info;
std::map<served::method, served_req_handler> _handlers;

public:
Expand Down
15 changes: 14 additions & 1 deletion src/served/multiplexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,21 @@ multiplexer::get_segments(const std::string & path)
served::methods_handler &
multiplexer::handle(const std::string & path, const std::string info /* = "" */)
{
// Remove any duplicates.
for ( auto it = _handler_candidates.begin(); it != _handler_candidates.end(); )
{
if ( std::get<2>(*it) == path )
{
it = _handler_candidates.erase(it);
}
else
{
++it;
}
}

_handler_candidates.push_back(
path_handler_candidate(get_segments(path), served::methods_handler(_base_path + path, info)));
path_handler_candidate(get_segments(path), served::methods_handler(_base_path + path, info), path));

return std::get<1>(_handler_candidates.back());
}
Expand Down
8 changes: 4 additions & 4 deletions src/served/multiplexer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ typedef std::function<void(response &, request &)> served_plugin_req_handler;
*/
class multiplexer
{
typedef std::vector<served_plugin_req_handler> plugin_handler_list;
typedef std::vector<served_plugin_req_handler> plugin_handler_list;

typedef std::vector<served::mux::segment_matcher_ptr> path_compiled_segments;
typedef std::tuple<path_compiled_segments, served::methods_handler> path_handler_candidate;
typedef std::vector<path_handler_candidate> path_handler_candidates;
typedef std::vector<served::mux::segment_matcher_ptr> path_compiled_segments;
typedef std::tuple<path_compiled_segments, served::methods_handler, std::string> path_handler_candidate;
typedef std::vector<path_handler_candidate> path_handler_candidates;

const std::string _base_path;
path_compiled_segments _base_path_segments;
Expand Down
75 changes: 73 additions & 2 deletions src/served/multiplexer.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ TEST_CASE("multiplexer method routing", "[mux]")

// This route should be disregarded in favour of a 405 error from the previous route.
request_router_story s2;
s2.pattern = "/foo/bar";
s2.pattern = "/foo";

served::multiplexer mux;
mux.handle(s1.pattern)
Expand Down Expand Up @@ -263,6 +263,78 @@ TEST_CASE("multiplexer method routing", "[mux]")
}
}

TEST_CASE("multiplexer overwriting", "[mux]")
{
SECTION("Confirm overwrite not accept GET")
{
request_router_story s1;
s1.pattern = "/foo/bar";

request_router_story s2;
s2.pattern = "/foo/bar";

served::multiplexer mux;
mux.handle(s1.pattern)
.post(path_collecting_functor(s1))
.del(path_collecting_functor(s1));

mux.handle(s2.pattern)
.get(path_collecting_functor(s2))
.head(path_collecting_functor(s2))
.put(path_collecting_functor(s2));

try
{
INFO("Confirming exception was 405 NOT ALLOWED");

served::response res;
served::request req;
served::uri url;

url.set_path(s1.pattern);
req.set_destination(url);
req.set_method(served::method::POST);

mux.forward_to_handler( res, req );

// Should have thrown by this point.
CHECK(false);
}
catch (const served::request_error & e)
{
INFO("Confirming exception was 405 NOT ALLOWED");
CHECK(e.get_status_code() == served::status_4XX::METHOD_NOT_ALLOWED);
}

{
served::response res;
served::request req;
served::uri url;

url.set_path(s1.pattern);
req.set_destination(url);

INFO("Checking accepts GET");
req.set_method(served::method::GET);
CHECK_NOTHROW(mux.forward_to_handler( res, req ));

INFO("Checking accepts HEAD");
req.set_method(served::method::HEAD);
CHECK_NOTHROW(mux.forward_to_handler( res, req ));

INFO("Checking accepts PUT");
req.set_method(served::method::PUT);
CHECK_NOTHROW(mux.forward_to_handler( res, req ));

INFO("Checking for no routed requests to overridden handler (0 requests)");
CHECK(s1.received.size() == 0);

INFO("Checking for all routed requests to override handler (3 requests)");
CHECK(s2.received.size() == 3);
}
}
}

TEST_CASE("multiplexer hierarchy test", "[mux]")
{
SECTION("shorter pattern first")
Expand All @@ -278,7 +350,6 @@ TEST_CASE("multiplexer hierarchy test", "[mux]")
mux.handle("/" ).get(expected_call);
mux.handle("/first/second").get(dummy_call);
mux.handle("/testing" ).get(dummy_call);
mux.handle("/" ).get(dummy_call);

served::response res;
served::request req;
Expand Down

0 comments on commit 9699b8c

Please sign in to comment.