Refactoring according Google C++ Style.
This commit is contained in:
@@ -1,78 +1,50 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "boost/algorithm/string.hpp"
|
||||
#include "json/json.h" // jsoncpp
|
||||
#include "json/json.h"
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/http_client.h"
|
||||
#include "webcc/http_request.h"
|
||||
#include "webcc/http_response.h"
|
||||
#include "webcc/rest_client.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class BookClientBase {
|
||||
public:
|
||||
BookClientBase(const std::string& host, const std::string& port)
|
||||
: host_(host), port_(port) {
|
||||
// Write a JSON object to string.
|
||||
std::string JsonToString(const Json::Value& json) {
|
||||
Json::StreamWriterBuilder builder;
|
||||
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:
|
||||
BookListClient(const std::string& host, const std::string& port)
|
||||
: BookClientBase(host, port) {
|
||||
: rest_client_(host, port) {
|
||||
}
|
||||
|
||||
bool ListBooks() {
|
||||
webcc::HttpResponse http_response;
|
||||
if (!Request(webcc::kHttpGet, "/books", "", &http_response)) {
|
||||
if (!rest_client_.Get("/books", &http_response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "result:\n" << http_response.content() << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateBook(const std::string& id,
|
||||
const std::string& title,
|
||||
double price) {
|
||||
Json::Value root(Json::objectValue);
|
||||
root["id"] = id;
|
||||
root["title"] = title;
|
||||
root["price"] = price;
|
||||
|
||||
Json::StreamWriterBuilder builder;
|
||||
std::string book_json = Json::writeString(builder, root);
|
||||
Json::Value json(Json::objectValue);
|
||||
json["id"] = id;
|
||||
json["title"] = title;
|
||||
json["price"] = price;
|
||||
|
||||
webcc::HttpResponse http_response;
|
||||
if (!Request(webcc::kHttpPost, "/books", book_json, &http_response)) {
|
||||
if (!rest_client_.Post("/books", JsonToString(json), &http_response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -80,58 +52,58 @@ public:
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
webcc::RestClient rest_client_;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class BookDetailClient : public BookClientBase {
|
||||
class BookDetailClient {
|
||||
public:
|
||||
BookDetailClient(const std::string& host, const std::string& port)
|
||||
: BookClientBase(host, port) {
|
||||
: rest_client_(host, port) {
|
||||
}
|
||||
|
||||
bool GetBook(const std::string& id) {
|
||||
webcc::HttpResponse http_response;
|
||||
if (!Request(webcc::kHttpGet, "/book/" + id, "", &http_response)) {
|
||||
if (!rest_client_.Get("/book/" + id, &http_response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << http_response.content() << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateBook(const std::string& id,
|
||||
const std::string& title,
|
||||
double price) {
|
||||
Json::Value root(Json::objectValue);
|
||||
// root["id"] = id; // NOTE: ID is already in the URL.
|
||||
root["title"] = title;
|
||||
root["price"] = price;
|
||||
|
||||
Json::StreamWriterBuilder builder;
|
||||
std::string book_json = Json::writeString(builder, root);
|
||||
// NOTE: ID is already in the URL.
|
||||
Json::Value json(Json::objectValue);
|
||||
json["title"] = title;
|
||||
json["price"] = price;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::cout << http_response.status() << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeleteBook(const std::string& id) {
|
||||
webcc::HttpResponse http_response;
|
||||
if (!Request(webcc::kHttpDelete, "/book/" + id, "", &http_response)) {
|
||||
if (!rest_client_.Delete("/book/" + id, &http_response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << http_response.content() << std::endl;
|
||||
|
||||
std::cout << http_response.status() << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
webcc::RestClient rest_client_;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -147,13 +119,8 @@ void Help(const char* argv0) {
|
||||
|
||||
std::string GetUserInput() {
|
||||
char input[256];
|
||||
// std::size_t length = 0;
|
||||
// do {
|
||||
std::cout << ">> ";
|
||||
std::cin.getline(input, 256);
|
||||
// length = strlen(input);
|
||||
// } while (length == 0);
|
||||
|
||||
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,
|
||||
const webcc::UrlQuery& query,
|
||||
std::string* response_content) {
|
||||
if (url_sub_matches.size() != 1) {
|
||||
return false;
|
||||
|
||||
@@ -32,6 +32,7 @@ class BookListService : public webcc::RestListService {
|
||||
class BookDetailService : public webcc::RestDetailService {
|
||||
protected:
|
||||
bool Get(const std::vector<std::string>& url_sub_matches,
|
||||
const webcc::UrlQuery& query,
|
||||
std::string* response_content) final;
|
||||
|
||||
bool Patch(const std::vector<std::string>& url_sub_matches,
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
add_definitions(-DBOOST_ASIO_NO_DEPRECATED)
|
||||
|
||||
set(SRCS
|
||||
common.cc
|
||||
common.h
|
||||
globals.cc
|
||||
globals.h
|
||||
http_client.cc
|
||||
http_client.h
|
||||
http_message.cc
|
||||
@@ -27,7 +27,12 @@ set(SRCS
|
||||
logger.cc
|
||||
logger.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_service.cc
|
||||
rest_service.h
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "webcc/common.h"
|
||||
#include "webcc/globals.h"
|
||||
|
||||
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)
|
||||
: key_(key), value_(value) {
|
||||
}
|
||||
@@ -110,4 +103,8 @@ Parameter& Parameter::operator=(Parameter&& rhs) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string Parameter::ToString() const {
|
||||
return key_ + "=" + value_;
|
||||
}
|
||||
|
||||
} // namespace webcc
|
||||
@@ -1,17 +1,47 @@
|
||||
#ifndef WEBCC_COMMON_H_
|
||||
#define WEBCC_COMMON_H_
|
||||
|
||||
// Common definitions.
|
||||
#ifndef WEBCC_GLOBALS_H_
|
||||
#define WEBCC_GLOBALS_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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.
|
||||
// TODO: Configurable for client and server separately.
|
||||
// TODO: Configurable
|
||||
const std::size_t kBufferSize = 1024;
|
||||
|
||||
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 kTextJsonUtf8;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// HTTP methods (verbs) in string ("HEAD", "GET", etc.).
|
||||
// NOTE: Don't use enum to avoid converting back and forth.
|
||||
extern const std::string kHttpHead;
|
||||
@@ -54,11 +82,9 @@ struct HttpStatus {
|
||||
};
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Error codes.
|
||||
enum Error {
|
||||
kNoError = 0, // OK
|
||||
kNoError = 0,
|
||||
|
||||
kHostResolveError,
|
||||
kEndpointConnectError,
|
||||
@@ -83,31 +109,14 @@ enum Error {
|
||||
// Return a descriptive message for the given error code.
|
||||
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.
|
||||
class Parameter {
|
||||
public:
|
||||
Parameter() = default;
|
||||
Parameter(const Parameter& rhs) = default;
|
||||
Parameter& operator=(const Parameter& rhs) = default;
|
||||
Parameter(const Parameter&) = default;
|
||||
Parameter& operator=(const Parameter&) = default;
|
||||
|
||||
Parameter(const std::string& key, const char* value);
|
||||
Parameter(const std::string& key, const std::string& value);
|
||||
@@ -138,6 +147,9 @@ public:
|
||||
return value_.c_str();
|
||||
}
|
||||
|
||||
// Return "key=value" string.
|
||||
std::string ToString() const;
|
||||
|
||||
private:
|
||||
std::string key_;
|
||||
std::string value_;
|
||||
@@ -145,4 +157,4 @@ private:
|
||||
|
||||
} // 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/lambda/bind.hpp"
|
||||
#include "boost/lambda/lambda.hpp"
|
||||
|
||||
#if 0
|
||||
#include "boost/asio.hpp"
|
||||
#else
|
||||
#include "boost/asio/connect.hpp"
|
||||
#include "boost/asio/read.hpp"
|
||||
#include "boost/asio/write.hpp"
|
||||
#endif
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/http_request.h"
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
#include <memory>
|
||||
|
||||
#include "boost/asio/deadline_timer.hpp"
|
||||
#include "boost/asio/ip/tcp.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"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include "webcc/common.h"
|
||||
|
||||
#include "webcc/globals.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
@@ -16,9 +17,6 @@ public:
|
||||
// Base class for HTTP request and response messages.
|
||||
class HttpMessage {
|
||||
public:
|
||||
HttpMessage() = default;
|
||||
HttpMessage(const HttpMessage&) = default;
|
||||
HttpMessage& operator=(const HttpMessage&) = default;
|
||||
virtual ~HttpMessage() = default;
|
||||
|
||||
const std::string& start_line() const {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
#define WEBCC_HTTP_PARSER_H_
|
||||
|
||||
#include <string>
|
||||
#include "webcc/common.h"
|
||||
|
||||
#include "webcc/globals.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
@@ -15,9 +16,6 @@ public:
|
||||
|
||||
virtual ~HttpParser() = default;
|
||||
|
||||
HttpParser(const HttpParser&) = delete;
|
||||
HttpParser& operator=(const HttpParser&) = delete;
|
||||
|
||||
bool finished() const {
|
||||
return finished_;
|
||||
}
|
||||
@@ -53,6 +51,8 @@ protected:
|
||||
bool content_length_parsed_;
|
||||
bool header_parsed_;
|
||||
bool finished_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(HttpParser);
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#define WEBCC_HTTP_REQUEST_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "boost/asio/buffer.hpp" // for const_buffer
|
||||
|
||||
#include "webcc/http_message.h"
|
||||
|
||||
namespace webcc {
|
||||
@@ -12,10 +14,6 @@ class HttpRequest;
|
||||
std::ostream& operator<<(std::ostream& os, const HttpRequest& request);
|
||||
|
||||
class HttpRequest : public HttpMessage {
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os,
|
||||
const HttpRequest& request);
|
||||
|
||||
public:
|
||||
HttpRequest() = default;
|
||||
HttpRequest(const HttpRequest&) = default;
|
||||
@@ -47,8 +45,9 @@ public:
|
||||
return port_;
|
||||
}
|
||||
|
||||
// \param host Descriptive host name or numeric IP address.
|
||||
// \param port Numeric port number, "80" will be used if it's empty.
|
||||
// Set host name and port number.
|
||||
// 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);
|
||||
|
||||
// Compose start line, etc.
|
||||
@@ -64,6 +63,8 @@ public:
|
||||
std::string Dump() const;
|
||||
|
||||
private:
|
||||
friend std::ostream& operator<<(std::ostream& os, const HttpRequest& request);
|
||||
|
||||
// HTTP method.
|
||||
std::string method_;
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/common.h"
|
||||
#include "webcc/globals.h"
|
||||
#include "webcc/http_request.h"
|
||||
#include "webcc/http_response.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
|
||||
@@ -18,13 +18,8 @@ class HttpResponse;
|
||||
// The common handler for all incoming requests.
|
||||
class HttpRequestHandler {
|
||||
public:
|
||||
HttpRequestHandler(const HttpRequestHandler&) = delete;
|
||||
HttpRequestHandler& operator=(const HttpRequestHandler&) = delete;
|
||||
|
||||
HttpRequestHandler() = default;
|
||||
|
||||
virtual ~HttpRequestHandler() {
|
||||
}
|
||||
virtual ~HttpRequestHandler() = default;
|
||||
|
||||
// Put the session into the queue.
|
||||
void Enqueue(HttpSessionPtr session);
|
||||
@@ -41,9 +36,10 @@ private:
|
||||
// Called by the worker routine.
|
||||
virtual void HandleSession(HttpSessionPtr session) = 0;
|
||||
|
||||
private:
|
||||
Queue<HttpSessionPtr> queue_;
|
||||
boost::thread_group workers_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(HttpRequestHandler);
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
@@ -16,7 +16,6 @@ public:
|
||||
private:
|
||||
Error ParseStartLine(const std::string& line) override;
|
||||
|
||||
private:
|
||||
HttpRequest* request_;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "webcc/http_response.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#define WEBCC_HTTP_RESPONSE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "boost/asio/buffer.hpp" // for const_buffer
|
||||
|
||||
#include "webcc/http_message.h"
|
||||
|
||||
namespace webcc {
|
||||
@@ -12,9 +14,6 @@ class HttpResponse;
|
||||
std::ostream& operator<<(std::ostream& os, const HttpResponse& response);
|
||||
|
||||
class HttpResponse : public HttpMessage {
|
||||
friend std::ostream& operator<<(std::ostream& os,
|
||||
const HttpResponse& response);
|
||||
|
||||
public:
|
||||
HttpResponse() = default;
|
||||
~HttpResponse() override = default;
|
||||
@@ -39,6 +38,9 @@ public:
|
||||
static HttpResponse Fault(HttpStatus::Enum status);
|
||||
|
||||
private:
|
||||
friend std::ostream& operator<<(std::ostream& os,
|
||||
const HttpResponse& response);
|
||||
|
||||
int status_ = HttpStatus::kOK;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "webcc/http_response_parser.h"
|
||||
|
||||
#include "boost/lexical_cast.hpp"
|
||||
|
||||
#include "webcc/http_response.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "webcc/http_server.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <csignal>
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/http_request_handler.h"
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/soap_service.h"
|
||||
#include "webcc/utility.h"
|
||||
|
||||
|
||||
@@ -4,13 +4,12 @@
|
||||
#include <string>
|
||||
#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/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"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#include "boost/asio/write.hpp"
|
||||
#include "boost/date_time/posix_time/posix_time.hpp"
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/http_request_handler.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#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_parser.h"
|
||||
#include "webcc/http_response.h"
|
||||
|
||||
+3
-3
@@ -15,11 +15,11 @@ namespace webcc {
|
||||
template <typename T>
|
||||
class Queue {
|
||||
public:
|
||||
Queue(const Queue& rhs) = delete;
|
||||
Queue& operator=(const Queue& rhs) = delete;
|
||||
|
||||
Queue() = default;
|
||||
|
||||
Queue(const Queue&) = delete;
|
||||
Queue& operator=(const Queue&) = delete;
|
||||
|
||||
T PopOrWait() {
|
||||
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.
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webcc/http_request_handler.h"
|
||||
#include "webcc/http_server.h"
|
||||
#include "webcc/rest_request_handler.h"
|
||||
#include "webcc/rest_service.h"
|
||||
|
||||
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 {
|
||||
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.
|
||||
// The URL should start with "/" and could be a regular expression or not.
|
||||
// E.g., "/instances". "/instances/(\\d+)"
|
||||
// NOTE: Registering to the same URL multiple times is allowed, but only the
|
||||
// last one takes effect.
|
||||
bool RegisterService(RestServicePtr service, const std::string& url);
|
||||
|
||||
private:
|
||||
HttpRequestHandler* GetRequestHandler() override {
|
||||
return request_handler_;
|
||||
bool RegisterService(RestServicePtr service, const std::string& url) {
|
||||
return request_handler_.RegisterService(service, url);
|
||||
}
|
||||
|
||||
private:
|
||||
RestRequestHandler* request_handler_;
|
||||
HttpRequestHandler* GetRequestHandler() override {
|
||||
return &request_handler_;
|
||||
}
|
||||
|
||||
private:
|
||||
RestRequestHandler request_handler_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "webcc/rest_service.h"
|
||||
|
||||
#include "webcc/logger.h"
|
||||
|
||||
namespace webcc {
|
||||
@@ -29,7 +30,7 @@ bool RestDetailService::Handle(const std::string& http_method,
|
||||
const std::string& request_content,
|
||||
std::string* response_content) {
|
||||
if (http_method == kHttpGet) {
|
||||
return Get(url_sub_matches, response_content);
|
||||
return Get(url_sub_matches, query, response_content);
|
||||
}
|
||||
|
||||
if (http_method == kHttpPut) {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webcc/common.h"
|
||||
#include "webcc/globals.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
@@ -28,12 +28,9 @@ public:
|
||||
}
|
||||
|
||||
// Handle REST request, output the response.
|
||||
// \param http_method GET, POST, etc.
|
||||
// \param url_sub_matches The regex sub-matches in the URL,
|
||||
// usually resource ID.
|
||||
// \param query Query parameters in the URL.
|
||||
// \param request_content Request JSON.
|
||||
// \param response_content Output response JSON.
|
||||
// The regex sub-matches of the URL (usually resource IDs) were stored in
|
||||
// |url_sub_matches|. The |query| part of the URL is normally only for GET
|
||||
// request. Both the request and response contents are JSON strings.
|
||||
virtual bool Handle(const std::string& http_method,
|
||||
const std::vector<std::string>& url_sub_matches,
|
||||
const UrlQuery& query,
|
||||
@@ -79,6 +76,7 @@ class RestDetailService : public RestService {
|
||||
|
||||
protected:
|
||||
virtual bool Get(const std::vector<std::string>& url_sub_matches,
|
||||
const UrlQuery& query,
|
||||
std::string* response_content) {
|
||||
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 <vector>
|
||||
|
||||
#include "webcc/common.h"
|
||||
#include "webcc/globals.h"
|
||||
#include "webcc/soap_message.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
#include "webcc/soap_message.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "webcc/soap_xml.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
const SoapNamespace kSoapEnvNamespace{
|
||||
"soap",
|
||||
"http://schemas.xmlsoap.org/soap/envelope/"
|
||||
};
|
||||
|
||||
void SoapMessage::ToXml(std::string* xml_string) {
|
||||
assert(soapenv_ns_.IsValid() &&
|
||||
service_ns_.IsValid() &&
|
||||
|
||||
@@ -2,14 +2,33 @@
|
||||
#define WEBCC_SOAP_MESSAGE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "pugixml/pugixml.hpp"
|
||||
#include "webcc/common.h"
|
||||
|
||||
#include "webcc/globals.h"
|
||||
|
||||
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.
|
||||
class SoapMessage {
|
||||
public:
|
||||
virtual ~SoapMessage() {}
|
||||
|
||||
// E.g., set as kSoapEnvNamespace.
|
||||
void set_soapenv_ns(const SoapNamespace& soapenv_ns) {
|
||||
soapenv_ns_ = soapenv_ns;
|
||||
@@ -34,8 +53,6 @@ public:
|
||||
bool FromXml(const std::string& xml_string);
|
||||
|
||||
protected:
|
||||
SoapMessage() = default;
|
||||
|
||||
// Convert to SOAP body XML.
|
||||
virtual void ToXmlBody(pugi::xml_node xbody) = 0;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "webcc/soap_request.h"
|
||||
|
||||
#include "webcc/soap_xml.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define WEBCC_SOAP_REQUEST_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "webcc/soap_message.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "webcc/soap_server.h"
|
||||
#include "webcc/soap_request_handler.h"
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/soap_request.h"
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
namespace webcc {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool SoapRequestHandler::RegisterService(SoapServicePtr service,
|
||||
const std::string& url) {
|
||||
assert(service);
|
||||
@@ -55,20 +53,4 @@ SoapServicePtr SoapRequestHandler::GetServiceByUrl(const std::string& url) {
|
||||
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
|
||||
@@ -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.
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "webcc/http_request_handler.h"
|
||||
#include "webcc/soap_request_handler.h"
|
||||
#include "webcc/http_server.h"
|
||||
|
||||
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 {
|
||||
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);
|
||||
|
||||
private:
|
||||
HttpRequestHandler* GetRequestHandler() override {
|
||||
return request_handler_;
|
||||
bool RegisterService(SoapServicePtr service, const std::string& url) {
|
||||
return request_handler_.RegisterService(service, url);
|
||||
}
|
||||
|
||||
private:
|
||||
SoapRequestHandler* request_handler_;
|
||||
HttpRequestHandler* GetRequestHandler() override {
|
||||
return &request_handler_;
|
||||
}
|
||||
|
||||
SoapRequestHandler request_handler_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
#define WEBCC_SOAP_SERVICE_H_
|
||||
|
||||
#include <memory>
|
||||
#include "webcc/common.h"
|
||||
|
||||
#include "webcc/globals.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
@@ -12,8 +13,7 @@ class SoapResponse;
|
||||
// Base class for your SOAP service.
|
||||
class SoapService {
|
||||
public:
|
||||
virtual ~SoapService() {
|
||||
}
|
||||
virtual ~SoapService() = default;
|
||||
|
||||
// Handle SOAP request, output the response.
|
||||
virtual bool Handle(const SoapRequest& soap_request,
|
||||
|
||||
+8
-16
@@ -3,8 +3,7 @@
|
||||
namespace webcc {
|
||||
namespace soap_xml {
|
||||
|
||||
void SplitName(const pugi::xml_node& xnode,
|
||||
std::string* prefix,
|
||||
void SplitName(const pugi::xml_node& xnode, std::string* prefix,
|
||||
std::string* name) {
|
||||
std::string full_name = xnode.name();
|
||||
|
||||
@@ -39,14 +38,12 @@ std::string GetNameNoPrefix(const pugi::xml_node& xnode) {
|
||||
return name;
|
||||
}
|
||||
|
||||
pugi::xml_node AddChild(pugi::xml_node& xnode,
|
||||
const std::string& ns,
|
||||
pugi::xml_node AddChild(pugi::xml_node& xnode, const std::string& ns,
|
||||
const std::string& name) {
|
||||
return xnode.append_child((ns + ":" + name).c_str());
|
||||
}
|
||||
|
||||
pugi::xml_node GetChild(pugi::xml_node& xnode,
|
||||
const std::string& ns,
|
||||
pugi::xml_node GetChild(pugi::xml_node& xnode, const std::string& ns,
|
||||
const std::string& name) {
|
||||
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();
|
||||
}
|
||||
|
||||
void AddAttr(pugi::xml_node& xnode,
|
||||
const std::string& ns,
|
||||
const std::string& name,
|
||||
const std::string& value) {
|
||||
void AddAttr(pugi::xml_node& xnode, const std::string& ns,
|
||||
const std::string& name, const std::string& value) {
|
||||
std::string ns_name = ns + ":" + name;
|
||||
xnode.append_attribute(ns_name.c_str()) = value.c_str();
|
||||
}
|
||||
|
||||
void AddNSAttr(pugi::xml_node& xnode,
|
||||
const std::string& ns_name,
|
||||
void AddNSAttr(pugi::xml_node& xnode, const std::string& ns_name,
|
||||
const std::string& ns_url) {
|
||||
AddAttr(xnode, "xmlns", ns_name, ns_url);
|
||||
}
|
||||
|
||||
std::string GetNSAttr(pugi::xml_node& xnode,
|
||||
const std::string& ns_name) {
|
||||
std::string GetNSAttr(pugi::xml_node& xnode, const std::string& ns_name) {
|
||||
std::string attr_name = "xmlns:" + ns_name;
|
||||
return xnode.attribute(attr_name.c_str()).as_string();
|
||||
}
|
||||
|
||||
bool PrettyPrint(std::ostream& os,
|
||||
const std::string& xml_string,
|
||||
bool PrettyPrint(std::ostream& os, const std::string& xml_string,
|
||||
const char* indent) {
|
||||
pugi::xml_document xdoc;
|
||||
if (!xdoc.load_string(xml_string.c_str())) {
|
||||
|
||||
+14
-23
@@ -1,19 +1,19 @@
|
||||
#ifndef WEBCC_SOAP_XML_H_
|
||||
#define WEBCC_SOAP_XML_H_
|
||||
|
||||
// XML utilities.
|
||||
// XML helpers for SOAP messages.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "pugixml/pugixml.hpp"
|
||||
|
||||
namespace webcc {
|
||||
namespace soap_xml {
|
||||
|
||||
// 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".
|
||||
void SplitName(const pugi::xml_node& xnode,
|
||||
std::string* prefix = NULL,
|
||||
void SplitName(const pugi::xml_node& xnode, std::string* prefix = NULL,
|
||||
std::string* name = NULL);
|
||||
|
||||
// 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.
|
||||
// E.g., AppendChild(xnode, "soapenv", "Envelope") will append a child with
|
||||
// name "soapenv:Envelope".
|
||||
pugi::xml_node AddChild(pugi::xml_node& xnode,
|
||||
const std::string& ns,
|
||||
pugi::xml_node AddChild(pugi::xml_node& xnode, const std::string& ns,
|
||||
const std::string& name);
|
||||
|
||||
pugi::xml_node GetChild(pugi::xml_node& xnode,
|
||||
const std::string& ns,
|
||||
pugi::xml_node GetChild(pugi::xml_node& xnode, const std::string& ns,
|
||||
const std::string& name);
|
||||
|
||||
// TODO: Remove
|
||||
pugi::xml_node GetChildNoNS(pugi::xml_node& xnode,
|
||||
const std::string& name);
|
||||
pugi::xml_node GetChildNoNS(pugi::xml_node& xnode, const std::string& name);
|
||||
|
||||
// Add an attribute with the given name which is prefixed by a namespace.
|
||||
void AddAttr(pugi::xml_node& xnode,
|
||||
const std::string& ns,
|
||||
const std::string& name,
|
||||
const std::string& value);
|
||||
void AddAttr(pugi::xml_node& xnode, const std::string& ns,
|
||||
const std::string& name, const std::string& value);
|
||||
|
||||
// Append "xmlns" attribute.
|
||||
// E.g., if the namespace is
|
||||
// { "soapenv", "http://schemas.xmlsoap.org/soap/envelope/" }
|
||||
// the attribute added will be
|
||||
// xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||
void AddNSAttr(pugi::xml_node& xnode,
|
||||
const std::string& ns_name,
|
||||
void AddNSAttr(pugi::xml_node& xnode, const std::string& ns_name,
|
||||
const std::string& ns_url);
|
||||
|
||||
// Get namespace attribute value.
|
||||
// E.g., if the given namespace name is "soapenv", the value of
|
||||
// attribute "xmlns:soapenv" will be returned.
|
||||
std::string GetNSAttr(pugi::xml_node& xnode,
|
||||
const std::string& ns_name);
|
||||
std::string GetNSAttr(pugi::xml_node& xnode, const std::string& ns_name);
|
||||
|
||||
// An XML writer writing to a referenced string.
|
||||
// Example:
|
||||
@@ -68,12 +61,11 @@ std::string GetNSAttr(pugi::xml_node& xnode,
|
||||
// xdoc.save(writer, "\t", pugi::format_default, pugi::encoding_utf8);
|
||||
class XmlStrRefWriter : public pugi::xml_writer {
|
||||
public:
|
||||
explicit XmlStrRefWriter(std::string* result)
|
||||
: result_(result) {
|
||||
explicit XmlStrRefWriter(std::string* result) : result_(result) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -82,8 +74,7 @@ private:
|
||||
};
|
||||
|
||||
// Print the XML string to output stream in pretty format.
|
||||
bool PrettyPrint(std::ostream& os,
|
||||
const std::string& xml_string,
|
||||
bool PrettyPrint(std::ostream& os, const std::string& xml_string,
|
||||
const char* indent = "\t");
|
||||
|
||||
} // namespace soap_xml
|
||||
|
||||
+105
-10
@@ -6,13 +6,78 @@
|
||||
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) {
|
||||
if (!HasKey(key)) {
|
||||
if (!Has(key)) {
|
||||
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) {
|
||||
auto it = Find(key);
|
||||
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 {
|
||||
static const std::string kEmptyValue;
|
||||
|
||||
const std::string& UrlQuery::Get(const std::string& key) const {
|
||||
auto it = Find(key);
|
||||
if (it != parameters_.end()) {
|
||||
return it->value();
|
||||
}
|
||||
|
||||
static const std::string 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 {
|
||||
return std::find_if(parameters_.begin(),
|
||||
parameters_.end(),
|
||||
@@ -38,13 +118,18 @@ UrlQuery::ConstIterator UrlQuery::Find(const std::string& key) const {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Url::Url(const std::string& str) {
|
||||
std::size_t pos = str.find('?');
|
||||
if (pos == std::string::npos) {
|
||||
path_ = str;
|
||||
Url::Url(const std::string& str, bool decode) {
|
||||
if (!decode || str.find('%') == std::string::npos) {
|
||||
Init(str);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string decoded;
|
||||
if (Decode(str, &decoded)) {
|
||||
Init(decoded);
|
||||
} else {
|
||||
path_ = str.substr(0, pos);
|
||||
query_ = str.substr(pos + 1);
|
||||
// TODO: Exception?
|
||||
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
|
||||
|
||||
+26
-8
@@ -11,27 +11,44 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webcc/common.h"
|
||||
#include "webcc/globals.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// URL query parameters.
|
||||
class UrlQuery {
|
||||
public:
|
||||
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 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();
|
||||
}
|
||||
|
||||
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:
|
||||
typedef Parameters::const_iterator ConstIterator;
|
||||
ConstIterator Find(const std::string& key) const;
|
||||
@@ -40,13 +57,12 @@ private:
|
||||
Parameters parameters_;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class Url {
|
||||
public:
|
||||
typedef std::map<std::string, std::string> Query;
|
||||
|
||||
Url(const std::string& str);
|
||||
Url() = default;
|
||||
Url(const std::string& str, bool decode);
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
@@ -73,6 +89,8 @@ public:
|
||||
static void SplitQuery(const std::string& str, UrlQuery* query);
|
||||
|
||||
private:
|
||||
void Init(const std::string& str);
|
||||
|
||||
std::string path_;
|
||||
std::string query_;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "webcc/utility.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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"
|
||||
|
||||
using namespace webcc;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class TestRestService : public RestService {
|
||||
public:
|
||||
bool Handle(const std::string& http_method,
|
||||
@@ -16,8 +14,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TEST(RestServiceManager, URL_RegexBasic) {
|
||||
RestServiceManager service_manager;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user