Refine logger; add UrlQuery for query parameters.
This commit is contained in:
@@ -19,6 +19,7 @@ if(WEBCC_BUILD_UNITTEST)
|
|||||||
enable_testing()
|
enable_testing()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Automatically detect _WIN32_WINNT for Asio.
|
||||||
# See: https://stackoverflow.com/a/40217291
|
# See: https://stackoverflow.com/a/40217291
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
macro(get_WIN32_WINNT version)
|
macro(get_WIN32_WINNT version)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "calc_client.h"
|
#include "calc_client.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "boost/lexical_cast.hpp"
|
#include "boost/lexical_cast.hpp"
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
// Set to 0 to test our own calculator server created with webcc.
|
// Set to 0 to test our own calculator server created with webcc.
|
||||||
#define ACCESS_PARASOFT 0
|
#define ACCESS_PARASOFT 0
|
||||||
@@ -71,8 +72,9 @@ bool CalcClient::Calc(const std::string& operation,
|
|||||||
|
|
||||||
// Error handling if any.
|
// Error handling if any.
|
||||||
if (error != webcc::kNoError) {
|
if (error != webcc::kNoError) {
|
||||||
std::cerr << "Error: " << error;
|
LOG_ERRO("Operation '%s' failed: %s",
|
||||||
std::cerr << ", " << webcc::GetErrorMessage(error) << std::endl;
|
operation.c_str(),
|
||||||
|
webcc::GetErrorMessage(error));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include "webcc/logger.h"
|
||||||
#include "calc_client.h"
|
#include "calc_client.h"
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
LOG_INIT(webcc::INFO, 0);
|
||||||
|
|
||||||
CalcClient calc;
|
CalcClient calc;
|
||||||
|
|
||||||
double x = 1.0;
|
double x = 1.0;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ int main(int argc, char* argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INIT(webcc::VERB, 0);
|
LOG_INIT(webcc::INFO, 0);
|
||||||
|
|
||||||
unsigned short port = std::atoi(argv[1]);
|
unsigned short port = std::atoi(argv[1]);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -63,7 +63,7 @@ const char* GetErrorMessage(Error error) {
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
const Namespace kSoapEnvNamespace{
|
const SoapNamespace kSoapEnvNamespace{
|
||||||
"soap",
|
"soap",
|
||||||
"http://schemas.xmlsoap.org/soap/envelope/"
|
"http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
};
|
};
|
||||||
|
|||||||
+3
-5
@@ -87,8 +87,7 @@ const char* GetErrorMessage(Error error);
|
|||||||
|
|
||||||
// XML namespace name/url pair.
|
// XML namespace name/url pair.
|
||||||
// E.g., { "soap", "http://schemas.xmlsoap.org/soap/envelope/" }
|
// E.g., { "soap", "http://schemas.xmlsoap.org/soap/envelope/" }
|
||||||
// TODO: Rename (add soap prefix)
|
class SoapNamespace {
|
||||||
class Namespace {
|
|
||||||
public:
|
public:
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string url;
|
std::string url;
|
||||||
@@ -99,12 +98,11 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// CSoap's default namespace for SOAP Envelope.
|
// CSoap's default namespace for SOAP Envelope.
|
||||||
extern const Namespace kSoapEnvNamespace;
|
extern const SoapNamespace kSoapEnvNamespace;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Parameter in the SOAP request envelope.
|
// Key-value parameter.
|
||||||
// TODO: Rename (add soap prefix)
|
|
||||||
class Parameter {
|
class Parameter {
|
||||||
public:
|
public:
|
||||||
Parameter() = default;
|
Parameter() = default;
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ class HttpResponse;
|
|||||||
class HttpClient {
|
class HttpClient {
|
||||||
public:
|
public:
|
||||||
HttpClient();
|
HttpClient();
|
||||||
|
|
||||||
~HttpClient() = default;
|
~HttpClient() = default;
|
||||||
|
|
||||||
HttpClient(const HttpClient&) = delete;
|
HttpClient(const HttpClient&) = delete;
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ void HttpRequestHandler::Start(std::size_t count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HttpRequestHandler::Stop() {
|
void HttpRequestHandler::Stop() {
|
||||||
LOG_VERB("Stopping workers...");
|
LOG_INFO("Stopping workers...");
|
||||||
|
|
||||||
// Close pending sessions.
|
// Close pending sessions.
|
||||||
for (HttpSessionPtr conn = queue_.Pop(); conn; conn = queue_.Pop()) {
|
for (HttpSessionPtr conn = queue_.Pop(); conn; conn = queue_.Pop()) {
|
||||||
LOG_VERB("Closing pending session...");
|
LOG_INFO("Closing pending session...");
|
||||||
conn->Stop();
|
conn->Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enqueue a null session to trigger the first worker to stop.
|
// Enqueue a null session to trigger the first worker to stop.
|
||||||
@@ -35,17 +35,17 @@ void HttpRequestHandler::Stop() {
|
|||||||
|
|
||||||
workers_.join_all();
|
workers_.join_all();
|
||||||
|
|
||||||
LOG_VERB("All workers have been stopped.");
|
LOG_INFO("All workers have been stopped.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpRequestHandler::WorkerRoutine() {
|
void HttpRequestHandler::WorkerRoutine() {
|
||||||
LOG_VERB("Worker is running.");
|
LOG_INFO("Worker is running.");
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
HttpSessionPtr session = queue_.PopOrWait();
|
HttpSessionPtr session = queue_.PopOrWait();
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
LOG_VERB("Worker is going to stop.");
|
LOG_INFO("Worker is going to stop.");
|
||||||
|
|
||||||
// For stopping next worker.
|
// For stopping next worker.
|
||||||
queue_.Push(HttpSessionPtr());
|
queue_.Push(HttpSessionPtr());
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ HttpServer::HttpServer(unsigned short port, std::size_t workers)
|
|||||||
signals_.add(SIGQUIT);
|
signals_.add(SIGQUIT);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DoAwaitStop();
|
AsyncAwaitStop();
|
||||||
|
|
||||||
// NOTE:
|
// NOTE:
|
||||||
// "reuse_addr=true" means option SO_REUSEADDR will be set.
|
// "reuse_addr=true" means option SO_REUSEADDR will be set.
|
||||||
@@ -37,16 +37,13 @@ HttpServer::HttpServer(unsigned short port, std::size_t workers)
|
|||||||
tcp::endpoint(tcp::v4(), port),
|
tcp::endpoint(tcp::v4(), port),
|
||||||
true)); // reuse_addr
|
true)); // reuse_addr
|
||||||
|
|
||||||
DoAccept();
|
AsyncAccept();
|
||||||
}
|
|
||||||
|
|
||||||
HttpServer::~HttpServer() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpServer::Run() {
|
void HttpServer::Run() {
|
||||||
assert(GetRequestHandler() != NULL);
|
assert(GetRequestHandler() != NULL);
|
||||||
|
|
||||||
LOG_VERB("Server is going to run...");
|
LOG_INFO("Server is going to run...");
|
||||||
|
|
||||||
// Start worker threads.
|
// Start worker threads.
|
||||||
GetRequestHandler()->Start(workers_);
|
GetRequestHandler()->Start(workers_);
|
||||||
@@ -58,7 +55,7 @@ void HttpServer::Run() {
|
|||||||
io_context_.run();
|
io_context_.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpServer::DoAccept() {
|
void HttpServer::AsyncAccept() {
|
||||||
acceptor_->async_accept(
|
acceptor_->async_accept(
|
||||||
[this](boost::system::error_code ec, tcp::socket socket) {
|
[this](boost::system::error_code ec, tcp::socket socket) {
|
||||||
// Check whether the server was stopped by a signal before this
|
// Check whether the server was stopped by a signal before this
|
||||||
@@ -68,17 +65,19 @@ void HttpServer::DoAccept() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
|
LOG_INFO("Accepted a connection.");
|
||||||
|
|
||||||
HttpSessionPtr session{
|
HttpSessionPtr session{
|
||||||
new HttpSession(std::move(socket), GetRequestHandler())
|
new HttpSession(std::move(socket), GetRequestHandler())
|
||||||
};
|
};
|
||||||
session->Start();
|
session->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
DoAccept();
|
AsyncAccept();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpServer::DoAwaitStop() {
|
void HttpServer::AsyncAwaitStop() {
|
||||||
signals_.async_wait(
|
signals_.async_wait(
|
||||||
[this](boost::system::error_code, int /*signo*/) {
|
[this](boost::system::error_code, int /*signo*/) {
|
||||||
// The server is stopped by canceling all outstanding asynchronous
|
// The server is stopped by canceling all outstanding asynchronous
|
||||||
|
|||||||
@@ -26,17 +26,17 @@ public:
|
|||||||
|
|
||||||
HttpServer(unsigned short port, std::size_t workers);
|
HttpServer(unsigned short port, std::size_t workers);
|
||||||
|
|
||||||
virtual ~HttpServer();
|
virtual ~HttpServer() = default;
|
||||||
|
|
||||||
// Run the server's io_service loop.
|
// Run the server's io_service loop.
|
||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Initiate an asynchronous accept operation.
|
// Initiate an asynchronous accept operation.
|
||||||
void DoAccept();
|
void AsyncAccept();
|
||||||
|
|
||||||
// Wait for a request to stop the server.
|
// Wait for a request to stop the server.
|
||||||
void DoAwaitStop();
|
void AsyncAwaitStop();
|
||||||
|
|
||||||
// Get the handler for incoming requests.
|
// Get the handler for incoming requests.
|
||||||
virtual HttpRequestHandler* GetRequestHandler() = 0;
|
virtual HttpRequestHandler* GetRequestHandler() = 0;
|
||||||
|
|||||||
+24
-27
@@ -17,14 +17,11 @@ HttpSession::HttpSession(boost::asio::ip::tcp::socket socket,
|
|||||||
, request_parser_(&request_) {
|
, request_parser_(&request_) {
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpSession::~HttpSession() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpSession::Start() {
|
void HttpSession::Start() {
|
||||||
DoRead();
|
AsyncRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::Stop() {
|
void HttpSession::Close() {
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
socket_.close(ec);
|
socket_.close(ec);
|
||||||
}
|
}
|
||||||
@@ -35,33 +32,24 @@ void HttpSession::SetResponseContent(std::string&& content,
|
|||||||
response_.SetContentType(content_type);
|
response_.SetContentType(content_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::SendResponse(int status) {
|
void HttpSession::SendResponse(HttpStatus::Enum status) {
|
||||||
response_.set_status(status);
|
response_.set_status(status);
|
||||||
DoWrite();
|
AsyncWrite();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::DoRead() {
|
void HttpSession::AsyncRead() {
|
||||||
socket_.async_read_some(boost::asio::buffer(buffer_),
|
socket_.async_read_some(boost::asio::buffer(buffer_),
|
||||||
std::bind(&HttpSession::HandleRead,
|
std::bind(&HttpSession::ReadHandler,
|
||||||
shared_from_this(),
|
shared_from_this(),
|
||||||
std::placeholders::_1,
|
std::placeholders::_1,
|
||||||
std::placeholders::_2));
|
std::placeholders::_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::DoWrite() {
|
void HttpSession::ReadHandler(boost::system::error_code ec,
|
||||||
boost::asio::async_write(socket_,
|
std::size_t length) {
|
||||||
response_.ToBuffers(),
|
|
||||||
std::bind(&HttpSession::HandleWrite,
|
|
||||||
shared_from_this(),
|
|
||||||
std::placeholders::_1,
|
|
||||||
std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpSession::HandleRead(boost::system::error_code ec,
|
|
||||||
std::size_t length) {
|
|
||||||
if (ec) {
|
if (ec) {
|
||||||
if (ec != boost::asio::error::operation_aborted) {
|
if (ec != boost::asio::error::operation_aborted) {
|
||||||
Stop();
|
Close();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -71,13 +59,13 @@ void HttpSession::HandleRead(boost::system::error_code ec,
|
|||||||
if (error != kNoError) {
|
if (error != kNoError) {
|
||||||
// Bad request.
|
// Bad request.
|
||||||
response_ = HttpResponse::Fault(HttpStatus::kBadRequest);
|
response_ = HttpResponse::Fault(HttpStatus::kBadRequest);
|
||||||
DoWrite();
|
AsyncWrite();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request_parser_.finished()) {
|
if (!request_parser_.finished()) {
|
||||||
// Continue to read the request.
|
// Continue to read the request.
|
||||||
DoRead();
|
AsyncRead();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,14 +75,23 @@ void HttpSession::HandleRead(boost::system::error_code ec,
|
|||||||
request_handler_->Enqueue(shared_from_this());
|
request_handler_->Enqueue(shared_from_this());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HttpSession::AsyncWrite() {
|
||||||
|
boost::asio::async_write(socket_,
|
||||||
|
response_.ToBuffers(),
|
||||||
|
std::bind(&HttpSession::WriteHandler,
|
||||||
|
shared_from_this(),
|
||||||
|
std::placeholders::_1,
|
||||||
|
std::placeholders::_2));
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE:
|
// NOTE:
|
||||||
// This write handler will be called from main thread (the thread calling
|
// This write handler will be called from main thread (the thread calling
|
||||||
// io_context.run), even though DoWrite() is invoked by worker threads. This is
|
// io_context.run), even though DoWrite() is invoked by worker threads. This is
|
||||||
// ensured by Asio.
|
// ensured by Asio.
|
||||||
void HttpSession::HandleWrite(boost::system::error_code ec,
|
void HttpSession::WriteHandler(boost::system::error_code ec,
|
||||||
std::size_t length) {
|
std::size_t length) {
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
LOG_VERB("Response has been sent back.");
|
LOG_INFO("Response has been sent back.");
|
||||||
|
|
||||||
// Initiate graceful connection closure.
|
// Initiate graceful connection closure.
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
@@ -104,7 +101,7 @@ void HttpSession::HandleWrite(boost::system::error_code ec,
|
|||||||
LOG_ERRO("Sending response error: %s", ec.message().c_str());
|
LOG_ERRO("Sending response error: %s", ec.message().c_str());
|
||||||
|
|
||||||
if (ec != boost::asio::error::operation_aborted) {
|
if (ec != boost::asio::error::operation_aborted) {
|
||||||
Stop();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,29 +23,30 @@ public:
|
|||||||
HttpSession(boost::asio::ip::tcp::socket socket,
|
HttpSession(boost::asio::ip::tcp::socket socket,
|
||||||
HttpRequestHandler* handler);
|
HttpRequestHandler* handler);
|
||||||
|
|
||||||
~HttpSession();
|
~HttpSession() = default;
|
||||||
|
|
||||||
const HttpRequest& request() const {
|
const HttpRequest& request() const {
|
||||||
return request_;
|
return request_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start to read and process the client request.
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
void Stop();
|
// Close the socket.
|
||||||
|
void Close();
|
||||||
|
|
||||||
void SetResponseContent(std::string&& content,
|
void SetResponseContent(std::string&& content,
|
||||||
const std::string& content_type);
|
const std::string& content_type);
|
||||||
|
|
||||||
// Write response back to the client with the given HTTP status.
|
// Send response to client with the given status.
|
||||||
void SendResponse(int status);
|
void SendResponse(HttpStatus::Enum status);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DoRead();
|
void AsyncRead();
|
||||||
|
void ReadHandler(boost::system::error_code ec, std::size_t length);
|
||||||
|
|
||||||
void DoWrite();
|
void AsyncWrite();
|
||||||
|
void WriteHandler(boost::system::error_code ec, std::size_t length);
|
||||||
void HandleRead(boost::system::error_code ec, std::size_t length);
|
|
||||||
void HandleWrite(boost::system::error_code ec, std::size_t length);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Socket for the connection.
|
// Socket for the connection.
|
||||||
|
|||||||
+2
-2
@@ -3,10 +3,10 @@
|
|||||||
|
|
||||||
// Simple console logger.
|
// Simple console logger.
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
#if WEBCC_ENABLE_LOG
|
#if WEBCC_ENABLE_LOG
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
enum LogLevel {
|
enum LogLevel {
|
||||||
VERB = 0,
|
VERB = 0,
|
||||||
INFO,
|
INFO,
|
||||||
|
|||||||
@@ -76,7 +76,8 @@ void RestRequestHandler::HandleSession(HttpSessionPtr session) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Only for GET?
|
// TODO: Only for GET?
|
||||||
Url::Query query = Url::SplitQuery(url.query());
|
UrlQuery query;
|
||||||
|
Url::SplitQuery(url.query(), &query);
|
||||||
|
|
||||||
std::string content;
|
std::string content;
|
||||||
bool ok = service->Handle(session->request().method(),
|
bool ok = service->Handle(session->request().method(),
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
|
class UrlQuery;
|
||||||
|
|
||||||
// Base class for your REST service.
|
// Base class for your REST service.
|
||||||
class RestService {
|
class RestService {
|
||||||
public:
|
public:
|
||||||
@@ -20,12 +22,12 @@ public:
|
|||||||
// \param http_method GET, POST, etc.
|
// \param http_method GET, POST, etc.
|
||||||
// \param url_sub_matches The regex sub-matches in the URL,
|
// \param url_sub_matches The regex sub-matches in the URL,
|
||||||
// usually resource ID.
|
// usually resource ID.
|
||||||
// \param query Query parameters in the URL, key value pairs.
|
// \param query Query parameters in the URL.
|
||||||
// \param request_content Request JSON.
|
// \param request_content Request JSON.
|
||||||
// \param response_content Output response JSON.
|
// \param response_content Output response JSON.
|
||||||
virtual bool Handle(const std::string& http_method,
|
virtual bool Handle(const std::string& http_method,
|
||||||
const std::vector<std::string>& url_sub_matches,
|
const std::vector<std::string>& url_sub_matches,
|
||||||
const std::map<std::string, std::string>& query,
|
const UrlQuery& query,
|
||||||
const std::string& request_content,
|
const std::string& request_content,
|
||||||
std::string* response_content) = 0;
|
std::string* response_content) = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ protected:
|
|||||||
// -1 means default timeout (normally 30s) will be used.
|
// -1 means default timeout (normally 30s) will be used.
|
||||||
int timeout_seconds_ = -1;
|
int timeout_seconds_ = -1;
|
||||||
|
|
||||||
Namespace soapenv_ns_; // SOAP envelope namespace.
|
SoapNamespace soapenv_ns_; // SOAP envelope namespace.
|
||||||
Namespace service_ns_; // Namespace for your web service.
|
SoapNamespace service_ns_; // Namespace for your web service.
|
||||||
|
|
||||||
// Request URL.
|
// Request URL.
|
||||||
std::string url_;
|
std::string url_;
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ namespace webcc {
|
|||||||
class SoapMessage {
|
class SoapMessage {
|
||||||
public:
|
public:
|
||||||
// E.g., set as kSoapEnvNamespace.
|
// E.g., set as kSoapEnvNamespace.
|
||||||
void set_soapenv_ns(const Namespace& soapenv_ns) {
|
void set_soapenv_ns(const SoapNamespace& soapenv_ns) {
|
||||||
soapenv_ns_ = soapenv_ns;
|
soapenv_ns_ = soapenv_ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_service_ns(const Namespace& service_ns) {
|
void set_service_ns(const SoapNamespace& service_ns) {
|
||||||
service_ns_ = service_ns;
|
service_ns_ = service_ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,8 +34,7 @@ public:
|
|||||||
bool FromXml(const std::string& xml_string);
|
bool FromXml(const std::string& xml_string);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SoapMessage() {
|
SoapMessage() = default;
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to SOAP body XML.
|
// Convert to SOAP body XML.
|
||||||
virtual void ToXmlBody(pugi::xml_node xbody) = 0;
|
virtual void ToXmlBody(pugi::xml_node xbody) = 0;
|
||||||
@@ -44,8 +43,8 @@ protected:
|
|||||||
virtual bool FromXmlBody(pugi::xml_node xbody) = 0;
|
virtual bool FromXmlBody(pugi::xml_node xbody) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Namespace soapenv_ns_; // SOAP envelope namespace.
|
SoapNamespace soapenv_ns_; // SOAP envelope namespace.
|
||||||
Namespace service_ns_; // Namespace for your web service.
|
SoapNamespace service_ns_; // Namespace for your web service.
|
||||||
|
|
||||||
std::string operation_;
|
std::string operation_;
|
||||||
};
|
};
|
||||||
|
|||||||
+39
-9
@@ -1,9 +1,43 @@
|
|||||||
#include "webcc/url.h"
|
#include "webcc/url.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void UrlQuery::Add(std::string&& key, std::string&& value) {
|
||||||
|
if (!HasKey(key)) {
|
||||||
|
parameters_.push_back({ std::move(key), std::move(value) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UrlQuery::Remove(const std::string& key) {
|
||||||
|
auto it = Find(key);
|
||||||
|
if (it != parameters_.end()) {
|
||||||
|
parameters_.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& UrlQuery::GetValue(const std::string& key) const {
|
||||||
|
static const std::string kEmptyValue;
|
||||||
|
|
||||||
|
auto it = Find(key);
|
||||||
|
if (it != parameters_.end()) {
|
||||||
|
return it->value();
|
||||||
|
}
|
||||||
|
return kEmptyValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UrlQuery::ConstIterator UrlQuery::Find(const std::string& key) const {
|
||||||
|
return std::find_if(parameters_.begin(),
|
||||||
|
parameters_.end(),
|
||||||
|
[&key](const Parameter& p) { return p.key() == key; });
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
Url::Url(const std::string& str) {
|
Url::Url(const std::string& str) {
|
||||||
std::size_t pos = str.find('?');
|
std::size_t pos = str.find('?');
|
||||||
if (pos == std::string::npos) {
|
if (pos == std::string::npos) {
|
||||||
@@ -44,33 +78,29 @@ static bool SplitKeyValue(const std::string& kv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
Url::Query Url::SplitQuery(const std::string& query) {
|
void Url::SplitQuery(const std::string& str, UrlQuery* query) {
|
||||||
const std::size_t NPOS = std::string::npos;
|
const std::size_t NPOS = std::string::npos;
|
||||||
|
|
||||||
Query result;
|
|
||||||
|
|
||||||
// Split into key value pairs separated by '&'.
|
// Split into key value pairs separated by '&'.
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
while (i != NPOS) {
|
while (i != NPOS) {
|
||||||
std::size_t j = query.find_first_of('&', i);
|
std::size_t j = str.find_first_of('&', i);
|
||||||
|
|
||||||
std::string kv;
|
std::string kv;
|
||||||
if (j == NPOS) {
|
if (j == NPOS) {
|
||||||
kv = query.substr(i);
|
kv = str.substr(i);
|
||||||
i = NPOS;
|
i = NPOS;
|
||||||
} else {
|
} else {
|
||||||
kv = query.substr(i, j - i);
|
kv = str.substr(i, j - i);
|
||||||
i = j + 1;
|
i = j + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string key;
|
std::string key;
|
||||||
std::string value;
|
std::string value;
|
||||||
if (SplitKeyValue(kv, &key, &value)) {
|
if (SplitKeyValue(kv, &key, &value)) {
|
||||||
result[key] = value; // TODO: Move
|
query->Add(std::move(key), std::move(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
+31
-2
@@ -11,8 +11,37 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webcc/common.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// URL query parameters.
|
||||||
|
class UrlQuery {
|
||||||
|
public:
|
||||||
|
typedef std::vector<Parameter> Parameters;
|
||||||
|
|
||||||
|
void Add(std::string&& key, std::string&& value);
|
||||||
|
|
||||||
|
void Remove(const std::string& key);
|
||||||
|
|
||||||
|
const std::string& GetValue(const std::string& key) const;
|
||||||
|
|
||||||
|
bool HasKey(const std::string& key) const {
|
||||||
|
return Find(key) != parameters_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef Parameters::const_iterator ConstIterator;
|
||||||
|
ConstIterator Find(const std::string& key) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Parameters parameters_;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class Url {
|
class Url {
|
||||||
public:
|
public:
|
||||||
typedef std::map<std::string, std::string> Query;
|
typedef std::map<std::string, std::string> Query;
|
||||||
@@ -40,8 +69,8 @@ public:
|
|||||||
// Split a path into its hierarchical components.
|
// Split a path into its hierarchical components.
|
||||||
static std::vector<std::string> SplitPath(const std::string& path);
|
static std::vector<std::string> SplitPath(const std::string& path);
|
||||||
|
|
||||||
// Split query string into key-value map.
|
// Split query string into key-value parameters.
|
||||||
static Query SplitQuery(const std::string& query);
|
static void SplitQuery(const std::string& str, UrlQuery* query);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string path_;
|
std::string path_;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ namespace webcc {
|
|||||||
|
|
||||||
// Print the resolved endpoints.
|
// Print the resolved endpoints.
|
||||||
// NOTE: Endpoint is one word, don't use "end point".
|
// NOTE: Endpoint is one word, don't use "end point".
|
||||||
// TODO
|
|
||||||
void DumpEndpoints(tcp::resolver::results_type& endpoints) {
|
void DumpEndpoints(tcp::resolver::results_type& endpoints) {
|
||||||
std::cout << "Endpoints: " << endpoints.size() << std::endl;
|
std::cout << "Endpoints: " << endpoints.size() << std::endl;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user