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;

Reply via email to