Add response builder to simplify the response build; refine the service interfaces.

master
Chunting Gu 6 years ago
parent 4140b4f94c
commit 8a7f53313b

@ -8,21 +8,21 @@
class FileUploadService : public webcc::Service {
public:
void Handle(const webcc::RestRequest& request,
webcc::RestResponse* response) override {
if (request.http->method() == "POST") {
std::cout << "files: " << request.http->form_parts().size() << std::endl;
webcc::ResponsePtr Handle(webcc::RequestPtr request,
const webcc::UrlArgs& args) override {
if (request->method() == "POST") {
std::cout << "files: " << request->form_parts().size() << std::endl;
for (auto& part : request.http->form_parts()) {
for (auto& part : request->form_parts()) {
std::cout << "name: " << part->name() << std::endl;
std::cout << "data: " << std::endl << part->data() << std::endl;
}
response->content = "OK";
response->media_type = webcc::media_types::kTextPlain;
response->charset = "utf-8";
response->status = webcc::Status::kCreated;
// TODO: media_type: webcc::media_types::kTextPlain; charset = "utf-8";
return webcc::ResponseBuilder{}.Created().Data("OK")();
}
return webcc::ResponseBuilder{}.NotImplemented()();
}
};

@ -4,9 +4,12 @@
#include <thread>
#include <vector>
#include "boost/core/ignore_unused.hpp"
#include "json/json.h"
#include "webcc/logger.h"
#include "webcc/response_builder.h"
#include "webcc/server.h"
#include "examples/common/book.h"
@ -41,11 +44,10 @@ public:
protected:
// Get a list of books based on query parameters.
void Get(const webcc::UrlQuery& query, webcc::RestResponse* response) override;
webcc::ResponsePtr Get(const webcc::UrlQuery& query) override;
// Create a new book.
void Post(const std::string& request_content,
webcc::RestResponse* response) override;
webcc::ResponsePtr Post(webcc::RequestPtr request) override;
private:
// Sleep some seconds before send back the response.
@ -55,7 +57,7 @@ private:
// -----------------------------------------------------------------------------
// The URL is like '/books/{BookID}', and the 'url_matches' parameter
// The URL is like '/books/{BookID}', and the 'args' parameter
// contains the matched book ID.
class BookDetailService : public webcc::DetailService {
public:
@ -65,18 +67,15 @@ public:
protected:
// Get the detailed information of a book.
void Get(const webcc::UrlMatches& url_matches,
const webcc::UrlQuery& query,
webcc::RestResponse* response) override;
webcc::ResponsePtr Get(const webcc::UrlArgs& args,
const webcc::UrlQuery& query) override;
// Update a book.
void Put(const webcc::UrlMatches& url_matches,
const std::string& request_content,
webcc::RestResponse* response) override;
webcc::ResponsePtr Put(webcc::RequestPtr request,
const webcc::UrlArgs& args) override;
// Delete a book.
void Delete(const webcc::UrlMatches& url_matches,
webcc::RestResponse* response) override;
webcc::ResponsePtr Delete(const webcc::UrlArgs& args) override;
private:
// Sleep some seconds before send back the response.
@ -87,8 +86,9 @@ private:
// -----------------------------------------------------------------------------
// Return all books as a JSON array.
void BookListService::Get(const webcc::UrlQuery& /*query*/,
webcc::RestResponse* response) {
webcc::ResponsePtr BookListService::Get(const webcc::UrlQuery& query) {
boost::ignore_unused(query);
Sleep(sleep_seconds_);
Json::Value json(Json::arrayValue);
@ -97,103 +97,86 @@ void BookListService::Get(const webcc::UrlQuery& /*query*/,
json.append(BookToJson(book));
}
// TODO: Simplify
response->content = JsonToString(json);
response->media_type = webcc::media_types::kApplicationJson;
response->charset = "utf-8";
response->status = webcc::Status::kOK;
// TODO: charset = "utf-8"
return webcc::ResponseBuilder{}.OK().Data(JsonToString(json)).Json()();
}
void BookListService::Post(const std::string& request_content,
webcc::RestResponse* response) {
webcc::ResponsePtr BookListService::Post(webcc::RequestPtr request) {
Sleep(sleep_seconds_);
Book book;
if (JsonStringToBook(request_content, &book)) {
if (JsonStringToBook(request->content(), &book)) {
std::string id = g_book_store.AddBook(book);
Json::Value json;
json["id"] = id;
response->content = JsonToString(json);
response->media_type = webcc::media_types::kApplicationJson;
response->charset = "utf-8";
response->status = webcc::Status::kCreated;
// TODO: charset = "utf-8"
return webcc::ResponseBuilder{}.Created().Data(JsonToString(json)).Json()();
} else {
// Invalid JSON
response->status = webcc::Status::kBadRequest;
return webcc::ResponseBuilder{}.BadRequest()();
}
}
// -----------------------------------------------------------------------------
void BookDetailService::Get(const webcc::UrlMatches& url_matches,
const webcc::UrlQuery& query,
webcc::RestResponse* response) {
webcc::ResponsePtr BookDetailService::Get(const webcc::UrlArgs& args,
const webcc::UrlQuery& query) {
Sleep(sleep_seconds_);
if (url_matches.size() != 1) {
if (args.size() != 1) {
// Using kNotFound means the resource specified by the URL cannot be found.
// kBadRequest could be another choice.
response->status = webcc::Status::kNotFound;
return;
return webcc::ResponseBuilder{}.NotFound()();
}
const std::string& book_id = url_matches[0];
const std::string& book_id = args[0];
const Book& book = g_book_store.GetBook(book_id);
if (book.IsNull()) {
response->status = webcc::Status::kNotFound;
return;
return webcc::ResponseBuilder{}.NotFound()();
}
response->content = BookToJsonString(book);
response->media_type = webcc::media_types::kApplicationJson;
response->charset = "utf-8";
response->status = webcc::Status::kOK;
// TODO: charset = "utf-8"
return webcc::ResponseBuilder{}.OK().Data(BookToJsonString(book)).Json()();
}
void BookDetailService::Put(const webcc::UrlMatches& url_matches,
const std::string& request_content,
webcc::RestResponse* response) {
webcc::ResponsePtr BookDetailService::Put(webcc::RequestPtr request,
const webcc::UrlArgs& args) {
Sleep(sleep_seconds_);
if (url_matches.size() != 1) {
response->status = webcc::Status::kNotFound;
return;
if (args.size() != 1) {
return webcc::ResponseBuilder{}.NotFound()();
}
const std::string& book_id = url_matches[0];
const std::string& book_id = args[0];
Book book;
if (!JsonStringToBook(request_content, &book)) {
response->status = webcc::Status::kBadRequest;
return;
if (!JsonStringToBook(request->content(), &book)) {
return webcc::ResponseBuilder{}.BadRequest()();
}
book.id = book_id;
g_book_store.UpdateBook(book);
response->status = webcc::Status::kOK;
return webcc::ResponseBuilder{}.OK()();
}
void BookDetailService::Delete(const webcc::UrlMatches& url_matches,
webcc::RestResponse* response) {
webcc::ResponsePtr BookDetailService::Delete(const webcc::UrlArgs& args) {
Sleep(sleep_seconds_);
if (url_matches.size() != 1) {
response->status = webcc::Status::kNotFound;
return;
if (args.size() != 1) {
return webcc::ResponseBuilder{}.NotFound()();
}
const std::string& book_id = url_matches[0];
const std::string& book_id = args[0];
if (!g_book_store.DeleteBook(book_id)) {
response->status = webcc::Status::kNotFound;
return;
return webcc::ResponseBuilder{}.NotFound()();
}
response->status = webcc::Status::kOK;
return webcc::ResponseBuilder{}.OK()();
}
// -----------------------------------------------------------------------------

@ -6,9 +6,9 @@
class MyService : public webcc::Service {
public:
void Handle(const webcc::RestRequest& request,
webcc::RestResponse* response) override {
response->status = webcc::Status::kOK;
webcc::ResponsePtr Handle(webcc::RequestPtr request,
const webcc::UrlArgs& args) override {
return webcc::ResponseBuilder{}.OK()();
}
};
@ -17,22 +17,21 @@ public:
TEST(ServiceManagerTest, URL_RegexBasic) {
webcc::ServiceManager service_manager;
service_manager.AddService(std::make_shared<MyService>(),
"/instance/(\\d+)", true);
std::vector<std::string> matches;
service_manager.Add(std::make_shared<MyService>(), "/instance/(\\d+)", true);
std::string url = "/instance/12345";
webcc::ServicePtr service = service_manager.GetService(url, &matches);
webcc::UrlArgs args;
webcc::ServicePtr service = service_manager.Get(url, &args);
EXPECT_TRUE(!!service);
EXPECT_EQ(1, matches.size());
EXPECT_EQ("12345", matches[0]);
EXPECT_EQ(1, args.size());
EXPECT_EQ("12345", args[0]);
url = "/instance/abcde";
matches.clear();
service = service_manager.GetService(url, &matches);
args.clear();
service = service_manager.Get(url, &args);
EXPECT_FALSE(!!service);
}
@ -40,25 +39,24 @@ TEST(ServiceManagerTest, URL_RegexBasic) {
TEST(RestServiceManagerTest, URL_RegexMultiple) {
webcc::ServiceManager service_manager;
service_manager.AddService(std::make_shared<MyService>(),
"/study/(\\d+)/series/(\\d+)/instance/(\\d+)",
true);
std::vector<std::string> matches;
service_manager.Add(std::make_shared<MyService>(),
"/study/(\\d+)/series/(\\d+)/instance/(\\d+)", true);
std::string url = "/study/1/series/2/instance/3";
webcc::ServicePtr service = service_manager.GetService(url, &matches);
webcc::UrlArgs args;
webcc::ServicePtr service = service_manager.Get(url, &args);
EXPECT_TRUE(!!service);
EXPECT_EQ(3, matches.size());
EXPECT_EQ("1", matches[0]);
EXPECT_EQ("2", matches[1]);
EXPECT_EQ("3", matches[2]);
EXPECT_EQ(3, args.size());
EXPECT_EQ("1", args[0]);
EXPECT_EQ("2", args[1]);
EXPECT_EQ("3", args[2]);
url = "/study/a/series/b/instance/c";
matches.clear();
service = service_manager.GetService(url, &matches);
args.clear();
service = service_manager.Get(url, &args);
EXPECT_FALSE(!!service);
}
@ -66,13 +64,13 @@ TEST(RestServiceManagerTest, URL_RegexMultiple) {
TEST(RestServiceManagerTest, URL_NonRegex) {
webcc::ServiceManager service_manager;
service_manager.AddService(std::make_shared<MyService>(), "/instances",
false);
service_manager.Add(std::make_shared<MyService>(), "/instances", false);
std::vector<std::string> matches;
std::string url = "/instances";
webcc::ServicePtr service = service_manager.GetService(url, &matches);
webcc::UrlArgs args;
webcc::ServicePtr service = service_manager.Get(url, &args);
EXPECT_TRUE(!!service);
EXPECT_EQ(0, matches.size());
EXPECT_TRUE(args.empty());
}

@ -8,7 +8,7 @@ TEST(UrlTest, Basic) {
EXPECT_EQ("http", url.scheme());
EXPECT_EQ("example.com", url.host());
EXPECT_EQ("", url.port());
EXPECT_EQ("path", url.path());
EXPECT_EQ("/path", url.path());
EXPECT_EQ("", url.query());
}
@ -28,7 +28,7 @@ TEST(UrlTest, NoPath2) {
EXPECT_EQ("http", url.scheme());
EXPECT_EQ("example.com", url.host());
EXPECT_EQ("", url.port());
EXPECT_EQ("", url.path());
EXPECT_EQ("/", url.path());
EXPECT_EQ("", url.query());
}
@ -48,7 +48,7 @@ TEST(UrlTest, NoPath4) {
EXPECT_EQ("http", url.scheme());
EXPECT_EQ("example.com", url.host());
EXPECT_EQ("", url.port());
EXPECT_EQ("", url.path());
EXPECT_EQ("/", url.path());
EXPECT_EQ("key=value", url.query());
}
@ -58,7 +58,7 @@ TEST(UrlTest, NoScheme) {
EXPECT_EQ("", url.scheme());
EXPECT_EQ("", url.host());
EXPECT_EQ("", url.port());
EXPECT_EQ("path/to", url.path());
EXPECT_EQ("/path/to", url.path());
EXPECT_EQ("", url.query());
}
@ -68,7 +68,7 @@ TEST(UrlTest, NoScheme2) {
EXPECT_EQ("", url.scheme());
EXPECT_EQ("", url.host());
EXPECT_EQ("", url.port());
EXPECT_EQ("path/to", url.path());
EXPECT_EQ("/path/to", url.path());
EXPECT_EQ("key=value", url.query());
}
@ -78,6 +78,6 @@ TEST(UrlTest, Full) {
EXPECT_EQ("https", url.scheme());
EXPECT_EQ("localhost", url.host());
EXPECT_EQ("3000", url.port());
EXPECT_EQ("path/to", url.path());
EXPECT_EQ("/path/to", url.path());
EXPECT_EQ("key=value", url.query());
}

@ -43,6 +43,7 @@ public:
// Send response to client.
void SendResponse(ResponsePtr response);
// TODO: Remove
void SendResponse(Status status);
private:

@ -11,7 +11,6 @@ namespace webcc {
class RequestBuilder {
public:
RequestBuilder() = default;
~RequestBuilder() = default;
RequestBuilder(const RequestBuilder&) = delete;
RequestBuilder& operator=(const RequestBuilder&) = delete;
@ -119,13 +118,13 @@ private:
// Files to upload for a POST request.
std::vector<FormPartPtr> form_parts_;
// Compress the request content.
// Compress the content.
// NOTE: Most servers don't support compressed requests.
// Even the requests module from Python doesn't have a built-in support.
// See: https://github.com/kennethreitz/requests/issues/1753
bool gzip_ = false;
// Additional request headers.
// Additional headers.
std::vector<std::string> headers_;
// Persistent connection.

@ -21,7 +21,7 @@ RequestHandler::RequestHandler(const std::string& doc_root)
bool RequestHandler::Bind(ServicePtr service, const std::string& url,
bool is_regex) {
return service_manager_.AddService(service, url, is_regex);
return service_manager_.Add(service, url, is_regex);
}
void RequestHandler::Enqueue(ConnectionPtr connection) {
@ -80,14 +80,12 @@ void RequestHandler::HandleConnection(ConnectionPtr connection) {
auto request = connection->request();
const Url& url = request->url();
RestRequest rest_request{ request };
UrlArgs args;
LOG_INFO("Request URL path: %s", url.path().c_str());
// Get service by URL path.
auto service = service_manager_.GetService(url.path(),
&rest_request.url_matches);
auto service = service_manager_.Get(url.path(), &args);
if (!service) {
LOG_WARN("No service matches the URL path: %s", url.path().c_str());
@ -99,20 +97,14 @@ void RequestHandler::HandleConnection(ConnectionPtr connection) {
return;
}
RestResponse rest_response;
service->Handle(rest_request, &rest_response);
auto response = std::make_shared<Response>(rest_response.status);
if (!rest_response.content.empty()) {
if (!rest_response.media_type.empty()) {
response->SetContentType(rest_response.media_type, rest_response.charset);
}
SetContent(request, response, std::move(rest_response.content));
}
ResponsePtr response = service->Handle(request, args);
// Send response back to client.
connection->SendResponse(response);
if (response) {
connection->SendResponse(response);
} else {
connection->SendResponse(Status::kNotImplemented);
}
}
bool RequestHandler::ServeStatic(ConnectionPtr connection) {

@ -13,8 +13,7 @@ using ResponsePtr = std::shared_ptr<Response>;
class Response : public Message {
public:
explicit Response(Status status = Status::kOK)
: status_(status) {
explicit Response(Status status = Status::kOK) : status_(status) {
}
~Response() override = default;

@ -0,0 +1,57 @@
#include "webcc/response_builder.h"
#include "webcc/base64.h"
#include "webcc/logger.h"
#include "webcc/utility.h"
#if WEBCC_ENABLE_GZIP
#include "webcc/gzip.h"
#endif
namespace webcc {
ResponsePtr ResponseBuilder::operator()() {
assert(headers_.size() % 2 == 0);
auto request = std::make_shared<Response>(code_);
for (std::size_t i = 1; i < headers_.size(); i += 2) {
request->SetHeader(std::move(headers_[i - 1]), std::move(headers_[i]));
}
if (!data_.empty()) {
SetContent(request, std::move(data_));
// TODO: charset.
if (json_) {
request->SetContentType(media_types::kApplicationJson, "");
}
}
return request;
}
ResponseBuilder& ResponseBuilder::Date() {
headers_.push_back(headers::kDate);
headers_.push_back(utility::GetTimestamp());
return *this;
}
void ResponseBuilder::SetContent(ResponsePtr response, std::string&& data) {
#if WEBCC_ENABLE_GZIP
if (gzip_ && data.size() > kGzipThreshold) {
std::string compressed;
if (gzip::Compress(data, &compressed)) {
response->SetContent(std::move(compressed), true);
response->SetHeader(headers::kContentEncoding, "gzip");
return;
}
LOG_WARN("Cannot compress the content data!");
}
#endif // WEBCC_ENABLE_GZIP
response->SetContent(std::move(data), true);
}
} // namespace webcc

@ -0,0 +1,88 @@
#ifndef WEBCC_RESPONSE_BUILDER_H_
#define WEBCC_RESPONSE_BUILDER_H_
#include <string>
#include <vector>
#include "webcc/response.h"
namespace webcc {
class ResponseBuilder {
public:
ResponseBuilder() = default;
ResponseBuilder(const ResponseBuilder&) = delete;
ResponseBuilder& operator=(const ResponseBuilder&) = delete;
// Build the response.
ResponsePtr operator()();
// NOTE:
// The naming convention doesn't follow Google C++ Style for
// consistency and simplicity.
// Some shortcuts for different status codes:
ResponseBuilder& OK() { return Code(Status::kOK); }
ResponseBuilder& Created() { return Code(Status::kCreated); }
ResponseBuilder& BadRequest() { return Code(Status::kBadRequest); }
ResponseBuilder& NotFound() { return Code(Status::kNotFound); }
ResponseBuilder& NotImplemented() { return Code(Status::kNotImplemented); }
ResponseBuilder& Code(Status code) {
code_ = code;
return *this;
}
ResponseBuilder& Data(const std::string& data) {
data_ = data;
return *this;
}
ResponseBuilder& Data(std::string&& data) {
data_ = std::move(data);
return *this;
}
ResponseBuilder& Json(bool json = true) {
json_ = json;
return *this;
}
ResponseBuilder& Gzip(bool gzip = true) {
gzip_ = gzip;
return *this;
}
ResponseBuilder& Header(const std::string& key, const std::string& value) {
headers_.push_back(key);
headers_.push_back(value);
return *this;
}
// Add the Date header to the response.
ResponseBuilder& Date();
private:
void SetContent(ResponsePtr response, std::string&& data);
private:
// Status code.
Status code_ = Status::kOK;
// Data to send in the body of the request.
std::string data_;
// Is the data to send a JSON string?
bool json_ = false;
// Compress the response content.
bool gzip_ = false;
// Additional headers.
std::vector<std::string> headers_;
};
} // namespace webcc
#endif // WEBCC_RESPONSE_BUILDER_H_

@ -6,40 +6,62 @@ namespace webcc {
// -----------------------------------------------------------------------------
void ListService::Handle(const RestRequest& request, RestResponse* response) {
const std::string& method = request.http->method();
if (method == methods::kGet) {
Get(UrlQuery(request.http->url().query()), response);
ResponsePtr ListService::Handle(RequestPtr request, const UrlArgs& args) {
if (request->method() == methods::kGet) {
return Get(UrlQuery(request->url().query()));
}
if (request->method() == methods::kPost) {
return Post(request);
}
return ResponsePtr();
}
} else if (method == methods::kPost) {
Post(request.http->content(), response);
ResponsePtr ListService::Get(const UrlQuery& query) {
return ResponsePtr();
}
} else {
LOG_ERRO("ListService doesn't support '%s' method.", method.c_str());
}
ResponsePtr ListService::Post(RequestPtr request) {
return ResponsePtr();
}
// -----------------------------------------------------------------------------
void DetailService::Handle(const RestRequest& request, RestResponse* response) {
const std::string& method = request.http->method();
ResponsePtr DetailService::Handle(RequestPtr request, const UrlArgs& args) {
if (request->method() == methods::kGet) {
return Get(args, UrlQuery(request->url().query()));
}
if (request->method() == methods::kPut) {
return Put(request, args);
}
if (request->method() == methods::kPatch) {
return Patch(request, args);
}
if (request->method() == methods::kDelete) {
return Delete(args);
}
if (method == methods::kGet) {
Get(request.url_matches, UrlQuery(request.http->url().query()), response);
return ResponsePtr();
}
} else if (method == methods::kPut) {
Put(request.url_matches, request.http->content(), response);
ResponsePtr DetailService::Get(const UrlArgs& args, const UrlQuery& query) {
return ResponsePtr();
}
} else if (method == methods::kPatch) {
Patch(request.url_matches, request.http->content(), response);
ResponsePtr DetailService::Put(RequestPtr request, const UrlArgs& args) {
return ResponsePtr();
}
} else if (method == methods::kDelete) {
Delete(request.url_matches, response);
ResponsePtr DetailService::Patch(RequestPtr request, const UrlArgs& args) {
return ResponsePtr();
}
} else {
LOG_ERRO("DetailService doesn't support '%s' method.", method.c_str());
}
ResponsePtr DetailService::Delete(const UrlArgs& args) {
return ResponsePtr();
}
} // namespace webcc

@ -15,32 +15,17 @@
#include "webcc/globals.h"
#include "webcc/request.h"
#include "webcc/response.h"
#include "webcc/response_builder.h"
#include "webcc/url.h"
namespace webcc {
// -----------------------------------------------------------------------------
// Regex sub-matches of the URL.
using UrlMatches = std::vector<std::string>;
struct RestRequest {
// Original HTTP request.
RequestPtr http;
// Regex sub-matches of the URL (usually resource ID's).
UrlMatches url_matches;
};
// TODO: Add ResponseBuilder instead.
struct RestResponse {
Status status;
std::string content;
std::string media_type;
std::string charset;
};
// Regex sub-matches of the URL (usually resource ID's).
// Could also be considered as arguments, so named as UrlArgs.
using UrlArgs = std::vector<std::string>;
// -----------------------------------------------------------------------------
@ -49,8 +34,8 @@ class Service {
public:
virtual ~Service() = default;
// Handle request, output response.
virtual void Handle(const RestRequest& request, RestResponse* response) = 0;
// Handle request, return response.
virtual ResponsePtr Handle(RequestPtr request, const UrlArgs& args) = 0;
};
using ServicePtr = std::shared_ptr<Service>;
@ -59,42 +44,28 @@ using ServicePtr = std::shared_ptr<Service>;
class ListService : public Service {
public:
void Handle(const RestRequest& request, RestResponse* response) override;
ResponsePtr Handle(RequestPtr request, const UrlArgs& args) override;
protected:
virtual void Get(const UrlQuery& query, RestResponse* response) {
}
virtual ResponsePtr Get(const UrlQuery& query);
virtual void Post(const std::string& request_content,
RestResponse* response) {
}
virtual ResponsePtr Post(RequestPtr request);
};
// -----------------------------------------------------------------------------
class DetailService : public Service {
public:
void Handle(const RestRequest& request, RestResponse* response) override;
ResponsePtr Handle(RequestPtr request, const UrlArgs& args) override;
protected:
virtual void Get(const UrlMatches& url_matches,
const UrlQuery& query,
RestResponse* response) {
}
virtual void Put(const UrlMatches& url_matches,
const std::string& request_content,
RestResponse* response) {
}
virtual void Patch(const UrlMatches& url_matches,
const std::string& request_content,
RestResponse* response) {
}
virtual void Delete(const UrlMatches& url_matches,
RestResponse* response) {
}
virtual ResponsePtr Get(const UrlArgs& args, const UrlQuery& query);
virtual ResponsePtr Put(RequestPtr request, const UrlArgs& args);
virtual ResponsePtr Patch(RequestPtr request, const UrlArgs& args);
virtual ResponsePtr Delete(const UrlArgs& args);
};
} // namespace webcc

@ -6,8 +6,8 @@
namespace webcc {
bool ServiceManager::AddService(ServicePtr service, const std::string& url,
bool is_regex) {
bool ServiceManager::Add(ServicePtr service, const std::string& url,
bool is_regex) {
assert(service);
Item item(service, url, is_regex);
@ -30,9 +30,8 @@ bool ServiceManager::AddService(ServicePtr service, const std::string& url,
}
}
ServicePtr ServiceManager::GetService(const std::string& url,
UrlMatches* matches) {
assert(matches != nullptr);
ServicePtr ServiceManager::Get(const std::string& url, UrlArgs* args) {
assert(args != nullptr);
for (Item& item : items_) {
if (item.is_regex) {
@ -42,7 +41,7 @@ ServicePtr ServiceManager::GetService(const std::string& url,
// Any sub-matches?
// NOTE: Start from 1 because match[0] is the whole string itself.
for (size_t i = 1; i < match.size(); ++i) {
matches->push_back(match[i].str());
args->push_back(match[i].str());
}
return item.service;

@ -21,13 +21,13 @@ public:
// The |url| should start with "/" and will be treated as a regular expression
// if |regex| is true.
// Examples: "/instances", "/instances/(\\d+)".
bool AddService(ServicePtr service, const std::string& url, bool is_regex);
bool Add(ServicePtr service, const std::string& url, bool is_regex);
// The |matches| is only available when the |url| bound to the service is a
// regular expression and has sub-expressions.
// E.g., the URL bound to the service is "/instances/(\\d+)", now match
// "/instances/12345" against it, you will get one match of "12345".
ServicePtr GetService(const std::string& url, UrlMatches* matches);
ServicePtr Get(const std::string& url, UrlArgs* args);
private:
class Item {

Loading…
Cancel
Save