Refactoring according Google C++ Style.
parent
189a246221
commit
1bab2da8f4
@ -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_
|
@ -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
|
@ -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_
|
@ -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_
|
Loading…
Reference in New Issue