Refactoring according Google C++ Style.
This commit is contained in:
@@ -1,78 +1,50 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "boost/algorithm/string.hpp"
|
#include "boost/algorithm/string.hpp"
|
||||||
#include "json/json.h" // jsoncpp
|
#include "json/json.h"
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
#include "webcc/logger.h"
|
||||||
#include "webcc/http_client.h"
|
#include "webcc/http_client.h"
|
||||||
#include "webcc/http_request.h"
|
#include "webcc/http_request.h"
|
||||||
#include "webcc/http_response.h"
|
#include "webcc/http_response.h"
|
||||||
|
#include "webcc/rest_client.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class BookClientBase {
|
// Write a JSON object to string.
|
||||||
public:
|
std::string JsonToString(const Json::Value& json) {
|
||||||
BookClientBase(const std::string& host, const std::string& port)
|
Json::StreamWriterBuilder builder;
|
||||||
: host_(host), port_(port) {
|
return Json::writeString(builder, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request(const std::string& method,
|
|
||||||
const std::string& url,
|
|
||||||
const std::string& content,
|
|
||||||
webcc::HttpResponse* http_response) {
|
|
||||||
webcc::HttpRequest http_request;
|
|
||||||
|
|
||||||
http_request.set_method(method);
|
|
||||||
http_request.set_url(url);
|
|
||||||
http_request.SetHost(host_, port_);
|
|
||||||
if (!content.empty()) { // TODO
|
|
||||||
http_request.SetContent(content);
|
|
||||||
}
|
|
||||||
http_request.Build();
|
|
||||||
|
|
||||||
webcc::HttpClient http_client;
|
|
||||||
webcc::Error error = http_client.MakeRequest(http_request, http_response);
|
|
||||||
|
|
||||||
return error == webcc::kNoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::string host_;
|
|
||||||
std::string port_;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class BookListClient : public BookClientBase {
|
class BookListClient {
|
||||||
public:
|
public:
|
||||||
BookListClient(const std::string& host, const std::string& port)
|
BookListClient(const std::string& host, const std::string& port)
|
||||||
: BookClientBase(host, port) {
|
: rest_client_(host, port) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ListBooks() {
|
bool ListBooks() {
|
||||||
webcc::HttpResponse http_response;
|
webcc::HttpResponse http_response;
|
||||||
if (!Request(webcc::kHttpGet, "/books", "", &http_response)) {
|
if (!rest_client_.Get("/books", &http_response)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "result:\n" << http_response.content() << std::endl;
|
std::cout << "result:\n" << http_response.content() << std::endl;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CreateBook(const std::string& id,
|
bool CreateBook(const std::string& id,
|
||||||
const std::string& title,
|
const std::string& title,
|
||||||
double price) {
|
double price) {
|
||||||
Json::Value root(Json::objectValue);
|
Json::Value json(Json::objectValue);
|
||||||
root["id"] = id;
|
json["id"] = id;
|
||||||
root["title"] = title;
|
json["title"] = title;
|
||||||
root["price"] = price;
|
json["price"] = price;
|
||||||
|
|
||||||
Json::StreamWriterBuilder builder;
|
|
||||||
std::string book_json = Json::writeString(builder, root);
|
|
||||||
|
|
||||||
webcc::HttpResponse http_response;
|
webcc::HttpResponse http_response;
|
||||||
if (!Request(webcc::kHttpPost, "/books", book_json, &http_response)) {
|
if (!rest_client_.Post("/books", JsonToString(json), &http_response)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,58 +52,58 @@ public:
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
webcc::RestClient rest_client_;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class BookDetailClient : public BookClientBase {
|
class BookDetailClient {
|
||||||
public:
|
public:
|
||||||
BookDetailClient(const std::string& host, const std::string& port)
|
BookDetailClient(const std::string& host, const std::string& port)
|
||||||
: BookClientBase(host, port) {
|
: rest_client_(host, port) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetBook(const std::string& id) {
|
bool GetBook(const std::string& id) {
|
||||||
webcc::HttpResponse http_response;
|
webcc::HttpResponse http_response;
|
||||||
if (!Request(webcc::kHttpGet, "/book/" + id, "", &http_response)) {
|
if (!rest_client_.Get("/book/" + id, &http_response)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << http_response.content() << std::endl;
|
std::cout << http_response.content() << std::endl;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UpdateBook(const std::string& id,
|
bool UpdateBook(const std::string& id,
|
||||||
const std::string& title,
|
const std::string& title,
|
||||||
double price) {
|
double price) {
|
||||||
Json::Value root(Json::objectValue);
|
// NOTE: ID is already in the URL.
|
||||||
// root["id"] = id; // NOTE: ID is already in the URL.
|
Json::Value json(Json::objectValue);
|
||||||
root["title"] = title;
|
json["title"] = title;
|
||||||
root["price"] = price;
|
json["price"] = price;
|
||||||
|
|
||||||
Json::StreamWriterBuilder builder;
|
|
||||||
std::string book_json = Json::writeString(builder, root);
|
|
||||||
|
|
||||||
webcc::HttpResponse http_response;
|
webcc::HttpResponse http_response;
|
||||||
if (!Request(webcc::kHttpPost, "/book/" + id, book_json, &http_response)) {
|
if (!rest_client_.Post("/book/" + id, JsonToString(json), &http_response)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << http_response.status() << std::endl;
|
std::cout << http_response.status() << std::endl;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeleteBook(const std::string& id) {
|
bool DeleteBook(const std::string& id) {
|
||||||
webcc::HttpResponse http_response;
|
webcc::HttpResponse http_response;
|
||||||
if (!Request(webcc::kHttpDelete, "/book/" + id, "", &http_response)) {
|
if (!rest_client_.Delete("/book/" + id, &http_response)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << http_response.content() << std::endl;
|
std::cout << http_response.status() << std::endl;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
webcc::RestClient rest_client_;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -147,13 +119,8 @@ void Help(const char* argv0) {
|
|||||||
|
|
||||||
std::string GetUserInput() {
|
std::string GetUserInput() {
|
||||||
char input[256];
|
char input[256];
|
||||||
// std::size_t length = 0;
|
|
||||||
// do {
|
|
||||||
std::cout << ">> ";
|
std::cout << ">> ";
|
||||||
std::cin.getline(input, 256);
|
std::cin.getline(input, 256);
|
||||||
// length = strlen(input);
|
|
||||||
// } while (length == 0);
|
|
||||||
|
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ bool BookListService::Post(const std::string& request_content,
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
bool BookDetailService::Get(const std::vector<std::string>& url_sub_matches,
|
bool BookDetailService::Get(const std::vector<std::string>& url_sub_matches,
|
||||||
|
const webcc::UrlQuery& query,
|
||||||
std::string* response_content) {
|
std::string* response_content) {
|
||||||
if (url_sub_matches.size() != 1) {
|
if (url_sub_matches.size() != 1) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class BookListService : public webcc::RestListService {
|
|||||||
class BookDetailService : public webcc::RestDetailService {
|
class BookDetailService : public webcc::RestDetailService {
|
||||||
protected:
|
protected:
|
||||||
bool Get(const std::vector<std::string>& url_sub_matches,
|
bool Get(const std::vector<std::string>& url_sub_matches,
|
||||||
|
const webcc::UrlQuery& query,
|
||||||
std::string* response_content) final;
|
std::string* response_content) final;
|
||||||
|
|
||||||
bool Patch(const std::vector<std::string>& url_sub_matches,
|
bool Patch(const std::vector<std::string>& url_sub_matches,
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
add_definitions(-DBOOST_ASIO_NO_DEPRECATED)
|
add_definitions(-DBOOST_ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
set(SRCS
|
set(SRCS
|
||||||
common.cc
|
globals.cc
|
||||||
common.h
|
globals.h
|
||||||
http_client.cc
|
http_client.cc
|
||||||
http_client.h
|
http_client.h
|
||||||
http_message.cc
|
http_message.cc
|
||||||
@@ -27,7 +27,12 @@ set(SRCS
|
|||||||
logger.cc
|
logger.cc
|
||||||
logger.h
|
logger.h
|
||||||
queue.h
|
queue.h
|
||||||
rest_server.cc
|
rest_client.cc
|
||||||
|
rest_client.h
|
||||||
|
rest_request_handler.cc
|
||||||
|
rest_request_handler.h
|
||||||
|
rest_service_manager.cc
|
||||||
|
rest_service_manager.h
|
||||||
rest_server.h
|
rest_server.h
|
||||||
rest_service.cc
|
rest_service.cc
|
||||||
rest_service.h
|
rest_service.h
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "webcc/common.h"
|
#include "webcc/globals.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
@@ -63,13 +63,6 @@ const char* GetErrorMessage(Error error) {
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
const SoapNamespace kSoapEnvNamespace{
|
|
||||||
"soap",
|
|
||||||
"http://schemas.xmlsoap.org/soap/envelope/"
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
Parameter::Parameter(const std::string& key, const char* value)
|
Parameter::Parameter(const std::string& key, const char* value)
|
||||||
: key_(key), value_(value) {
|
: key_(key), value_(value) {
|
||||||
}
|
}
|
||||||
@@ -110,4 +103,8 @@ Parameter& Parameter::operator=(Parameter&& rhs) {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Parameter::ToString() const {
|
||||||
|
return key_ + "=" + value_;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
@@ -1,17 +1,47 @@
|
|||||||
#ifndef WEBCC_COMMON_H_
|
#ifndef WEBCC_GLOBALS_H_
|
||||||
#define WEBCC_COMMON_H_
|
#define WEBCC_GLOBALS_H_
|
||||||
|
|
||||||
// Common definitions.
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// -----------------------------------------------------------------------------
|
||||||
|
// Macros
|
||||||
|
|
||||||
|
// Explicitly declare the assignment operator as deleted.
|
||||||
|
#define DISALLOW_ASSIGN(TypeName) TypeName& operator=(const TypeName&) = delete;
|
||||||
|
|
||||||
|
// Explicitly declare the copy constructor and assignment operator as deleted.
|
||||||
|
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||||
|
TypeName(const TypeName&) = delete; \
|
||||||
|
DISALLOW_ASSIGN(TypeName)
|
||||||
|
|
||||||
|
// Explicitly declare all implicit constructors as deleted, namely the
|
||||||
|
// default constructor, copy constructor and operator= functions.
|
||||||
|
// This is especially useful for classes containing only static methods.
|
||||||
|
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
|
||||||
|
TypeName() = delete; \
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||||
|
|
||||||
|
// Disallow copying a type, but provide default construction, move construction
|
||||||
|
// and move assignment. Especially useful for move-only structs.
|
||||||
|
#define MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(TypeName) \
|
||||||
|
TypeName() = default; \
|
||||||
|
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(TypeName)
|
||||||
|
|
||||||
|
// Disallow copying a type, and only provide move construction and move
|
||||||
|
// assignment. Especially useful for move-only structs.
|
||||||
|
#define MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(TypeName) \
|
||||||
|
TypeName(TypeName&&) = default; \
|
||||||
|
TypeName& operator=(TypeName&&) = default; \
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
|
||||||
// Buffer size for sending HTTP request and receiving HTTP response.
|
// Buffer size for sending HTTP request and receiving HTTP response.
|
||||||
// TODO: Configurable for client and server separately.
|
// TODO: Configurable
|
||||||
const std::size_t kBufferSize = 1024;
|
const std::size_t kBufferSize = 1024;
|
||||||
|
|
||||||
const std::size_t kInvalidLength = static_cast<std::size_t>(-1);
|
const std::size_t kInvalidLength = static_cast<std::size_t>(-1);
|
||||||
@@ -24,8 +54,6 @@ extern const std::string kHost;
|
|||||||
extern const std::string kTextXmlUtf8;
|
extern const std::string kTextXmlUtf8;
|
||||||
extern const std::string kTextJsonUtf8;
|
extern const std::string kTextJsonUtf8;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// HTTP methods (verbs) in string ("HEAD", "GET", etc.).
|
// HTTP methods (verbs) in string ("HEAD", "GET", etc.).
|
||||||
// NOTE: Don't use enum to avoid converting back and forth.
|
// NOTE: Don't use enum to avoid converting back and forth.
|
||||||
extern const std::string kHttpHead;
|
extern const std::string kHttpHead;
|
||||||
@@ -54,11 +82,9 @@ struct HttpStatus {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Error codes.
|
// Error codes.
|
||||||
enum Error {
|
enum Error {
|
||||||
kNoError = 0, // OK
|
kNoError = 0,
|
||||||
|
|
||||||
kHostResolveError,
|
kHostResolveError,
|
||||||
kEndpointConnectError,
|
kEndpointConnectError,
|
||||||
@@ -83,31 +109,14 @@ enum Error {
|
|||||||
// Return a descriptive message for the given error code.
|
// Return a descriptive message for the given error code.
|
||||||
const char* GetErrorMessage(Error error);
|
const char* GetErrorMessage(Error error);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// XML namespace name/url pair.
|
|
||||||
// E.g., { "soap", "http://schemas.xmlsoap.org/soap/envelope/" }
|
|
||||||
class SoapNamespace {
|
|
||||||
public:
|
|
||||||
std::string name;
|
|
||||||
std::string url;
|
|
||||||
|
|
||||||
bool IsValid() const {
|
|
||||||
return !name.empty() && !url.empty();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// CSoap's default namespace for SOAP Envelope.
|
|
||||||
extern const SoapNamespace kSoapEnvNamespace;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Key-value parameter.
|
// Key-value parameter.
|
||||||
class Parameter {
|
class Parameter {
|
||||||
public:
|
public:
|
||||||
Parameter() = default;
|
Parameter() = default;
|
||||||
Parameter(const Parameter& rhs) = default;
|
Parameter(const Parameter&) = default;
|
||||||
Parameter& operator=(const Parameter& rhs) = default;
|
Parameter& operator=(const Parameter&) = default;
|
||||||
|
|
||||||
Parameter(const std::string& key, const char* value);
|
Parameter(const std::string& key, const char* value);
|
||||||
Parameter(const std::string& key, const std::string& value);
|
Parameter(const std::string& key, const std::string& value);
|
||||||
@@ -138,6 +147,9 @@ public:
|
|||||||
return value_.c_str();
|
return value_.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return "key=value" string.
|
||||||
|
std::string ToString() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string key_;
|
std::string key_;
|
||||||
std::string value_;
|
std::string value_;
|
||||||
@@ -145,4 +157,4 @@ private:
|
|||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|
||||||
#endif // WEBCC_COMMON_H_
|
#endif // WEBCC_GLOBALS_H_
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
#include "webcc/http_async_client.h"
|
||||||
|
|
||||||
|
#if WEBCC_DEBUG_OUTPUT
|
||||||
|
#include <iostream>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#include "boost/asio.hpp"
|
||||||
|
#else
|
||||||
|
#include "boost/asio/connect.hpp"
|
||||||
|
//#include "boost/asio/ip/tcp.hpp"
|
||||||
|
#include "boost/asio/read.hpp"
|
||||||
|
#include "boost/asio/write.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "webcc/http_response_parser.h"
|
||||||
|
#include "webcc/http_request.h"
|
||||||
|
#include "webcc/http_response.h"
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
HttpAsyncClient::HttpAsyncClient(boost::asio::io_context& io_context)
|
||||||
|
: socket_(io_context) {
|
||||||
|
|
||||||
|
resolver_.reset(new tcp::resolver(io_context));
|
||||||
|
|
||||||
|
response_.reset(new HttpResponse());
|
||||||
|
parser_.reset(new HttpResponseParser(response_.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Error HttpAsyncClient::SendRequest(std::shared_ptr<HttpRequest> request,
|
||||||
|
HttpResponseHandler response_handler) {
|
||||||
|
request_ = request;
|
||||||
|
|
||||||
|
std::string port = request->port();
|
||||||
|
if (port.empty()) {
|
||||||
|
port = "80";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto handler = std::bind(&HttpAsyncClient::HandleResolve,
|
||||||
|
this,
|
||||||
|
std::placeholders::_1,
|
||||||
|
std::placeholders::_2);
|
||||||
|
|
||||||
|
resolver_->async_resolve(tcp::v4(), request->host(), port, handler);
|
||||||
|
|
||||||
|
return kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpAsyncClient::HandleResolve(boost::system::error_code ec,
|
||||||
|
tcp::resolver::results_type results) {
|
||||||
|
if (ec) {
|
||||||
|
std::cerr << "Resolve: " << ec.message() << std::endl;
|
||||||
|
// return kHostResolveError;
|
||||||
|
} else {
|
||||||
|
endpoints_ = results;
|
||||||
|
DoConnect(endpoints_.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpAsyncClient::DoConnect(tcp::resolver::results_type::iterator endpoint_it) {
|
||||||
|
if (endpoint_it != endpoints_.end()) {
|
||||||
|
socket_.async_connect(endpoint_it->endpoint(),
|
||||||
|
std::bind(&HttpAsyncClient::HandleConnect,
|
||||||
|
this,
|
||||||
|
std::placeholders::_1,
|
||||||
|
endpoint_it));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpAsyncClient::HandleConnect(boost::system::error_code ec,
|
||||||
|
tcp::resolver::results_type::iterator endpoint_it) {
|
||||||
|
if (ec) {
|
||||||
|
// Will be here if the end point is v6.
|
||||||
|
std::cout << "Connect error: " << ec.message() << std::endl;
|
||||||
|
// return kEndpointConnectError;
|
||||||
|
|
||||||
|
socket_.close();
|
||||||
|
|
||||||
|
// Try the next available endpoint.
|
||||||
|
DoConnect(++endpoint_it);
|
||||||
|
} else {
|
||||||
|
DoWrite();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send HTTP request.
|
||||||
|
void HttpAsyncClient::DoWrite() {
|
||||||
|
boost::asio::async_write(socket_,
|
||||||
|
request_->ToBuffers(),
|
||||||
|
std::bind(&HttpAsyncClient::HandleWrite,
|
||||||
|
this,
|
||||||
|
std::placeholders::_1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpAsyncClient::HandleWrite(boost::system::error_code ec) {
|
||||||
|
if (ec) {
|
||||||
|
//return kSocketWriteError;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DoRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpAsyncClient::DoRead() {
|
||||||
|
socket_.async_read_some(boost::asio::buffer(buffer_),
|
||||||
|
std::bind(&HttpAsyncClient::HandleRead,
|
||||||
|
this,
|
||||||
|
std::placeholders::_1,
|
||||||
|
std::placeholders::_2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpAsyncClient::HandleRead(boost::system::error_code ec,
|
||||||
|
std::size_t length) {
|
||||||
|
if (ec || length == 0) {
|
||||||
|
//return kSocketReadError;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if WEBCC_DEBUG_OUTPUT
|
||||||
|
// NOTE: the content XML might not be well formated.
|
||||||
|
std::cout.write(buffer_.data(), length);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Parse the response piece just read.
|
||||||
|
// If the content has been fully received, next time flag "finished_"
|
||||||
|
// will be set.
|
||||||
|
Error error = parser_->Parse(buffer_.data(), length);
|
||||||
|
|
||||||
|
if (error != kNoError) {
|
||||||
|
//return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser_->finished()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read and parse HTTP response.
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// We must stop trying to read once all content has been received,
|
||||||
|
// because some servers will block extra call to read_some().
|
||||||
|
//while (!parser_.finished()) {
|
||||||
|
// size_t length = socket_.read_some(boost::asio::buffer(buffer_), ec);
|
||||||
|
|
||||||
|
//if (length == 0 || ec) {
|
||||||
|
// return kSocketReadError;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Parse the response piece just read.
|
||||||
|
// If the content has been fully received, next time flag "finished_"
|
||||||
|
// will be set.
|
||||||
|
//Error error = parser_.Parse(buffer_.data(), length);
|
||||||
|
|
||||||
|
//if (error != kNoError) {
|
||||||
|
// return error;
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
//#if WEBCC_DEBUG_OUTPUT
|
||||||
|
// std::cout << std::endl;
|
||||||
|
// std::cout << "--- RESPONSE (PARSED) ---" << std::endl;
|
||||||
|
// std::cout << *response << std::endl;
|
||||||
|
//#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#ifndef WEBCC_HTTP_ASYNC_CLIENT_H_
|
||||||
|
#define WEBCC_HTTP_ASYNC_CLIENT_H_
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "boost/smart_ptr/scoped_ptr.hpp"
|
||||||
|
|
||||||
|
#include "boost/asio/io_context.hpp"
|
||||||
|
#include "boost/asio/ip/tcp.hpp"
|
||||||
|
|
||||||
|
#include "webcc/common.h"
|
||||||
|
#include "webcc/http_response_parser.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class HttpRequest;
|
||||||
|
class HttpResponse;
|
||||||
|
|
||||||
|
typedef void(*HttpResponseHandler)(std::shared_ptr<HttpResponse>);
|
||||||
|
|
||||||
|
class HttpAsyncClient {
|
||||||
|
public:
|
||||||
|
HttpAsyncClient(boost::asio::io_context& io_context);
|
||||||
|
|
||||||
|
Error SendRequest(std::shared_ptr<HttpRequest> request,
|
||||||
|
HttpResponseHandler response_handler);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void HandleResolve(boost::system::error_code ec,
|
||||||
|
boost::asio::ip::tcp::resolver::results_type results);
|
||||||
|
|
||||||
|
void DoConnect(boost::asio::ip::tcp::resolver::results_type::iterator endpoint_it);
|
||||||
|
|
||||||
|
void HandleConnect(boost::system::error_code ec,
|
||||||
|
boost::asio::ip::tcp::resolver::results_type::iterator endpoint_it);
|
||||||
|
|
||||||
|
void DoWrite();
|
||||||
|
|
||||||
|
void HandleWrite(boost::system::error_code ec);
|
||||||
|
|
||||||
|
void DoRead();
|
||||||
|
|
||||||
|
void HandleRead(boost::system::error_code ec, std::size_t length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::asio::ip::tcp::socket socket_;
|
||||||
|
|
||||||
|
std::shared_ptr<HttpRequest> request_;
|
||||||
|
|
||||||
|
std::unique_ptr<boost::asio::ip::tcp::resolver> resolver_;
|
||||||
|
boost::asio::ip::tcp::resolver::results_type endpoints_;
|
||||||
|
|
||||||
|
std::array<char, kBufferSize> buffer_;
|
||||||
|
|
||||||
|
std::unique_ptr<HttpResponseParser> parser_;
|
||||||
|
|
||||||
|
std::shared_ptr<HttpResponse> response_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_HTTP_ASYNC_CLIENT_H_
|
||||||
@@ -3,14 +3,9 @@
|
|||||||
#include "boost/date_time/posix_time/posix_time.hpp"
|
#include "boost/date_time/posix_time/posix_time.hpp"
|
||||||
#include "boost/lambda/bind.hpp"
|
#include "boost/lambda/bind.hpp"
|
||||||
#include "boost/lambda/lambda.hpp"
|
#include "boost/lambda/lambda.hpp"
|
||||||
|
|
||||||
#if 0
|
|
||||||
#include "boost/asio.hpp"
|
|
||||||
#else
|
|
||||||
#include "boost/asio/connect.hpp"
|
#include "boost/asio/connect.hpp"
|
||||||
#include "boost/asio/read.hpp"
|
#include "boost/asio/read.hpp"
|
||||||
#include "boost/asio/write.hpp"
|
#include "boost/asio/write.hpp"
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
#include "webcc/logger.h"
|
||||||
#include "webcc/http_request.h"
|
#include "webcc/http_request.h"
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "boost/asio/deadline_timer.hpp"
|
#include "boost/asio/deadline_timer.hpp"
|
||||||
#include "boost/asio/ip/tcp.hpp"
|
|
||||||
#include "boost/asio/io_context.hpp"
|
#include "boost/asio/io_context.hpp"
|
||||||
|
#include "boost/asio/ip/tcp.hpp"
|
||||||
|
|
||||||
#include "webcc/common.h"
|
#include "webcc/globals.h"
|
||||||
#include "webcc/http_response_parser.h"
|
#include "webcc/http_response_parser.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "webcc/common.h"
|
|
||||||
|
#include "webcc/globals.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
@@ -16,9 +17,6 @@ public:
|
|||||||
// Base class for HTTP request and response messages.
|
// Base class for HTTP request and response messages.
|
||||||
class HttpMessage {
|
class HttpMessage {
|
||||||
public:
|
public:
|
||||||
HttpMessage() = default;
|
|
||||||
HttpMessage(const HttpMessage&) = default;
|
|
||||||
HttpMessage& operator=(const HttpMessage&) = default;
|
|
||||||
virtual ~HttpMessage() = default;
|
virtual ~HttpMessage() = default;
|
||||||
|
|
||||||
const std::string& start_line() const {
|
const std::string& start_line() const {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
#define WEBCC_HTTP_PARSER_H_
|
#define WEBCC_HTTP_PARSER_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "webcc/common.h"
|
|
||||||
|
#include "webcc/globals.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
@@ -15,9 +16,6 @@ public:
|
|||||||
|
|
||||||
virtual ~HttpParser() = default;
|
virtual ~HttpParser() = default;
|
||||||
|
|
||||||
HttpParser(const HttpParser&) = delete;
|
|
||||||
HttpParser& operator=(const HttpParser&) = delete;
|
|
||||||
|
|
||||||
bool finished() const {
|
bool finished() const {
|
||||||
return finished_;
|
return finished_;
|
||||||
}
|
}
|
||||||
@@ -53,6 +51,8 @@ protected:
|
|||||||
bool content_length_parsed_;
|
bool content_length_parsed_;
|
||||||
bool header_parsed_;
|
bool header_parsed_;
|
||||||
bool finished_;
|
bool finished_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(HttpParser);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
#define WEBCC_HTTP_REQUEST_H_
|
#define WEBCC_HTTP_REQUEST_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "boost/asio/buffer.hpp" // for const_buffer
|
#include "boost/asio/buffer.hpp" // for const_buffer
|
||||||
|
|
||||||
#include "webcc/http_message.h"
|
#include "webcc/http_message.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
@@ -12,10 +14,6 @@ class HttpRequest;
|
|||||||
std::ostream& operator<<(std::ostream& os, const HttpRequest& request);
|
std::ostream& operator<<(std::ostream& os, const HttpRequest& request);
|
||||||
|
|
||||||
class HttpRequest : public HttpMessage {
|
class HttpRequest : public HttpMessage {
|
||||||
|
|
||||||
friend std::ostream& operator<<(std::ostream& os,
|
|
||||||
const HttpRequest& request);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HttpRequest() = default;
|
HttpRequest() = default;
|
||||||
HttpRequest(const HttpRequest&) = default;
|
HttpRequest(const HttpRequest&) = default;
|
||||||
@@ -47,8 +45,9 @@ public:
|
|||||||
return port_;
|
return port_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// \param host Descriptive host name or numeric IP address.
|
// Set host name and port number.
|
||||||
// \param port Numeric port number, "80" will be used if it's empty.
|
// The |host| is a descriptive name or a numeric IP address. The |port| is
|
||||||
|
// a numeric number (e.g., "9000") and "80" will be used if it's empty.
|
||||||
void SetHost(const std::string& host, const std::string& port);
|
void SetHost(const std::string& host, const std::string& port);
|
||||||
|
|
||||||
// Compose start line, etc.
|
// Compose start line, etc.
|
||||||
@@ -64,6 +63,8 @@ public:
|
|||||||
std::string Dump() const;
|
std::string Dump() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend std::ostream& operator<<(std::ostream& os, const HttpRequest& request);
|
||||||
|
|
||||||
// HTTP method.
|
// HTTP method.
|
||||||
std::string method_;
|
std::string method_;
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
#include "webcc/globals.h"
|
||||||
#include "webcc/common.h"
|
|
||||||
#include "webcc/http_request.h"
|
#include "webcc/http_request.h"
|
||||||
#include "webcc/http_response.h"
|
#include "webcc/http_response.h"
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
|
|||||||
@@ -18,13 +18,8 @@ class HttpResponse;
|
|||||||
// The common handler for all incoming requests.
|
// The common handler for all incoming requests.
|
||||||
class HttpRequestHandler {
|
class HttpRequestHandler {
|
||||||
public:
|
public:
|
||||||
HttpRequestHandler(const HttpRequestHandler&) = delete;
|
|
||||||
HttpRequestHandler& operator=(const HttpRequestHandler&) = delete;
|
|
||||||
|
|
||||||
HttpRequestHandler() = default;
|
HttpRequestHandler() = default;
|
||||||
|
virtual ~HttpRequestHandler() = default;
|
||||||
virtual ~HttpRequestHandler() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put the session into the queue.
|
// Put the session into the queue.
|
||||||
void Enqueue(HttpSessionPtr session);
|
void Enqueue(HttpSessionPtr session);
|
||||||
@@ -41,9 +36,10 @@ private:
|
|||||||
// Called by the worker routine.
|
// Called by the worker routine.
|
||||||
virtual void HandleSession(HttpSessionPtr session) = 0;
|
virtual void HandleSession(HttpSessionPtr session) = 0;
|
||||||
|
|
||||||
private:
|
|
||||||
Queue<HttpSessionPtr> queue_;
|
Queue<HttpSessionPtr> queue_;
|
||||||
boost::thread_group workers_;
|
boost::thread_group workers_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(HttpRequestHandler);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
Error ParseStartLine(const std::string& line) override;
|
Error ParseStartLine(const std::string& line) override;
|
||||||
|
|
||||||
private:
|
|
||||||
HttpRequest* request_;
|
HttpRequest* request_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "webcc/http_response.h"
|
#include "webcc/http_response.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
#define WEBCC_HTTP_RESPONSE_H_
|
#define WEBCC_HTTP_RESPONSE_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "boost/asio/buffer.hpp" // for const_buffer
|
#include "boost/asio/buffer.hpp" // for const_buffer
|
||||||
|
|
||||||
#include "webcc/http_message.h"
|
#include "webcc/http_message.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
@@ -12,9 +14,6 @@ class HttpResponse;
|
|||||||
std::ostream& operator<<(std::ostream& os, const HttpResponse& response);
|
std::ostream& operator<<(std::ostream& os, const HttpResponse& response);
|
||||||
|
|
||||||
class HttpResponse : public HttpMessage {
|
class HttpResponse : public HttpMessage {
|
||||||
friend std::ostream& operator<<(std::ostream& os,
|
|
||||||
const HttpResponse& response);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HttpResponse() = default;
|
HttpResponse() = default;
|
||||||
~HttpResponse() override = default;
|
~HttpResponse() override = default;
|
||||||
@@ -39,6 +38,9 @@ public:
|
|||||||
static HttpResponse Fault(HttpStatus::Enum status);
|
static HttpResponse Fault(HttpStatus::Enum status);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend std::ostream& operator<<(std::ostream& os,
|
||||||
|
const HttpResponse& response);
|
||||||
|
|
||||||
int status_ = HttpStatus::kOK;
|
int status_ = HttpStatus::kOK;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "webcc/http_response_parser.h"
|
#include "webcc/http_response_parser.h"
|
||||||
|
|
||||||
#include "boost/lexical_cast.hpp"
|
#include "boost/lexical_cast.hpp"
|
||||||
|
|
||||||
#include "webcc/http_response.h"
|
#include "webcc/http_response.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#include "webcc/http_server.h"
|
#include "webcc/http_server.h"
|
||||||
|
|
||||||
#include <signal.h>
|
#include <csignal>
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
#include "webcc/http_request_handler.h"
|
#include "webcc/http_request_handler.h"
|
||||||
|
#include "webcc/logger.h"
|
||||||
#include "webcc/soap_service.h"
|
#include "webcc/soap_service.h"
|
||||||
#include "webcc/utility.h"
|
#include "webcc/utility.h"
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,12 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "boost/asio/io_context.hpp"
|
||||||
|
#include "boost/asio/ip/tcp.hpp"
|
||||||
|
#include "boost/asio/signal_set.hpp"
|
||||||
#include "boost/scoped_ptr.hpp"
|
#include "boost/scoped_ptr.hpp"
|
||||||
#include "boost/thread/thread.hpp"
|
#include "boost/thread/thread.hpp"
|
||||||
|
|
||||||
#include "boost/asio/io_context.hpp"
|
|
||||||
#include "boost/asio/signal_set.hpp"
|
|
||||||
#include "boost/asio/ip/tcp.hpp"
|
|
||||||
|
|
||||||
#include "webcc/http_session.h"
|
#include "webcc/http_session.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
#include "boost/asio/write.hpp"
|
#include "boost/asio/write.hpp"
|
||||||
#include "boost/date_time/posix_time/posix_time.hpp"
|
#include "boost/date_time/posix_time/posix_time.hpp"
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
#include "webcc/http_request_handler.h"
|
#include "webcc/http_request_handler.h"
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include "boost/asio/ip/tcp.hpp" // for ip::tcp::socket
|
#include "boost/asio/ip/tcp.hpp" // for ip::tcp::socket
|
||||||
|
|
||||||
#include "webcc/common.h"
|
#include "webcc/globals.h"
|
||||||
#include "webcc/http_request.h"
|
#include "webcc/http_request.h"
|
||||||
#include "webcc/http_request_parser.h"
|
#include "webcc/http_request_parser.h"
|
||||||
#include "webcc/http_response.h"
|
#include "webcc/http_response.h"
|
||||||
|
|||||||
+3
-3
@@ -15,11 +15,11 @@ namespace webcc {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
class Queue {
|
class Queue {
|
||||||
public:
|
public:
|
||||||
Queue(const Queue& rhs) = delete;
|
|
||||||
Queue& operator=(const Queue& rhs) = delete;
|
|
||||||
|
|
||||||
Queue() = default;
|
Queue() = default;
|
||||||
|
|
||||||
|
Queue(const Queue&) = delete;
|
||||||
|
Queue& operator=(const Queue&) = delete;
|
||||||
|
|
||||||
T PopOrWait() {
|
T PopOrWait() {
|
||||||
boost::unique_lock<boost::mutex> lock(mutex_);
|
boost::unique_lock<boost::mutex> lock(mutex_);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#include "webcc/rest_client.h"
|
||||||
|
|
||||||
|
#include "webcc/http_client.h"
|
||||||
|
#include "webcc/http_request.h"
|
||||||
|
#include "webcc/http_response.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
bool RestClient::Request(const std::string& method,
|
||||||
|
const std::string& url,
|
||||||
|
const std::string& content,
|
||||||
|
HttpResponse* response) {
|
||||||
|
HttpRequest request;
|
||||||
|
|
||||||
|
request.set_method(method);
|
||||||
|
request.set_url(url);
|
||||||
|
request.SetHost(host_, port_);
|
||||||
|
|
||||||
|
if (!content.empty()) {
|
||||||
|
request.SetContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
request.Build();
|
||||||
|
|
||||||
|
HttpClient http_client;
|
||||||
|
Error error = http_client.MakeRequest(request, response);
|
||||||
|
|
||||||
|
return error == kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#ifndef WEBCC_REST_CLIENT_H_
|
||||||
|
#define WEBCC_REST_CLIENT_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "webcc/globals.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class HttpResponse;
|
||||||
|
|
||||||
|
class RestClient {
|
||||||
|
public:
|
||||||
|
RestClient(const std::string& host, const std::string& port)
|
||||||
|
: host_(host), port_(port) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Get(const std::string& url, HttpResponse* response) {
|
||||||
|
return Request(kHttpGet, url, "", response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Post(const std::string& url,
|
||||||
|
const std::string& content,
|
||||||
|
HttpResponse* response) {
|
||||||
|
return Request(kHttpPost, url, content, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Put(const std::string& url,
|
||||||
|
const std::string& content,
|
||||||
|
HttpResponse* response) {
|
||||||
|
return Request(kHttpPut, url, content, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Patch(const std::string& url,
|
||||||
|
const std::string& content,
|
||||||
|
HttpResponse* response) {
|
||||||
|
return Request(kHttpPatch, url, content, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Delete(const std::string& url, HttpResponse* response) {
|
||||||
|
return Request(kHttpDelete, url, "", response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool Request(const std::string& method,
|
||||||
|
const std::string& url,
|
||||||
|
const std::string& content,
|
||||||
|
HttpResponse* response);
|
||||||
|
|
||||||
|
std::string host_;
|
||||||
|
std::string port_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_REST_CLIENT_H_
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#include "webcc/rest_request_handler.h"
|
||||||
|
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
#include "webcc/url.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
bool RestRequestHandler::RegisterService(RestServicePtr service,
|
||||||
|
const std::string& url) {
|
||||||
|
return service_manager_.AddService(service, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestRequestHandler::HandleSession(HttpSessionPtr session) {
|
||||||
|
Url url(session->request().url(), true);
|
||||||
|
|
||||||
|
if (!url.IsValid()) {
|
||||||
|
session->SendResponse(HttpStatus::kBadRequest);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> sub_matches;
|
||||||
|
RestServicePtr service = service_manager_.GetService(url.path(),
|
||||||
|
&sub_matches);
|
||||||
|
if (!service) {
|
||||||
|
LOG_WARN("No service matches the URL: %s", url.path().c_str());
|
||||||
|
session->SendResponse(HttpStatus::kBadRequest);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Only for GET?
|
||||||
|
UrlQuery query;
|
||||||
|
Url::SplitQuery(url.query(), &query);
|
||||||
|
|
||||||
|
std::string content;
|
||||||
|
bool ok = service->Handle(session->request().method(),
|
||||||
|
sub_matches,
|
||||||
|
query,
|
||||||
|
session->request().content(),
|
||||||
|
&content);
|
||||||
|
if (!ok) {
|
||||||
|
// TODO: Could be other than kBadRequest.
|
||||||
|
session->SendResponse(HttpStatus::kBadRequest);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
session->SetResponseContent(std::move(content), kTextJsonUtf8);
|
||||||
|
session->SendResponse(HttpStatus::kOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef WEBCC_REST_REQUEST_HANDLER_H_
|
||||||
|
#define WEBCC_REST_REQUEST_HANDLER_H_
|
||||||
|
|
||||||
|
// HTTP server handling REST requests.
|
||||||
|
|
||||||
|
#include "webcc/http_request_handler.h"
|
||||||
|
#include "webcc/rest_service_manager.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class RestRequestHandler : public HttpRequestHandler {
|
||||||
|
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);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void HandleSession(HttpSessionPtr session) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
RestServiceManager service_manager_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_REST_REQUEST_HANDLER_H_
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
#include "webcc/rest_server.h"
|
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
#include "webcc/url.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool RestServiceManager::AddService(RestServicePtr service,
|
|
||||||
const std::string& url) {
|
|
||||||
assert(service);
|
|
||||||
|
|
||||||
ServiceItem item(service, url);
|
|
||||||
|
|
||||||
std::regex::flag_type flags = std::regex::ECMAScript | std::regex::icase;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Compile the regex.
|
|
||||||
item.url_regex.assign(url, flags);
|
|
||||||
|
|
||||||
service_items_.push_back(item);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} catch (std::regex_error& e) {
|
|
||||||
LOG_ERRO("URL is not a valid regular expression: %s", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RestServicePtr RestServiceManager::GetService(
|
|
||||||
const std::string& url,
|
|
||||||
std::vector<std::string>* sub_matches) {
|
|
||||||
assert(sub_matches != NULL);
|
|
||||||
|
|
||||||
for (ServiceItem& item : service_items_) {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
return item.service;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return RestServicePtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool RestRequestHandler::RegisterService(RestServicePtr service,
|
|
||||||
const std::string& url) {
|
|
||||||
return service_manager_.AddService(service, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestRequestHandler::HandleSession(HttpSessionPtr session) {
|
|
||||||
Url url(session->request().url());
|
|
||||||
|
|
||||||
if (!url.IsValid()) {
|
|
||||||
session->SendResponse(HttpStatus::kBadRequest);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> sub_matches;
|
|
||||||
RestServicePtr service = service_manager_.GetService(url.path(), &sub_matches);
|
|
||||||
if (!service) {
|
|
||||||
LOG_WARN("No service matches the URL: %s", url.path().c_str());
|
|
||||||
session->SendResponse(HttpStatus::kBadRequest);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Only for GET?
|
|
||||||
UrlQuery query;
|
|
||||||
Url::SplitQuery(url.query(), &query);
|
|
||||||
|
|
||||||
std::string content;
|
|
||||||
bool ok = service->Handle(session->request().method(),
|
|
||||||
sub_matches,
|
|
||||||
query,
|
|
||||||
session->request().content(),
|
|
||||||
&content);
|
|
||||||
if (!ok) {
|
|
||||||
// TODO: Could be other than kBadRequest.
|
|
||||||
session->SendResponse(HttpStatus::kBadRequest);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
session->SetResponseContent(std::move(content), kTextJsonUtf8);
|
|
||||||
session->SendResponse(HttpStatus::kOK);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
RestServer::RestServer(unsigned short port, std::size_t workers)
|
|
||||||
: HttpServer(port, workers)
|
|
||||||
, request_handler_(new RestRequestHandler()) {
|
|
||||||
}
|
|
||||||
|
|
||||||
RestServer::~RestServer() {
|
|
||||||
delete request_handler_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RestServer::RegisterService(RestServicePtr service,
|
|
||||||
const std::string& url) {
|
|
||||||
return request_handler_->RegisterService(service, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
+13
-83
@@ -3,106 +3,36 @@
|
|||||||
|
|
||||||
// HTTP server handling REST requests.
|
// HTTP server handling REST requests.
|
||||||
|
|
||||||
#include <regex>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "webcc/http_request_handler.h"
|
|
||||||
#include "webcc/http_server.h"
|
#include "webcc/http_server.h"
|
||||||
|
#include "webcc/rest_request_handler.h"
|
||||||
#include "webcc/rest_service.h"
|
#include "webcc/rest_service.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
class Url;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class RestServiceManager {
|
|
||||||
public:
|
|
||||||
RestServiceManager() = default;
|
|
||||||
RestServiceManager(const RestServiceManager&) = delete;
|
|
||||||
RestServiceManager& operator=(const RestServiceManager&) = delete;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Parameter 'sub_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 sub-match of "12345".
|
|
||||||
RestServicePtr GetService(const std::string& url,
|
|
||||||
std::vector<std::string>* sub_matches);
|
|
||||||
|
|
||||||
private:
|
|
||||||
class ServiceItem {
|
|
||||||
public:
|
|
||||||
ServiceItem(RestServicePtr _service, const std::string& _url)
|
|
||||||
: service(_service), url(_url) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ServiceItem(const ServiceItem& rhs) = default;
|
|
||||||
ServiceItem& operator=(const ServiceItem& rhs) = default;
|
|
||||||
|
|
||||||
ServiceItem(ServiceItem&& rhs)
|
|
||||||
: url(std::move(rhs.url))
|
|
||||||
, url_regex(std::move(rhs.url_regex))
|
|
||||||
, service(rhs.service) { // No move
|
|
||||||
}
|
|
||||||
|
|
||||||
RestServicePtr service;
|
|
||||||
|
|
||||||
// URL string, e.g., "/instances/(\\d+)".
|
|
||||||
std::string url;
|
|
||||||
|
|
||||||
// Compiled regex for URL string.
|
|
||||||
std::regex url_regex;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<ServiceItem> service_items_;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class RestRequestHandler : public HttpRequestHandler {
|
|
||||||
public:
|
|
||||||
RestRequestHandler() = 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);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void HandleSession(HttpSessionPtr session) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
RestServiceManager service_manager_;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class RestServer : public HttpServer {
|
class RestServer : public HttpServer {
|
||||||
public:
|
public:
|
||||||
RestServer(unsigned short port, std::size_t workers);
|
RestServer(unsigned short port, std::size_t workers)
|
||||||
|
: HttpServer(port, workers) {
|
||||||
|
}
|
||||||
|
|
||||||
~RestServer() override;
|
~RestServer() override = default;
|
||||||
|
|
||||||
// Register a REST service to the given URL path.
|
// Register a REST service to the given URL path.
|
||||||
// The URL should start with "/" and could be a regular expression or not.
|
// The URL should start with "/" and could be a regular expression or not.
|
||||||
// E.g., "/instances". "/instances/(\\d+)"
|
// E.g., "/instances". "/instances/(\\d+)"
|
||||||
// NOTE: Registering to the same URL multiple times is allowed, but only the
|
// NOTE: Registering to the same URL multiple times is allowed, but only the
|
||||||
// last one takes effect.
|
// last one takes effect.
|
||||||
bool RegisterService(RestServicePtr service, const std::string& url);
|
bool RegisterService(RestServicePtr service, const std::string& url) {
|
||||||
|
return request_handler_.RegisterService(service, url);
|
||||||
private:
|
|
||||||
HttpRequestHandler* GetRequestHandler() override {
|
|
||||||
return request_handler_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RestRequestHandler* request_handler_;
|
HttpRequestHandler* GetRequestHandler() override {
|
||||||
|
return &request_handler_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RestRequestHandler request_handler_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "webcc/rest_service.h"
|
#include "webcc/rest_service.h"
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
@@ -29,7 +30,7 @@ bool RestDetailService::Handle(const std::string& http_method,
|
|||||||
const std::string& request_content,
|
const std::string& request_content,
|
||||||
std::string* response_content) {
|
std::string* response_content) {
|
||||||
if (http_method == kHttpGet) {
|
if (http_method == kHttpGet) {
|
||||||
return Get(url_sub_matches, response_content);
|
return Get(url_sub_matches, query, response_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (http_method == kHttpPut) {
|
if (http_method == kHttpPut) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "webcc/common.h"
|
#include "webcc/globals.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
@@ -28,12 +28,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle REST request, output the response.
|
// Handle REST request, output the response.
|
||||||
// \param http_method GET, POST, etc.
|
// The regex sub-matches of the URL (usually resource IDs) were stored in
|
||||||
// \param url_sub_matches The regex sub-matches in the URL,
|
// |url_sub_matches|. The |query| part of the URL is normally only for GET
|
||||||
// usually resource ID.
|
// request. Both the request and response contents are JSON strings.
|
||||||
// \param query Query parameters in the URL.
|
|
||||||
// \param request_content Request 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 UrlQuery& query,
|
const UrlQuery& query,
|
||||||
@@ -79,6 +76,7 @@ class RestDetailService : public RestService {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool Get(const std::vector<std::string>& url_sub_matches,
|
virtual bool Get(const std::vector<std::string>& url_sub_matches,
|
||||||
|
const UrlQuery& query,
|
||||||
std::string* response_content) {
|
std::string* response_content) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#include "webcc/rest_service_manager.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
bool RestServiceManager::AddService(RestServicePtr service,
|
||||||
|
const std::string& url) {
|
||||||
|
assert(service);
|
||||||
|
|
||||||
|
ServiceItem item(service, url);
|
||||||
|
|
||||||
|
std::regex::flag_type flags = std::regex::ECMAScript | std::regex::icase;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Compile the regex.
|
||||||
|
item.url_regex.assign(url, flags);
|
||||||
|
|
||||||
|
service_items_.push_back(item);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (std::regex_error& e) {
|
||||||
|
LOG_ERRO("URL is not a valid regular expression: %s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RestServicePtr RestServiceManager::GetService(
|
||||||
|
const std::string& url,
|
||||||
|
std::vector<std::string>* sub_matches) {
|
||||||
|
|
||||||
|
assert(sub_matches != NULL);
|
||||||
|
|
||||||
|
for (ServiceItem& item : service_items_) {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.service;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RestServicePtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef WEBCC_REST_SERVICE_MANAGER_H_
|
||||||
|
#define WEBCC_REST_SERVICE_MANAGER_H_
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webcc/rest_service.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class RestServiceManager {
|
||||||
|
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 |sub_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 sub-match of "12345".
|
||||||
|
RestServicePtr GetService(const std::string& url,
|
||||||
|
std::vector<std::string>* sub_matches);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class ServiceItem {
|
||||||
|
public:
|
||||||
|
ServiceItem(RestServicePtr _service, const std::string& _url)
|
||||||
|
: service(_service), url(_url) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceItem(const ServiceItem& rhs) = default;
|
||||||
|
ServiceItem& operator=(const ServiceItem& rhs) = default;
|
||||||
|
|
||||||
|
ServiceItem(ServiceItem&& rhs)
|
||||||
|
: url(std::move(rhs.url)),
|
||||||
|
url_regex(std::move(rhs.url_regex)),
|
||||||
|
service(rhs.service) { // No move
|
||||||
|
}
|
||||||
|
|
||||||
|
RestServicePtr service;
|
||||||
|
|
||||||
|
// URL string, e.g., "/instances/(\\d+)".
|
||||||
|
std::string url;
|
||||||
|
|
||||||
|
// Compiled regex for URL string.
|
||||||
|
std::regex url_regex;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<ServiceItem> service_items_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(RestServiceManager);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_REST_SERVICE_MANAGER_H_
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "webcc/common.h"
|
#include "webcc/globals.h"
|
||||||
|
#include "webcc/soap_message.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
#include "webcc/soap_message.h"
|
#include "webcc/soap_message.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include "webcc/soap_xml.h"
|
#include "webcc/soap_xml.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
|
const SoapNamespace kSoapEnvNamespace{
|
||||||
|
"soap",
|
||||||
|
"http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
};
|
||||||
|
|
||||||
void SoapMessage::ToXml(std::string* xml_string) {
|
void SoapMessage::ToXml(std::string* xml_string) {
|
||||||
assert(soapenv_ns_.IsValid() &&
|
assert(soapenv_ns_.IsValid() &&
|
||||||
service_ns_.IsValid() &&
|
service_ns_.IsValid() &&
|
||||||
|
|||||||
@@ -2,14 +2,33 @@
|
|||||||
#define WEBCC_SOAP_MESSAGE_H_
|
#define WEBCC_SOAP_MESSAGE_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "pugixml/pugixml.hpp"
|
#include "pugixml/pugixml.hpp"
|
||||||
#include "webcc/common.h"
|
|
||||||
|
#include "webcc/globals.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
|
// XML namespace name/url pair.
|
||||||
|
// E.g., { "soap", "http://schemas.xmlsoap.org/soap/envelope/" }
|
||||||
|
class SoapNamespace {
|
||||||
|
public:
|
||||||
|
std::string name;
|
||||||
|
std::string url;
|
||||||
|
|
||||||
|
bool IsValid() const {
|
||||||
|
return !name.empty() && !url.empty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// CSoap's default namespace for SOAP Envelope.
|
||||||
|
extern const SoapNamespace kSoapEnvNamespace;
|
||||||
|
|
||||||
// Base class for SOAP request and response.
|
// Base class for SOAP request and response.
|
||||||
class SoapMessage {
|
class SoapMessage {
|
||||||
public:
|
public:
|
||||||
|
virtual ~SoapMessage() {}
|
||||||
|
|
||||||
// E.g., set as kSoapEnvNamespace.
|
// E.g., set as kSoapEnvNamespace.
|
||||||
void set_soapenv_ns(const SoapNamespace& soapenv_ns) {
|
void set_soapenv_ns(const SoapNamespace& soapenv_ns) {
|
||||||
soapenv_ns_ = soapenv_ns;
|
soapenv_ns_ = soapenv_ns;
|
||||||
@@ -34,8 +53,6 @@ public:
|
|||||||
bool FromXml(const std::string& xml_string);
|
bool FromXml(const std::string& xml_string);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
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;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "webcc/soap_request.h"
|
#include "webcc/soap_request.h"
|
||||||
|
|
||||||
#include "webcc/soap_xml.h"
|
#include "webcc/soap_xml.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define WEBCC_SOAP_REQUEST_H_
|
#define WEBCC_SOAP_REQUEST_H_
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "webcc/soap_message.h"
|
#include "webcc/soap_message.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "webcc/soap_server.h"
|
#include "webcc/soap_request_handler.h"
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
#include "webcc/logger.h"
|
||||||
#include "webcc/soap_request.h"
|
#include "webcc/soap_request.h"
|
||||||
@@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool SoapRequestHandler::RegisterService(SoapServicePtr service,
|
bool SoapRequestHandler::RegisterService(SoapServicePtr service,
|
||||||
const std::string& url) {
|
const std::string& url) {
|
||||||
assert(service);
|
assert(service);
|
||||||
@@ -55,20 +53,4 @@ SoapServicePtr SoapRequestHandler::GetServiceByUrl(const std::string& url) {
|
|||||||
return SoapServicePtr();
|
return SoapServicePtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
SoapServer::SoapServer(unsigned short port, std::size_t workers)
|
|
||||||
: HttpServer(port, workers)
|
|
||||||
, request_handler_(new SoapRequestHandler()) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SoapServer::~SoapServer() {
|
|
||||||
delete request_handler_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SoapServer::RegisterService(SoapServicePtr service,
|
|
||||||
const std::string& url) {
|
|
||||||
return request_handler_->RegisterService(service, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef WEBCC_SOAP_REQUEST_HANDLER_H_
|
||||||
|
#define WEBCC_SOAP_REQUEST_HANDLER_H_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "webcc/http_request_handler.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class SoapRequestHandler : public HttpRequestHandler {
|
||||||
|
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);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void HandleSession(HttpSessionPtr session) override;
|
||||||
|
|
||||||
|
SoapServicePtr GetServiceByUrl(const std::string& url);
|
||||||
|
|
||||||
|
typedef std::map<std::string, SoapServicePtr> UrlServiceMap;
|
||||||
|
UrlServiceMap url_service_map_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_SOAP_REQUEST_HANDLER_H_
|
||||||
+12
-36
@@ -3,53 +3,29 @@
|
|||||||
|
|
||||||
// HTTP server handling SOAP requests.
|
// HTTP server handling SOAP requests.
|
||||||
|
|
||||||
#include <map>
|
#include "webcc/soap_request_handler.h"
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "webcc/http_request_handler.h"
|
|
||||||
#include "webcc/http_server.h"
|
#include "webcc/http_server.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class SoapRequestHandler : public HttpRequestHandler {
|
|
||||||
public:
|
|
||||||
SoapRequestHandler() = default;
|
|
||||||
|
|
||||||
// Register a SOAP service to the given URL path.
|
|
||||||
// \url URL path, must start with "/". E.g., "/calculator".
|
|
||||||
// NOTE: Registering to the same URL multiple times is allowed, but only the
|
|
||||||
// last one takes effect.
|
|
||||||
bool RegisterService(SoapServicePtr service, const std::string& url);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void HandleSession(HttpSessionPtr session) override;
|
|
||||||
|
|
||||||
SoapServicePtr GetServiceByUrl(const std::string& url);
|
|
||||||
|
|
||||||
private:
|
|
||||||
typedef std::map<std::string, SoapServicePtr> UrlServiceMap;
|
|
||||||
UrlServiceMap url_service_map_;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class SoapServer : public HttpServer {
|
class SoapServer : public HttpServer {
|
||||||
public:
|
public:
|
||||||
SoapServer(unsigned short port, std::size_t workers);
|
SoapServer(unsigned short port, std::size_t workers)
|
||||||
|
: HttpServer(port, workers) {
|
||||||
|
}
|
||||||
|
|
||||||
~SoapServer() override;
|
~SoapServer() override = default;
|
||||||
|
|
||||||
bool RegisterService(SoapServicePtr service, const std::string& url);
|
bool RegisterService(SoapServicePtr service, const std::string& url) {
|
||||||
|
return request_handler_.RegisterService(service, url);
|
||||||
private:
|
|
||||||
HttpRequestHandler* GetRequestHandler() override {
|
|
||||||
return request_handler_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SoapRequestHandler* request_handler_;
|
HttpRequestHandler* GetRequestHandler() override {
|
||||||
|
return &request_handler_;
|
||||||
|
}
|
||||||
|
|
||||||
|
SoapRequestHandler request_handler_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
#define WEBCC_SOAP_SERVICE_H_
|
#define WEBCC_SOAP_SERVICE_H_
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "webcc/common.h"
|
|
||||||
|
#include "webcc/globals.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
@@ -12,8 +13,7 @@ class SoapResponse;
|
|||||||
// Base class for your SOAP service.
|
// Base class for your SOAP service.
|
||||||
class SoapService {
|
class SoapService {
|
||||||
public:
|
public:
|
||||||
virtual ~SoapService() {
|
virtual ~SoapService() = default;
|
||||||
}
|
|
||||||
|
|
||||||
// Handle SOAP request, output the response.
|
// Handle SOAP request, output the response.
|
||||||
virtual bool Handle(const SoapRequest& soap_request,
|
virtual bool Handle(const SoapRequest& soap_request,
|
||||||
|
|||||||
+8
-16
@@ -3,8 +3,7 @@
|
|||||||
namespace webcc {
|
namespace webcc {
|
||||||
namespace soap_xml {
|
namespace soap_xml {
|
||||||
|
|
||||||
void SplitName(const pugi::xml_node& xnode,
|
void SplitName(const pugi::xml_node& xnode, std::string* prefix,
|
||||||
std::string* prefix,
|
|
||||||
std::string* name) {
|
std::string* name) {
|
||||||
std::string full_name = xnode.name();
|
std::string full_name = xnode.name();
|
||||||
|
|
||||||
@@ -39,14 +38,12 @@ std::string GetNameNoPrefix(const pugi::xml_node& xnode) {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
pugi::xml_node AddChild(pugi::xml_node& xnode,
|
pugi::xml_node AddChild(pugi::xml_node& xnode, const std::string& ns,
|
||||||
const std::string& ns,
|
|
||||||
const std::string& name) {
|
const std::string& name) {
|
||||||
return xnode.append_child((ns + ":" + name).c_str());
|
return xnode.append_child((ns + ":" + name).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
pugi::xml_node GetChild(pugi::xml_node& xnode,
|
pugi::xml_node GetChild(pugi::xml_node& xnode, const std::string& ns,
|
||||||
const std::string& ns,
|
|
||||||
const std::string& name) {
|
const std::string& name) {
|
||||||
return xnode.child((ns + ":" + name).c_str());
|
return xnode.child((ns + ":" + name).c_str());
|
||||||
}
|
}
|
||||||
@@ -72,28 +69,23 @@ pugi::xml_node GetChildNoNS(pugi::xml_node& xnode, const std::string& name) {
|
|||||||
return pugi::xml_node();
|
return pugi::xml_node();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddAttr(pugi::xml_node& xnode,
|
void AddAttr(pugi::xml_node& xnode, const std::string& ns,
|
||||||
const std::string& ns,
|
const std::string& name, const std::string& value) {
|
||||||
const std::string& name,
|
|
||||||
const std::string& value) {
|
|
||||||
std::string ns_name = ns + ":" + name;
|
std::string ns_name = ns + ":" + name;
|
||||||
xnode.append_attribute(ns_name.c_str()) = value.c_str();
|
xnode.append_attribute(ns_name.c_str()) = value.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNSAttr(pugi::xml_node& xnode,
|
void AddNSAttr(pugi::xml_node& xnode, const std::string& ns_name,
|
||||||
const std::string& ns_name,
|
|
||||||
const std::string& ns_url) {
|
const std::string& ns_url) {
|
||||||
AddAttr(xnode, "xmlns", ns_name, ns_url);
|
AddAttr(xnode, "xmlns", ns_name, ns_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetNSAttr(pugi::xml_node& xnode,
|
std::string GetNSAttr(pugi::xml_node& xnode, const std::string& ns_name) {
|
||||||
const std::string& ns_name) {
|
|
||||||
std::string attr_name = "xmlns:" + ns_name;
|
std::string attr_name = "xmlns:" + ns_name;
|
||||||
return xnode.attribute(attr_name.c_str()).as_string();
|
return xnode.attribute(attr_name.c_str()).as_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PrettyPrint(std::ostream& os,
|
bool PrettyPrint(std::ostream& os, const std::string& xml_string,
|
||||||
const std::string& xml_string,
|
|
||||||
const char* indent) {
|
const char* indent) {
|
||||||
pugi::xml_document xdoc;
|
pugi::xml_document xdoc;
|
||||||
if (!xdoc.load_string(xml_string.c_str())) {
|
if (!xdoc.load_string(xml_string.c_str())) {
|
||||||
|
|||||||
+14
-23
@@ -1,19 +1,19 @@
|
|||||||
#ifndef WEBCC_SOAP_XML_H_
|
#ifndef WEBCC_SOAP_XML_H_
|
||||||
#define WEBCC_SOAP_XML_H_
|
#define WEBCC_SOAP_XML_H_
|
||||||
|
|
||||||
// XML utilities.
|
// XML helpers for SOAP messages.
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "pugixml/pugixml.hpp"
|
#include "pugixml/pugixml.hpp"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
namespace soap_xml {
|
namespace soap_xml {
|
||||||
|
|
||||||
// Split the node name into namespace prefix and real name.
|
// Split the node name into namespace prefix and real name.
|
||||||
// E.g., if the node name is "soapenv:Envelope", it will be splited to
|
// E.g., if the node name is "soapenv:Envelope", it will be splitted to
|
||||||
// "soapenv" and "Envelope".
|
// "soapenv" and "Envelope".
|
||||||
void SplitName(const pugi::xml_node& xnode,
|
void SplitName(const pugi::xml_node& xnode, std::string* prefix = NULL,
|
||||||
std::string* prefix = NULL,
|
|
||||||
std::string* name = NULL);
|
std::string* name = NULL);
|
||||||
|
|
||||||
// Get the namespace prefix from node name.
|
// Get the namespace prefix from node name.
|
||||||
@@ -26,38 +26,31 @@ std::string GetNameNoPrefix(const pugi::xml_node& xnode);
|
|||||||
// Add a child with the given name which is prefixed by a namespace.
|
// Add a child with the given name which is prefixed by a namespace.
|
||||||
// E.g., AppendChild(xnode, "soapenv", "Envelope") will append a child with
|
// E.g., AppendChild(xnode, "soapenv", "Envelope") will append a child with
|
||||||
// name "soapenv:Envelope".
|
// name "soapenv:Envelope".
|
||||||
pugi::xml_node AddChild(pugi::xml_node& xnode,
|
pugi::xml_node AddChild(pugi::xml_node& xnode, const std::string& ns,
|
||||||
const std::string& ns,
|
|
||||||
const std::string& name);
|
const std::string& name);
|
||||||
|
|
||||||
pugi::xml_node GetChild(pugi::xml_node& xnode,
|
pugi::xml_node GetChild(pugi::xml_node& xnode, const std::string& ns,
|
||||||
const std::string& ns,
|
|
||||||
const std::string& name);
|
const std::string& name);
|
||||||
|
|
||||||
// TODO: Remove
|
// TODO: Remove
|
||||||
pugi::xml_node GetChildNoNS(pugi::xml_node& xnode,
|
pugi::xml_node GetChildNoNS(pugi::xml_node& xnode, const std::string& name);
|
||||||
const std::string& name);
|
|
||||||
|
|
||||||
// Add an attribute with the given name which is prefixed by a namespace.
|
// Add an attribute with the given name which is prefixed by a namespace.
|
||||||
void AddAttr(pugi::xml_node& xnode,
|
void AddAttr(pugi::xml_node& xnode, const std::string& ns,
|
||||||
const std::string& ns,
|
const std::string& name, const std::string& value);
|
||||||
const std::string& name,
|
|
||||||
const std::string& value);
|
|
||||||
|
|
||||||
// Append "xmlns" attribute.
|
// Append "xmlns" attribute.
|
||||||
// E.g., if the namespace is
|
// E.g., if the namespace is
|
||||||
// { "soapenv", "http://schemas.xmlsoap.org/soap/envelope/" }
|
// { "soapenv", "http://schemas.xmlsoap.org/soap/envelope/" }
|
||||||
// the attribute added will be
|
// the attribute added will be
|
||||||
// xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
// xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
void AddNSAttr(pugi::xml_node& xnode,
|
void AddNSAttr(pugi::xml_node& xnode, const std::string& ns_name,
|
||||||
const std::string& ns_name,
|
|
||||||
const std::string& ns_url);
|
const std::string& ns_url);
|
||||||
|
|
||||||
// Get namespace attribute value.
|
// Get namespace attribute value.
|
||||||
// E.g., if the given namespace name is "soapenv", the value of
|
// E.g., if the given namespace name is "soapenv", the value of
|
||||||
// attribute "xmlns:soapenv" will be returned.
|
// attribute "xmlns:soapenv" will be returned.
|
||||||
std::string GetNSAttr(pugi::xml_node& xnode,
|
std::string GetNSAttr(pugi::xml_node& xnode, const std::string& ns_name);
|
||||||
const std::string& ns_name);
|
|
||||||
|
|
||||||
// An XML writer writing to a referenced string.
|
// An XML writer writing to a referenced string.
|
||||||
// Example:
|
// Example:
|
||||||
@@ -68,12 +61,11 @@ std::string GetNSAttr(pugi::xml_node& xnode,
|
|||||||
// xdoc.save(writer, "\t", pugi::format_default, pugi::encoding_utf8);
|
// xdoc.save(writer, "\t", pugi::format_default, pugi::encoding_utf8);
|
||||||
class XmlStrRefWriter : public pugi::xml_writer {
|
class XmlStrRefWriter : public pugi::xml_writer {
|
||||||
public:
|
public:
|
||||||
explicit XmlStrRefWriter(std::string* result)
|
explicit XmlStrRefWriter(std::string* result) : result_(result) {
|
||||||
: result_(result) {
|
|
||||||
result_->clear();
|
result_->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void write(const void* data, size_t size) override {
|
void write(const void* data, size_t size) override {
|
||||||
result_->append(static_cast<const char*>(data), size);
|
result_->append(static_cast<const char*>(data), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,8 +74,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Print the XML string to output stream in pretty format.
|
// Print the XML string to output stream in pretty format.
|
||||||
bool PrettyPrint(std::ostream& os,
|
bool PrettyPrint(std::ostream& os, const std::string& xml_string,
|
||||||
const std::string& xml_string,
|
|
||||||
const char* indent = "\t");
|
const char* indent = "\t");
|
||||||
|
|
||||||
} // namespace soap_xml
|
} // namespace soap_xml
|
||||||
|
|||||||
+105
-10
@@ -6,13 +6,78 @@
|
|||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Helper functions to decode URL string.
|
||||||
|
|
||||||
|
// Convert a hex character digit to a decimal character value.
|
||||||
|
static bool HexToDecimal(char hex, int* decimal) {
|
||||||
|
if (hex >= '0' && hex <= '9') {
|
||||||
|
*decimal = hex - '0';
|
||||||
|
} else if (hex >= 'A' && hex <= 'F') {
|
||||||
|
*decimal = 10 + (hex - 'A');
|
||||||
|
} else if (hex >= 'a' && hex <= 'f') {
|
||||||
|
*decimal = 10 + (hex - 'a');
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Decode(const std::string& encoded, std::string* raw) {
|
||||||
|
for (auto iter = encoded.begin(); iter != encoded.end(); ++iter) {
|
||||||
|
if (*iter == '%') {
|
||||||
|
if (++iter == encoded.end()) {
|
||||||
|
// Invalid URI string, two hexadecimal digits must follow '%'.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int h_decimal = 0;
|
||||||
|
if (!HexToDecimal(*iter, &h_decimal)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++iter == encoded.end()) {
|
||||||
|
// Invalid URI string, two hexadecimal digits must follow '%'.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int l_decimal = 0;
|
||||||
|
if (!HexToDecimal(*iter, &l_decimal)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
raw->push_back(static_cast<char>((h_decimal << 4) + l_decimal));
|
||||||
|
|
||||||
|
} else if (*iter > 127 || *iter < 0) {
|
||||||
|
// Invalid encoded URI string, must be entirely ASCII.
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
raw->push_back(*iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
UrlQuery::UrlQuery(const std::map<std::string, std::string>& map) {
|
||||||
|
for (auto& pair : map) {
|
||||||
|
Add(pair.first, pair.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void UrlQuery::Add(std::string&& key, std::string&& value) {
|
void UrlQuery::Add(std::string&& key, std::string&& value) {
|
||||||
if (!HasKey(key)) {
|
if (!Has(key)) {
|
||||||
parameters_.push_back({ std::move(key), std::move(value) });
|
parameters_.push_back({ std::move(key), std::move(value) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UrlQuery::Add(const std::string& key, const std::string& value) {
|
||||||
|
if (!Has(key)) {
|
||||||
|
parameters_.push_back({ key, value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void UrlQuery::Remove(const std::string& key) {
|
void UrlQuery::Remove(const std::string& key) {
|
||||||
auto it = Find(key);
|
auto it = Find(key);
|
||||||
if (it != parameters_.end()) {
|
if (it != parameters_.end()) {
|
||||||
@@ -20,16 +85,31 @@ void UrlQuery::Remove(const std::string& key) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& UrlQuery::GetValue(const std::string& key) const {
|
const std::string& UrlQuery::Get(const std::string& key) const {
|
||||||
static const std::string kEmptyValue;
|
|
||||||
|
|
||||||
auto it = Find(key);
|
auto it = Find(key);
|
||||||
if (it != parameters_.end()) {
|
if (it != parameters_.end()) {
|
||||||
return it->value();
|
return it->value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const std::string kEmptyValue;
|
||||||
return kEmptyValue;
|
return kEmptyValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string UrlQuery::ToString() const {
|
||||||
|
if (parameters_.empty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string str = parameters_[0].ToString();
|
||||||
|
|
||||||
|
for (std::size_t i = 1; i < parameters_.size(); ++i) {
|
||||||
|
str += "&";
|
||||||
|
str += parameters_[i].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
UrlQuery::ConstIterator UrlQuery::Find(const std::string& key) const {
|
UrlQuery::ConstIterator UrlQuery::Find(const std::string& key) const {
|
||||||
return std::find_if(parameters_.begin(),
|
return std::find_if(parameters_.begin(),
|
||||||
parameters_.end(),
|
parameters_.end(),
|
||||||
@@ -38,13 +118,18 @@ UrlQuery::ConstIterator UrlQuery::Find(const std::string& key) const {
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
Url::Url(const std::string& str) {
|
Url::Url(const std::string& str, bool decode) {
|
||||||
std::size_t pos = str.find('?');
|
if (!decode || str.find('%') == std::string::npos) {
|
||||||
if (pos == std::string::npos) {
|
Init(str);
|
||||||
path_ = str;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string decoded;
|
||||||
|
if (Decode(str, &decoded)) {
|
||||||
|
Init(decoded);
|
||||||
} else {
|
} else {
|
||||||
path_ = str.substr(0, pos);
|
// TODO: Exception?
|
||||||
query_ = str.substr(pos + 1);
|
Init(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,4 +188,14 @@ void Url::SplitQuery(const std::string& str, UrlQuery* query) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Url::Init(const std::string& str) {
|
||||||
|
std::size_t pos = str.find('?');
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
path_ = str;
|
||||||
|
} else {
|
||||||
|
path_ = str.substr(0, pos);
|
||||||
|
query_ = str.substr(pos + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
+26
-8
@@ -11,27 +11,44 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "webcc/common.h"
|
#include "webcc/globals.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// URL query parameters.
|
// URL query parameters.
|
||||||
class UrlQuery {
|
class UrlQuery {
|
||||||
public:
|
public:
|
||||||
typedef std::vector<Parameter> Parameters;
|
typedef std::vector<Parameter> Parameters;
|
||||||
|
|
||||||
|
UrlQuery() = default;
|
||||||
|
|
||||||
|
// Construct from key-value pairs.
|
||||||
|
explicit UrlQuery(const std::map<std::string, std::string>& map);
|
||||||
|
|
||||||
|
void Add(const std::string& key, const std::string& value);
|
||||||
|
|
||||||
void Add(std::string&& key, std::string&& value);
|
void Add(std::string&& key, std::string&& value);
|
||||||
|
|
||||||
void Remove(const std::string& key);
|
void Remove(const std::string& key);
|
||||||
|
|
||||||
const std::string& GetValue(const std::string& key) const;
|
// Get a value by key.
|
||||||
|
// Return empty string if the key doesn't exist.
|
||||||
|
const std::string& Get(const std::string& key) const;
|
||||||
|
|
||||||
bool HasKey(const std::string& key) const {
|
bool Has(const std::string& key) const {
|
||||||
return Find(key) != parameters_.end();
|
return Find(key) != parameters_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsEmpty() const {
|
||||||
|
return parameters_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return key-value pairs concatenated by '&'.
|
||||||
|
// E.g., "item=12731&color=blue&size=large".
|
||||||
|
std::string ToString() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef Parameters::const_iterator ConstIterator;
|
typedef Parameters::const_iterator ConstIterator;
|
||||||
ConstIterator Find(const std::string& key) const;
|
ConstIterator Find(const std::string& key) const;
|
||||||
@@ -40,13 +57,12 @@ private:
|
|||||||
Parameters parameters_;
|
Parameters parameters_;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
class Url {
|
class Url {
|
||||||
public:
|
public:
|
||||||
typedef std::map<std::string, std::string> Query;
|
Url() = default;
|
||||||
|
Url(const std::string& str, bool decode);
|
||||||
Url(const std::string& str);
|
|
||||||
|
|
||||||
bool IsValid() const;
|
bool IsValid() const;
|
||||||
|
|
||||||
@@ -73,6 +89,8 @@ public:
|
|||||||
static void SplitQuery(const std::string& str, UrlQuery* query);
|
static void SplitQuery(const std::string& str, UrlQuery* query);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void Init(const std::string& str);
|
||||||
|
|
||||||
std::string path_;
|
std::string path_;
|
||||||
std::string query_;
|
std::string query_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "webcc/utility.h"
|
#include "webcc/utility.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
using tcp = boost::asio::ip::tcp;
|
using tcp = boost::asio::ip::tcp;
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
#include "webcc/rest_server.h"
|
#include "webcc/rest_service_manager.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
using namespace webcc;
|
using namespace webcc;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class TestRestService : public RestService {
|
class TestRestService : public RestService {
|
||||||
public:
|
public:
|
||||||
bool Handle(const std::string& http_method,
|
bool Handle(const std::string& http_method,
|
||||||
@@ -16,8 +14,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
TEST(RestServiceManager, URL_RegexBasic) {
|
TEST(RestServiceManager, URL_RegexBasic) {
|
||||||
RestServiceManager service_manager;
|
RestServiceManager service_manager;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user