Added support to handle queries to nested HTTP paths. Review: https://reviews.apache.org/r/37240
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/10cf391d Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/10cf391d Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/10cf391d Branch: refs/heads/master Commit: 10cf391d3a24ef8a3dc34804e5a26812ab64e02e Parents: e485df2 Author: Vinod Kone <[email protected]> Authored: Fri Aug 7 16:28:36 2015 -0700 Committer: Vinod Kone <[email protected]> Committed: Tue Aug 11 13:49:24 2015 -0700 ---------------------------------------------------------------------- 3rdparty/libprocess/include/process/http.hpp | 3 ++ 3rdparty/libprocess/src/process.cpp | 62 +++++++++++++++-------- 3rdparty/libprocess/src/tests/http_tests.cpp | 34 +++++++++++++ 3 files changed, 78 insertions(+), 21 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/10cf391d/3rdparty/libprocess/include/process/http.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/include/process/http.hpp b/3rdparty/libprocess/include/process/http.hpp index 3cd3f15..b829dad 100644 --- a/3rdparty/libprocess/include/process/http.hpp +++ b/3rdparty/libprocess/include/process/http.hpp @@ -108,7 +108,10 @@ struct Request // TODO(benh): Replace 'url', 'path', 'query', and 'fragment' with URL. std::string url; // (path?query#fragment) + + // TODO(vinod): Make this a 'Path' instead of 'string'. std::string path; + hashmap<std::string, std::string> query; std::string fragment; http://git-wip-us.apache.org/repos/asf/mesos/blob/10cf391d/3rdparty/libprocess/src/process.cpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/src/process.cpp b/3rdparty/libprocess/src/process.cpp index 2ce547b..22a1245 100644 --- a/3rdparty/libprocess/src/process.cpp +++ b/3rdparty/libprocess/src/process.cpp @@ -3026,24 +3026,42 @@ void ProcessBase::visit(const HttpEvent& event) CHECK(tokens.size() >= 1); CHECK_EQ(pid.id, http::decode(tokens[0]).get()); - const string name = tokens.size() > 1 ? tokens[1] : ""; + // First look to see if there is an HTTP handler that can handle the + // longest prefix of this path. - if (handlers.http.count(name) > 0) { - // Create the promise to link with whatever gets returned, as well - // as a future to wait for the response. - std::shared_ptr<Promise<Response>> promise(new Promise<Response>()); + // Remove the 'id' prefix from the path. + string name = strings::remove( + event.request->path, "/" + tokens[0], strings::PREFIX); - Future<Response>* future = new Future<Response>(promise->future()); + name = strings::trim(name, strings::PREFIX, "/"); - // Get the HttpProxy pid for this socket. - PID<HttpProxy> proxy = socket_manager->proxy(event.socket); + while (Path(name).dirname() != name) { + if (handlers.http.count(name) > 0) { + // Create the promise to link with whatever gets returned, as well + // as a future to wait for the response. + std::shared_ptr<Promise<Response>> promise(new Promise<Response>()); - // Let the HttpProxy know about this request (via the future). - dispatch(proxy, &HttpProxy::handle, future, *event.request); + Future<Response>* future = new Future<Response>(promise->future()); + + // Get the HttpProxy pid for this socket. + PID<HttpProxy> proxy = socket_manager->proxy(event.socket); - // Now call the handler and associate the response with the promise. - promise->associate(handlers.http[name](*event.request)); - } else if (assets.count(name) > 0) { + // Let the HttpProxy know about this request (via the future). + dispatch(proxy, &HttpProxy::handle, future, *event.request); + + // Now call the handler and associate the response with the promise. + promise->associate(handlers.http[name](*event.request)); + + return; + } + + name = Path(name).dirname(); + } + + // If no HTTP handler is found look in assets. + name = tokens.size() > 1 ? tokens[1] : ""; + + if (assets.count(name) > 0) { OK response; response.type = Response::PATH; response.path = assets[name].path; @@ -3073,16 +3091,18 @@ void ProcessBase::visit(const HttpEvent& event) // Enqueue the response with the HttpProxy so that it respects the // order of requests to account for HTTP/1.1 pipelining. dispatch(proxy, &HttpProxy::enqueue, response, *event.request); - } else { - VLOG(1) << "Returning '404 Not Found' for '" << event.request->path << "'"; - // Get the HttpProxy pid for this socket. - PID<HttpProxy> proxy = socket_manager->proxy(event.socket); - - // Enqueue the response with the HttpProxy so that it respects the - // order of requests to account for HTTP/1.1 pipelining. - dispatch(proxy, &HttpProxy::enqueue, NotFound(), *event.request); + return; } + + VLOG(1) << "Returning '404 Not Found' for '" << event.request->path << "'"; + + // Get the HttpProxy pid for this socket. + PID<HttpProxy> proxy = socket_manager->proxy(event.socket); + + // Enqueue the response with the HttpProxy so that it respects the + // order of requests to account for HTTP/1.1 pipelining. + dispatch(proxy, &HttpProxy::enqueue, NotFound(), *event.request); } http://git-wip-us.apache.org/repos/asf/mesos/blob/10cf391d/3rdparty/libprocess/src/tests/http_tests.cpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/src/tests/http_tests.cpp b/3rdparty/libprocess/src/tests/http_tests.cpp index f89d7ee..7351fa4 100644 --- a/3rdparty/libprocess/src/tests/http_tests.cpp +++ b/3rdparty/libprocess/src/tests/http_tests.cpp @@ -66,6 +66,11 @@ public: MOCK_METHOD1(get, Future<http::Response>(const http::Request&)); MOCK_METHOD1(post, Future<http::Response>(const http::Request&)); + void addRoute(const string& path, const HttpRequestHandler& handler) + { + route(path, None(), handler); + } + protected: virtual void initialize() { @@ -106,6 +111,8 @@ public: }; +// TODO(vinod): Use AWAIT_EXPECT_RESPONSE_STATUS_EQ in the tests. + TEST(HTTPTest, Auth) { Http http; @@ -433,6 +440,33 @@ TEST(HTTPTest, Get) } +TEST(HTTPTest, NestedGet) +{ + Http http; + + http.process->addRoute("/a/b/c", [] (const http::Request&) { + return http::OK(); + }); + + http.process->addRoute("/a", [] (const http::Request&) { + return http::Accepted(); + }); + + // The handler for "/a/b/c" should return 'http::OK()'. + Future<http::Response> response = http::get(http.process->self(), "/a/b/c"); + + AWAIT_READY(response); + ASSERT_EQ(http::statuses[200], response.get().status); + + // "/a/b" should be handled by "/a" handler and return + // 'http::Accepted()'. + response = http::get(http.process->self(), "/a/b"); + + AWAIT_READY(response); + ASSERT_EQ(http::statuses[202], response.get().status); +} + + TEST(HTTPTest, StreamingGetComplete) { Http http;
