diff --git a/example/rest_book_client/main.cc b/example/rest_book_client/main.cc index 04f95eb..f0e8173 100644 --- a/example/rest_book_client/main.cc +++ b/example/rest_book_client/main.cc @@ -26,18 +26,23 @@ public: } bool ListBooks() { + std::cout << "ListBooks" << std::endl; + webcc::HttpResponse http_response; if (!rest_client_.Get("/books", &http_response)) { return false; } - std::cout << "result:\n" << http_response.content() << std::endl; + std::cout << http_response.content() << std::endl; return true; } bool CreateBook(const std::string& id, const std::string& title, double price) { + std::cout << "CreateBook: " << id << " " << title << " " << price + << std::endl; + Json::Value json(Json::objectValue); json["id"] = id; json["title"] = title; @@ -66,6 +71,8 @@ public: } bool GetBook(const std::string& id) { + std::cout << "GetBook: " << id << std::endl; + webcc::HttpResponse http_response; if (!rest_client_.Get("/book/" + id, &http_response)) { return false; @@ -78,13 +85,16 @@ public: bool UpdateBook(const std::string& id, const std::string& title, double price) { + std::cout << "UpdateBook: " << id << " " << title << " " << price + << std::endl; + // NOTE: ID is already in the URL. Json::Value json(Json::objectValue); json["title"] = title; json["price"] = price; webcc::HttpResponse http_response; - if (!rest_client_.Post("/book/" + id, JsonToString(json), &http_response)) { + if (!rest_client_.Put("/book/" + id, JsonToString(json), &http_response)) { return false; } @@ -93,6 +103,8 @@ public: } bool DeleteBook(const std::string& id) { + std::cout << "DeleteBook: " << id << std::endl; + webcc::HttpResponse http_response; if (!rest_client_.Delete("/book/" + id, &http_response)) { return false; @@ -108,119 +120,35 @@ private: //////////////////////////////////////////////////////////////////////////////// -std::string g_host; -std::string g_port; - void Help(const char* argv0) { std::cout << "Usage: " << argv0 << " " << std::endl; std::cout << " E.g.," << std::endl; std::cout << " " << argv0 << " localhost 8080" << std::endl; } -std::string GetUserInput() { - char input[256]; - std::cout << ">> "; - std::cin.getline(input, 256); - return input; -} - -bool ParseJsonInput(const std::string& input, Json::Value* root) { - Json::CharReaderBuilder builder; - std::stringstream stream(input); - std::string errs; - if (Json::parseFromStream(builder, stream, root, &errs)) { - return true; - } else { - std::cerr << errs << std::endl; - return false; - } -} - int main(int argc, char* argv[]) { if (argc != 3) { Help(argv[0]); return 1; } - LOG_INIT(webcc::VERB, 0); - - g_host = argv[1]; - g_port = argv[2]; - - // Type commands to execute actions. - // Commands: list, create, detail, update, delete and exit. - // Examples: - // >> list - // >> create 1 { "title": "1984", "price": 12.3 } - // >> detail 1 - // >> update 1 { "title": "1Q84", "price": 32.1 } - // >> delete 1 - // >> exit - - // A very naive implementation of interaction mode. - while (true) { - std::string input = GetUserInput(); - boost::trim(input); - - std::string command; - std::size_t i = input.find(' '); - if (i == std::string::npos) { - command = input; - } else { - command = input.substr(0, i); - } + LOG_INIT(webcc::ERRO, 0); - if (command == "exit") { - break; - } - - if (command == "list") { - BookListClient client(g_host, g_port); - client.ListBooks(); - continue; - } - - ++i; + std::string host = argv[1]; + std::string port = argv[2]; - std::size_t j = input.find(' ', i); - std::string id = input.substr(i, j - i); - i = j + 1; - - if (command == "create") { - std::string json = input.substr(i); - - Json::Value root; - if (ParseJsonInput(json, &root)) { - BookListClient client(g_host, g_port); - client.CreateBook(id, root["title"].asString(), root["price"].asDouble()); - } - continue; - } + BookListClient list_client(host, port); + BookDetailClient detail_client(host, port); - if (command == "update") { - std::string json = input.substr(i); + list_client.ListBooks(); + list_client.CreateBook("1", "1984", 12.3); - Json::Value root; - if (ParseJsonInput(json, &root)) { - BookDetailClient client(g_host, g_port); - client.UpdateBook(id, root["title"].asString(), root["price"].asDouble()); - } - continue; - } - - if (command == "detail") { - BookDetailClient client(g_host, g_port); - client.GetBook(id); - continue; - } - - if (command == "delete") { - BookDetailClient client(g_host, g_port); - client.DeleteBook(id); - continue; - } - } + detail_client.GetBook("1"); + detail_client.UpdateBook("1", "1Q84", 32.1); + detail_client.GetBook("1"); + detail_client.DeleteBook("1"); + list_client.ListBooks(); return 0; } diff --git a/example/rest_book_server/book_services.cc b/example/rest_book_server/book_services.cc index be4795e..af7a197 100644 --- a/example/rest_book_server/book_services.cc +++ b/example/rest_book_server/book_services.cc @@ -62,6 +62,7 @@ public: if (it != books_.end()) { it->title = book.title; it->price = book.price; + return true; } return false; } @@ -162,10 +163,10 @@ bool BookDetailService::Get(const std::vector& url_sub_matches, return false; } -// Update a book partially. -bool BookDetailService::Patch(const std::vector& url_sub_matches, - const std::string& request_content, - std::string* response_content) { +// Update a book. +bool BookDetailService::Put(const std::vector& url_sub_matches, + const std::string& request_content, + std::string* response_content) { if (url_sub_matches.size() != 1) { return false; } diff --git a/example/rest_book_server/book_services.h b/example/rest_book_server/book_services.h index 061dc56..f124718 100644 --- a/example/rest_book_server/book_services.h +++ b/example/rest_book_server/book_services.h @@ -35,9 +35,9 @@ class BookDetailService : public webcc::RestDetailService { const webcc::UrlQuery& query, std::string* response_content) final; - bool Patch(const std::vector& url_sub_matches, - const std::string& request_content, - std::string* response_content) final; + bool Put(const std::vector& url_sub_matches, + const std::string& request_content, + std::string* response_content) final; bool Delete(const std::vector& url_sub_matches) final; }; diff --git a/example/rest_book_server/main.cc b/example/rest_book_server/main.cc index 1f276e0..94b2ee9 100644 --- a/example/rest_book_server/main.cc +++ b/example/rest_book_server/main.cc @@ -26,11 +26,9 @@ int main(int argc, char* argv[]) { try { webcc::RestServer server(port, workers); - server.RegisterService(std::make_shared(), - "/books"); + server.Bind(std::make_shared(), "/books", false); - server.RegisterService(std::make_shared(), - "/book/(\\d+)"); + server.Bind(std::make_shared(), "/book/(\\d+)", true); server.Run(); diff --git a/example/soap_calc_server/main.cc b/example/soap_calc_server/main.cc index 65f5846..c09989c 100644 --- a/example/soap_calc_server/main.cc +++ b/example/soap_calc_server/main.cc @@ -24,8 +24,7 @@ int main(int argc, char* argv[]) { try { webcc::SoapServer server(port, workers); - server.RegisterService(std::make_shared(), - "/calculator"); + server.Bind(std::make_shared(), "/calculator"); server.Run(); diff --git a/src/webcc/globals.cc b/src/webcc/globals.cc index ce0d9e1..46ad4c9 100644 --- a/src/webcc/globals.cc +++ b/src/webcc/globals.cc @@ -91,8 +91,8 @@ Parameter::Parameter(const std::string& key, bool value) } Parameter::Parameter(Parameter&& rhs) - : key_(std::move(rhs.key_)) - , value_(std::move(rhs.value_)) { + : key_(std::move(rhs.key_)), + value_(std::move(rhs.value_)) { } Parameter& Parameter::operator=(Parameter&& rhs) { diff --git a/src/webcc/globals.h b/src/webcc/globals.h index 5431cc1..7f6f60a 100644 --- a/src/webcc/globals.h +++ b/src/webcc/globals.h @@ -113,7 +113,7 @@ const char* GetErrorMessage(Error error); // Key-value parameter. class Parameter { -public: + public: Parameter() = default; Parameter(const Parameter&) = default; Parameter& operator=(const Parameter&) = default; @@ -150,7 +150,7 @@ public: // Return "key=value" string. std::string ToString() const; -private: + private: std::string key_; std::string value_; }; diff --git a/src/webcc/http_client.cc b/src/webcc/http_client.cc index 089c80f..72a403b 100644 --- a/src/webcc/http_client.cc +++ b/src/webcc/http_client.cc @@ -36,8 +36,7 @@ HttpClient::HttpClient() CheckDeadline(); } -Error HttpClient::MakeRequest(const HttpRequest& request, - HttpResponse* response) { +Error HttpClient::Request(const HttpRequest& request, HttpResponse* response) { assert(response != NULL); Error error = kNoError; diff --git a/src/webcc/http_client.h b/src/webcc/http_client.h index 8bd6bf8..d35fb8f 100644 --- a/src/webcc/http_client.h +++ b/src/webcc/http_client.h @@ -17,23 +17,19 @@ class HttpRequest; class HttpResponse; class HttpClient { -public: + public: HttpClient(); ~HttpClient() = default; - HttpClient(const HttpClient&) = delete; - HttpClient& operator=(const HttpClient&) = delete; - void set_timeout_seconds(int timeout_seconds) { timeout_seconds_ = timeout_seconds; } - // Make a HTTP request. // Connect to the server, send the request, wait until the response is // received. - Error MakeRequest(const HttpRequest& request, HttpResponse* response); + Error Request(const HttpRequest& request, HttpResponse* response); -private: + private: Error Connect(const HttpRequest& request); Error SendReqeust(const HttpRequest& request); @@ -42,7 +38,6 @@ private: void CheckDeadline(); -private: boost::asio::io_context io_context_; boost::asio::ip::tcp::socket socket_; @@ -57,6 +52,8 @@ private: // Timer for the timeout control. boost::asio::deadline_timer deadline_timer_; + + DISALLOW_COPY_AND_ASSIGN(HttpClient); }; } // namespace webcc diff --git a/src/webcc/http_message.h b/src/webcc/http_message.h index fbb9f79..ff160ae 100644 --- a/src/webcc/http_message.h +++ b/src/webcc/http_message.h @@ -9,14 +9,14 @@ namespace webcc { class HttpHeader { -public: + public: std::string name; std::string value; }; // Base class for HTTP request and response messages. class HttpMessage { -public: + public: virtual ~HttpMessage() = default; const std::string& start_line() const { @@ -51,13 +51,12 @@ public: SetContentLength(content_.size()); } -private: + protected: void SetContentLength(std::size_t content_length) { content_length_ = content_length; SetHeader(kContentLength, std::to_string(content_length)); } -protected: // Start line with trailing "\r\n". std::string start_line_; diff --git a/src/webcc/http_request_handler.h b/src/webcc/http_request_handler.h index 491546f..71e983d 100644 --- a/src/webcc/http_request_handler.h +++ b/src/webcc/http_request_handler.h @@ -17,7 +17,7 @@ class HttpResponse; // The common handler for all incoming requests. class HttpRequestHandler { -public: + public: HttpRequestHandler() = default; virtual ~HttpRequestHandler() = default; @@ -30,7 +30,7 @@ public: // Close pending sessions and stop worker threads. void Stop(); -private: + private: void WorkerRoutine(); // Called by the worker routine. diff --git a/src/webcc/http_request_parser.h b/src/webcc/http_request_parser.h index 3cd5807..2093840 100644 --- a/src/webcc/http_request_parser.h +++ b/src/webcc/http_request_parser.h @@ -8,12 +8,12 @@ namespace webcc { class HttpRequest; class HttpRequestParser : public HttpParser { -public: + public: explicit HttpRequestParser(HttpRequest* request); ~HttpRequestParser() override = default; -private: + private: Error ParseStartLine(const std::string& line) override; HttpRequest* request_; diff --git a/src/webcc/http_response.h b/src/webcc/http_response.h index 95b6c7c..26c309b 100644 --- a/src/webcc/http_response.h +++ b/src/webcc/http_response.h @@ -14,7 +14,7 @@ class HttpResponse; std::ostream& operator<<(std::ostream& os, const HttpResponse& response); class HttpResponse : public HttpMessage { -public: + public: HttpResponse() = default; ~HttpResponse() override = default; @@ -37,7 +37,7 @@ public: // Get a fault response when HTTP status is not OK. static HttpResponse Fault(HttpStatus::Enum status); -private: + private: friend std::ostream& operator<<(std::ostream& os, const HttpResponse& response); diff --git a/src/webcc/http_response_parser.h b/src/webcc/http_response_parser.h index 8978874..414c0cf 100644 --- a/src/webcc/http_response_parser.h +++ b/src/webcc/http_response_parser.h @@ -8,16 +8,15 @@ namespace webcc { class HttpResponse; class HttpResponseParser : public HttpParser { -public: + public: explicit HttpResponseParser(HttpResponse* response); ~HttpResponseParser() override = default; -private: + private: // Parse HTTP start line; E.g., "HTTP/1.1 200 OK". Error ParseStartLine(const std::string& line) override; -private: // The result response message. HttpResponse* response_; }; diff --git a/src/webcc/http_server.h b/src/webcc/http_server.h index 07241ff..eec7bdf 100644 --- a/src/webcc/http_server.h +++ b/src/webcc/http_server.h @@ -10,6 +10,7 @@ #include "boost/scoped_ptr.hpp" #include "boost/thread/thread.hpp" +#include "webcc/globals.h" #include "webcc/http_session.h" namespace webcc { @@ -19,10 +20,7 @@ class HttpRequestHandler; // HTTP server accepts TCP connections from TCP clients. // NOTE: Only support IPv4. class HttpServer { -public: - HttpServer(const HttpServer&) = delete; - HttpServer& operator=(const HttpServer&) = delete; - + public: HttpServer(unsigned short port, std::size_t workers); virtual ~HttpServer() = default; @@ -30,7 +28,7 @@ public: // Run the server's io_service loop. void Run(); -private: + private: // Initiate an asynchronous accept operation. void AsyncAccept(); @@ -40,7 +38,6 @@ private: // Get the handler for incoming requests. virtual HttpRequestHandler* GetRequestHandler() = 0; -private: // The number of worker threads. std::size_t workers_; @@ -52,6 +49,8 @@ private: // Acceptor used to listen for incoming connections. boost::scoped_ptr acceptor_; + + DISALLOW_COPY_AND_ASSIGN(HttpServer); }; } // namespace webcc diff --git a/src/webcc/http_session.h b/src/webcc/http_session.h index 6e3ccba..9cc11f7 100644 --- a/src/webcc/http_session.h +++ b/src/webcc/http_session.h @@ -16,7 +16,7 @@ namespace webcc { class HttpRequestHandler; class HttpSession : public std::enable_shared_from_this { -public: + public: HttpSession(const HttpSession&) = delete; HttpSession& operator=(const HttpSession&) = delete; @@ -41,14 +41,13 @@ public: // Send response to client with the given status. void SendResponse(HttpStatus::Enum status); -private: + private: void AsyncRead(); void ReadHandler(boost::system::error_code ec, std::size_t length); void AsyncWrite(); void WriteHandler(boost::system::error_code ec, std::size_t length); -private: // Socket for the connection. boost::asio::ip::tcp::socket socket_; diff --git a/src/webcc/queue.h b/src/webcc/queue.h index a61a0d8..84f398d 100644 --- a/src/webcc/queue.h +++ b/src/webcc/queue.h @@ -14,7 +14,7 @@ namespace webcc { template class Queue { -public: + public: Queue() = default; Queue(const Queue&) = delete; @@ -51,7 +51,7 @@ public: not_empty_cv_.notify_one(); } -private: + private: std::list message_list_; boost::mutex mutex_; boost::condition_variable not_empty_cv_; diff --git a/src/webcc/rest_client.cc b/src/webcc/rest_client.cc index a6440b9..433a06d 100644 --- a/src/webcc/rest_client.cc +++ b/src/webcc/rest_client.cc @@ -23,7 +23,7 @@ bool RestClient::Request(const std::string& method, request.Build(); HttpClient http_client; - Error error = http_client.MakeRequest(request, response); + Error error = http_client.Request(request, response); return error == kNoError; } diff --git a/src/webcc/rest_client.h b/src/webcc/rest_client.h index 5186a2a..ab80358 100644 --- a/src/webcc/rest_client.h +++ b/src/webcc/rest_client.h @@ -10,7 +10,7 @@ namespace webcc { class HttpResponse; class RestClient { -public: + public: RestClient(const std::string& host, const std::string& port) : host_(host), port_(port) { } @@ -41,7 +41,7 @@ public: return Request(kHttpDelete, url, "", response); } -private: + private: bool Request(const std::string& method, const std::string& url, const std::string& content, diff --git a/src/webcc/rest_request_handler.cc b/src/webcc/rest_request_handler.cc index 31e0192..d4ce3d2 100644 --- a/src/webcc/rest_request_handler.cc +++ b/src/webcc/rest_request_handler.cc @@ -5,9 +5,10 @@ namespace webcc { -bool RestRequestHandler::RegisterService(RestServicePtr service, - const std::string& url) { - return service_manager_.AddService(service, url); +bool RestRequestHandler::Bind(RestServicePtr service, + const std::string& url, + bool is_regex) { + return service_manager_.AddService(service, url, is_regex); } void RestRequestHandler::HandleSession(HttpSessionPtr session) { diff --git a/src/webcc/rest_request_handler.h b/src/webcc/rest_request_handler.h index 1e617a0..aa723da 100644 --- a/src/webcc/rest_request_handler.h +++ b/src/webcc/rest_request_handler.h @@ -9,18 +9,14 @@ namespace webcc { class RestRequestHandler : public HttpRequestHandler { -public: + public: ~RestRequestHandler() override = default; - // Register a REST service to the given URL path. - // The URL should start with "/" and could be a regular expression or not. - // E.g., "/instances". "/instances/(\\d+)" - bool RegisterService(RestServicePtr service, const std::string& url); + bool Bind(RestServicePtr service, const std::string& url, bool is_regex); -private: + private: void HandleSession(HttpSessionPtr session) override; -private: RestServiceManager service_manager_; }; diff --git a/src/webcc/rest_server.h b/src/webcc/rest_server.h index bbdfeba..a04663e 100644 --- a/src/webcc/rest_server.h +++ b/src/webcc/rest_server.h @@ -10,28 +10,30 @@ namespace webcc { class RestServer : public HttpServer { -public: + public: RestServer(unsigned short port, std::size_t workers) : HttpServer(port, workers) { } ~RestServer() override = default; - // Register a REST service to the given URL path. - // The URL should start with "/" and could be a regular expression or not. - // E.g., "/instances". "/instances/(\\d+)" - // NOTE: Registering to the same URL multiple times is allowed, but only the - // last one takes effect. - bool RegisterService(RestServicePtr service, const std::string& url) { - return request_handler_.RegisterService(service, url); + // Bind a REST service to the given URL path. + // The URL should start with "/" and it will be treated as a regular + // expression if |is_regex| is true. + // Examples: + // - "/instances" + // - "/instances/(\\d+)" + // Binding to the same URL multiple times is allowed, but only the last one + // takes effect. + bool Bind(RestServicePtr service, const std::string& url, bool is_regex) { + return request_handler_.Bind(service, url, is_regex); } -private: + private: HttpRequestHandler* GetRequestHandler() override { return &request_handler_; } -private: RestRequestHandler request_handler_; }; diff --git a/src/webcc/rest_service.h b/src/webcc/rest_service.h index 39e483f..bcfc739 100644 --- a/src/webcc/rest_service.h +++ b/src/webcc/rest_service.h @@ -23,7 +23,7 @@ class UrlQuery; // Base class for your REST service. class RestService { -public: + public: virtual ~RestService() { } diff --git a/src/webcc/rest_service_manager.cc b/src/webcc/rest_service_manager.cc index ad80dd9..19051e3 100644 --- a/src/webcc/rest_service_manager.cc +++ b/src/webcc/rest_service_manager.cc @@ -7,10 +7,16 @@ namespace webcc { bool RestServiceManager::AddService(RestServicePtr service, - const std::string& url) { + const std::string& url, + bool is_regex) { assert(service); - ServiceItem item(service, url); + ServiceItem item(service, url, is_regex); + + if (!is_regex) { + service_items_.push_back(std::move(item)); + return true; + } std::regex::flag_type flags = std::regex::ECMAScript | std::regex::icase; @@ -18,15 +24,13 @@ bool RestServiceManager::AddService(RestServicePtr service, // Compile the regex. item.url_regex.assign(url, flags); - service_items_.push_back(item); - + service_items_.push_back(std::move(item)); return true; } catch (std::regex_error& e) { LOG_ERRO("URL is not a valid regular expression: %s", e.what()); + return false; } - - return false; } RestServicePtr RestServiceManager::GetService( @@ -36,16 +40,22 @@ RestServicePtr RestServiceManager::GetService( assert(sub_matches != NULL); for (ServiceItem& item : service_items_) { - std::smatch match; + 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) { - sub_matches->push_back(match[i].str()); - } + 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) { + sub_matches->push_back(match[i].str()); + } - return item.service; + return item.service; + } + } else { + if (item.url == url) { + return item.service; + } } } diff --git a/src/webcc/rest_service_manager.h b/src/webcc/rest_service_manager.h index 2bb896e..5f0a721 100644 --- a/src/webcc/rest_service_manager.h +++ b/src/webcc/rest_service_manager.h @@ -9,13 +9,15 @@ namespace webcc { class RestServiceManager { -public: + public: RestServiceManager() = default; // Add a service and bind it with the given URL. - // The |url| should start with "/" and could be a regular expression or not. - // E.g., "/instances". "/instances/(\\d+)" - bool AddService(RestServicePtr service, const std::string& url); + // The |url| should start with "/" and will be treated as a regular expression + // if |regex| is true. + // Examples: "/instances", "/instances/(\\d+)". + bool AddService(RestServicePtr service, const std::string& url, + bool is_regex); // The |sub_matches| is only available when the |url| bound to the // service is a regular expression and has sub-expressions. @@ -24,20 +26,22 @@ public: RestServicePtr GetService(const std::string& url, std::vector* sub_matches); -private: + private: class ServiceItem { - public: - ServiceItem(RestServicePtr _service, const std::string& _url) - : service(_service), url(_url) { + public: + ServiceItem(RestServicePtr _service, const std::string& _url, + bool _is_regex) + : service(_service), url(_url), is_regex(_is_regex) { } - ServiceItem(const ServiceItem& rhs) = default; - ServiceItem& operator=(const ServiceItem& rhs) = default; + ServiceItem(const ServiceItem&) = default; + ServiceItem& operator=(const ServiceItem&) = default; ServiceItem(ServiceItem&& rhs) - : url(std::move(rhs.url)), - url_regex(std::move(rhs.url_regex)), - service(rhs.service) { // No move + : service(rhs.service), + url(std::move(rhs.url)), + is_regex(rhs.is_regex), + url_regex(std::move(rhs.url_regex)) { } RestServicePtr service; @@ -45,6 +49,9 @@ private: // 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; }; diff --git a/src/webcc/soap_client.cc b/src/webcc/soap_client.cc index 3bc1055..ea01cf2 100644 --- a/src/webcc/soap_client.cc +++ b/src/webcc/soap_client.cc @@ -53,7 +53,7 @@ Error SoapClient::Call(const std::string& operation, http_client.set_timeout_seconds(timeout_seconds_); } - Error error = http_client.MakeRequest(http_request, &http_response); + Error error = http_client.Request(http_request, &http_response); if (error != kNoError) { return error; diff --git a/src/webcc/soap_client.h b/src/webcc/soap_client.h index 28217a5..c06a158 100644 --- a/src/webcc/soap_client.h +++ b/src/webcc/soap_client.h @@ -13,10 +13,10 @@ namespace webcc { // Set URL, host, port, etc. in your sub-class before make the call. // class SoapClient { -public: + public: virtual ~SoapClient() = default; -protected: + protected: SoapClient() = default; // A generic wrapper to make a call. @@ -25,7 +25,6 @@ protected: std::vector&& parameters, std::string* result); -protected: // -1 means default timeout (normally 30s) will be used. int timeout_seconds_ = -1; diff --git a/src/webcc/soap_message.h b/src/webcc/soap_message.h index 6a581b2..e7f5240 100644 --- a/src/webcc/soap_message.h +++ b/src/webcc/soap_message.h @@ -12,7 +12,7 @@ namespace webcc { // XML namespace name/url pair. // E.g., { "soap", "http://schemas.xmlsoap.org/soap/envelope/" } class SoapNamespace { -public: + public: std::string name; std::string url; @@ -26,7 +26,7 @@ extern const SoapNamespace kSoapEnvNamespace; // Base class for SOAP request and response. class SoapMessage { -public: + public: virtual ~SoapMessage() {} // E.g., set as kSoapEnvNamespace. @@ -52,14 +52,13 @@ public: // Parse from SOAP request XML. bool FromXml(const std::string& xml_string); -protected: + protected: // Convert to SOAP body XML. virtual void ToXmlBody(pugi::xml_node xbody) = 0; // Parse from SOAP body XML. virtual bool FromXmlBody(pugi::xml_node xbody) = 0; -protected: SoapNamespace soapenv_ns_; // SOAP envelope namespace. SoapNamespace service_ns_; // Namespace for your web service. diff --git a/src/webcc/soap_request.h b/src/webcc/soap_request.h index 7fca9cd..7f75864 100644 --- a/src/webcc/soap_request.h +++ b/src/webcc/soap_request.h @@ -11,7 +11,7 @@ namespace webcc { // Used to compose the SOAP request envelope XML which will be sent as the HTTP // request body. class SoapRequest : public SoapMessage { -public: + public: void AddParameter(const Parameter& parameter); void AddParameter(Parameter&& parameter); @@ -19,11 +19,11 @@ public: // Get parameter value by key. const std::string& GetParameter(const std::string& key) const; -protected: + protected: void ToXmlBody(pugi::xml_node xbody) override; bool FromXmlBody(pugi::xml_node xbody) override; -private: + private: std::vector parameters_; }; diff --git a/src/webcc/soap_request_handler.cc b/src/webcc/soap_request_handler.cc index 3ef4345..564e4b3 100644 --- a/src/webcc/soap_request_handler.cc +++ b/src/webcc/soap_request_handler.cc @@ -6,8 +6,7 @@ namespace webcc { -bool SoapRequestHandler::RegisterService(SoapServicePtr service, - const std::string& url) { +bool SoapRequestHandler::Bind(SoapServicePtr service, const std::string& url) { assert(service); url_service_map_[url] = service; diff --git a/src/webcc/soap_request_handler.h b/src/webcc/soap_request_handler.h index e42ac1f..25fdf00 100644 --- a/src/webcc/soap_request_handler.h +++ b/src/webcc/soap_request_handler.h @@ -8,17 +8,13 @@ namespace webcc { class SoapRequestHandler : public HttpRequestHandler { -public: + public: SoapRequestHandler() = default; ~SoapRequestHandler() override = default; - // Register a SOAP service to the given URL path. - // The |url| path must start with "/", e.g., "/calculator". - // Registering to the same URL multiple times is allowed, but only the last - // one takes effect. - bool RegisterService(SoapServicePtr service, const std::string& url); + bool Bind(SoapServicePtr service, const std::string& url); -private: + private: void HandleSession(HttpSessionPtr session) override; SoapServicePtr GetServiceByUrl(const std::string& url); diff --git a/src/webcc/soap_response.h b/src/webcc/soap_response.h index 226fd6f..44ac008 100644 --- a/src/webcc/soap_response.h +++ b/src/webcc/soap_response.h @@ -7,7 +7,7 @@ namespace webcc { // SOAP response. class SoapResponse : public SoapMessage { -public: + public: // Could be "Price" for an operation/method like "GetXyzPrice". // Really depend on the service. // Most services use a general name "Result". @@ -27,12 +27,12 @@ public: result_ = std::move(result); } -protected: + protected: void ToXmlBody(pugi::xml_node xbody) override; bool FromXmlBody(pugi::xml_node xbody) override; -private: + private: // NOTE: // Multiple results might be necessary. But for most cases, single result // should be enough, because an API normally returns only one value. diff --git a/src/webcc/soap_server.h b/src/webcc/soap_server.h index 3327576..3e8d8af 100644 --- a/src/webcc/soap_server.h +++ b/src/webcc/soap_server.h @@ -9,18 +9,22 @@ namespace webcc { class SoapServer : public HttpServer { -public: + public: SoapServer(unsigned short port, std::size_t workers) : HttpServer(port, workers) { } ~SoapServer() override = default; - bool RegisterService(SoapServicePtr service, const std::string& url) { - return request_handler_.RegisterService(service, url); + // Bind a SOAP service to the given URL path. + // The |url| path must start with "/", e.g., "/calculator". + // Binding to the same URL multiple times is allowed, but only the last + // one takes effect. + bool Bind(SoapServicePtr service, const std::string& url) { + return request_handler_.Bind(service, url); } -private: + private: HttpRequestHandler* GetRequestHandler() override { return &request_handler_; } diff --git a/src/webcc/soap_service.h b/src/webcc/soap_service.h index d108442..4a8b64f 100644 --- a/src/webcc/soap_service.h +++ b/src/webcc/soap_service.h @@ -12,14 +12,14 @@ class SoapResponse; // Base class for your SOAP service. class SoapService { -public: + public: virtual ~SoapService() = default; // Handle SOAP request, output the response. virtual bool Handle(const SoapRequest& soap_request, SoapResponse* soap_response) = 0; -protected: + protected: HttpStatus::Enum http_status_ = HttpStatus::kOK; }; diff --git a/src/webcc/url.h b/src/webcc/url.h index 71aa9ec..2a3cd36 100644 --- a/src/webcc/url.h +++ b/src/webcc/url.h @@ -19,7 +19,7 @@ namespace webcc { // URL query parameters. class UrlQuery { -public: + public: typedef std::vector Parameters; UrlQuery() = default; @@ -49,18 +49,17 @@ public: // E.g., "item=12731&color=blue&size=large". std::string ToString() const; -private: + private: typedef Parameters::const_iterator ConstIterator; ConstIterator Find(const std::string& key) const; -private: Parameters parameters_; }; // ----------------------------------------------------------------------------- class Url { -public: + public: Url() = default; Url(const std::string& str, bool decode); @@ -88,7 +87,7 @@ public: // Split query string into key-value parameters. static void SplitQuery(const std::string& str, UrlQuery* query); -private: + private: void Init(const std::string& str); std::string path_; diff --git a/unittest/rest_service_manager_test.cc b/unittest/rest_service_manager_test.cc index 32a9734..c2f4177 100644 --- a/unittest/rest_service_manager_test.cc +++ b/unittest/rest_service_manager_test.cc @@ -20,7 +20,7 @@ TEST(RestServiceManager, URL_RegexBasic) { { RestServicePtr service = std::make_shared(); - service_manager.AddService(service, "/instances/(\\d+)"); + service_manager.AddService(service, "/instances/(\\d+)", true); } std::vector sub_matches; @@ -39,3 +39,20 @@ TEST(RestServiceManager, URL_RegexBasic) { EXPECT_FALSE(!!service); } + +TEST(RestServiceManager, URL_NonRegex) { + RestServiceManager service_manager; + + { + RestServicePtr service = std::make_shared(); + + service_manager.AddService(service, "/instances", false); + } + + std::vector sub_matches; + std::string url = "/instances"; + RestServicePtr service = service_manager.GetService(url, &sub_matches); + + EXPECT_TRUE(!!service); + EXPECT_EQ(0, sub_matches.size()); +}