Server API rework.
parent
a65294aeec
commit
a3cab444dc
@ -0,0 +1,39 @@
|
|||||||
|
// A general HTTP server serving static files.
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
#include "webcc/server.h"
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Help(const char* argv0) {
|
||||||
|
std::cout << "Usage: " << argv0 << " <port> <doc_root>" << std::endl;
|
||||||
|
std::cout << " E.g.," << std::endl;
|
||||||
|
std::cout << " " << argv0 << " 8080 D:/www" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc < 3) {
|
||||||
|
Help(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||||
|
|
||||||
|
std::uint16_t port = static_cast<std::uint16_t>(std::atoi(argv[1]));
|
||||||
|
std::string doc_root = argv[2];
|
||||||
|
|
||||||
|
try {
|
||||||
|
webcc::Server server(port, 1, doc_root);
|
||||||
|
|
||||||
|
server.Run();
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,67 +0,0 @@
|
|||||||
#include "webcc/service.h"
|
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponsePtr ListService::Get(const UrlQuery& query) {
|
|
||||||
return ResponsePtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponsePtr ListService::Post(RequestPtr request) {
|
|
||||||
return ResponsePtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResponsePtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponsePtr DetailService::Get(const UrlArgs& args, const UrlQuery& query) {
|
|
||||||
return ResponsePtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponsePtr DetailService::Put(RequestPtr request, const UrlArgs& args) {
|
|
||||||
return ResponsePtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponsePtr DetailService::Patch(RequestPtr request, const UrlArgs& args) {
|
|
||||||
return ResponsePtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponsePtr DetailService::Delete(const UrlArgs& args) {
|
|
||||||
return ResponsePtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
@ -1,73 +0,0 @@
|
|||||||
#ifndef WEBCC_SERVICE_H_
|
|
||||||
#define WEBCC_SERVICE_H_
|
|
||||||
|
|
||||||
// NOTE:
|
|
||||||
// The design of RestListService and RestDetailService is very similar to
|
|
||||||
// XxxListView and XxxDetailView in Python Django Rest Framework.
|
|
||||||
// Deriving from them instead of RestService can simplify your own REST services
|
|
||||||
// a lot. But if you find the filtered parameters cannot meet your needs, feel
|
|
||||||
// free to derive from RestService directly.
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#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 (usually resource ID's).
|
|
||||||
// Could also be considered as arguments, so named as UrlArgs.
|
|
||||||
using UrlArgs = std::vector<std::string>;
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Base class for your service.
|
|
||||||
class Service {
|
|
||||||
public:
|
|
||||||
virtual ~Service() = default;
|
|
||||||
|
|
||||||
// Handle request, return response.
|
|
||||||
virtual ResponsePtr Handle(RequestPtr request, const UrlArgs& args) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
using ServicePtr = std::shared_ptr<Service>;
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class ListService : public Service {
|
|
||||||
public:
|
|
||||||
ResponsePtr Handle(RequestPtr request, const UrlArgs& args) override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ResponsePtr Get(const UrlQuery& query);
|
|
||||||
|
|
||||||
virtual ResponsePtr Post(RequestPtr request);
|
|
||||||
};
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class DetailService : public Service {
|
|
||||||
public:
|
|
||||||
ResponsePtr Handle(RequestPtr request, const UrlArgs& args) override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
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
|
|
||||||
|
|
||||||
#endif // WEBCC_SERVICE_H_
|
|
@ -1,59 +0,0 @@
|
|||||||
#include "webcc/service_manager.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
bool ServiceManager::Add(ServicePtr service, const std::string& url,
|
|
||||||
bool is_regex) {
|
|
||||||
assert(service);
|
|
||||||
|
|
||||||
Item item(service, url, is_regex);
|
|
||||||
|
|
||||||
if (!is_regex) {
|
|
||||||
items_.push_back(std::move(item));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::regex::flag_type flags = std::regex::ECMAScript | std::regex::icase;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Compile the regex.
|
|
||||||
item.url_regex.assign(url, flags);
|
|
||||||
items_.push_back(std::move(item));
|
|
||||||
return true;
|
|
||||||
} catch (const std::regex_error& e) {
|
|
||||||
LOG_ERRO("URL is not a valid regular expression: %s", e.what());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ServicePtr ServiceManager::Get(const std::string& url, UrlArgs* args) {
|
|
||||||
assert(args != nullptr);
|
|
||||||
|
|
||||||
for (Item& item : items_) {
|
|
||||||
if (item.is_regex) {
|
|
||||||
std::smatch match;
|
|
||||||
|
|
||||||
if (std::regex_match(url, match, item.url_regex)) {
|
|
||||||
// Any sub-matches?
|
|
||||||
// NOTE: Start from 1 because match[0] is the whole string itself.
|
|
||||||
for (size_t i = 1; i < match.size(); ++i) {
|
|
||||||
args->push_back(match[i].str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return item.service;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (item.url == url) {
|
|
||||||
return item.service;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ServicePtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
@ -1,66 +0,0 @@
|
|||||||
#ifndef WEBCC_SERVICE_MANAGER_H_
|
|
||||||
#define WEBCC_SERVICE_MANAGER_H_
|
|
||||||
|
|
||||||
#include <regex> // NOLINT
|
|
||||||
#include <string>
|
|
||||||
#include <utility> // for move()
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "webcc/service.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
class ServiceManager {
|
|
||||||
public:
|
|
||||||
ServiceManager() = default;
|
|
||||||
|
|
||||||
ServiceManager(const ServiceManager&) = delete;
|
|
||||||
ServiceManager& operator=(const ServiceManager&) = delete;
|
|
||||||
|
|
||||||
// Add a service and bind it with the given URL.
|
|
||||||
// The |url| should start with "/" and will be treated as a regular expression
|
|
||||||
// if |regex| is true.
|
|
||||||
// Examples: "/instances", "/instances/(\\d+)".
|
|
||||||
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 Get(const std::string& url, UrlArgs* args);
|
|
||||||
|
|
||||||
private:
|
|
||||||
class Item {
|
|
||||||
public:
|
|
||||||
Item(ServicePtr _service, const std::string& _url, bool _is_regex)
|
|
||||||
: service(_service), url(_url), is_regex(_is_regex) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Item(const Item&) = default;
|
|
||||||
Item& operator=(const Item&) = default;
|
|
||||||
|
|
||||||
Item(Item&& rhs)
|
|
||||||
: service(rhs.service),
|
|
||||||
url(std::move(rhs.url)),
|
|
||||||
is_regex(rhs.is_regex),
|
|
||||||
url_regex(std::move(rhs.url_regex)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ServicePtr service;
|
|
||||||
|
|
||||||
// URL string, e.g., "/instances/(\\d+)".
|
|
||||||
std::string url;
|
|
||||||
|
|
||||||
// If the URL is a regular expression or not.
|
|
||||||
bool is_regex;
|
|
||||||
|
|
||||||
// Compiled regex for URL string.
|
|
||||||
std::regex url_regex;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Item> items_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_SERVICE_MANAGER_H_
|
|
@ -0,0 +1,43 @@
|
|||||||
|
#include "webcc/view.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//ResponsePtr DetailView::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);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return ResponsePtr();
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//ResponsePtr DetailView::Get(const UrlArgs& args, const UrlQuery& query) {
|
||||||
|
// return ResponsePtr();
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//ResponsePtr DetailView::Put(RequestPtr request, const UrlArgs& args) {
|
||||||
|
// return ResponsePtr();
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//ResponsePtr DetailView::Patch(RequestPtr request, const UrlArgs& args) {
|
||||||
|
// return ResponsePtr();
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//ResponsePtr DetailView::Delete(const UrlArgs& args) {
|
||||||
|
// return ResponsePtr();
|
||||||
|
//}
|
||||||
|
|
||||||
|
} // namespace webcc
|
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef WEBCC_VIEW_H_
|
||||||
|
#define WEBCC_VIEW_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "webcc/request.h"
|
||||||
|
#include "webcc/response.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class View {
|
||||||
|
public:
|
||||||
|
virtual ~View() = default;
|
||||||
|
|
||||||
|
virtual ResponsePtr Handle(RequestPtr request) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ViewPtr = std::shared_ptr<View>;
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_VIEW_H_
|
Loading…
Reference in New Issue