Remove http prefix
parent
2ea04b9705
commit
e621025cc3
@ -0,0 +1,82 @@
|
|||||||
|
#ifndef WEBCC_CONNECTION_H_
|
||||||
|
#define WEBCC_CONNECTION_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "boost/asio/ip/tcp.hpp" // for ip::tcp::socket
|
||||||
|
|
||||||
|
#include "webcc/globals.h"
|
||||||
|
#include "webcc/request.h"
|
||||||
|
#include "webcc/request_parser.h"
|
||||||
|
#include "webcc/response.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class Connection;
|
||||||
|
class ConnectionPool;
|
||||||
|
class RequestHandler;
|
||||||
|
|
||||||
|
using ConnectionPtr = std::shared_ptr<Connection>;
|
||||||
|
|
||||||
|
class Connection : public std::enable_shared_from_this<Connection> {
|
||||||
|
public:
|
||||||
|
Connection(boost::asio::ip::tcp::socket socket, ConnectionPool* pool,
|
||||||
|
RequestHandler* handler);
|
||||||
|
|
||||||
|
~Connection() = default;
|
||||||
|
|
||||||
|
Connection(const Connection&) = delete;
|
||||||
|
Connection& operator=(const Connection&) = delete;
|
||||||
|
|
||||||
|
RequestPtr request() const {
|
||||||
|
return request_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start to read and process the client request.
|
||||||
|
void Start();
|
||||||
|
|
||||||
|
// Close the socket.
|
||||||
|
void Close();
|
||||||
|
|
||||||
|
// Send response to client.
|
||||||
|
void SendResponse(ResponsePtr response);
|
||||||
|
|
||||||
|
void SendResponse(Status status);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void DoRead();
|
||||||
|
void OnRead(boost::system::error_code ec, std::size_t length);
|
||||||
|
|
||||||
|
void DoWrite();
|
||||||
|
void OnWrite(boost::system::error_code ec, std::size_t length);
|
||||||
|
|
||||||
|
// Shutdown the socket.
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
// Socket for the connection.
|
||||||
|
boost::asio::ip::tcp::socket socket_;
|
||||||
|
|
||||||
|
// The pool for this connection.
|
||||||
|
ConnectionPool* pool_;
|
||||||
|
|
||||||
|
// Buffer for incoming data.
|
||||||
|
std::vector<char> buffer_;
|
||||||
|
|
||||||
|
// The handler used to process the incoming request.
|
||||||
|
RequestHandler* request_handler_;
|
||||||
|
|
||||||
|
// The incoming request.
|
||||||
|
RequestPtr request_;
|
||||||
|
|
||||||
|
// The parser for the incoming request.
|
||||||
|
RequestParser request_parser_;
|
||||||
|
|
||||||
|
// The response to be sent back to the client.
|
||||||
|
ResponsePtr response_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_CONNECTION_H_
|
@ -1,25 +1,25 @@
|
|||||||
#include "webcc/http_connection_pool.h"
|
#include "webcc/connection_pool.h"
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
HttpConnectionPool::HttpConnectionPool() {
|
ConnectionPool::ConnectionPool() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpConnectionPool::Start(HttpConnectionPtr c) {
|
void ConnectionPool::Start(ConnectionPtr c) {
|
||||||
LOG_VERB("Starting connection...");
|
LOG_VERB("Starting connection...");
|
||||||
connections_.insert(c);
|
connections_.insert(c);
|
||||||
c->Start();
|
c->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpConnectionPool::Close(HttpConnectionPtr c) {
|
void ConnectionPool::Close(ConnectionPtr c) {
|
||||||
LOG_VERB("Closing connection...");
|
LOG_VERB("Closing connection...");
|
||||||
connections_.erase(c);
|
connections_.erase(c);
|
||||||
c->Close();
|
c->Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpConnectionPool::CloseAll() {
|
void ConnectionPool::CloseAll() {
|
||||||
LOG_VERB("Closing all (%u) connections...", connections_.size());
|
LOG_VERB("Closing all (%u) connections...", connections_.size());
|
||||||
for (auto& c : connections_) {
|
for (auto& c : connections_) {
|
||||||
c->Close();
|
c->Close();
|
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef WEBCC_CONNECTION_POOL_H_
|
||||||
|
#define WEBCC_CONNECTION_POOL_H_
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "webcc/connection.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class ConnectionPool {
|
||||||
|
public:
|
||||||
|
ConnectionPool(const ConnectionPool&) = delete;
|
||||||
|
ConnectionPool& operator=(const ConnectionPool&) = delete;
|
||||||
|
|
||||||
|
ConnectionPool();
|
||||||
|
|
||||||
|
// Add a connection to the pool and start it.
|
||||||
|
void Start(ConnectionPtr c);
|
||||||
|
|
||||||
|
// Close a connection.
|
||||||
|
void Close(ConnectionPtr c);
|
||||||
|
|
||||||
|
// Close all connections.
|
||||||
|
void CloseAll();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// The managed connections.
|
||||||
|
std::set<ConnectionPtr> connections_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_CONNECTION_POOL_H_
|
@ -1,82 +0,0 @@
|
|||||||
#ifndef WEBCC_HTTP_CONNECTION_H_
|
|
||||||
#define WEBCC_HTTP_CONNECTION_H_
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "boost/asio/ip/tcp.hpp" // for ip::tcp::socket
|
|
||||||
|
|
||||||
#include "webcc/globals.h"
|
|
||||||
#include "webcc/http_request.h"
|
|
||||||
#include "webcc/http_request_parser.h"
|
|
||||||
#include "webcc/http_response.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
class HttpConnection;
|
|
||||||
class HttpConnectionPool;
|
|
||||||
class HttpRequestHandler;
|
|
||||||
|
|
||||||
typedef std::shared_ptr<HttpConnection> HttpConnectionPtr;
|
|
||||||
|
|
||||||
class HttpConnection : public std::enable_shared_from_this<HttpConnection> {
|
|
||||||
public:
|
|
||||||
HttpConnection(boost::asio::ip::tcp::socket socket, HttpConnectionPool* pool,
|
|
||||||
HttpRequestHandler* handler);
|
|
||||||
|
|
||||||
~HttpConnection() = default;
|
|
||||||
|
|
||||||
HttpConnection(const HttpConnection&) = delete;
|
|
||||||
HttpConnection& operator=(const HttpConnection&) = delete;
|
|
||||||
|
|
||||||
HttpRequestPtr request() const {
|
|
||||||
return request_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start to read and process the client request.
|
|
||||||
void Start();
|
|
||||||
|
|
||||||
// Close the socket.
|
|
||||||
void Close();
|
|
||||||
|
|
||||||
// Send response to client.
|
|
||||||
void SendResponse(HttpResponsePtr response);
|
|
||||||
|
|
||||||
void SendResponse(http::Status status);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void DoRead();
|
|
||||||
void OnRead(boost::system::error_code ec, std::size_t length);
|
|
||||||
|
|
||||||
void DoWrite();
|
|
||||||
void OnWrite(boost::system::error_code ec, std::size_t length);
|
|
||||||
|
|
||||||
// Shutdown the socket.
|
|
||||||
void Shutdown();
|
|
||||||
|
|
||||||
// Socket for the connection.
|
|
||||||
boost::asio::ip::tcp::socket socket_;
|
|
||||||
|
|
||||||
// The pool for this connection.
|
|
||||||
HttpConnectionPool* pool_;
|
|
||||||
|
|
||||||
// Buffer for incoming data.
|
|
||||||
std::vector<char> buffer_;
|
|
||||||
|
|
||||||
// The handler used to process the incoming request.
|
|
||||||
HttpRequestHandler* request_handler_;
|
|
||||||
|
|
||||||
// The incoming request.
|
|
||||||
HttpRequestPtr request_;
|
|
||||||
|
|
||||||
// The parser for the incoming request.
|
|
||||||
HttpRequestParser request_parser_;
|
|
||||||
|
|
||||||
// The response to be sent back to the client.
|
|
||||||
HttpResponsePtr response_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_HTTP_CONNECTION_H_
|
|
@ -1,33 +0,0 @@
|
|||||||
#ifndef WEBCC_HTTP_CONNECTION_POOL_H_
|
|
||||||
#define WEBCC_HTTP_CONNECTION_POOL_H_
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "webcc/http_connection.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
class HttpConnectionPool {
|
|
||||||
public:
|
|
||||||
HttpConnectionPool(const HttpConnectionPool&) = delete;
|
|
||||||
HttpConnectionPool& operator=(const HttpConnectionPool&) = delete;
|
|
||||||
|
|
||||||
HttpConnectionPool();
|
|
||||||
|
|
||||||
// Add a connection to the pool and start it.
|
|
||||||
void Start(HttpConnectionPtr c);
|
|
||||||
|
|
||||||
// Close a connection.
|
|
||||||
void Close(HttpConnectionPtr c);
|
|
||||||
|
|
||||||
// Close all connections.
|
|
||||||
void CloseAll();
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// The managed connections.
|
|
||||||
std::set<HttpConnectionPtr> connections_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_HTTP_CONNECTION_POOL_H_
|
|
@ -1,92 +0,0 @@
|
|||||||
#include "webcc/http_request_builder.h"
|
|
||||||
|
|
||||||
#include "webcc/base64.h"
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
#include "webcc/utility.h"
|
|
||||||
#include "webcc/zlib_wrapper.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
HttpRequestPtr HttpRequestBuilder::Build() {
|
|
||||||
assert(parameters_.size() % 2 == 0);
|
|
||||||
assert(headers_.size() % 2 == 0);
|
|
||||||
|
|
||||||
auto request = std::make_shared<HttpRequest>(method_, url_);
|
|
||||||
|
|
||||||
for (std::size_t i = 1; i < parameters_.size(); i += 2) {
|
|
||||||
request->AddQuery(parameters_[i - 1], parameters_[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::size_t i = 1; i < headers_.size(); i += 2) {
|
|
||||||
request->SetHeader(std::move(headers_[i - 1]), std::move(headers_[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// No keep-alive?
|
|
||||||
if (!keep_alive_) {
|
|
||||||
request->SetHeader(http::headers::kConnection, "Close");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data_.empty()) {
|
|
||||||
SetContent(request, std::move(data_));
|
|
||||||
|
|
||||||
// TODO: Request-level charset.
|
|
||||||
if (json_) {
|
|
||||||
request->SetContentType(http::media_types::kApplicationJson, "");
|
|
||||||
}
|
|
||||||
} else if (!form_parts_.empty()) {
|
|
||||||
request->set_form_parts(std::move(form_parts_));
|
|
||||||
}
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& HttpRequestBuilder::File(const std::string& name,
|
|
||||||
const Path& path,
|
|
||||||
const std::string& mime_type) {
|
|
||||||
assert(!name.empty());
|
|
||||||
form_parts_.push_back(FormPart{ name, path, mime_type });
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& HttpRequestBuilder::Form(const std::string& name,
|
|
||||||
std::string&& data,
|
|
||||||
const std::string& mime_type) {
|
|
||||||
assert(!name.empty());
|
|
||||||
form_parts_.push_back(FormPart{ name, std::move(data), mime_type });
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& HttpRequestBuilder::Auth(const std::string& type,
|
|
||||||
const std::string& credentials) {
|
|
||||||
headers_.push_back(http::headers::kAuthorization);
|
|
||||||
headers_.push_back(type + " " + credentials);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& HttpRequestBuilder::AuthBasic(const std::string& login,
|
|
||||||
const std::string& password) {
|
|
||||||
auto credentials = Base64Encode(login + ":" + password);
|
|
||||||
return Auth("Basic", credentials);
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& HttpRequestBuilder::AuthToken(const std::string& token) {
|
|
||||||
return Auth("Token", token);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpRequestBuilder::SetContent(HttpRequestPtr request,
|
|
||||||
std::string&& data) {
|
|
||||||
if (gzip_ && data.size() > kGzipThreshold) {
|
|
||||||
std::string compressed;
|
|
||||||
if (Compress(data, &compressed)) {
|
|
||||||
request->SetContent(std::move(compressed), true);
|
|
||||||
request->SetHeader(http::headers::kContentEncoding, "gzip");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_WARN("Cannot compress the content data!");
|
|
||||||
}
|
|
||||||
|
|
||||||
request->SetContent(std::move(data), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
@ -1,144 +0,0 @@
|
|||||||
#ifndef WEBCC_HTTP_REQUEST_BUILDER_H_
|
|
||||||
#define WEBCC_HTTP_REQUEST_BUILDER_H_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "webcc/http_request.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
class HttpRequestBuilder {
|
|
||||||
public:
|
|
||||||
explicit HttpRequestBuilder(const std::string& method = "")
|
|
||||||
: method_(method) {
|
|
||||||
}
|
|
||||||
|
|
||||||
~HttpRequestBuilder() = default;
|
|
||||||
|
|
||||||
HttpRequestBuilder(const HttpRequestBuilder&) = delete;
|
|
||||||
HttpRequestBuilder& operator=(const HttpRequestBuilder&) = delete;
|
|
||||||
|
|
||||||
// Build the request.
|
|
||||||
HttpRequestPtr Build();
|
|
||||||
|
|
||||||
HttpRequestPtr operator()() {
|
|
||||||
return Build();
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& Get() { return Method(http::methods::kGet); }
|
|
||||||
HttpRequestBuilder& Head() { return Method(http::methods::kHead); }
|
|
||||||
HttpRequestBuilder& Post() { return Method(http::methods::kPost); }
|
|
||||||
HttpRequestBuilder& Put() { return Method(http::methods::kPut); }
|
|
||||||
HttpRequestBuilder& Delete() { return Method(http::methods::kDelete); }
|
|
||||||
HttpRequestBuilder& Patch() { return Method(http::methods::kPatch); }
|
|
||||||
|
|
||||||
// NOTE:
|
|
||||||
// The naming convention doesn't follow Google C++ Style for
|
|
||||||
// consistency and simplicity.
|
|
||||||
|
|
||||||
HttpRequestBuilder& Method(const std::string& method) {
|
|
||||||
method_ = method;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& Url(const std::string& url) {
|
|
||||||
url_ = url;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& Query(const std::string& key,
|
|
||||||
const std::string& value) {
|
|
||||||
parameters_.push_back(key);
|
|
||||||
parameters_.push_back(value);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& Data(const std::string& data) {
|
|
||||||
data_ = data;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& Data(std::string&& data) {
|
|
||||||
data_ = std::move(data);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& Json(bool json = true) {
|
|
||||||
json_ = json;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload a file.
|
|
||||||
HttpRequestBuilder& File(const std::string& name, const Path& path,
|
|
||||||
const std::string& mime_type = "");
|
|
||||||
|
|
||||||
HttpRequestBuilder& Form(FormPart&& part) {
|
|
||||||
form_parts_.push_back(std::move(part));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& Form(const std::string& name, std::string&& data,
|
|
||||||
const std::string& mime_type = "");
|
|
||||||
|
|
||||||
HttpRequestBuilder& Gzip(bool gzip = true) {
|
|
||||||
gzip_ = gzip;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& Header(const std::string& key,
|
|
||||||
const std::string& value) {
|
|
||||||
headers_.push_back(key);
|
|
||||||
headers_.push_back(value);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& KeepAlive(bool keep_alive = true) {
|
|
||||||
keep_alive_ = keep_alive;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& Auth(const std::string& type,
|
|
||||||
const std::string& credentials);
|
|
||||||
|
|
||||||
HttpRequestBuilder& AuthBasic(const std::string& login,
|
|
||||||
const std::string& password);
|
|
||||||
|
|
||||||
HttpRequestBuilder& AuthToken(const std::string& token);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void SetContent(HttpRequestPtr request, std::string&& data);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string method_;
|
|
||||||
|
|
||||||
std::string url_;
|
|
||||||
|
|
||||||
// URL query parameters.
|
|
||||||
std::vector<std::string> parameters_;
|
|
||||||
|
|
||||||
// Data to send in the body of the request.
|
|
||||||
std::string data_;
|
|
||||||
|
|
||||||
// Is the data to send a JSON string?
|
|
||||||
bool json_ = false;
|
|
||||||
|
|
||||||
// Files to upload for a POST request.
|
|
||||||
std::vector<FormPart> form_parts_;
|
|
||||||
|
|
||||||
// Compress the request content.
|
|
||||||
// NOTE: Most servers don't support compressed requests.
|
|
||||||
// Even the requests module from Python doesn't have a built-in support.
|
|
||||||
// See: https://github.com/kennethreitz/requests/issues/1753
|
|
||||||
bool gzip_ = false;
|
|
||||||
|
|
||||||
// Additional request headers.
|
|
||||||
std::vector<std::string> headers_;
|
|
||||||
|
|
||||||
// Persistent connection.
|
|
||||||
bool keep_alive_ = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_HTTP_REQUEST_BUILDER_H_
|
|
@ -1,48 +0,0 @@
|
|||||||
#ifndef WEBCC_HTTP_REQUEST_HANDLER_H_
|
|
||||||
#define WEBCC_HTTP_REQUEST_HANDLER_H_
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "webcc/http_connection.h"
|
|
||||||
#include "webcc/queue.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
class HttpRequest;
|
|
||||||
class HttpResponse;
|
|
||||||
|
|
||||||
// The common handler for all incoming requests.
|
|
||||||
class HttpRequestHandler {
|
|
||||||
public:
|
|
||||||
HttpRequestHandler() = default;
|
|
||||||
virtual ~HttpRequestHandler() = default;
|
|
||||||
|
|
||||||
HttpRequestHandler(const HttpRequestHandler&) = delete;
|
|
||||||
HttpRequestHandler& operator=(const HttpRequestHandler&) = delete;
|
|
||||||
|
|
||||||
// Put the connection into the queue.
|
|
||||||
void Enqueue(HttpConnectionPtr connection);
|
|
||||||
|
|
||||||
// Start worker threads.
|
|
||||||
void Start(std::size_t count);
|
|
||||||
|
|
||||||
// Clear pending connections from the queue and stop worker threads.
|
|
||||||
void Stop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void WorkerRoutine();
|
|
||||||
|
|
||||||
// Called by the worker routine.
|
|
||||||
virtual void HandleConnection(HttpConnectionPtr connection) = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Queue<HttpConnectionPtr> queue_;
|
|
||||||
|
|
||||||
std::vector<std::thread> workers_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_HTTP_REQUEST_HANDLER_H_
|
|
@ -1,39 +0,0 @@
|
|||||||
#ifndef WEBCC_HTTP_RESPONSE_H_
|
|
||||||
#define WEBCC_HTTP_RESPONSE_H_
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "webcc/http_message.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
class HttpResponse;
|
|
||||||
typedef std::shared_ptr<HttpResponse> HttpResponsePtr;
|
|
||||||
|
|
||||||
class HttpResponse : public HttpMessage {
|
|
||||||
public:
|
|
||||||
explicit HttpResponse(http::Status status = http::Status::kOK)
|
|
||||||
: status_(status) {
|
|
||||||
}
|
|
||||||
|
|
||||||
~HttpResponse() override = default;
|
|
||||||
|
|
||||||
int status() const {
|
|
||||||
return status_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_status(int status) {
|
|
||||||
status_ = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set start line according to status code.
|
|
||||||
void Prepare() final;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int status_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_HTTP_RESPONSE_H_
|
|
@ -1,30 +0,0 @@
|
|||||||
#ifndef WEBCC_HTTP_RESPONSE_PARSER_H_
|
|
||||||
#define WEBCC_HTTP_RESPONSE_PARSER_H_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "webcc/http_parser.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
class HttpResponse;
|
|
||||||
|
|
||||||
class HttpResponseParser : public HttpParser {
|
|
||||||
public:
|
|
||||||
explicit HttpResponseParser(HttpResponse* response = nullptr);
|
|
||||||
|
|
||||||
~HttpResponseParser() override = default;
|
|
||||||
|
|
||||||
void Init(HttpResponse* response);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Parse HTTP start line; E.g., "HTTP/1.1 200 OK".
|
|
||||||
bool ParseStartLine(const std::string& line) final;
|
|
||||||
|
|
||||||
// The result response message.
|
|
||||||
HttpResponse* response_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_HTTP_RESPONSE_PARSER_H_
|
|
@ -0,0 +1,91 @@
|
|||||||
|
#include "webcc/request_builder.h"
|
||||||
|
|
||||||
|
#include "webcc/base64.h"
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
#include "webcc/utility.h"
|
||||||
|
#include "webcc/zlib_wrapper.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
RequestPtr RequestBuilder::Build() {
|
||||||
|
assert(parameters_.size() % 2 == 0);
|
||||||
|
assert(headers_.size() % 2 == 0);
|
||||||
|
|
||||||
|
auto request = std::make_shared<Request>(method_, url_);
|
||||||
|
|
||||||
|
for (std::size_t i = 1; i < parameters_.size(); i += 2) {
|
||||||
|
request->AddQuery(parameters_[i - 1], parameters_[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 1; i < headers_.size(); i += 2) {
|
||||||
|
request->SetHeader(std::move(headers_[i - 1]), std::move(headers_[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// No keep-alive?
|
||||||
|
if (!keep_alive_) {
|
||||||
|
request->SetHeader(headers::kConnection, "Close");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data_.empty()) {
|
||||||
|
SetContent(request, std::move(data_));
|
||||||
|
|
||||||
|
// TODO: Request-level charset.
|
||||||
|
if (json_) {
|
||||||
|
request->SetContentType(media_types::kApplicationJson, "");
|
||||||
|
}
|
||||||
|
} else if (!form_parts_.empty()) {
|
||||||
|
request->set_form_parts(std::move(form_parts_));
|
||||||
|
}
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& RequestBuilder::File(const std::string& name,
|
||||||
|
const Path& path,
|
||||||
|
const std::string& media_type) {
|
||||||
|
assert(!name.empty());
|
||||||
|
form_parts_.push_back(FormPart{ name, path, media_type });
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& RequestBuilder::Form(const std::string& name,
|
||||||
|
std::string&& data,
|
||||||
|
const std::string& media_type) {
|
||||||
|
assert(!name.empty());
|
||||||
|
form_parts_.push_back(FormPart{ name, std::move(data), media_type });
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& RequestBuilder::Auth(const std::string& type,
|
||||||
|
const std::string& credentials) {
|
||||||
|
headers_.push_back(headers::kAuthorization);
|
||||||
|
headers_.push_back(type + " " + credentials);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& RequestBuilder::AuthBasic(const std::string& login,
|
||||||
|
const std::string& password) {
|
||||||
|
auto credentials = Base64Encode(login + ":" + password);
|
||||||
|
return Auth("Basic", credentials);
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& RequestBuilder::AuthToken(const std::string& token) {
|
||||||
|
return Auth("Token", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestBuilder::SetContent(RequestPtr request, std::string&& data) {
|
||||||
|
if (gzip_ && data.size() > kGzipThreshold) {
|
||||||
|
std::string compressed;
|
||||||
|
if (Compress(data, &compressed)) {
|
||||||
|
request->SetContent(std::move(compressed), true);
|
||||||
|
request->SetHeader(headers::kContentEncoding, "gzip");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_WARN("Cannot compress the content data!");
|
||||||
|
}
|
||||||
|
|
||||||
|
request->SetContent(std::move(data), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webcc
|
@ -0,0 +1,141 @@
|
|||||||
|
#ifndef WEBCC_REQUEST_BUILDER_H_
|
||||||
|
#define WEBCC_REQUEST_BUILDER_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webcc/request.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class RequestBuilder {
|
||||||
|
public:
|
||||||
|
explicit RequestBuilder(const std::string& method = "")
|
||||||
|
: method_(method) {
|
||||||
|
}
|
||||||
|
|
||||||
|
~RequestBuilder() = default;
|
||||||
|
|
||||||
|
RequestBuilder(const RequestBuilder&) = delete;
|
||||||
|
RequestBuilder& operator=(const RequestBuilder&) = delete;
|
||||||
|
|
||||||
|
// Build the request.
|
||||||
|
RequestPtr Build();
|
||||||
|
|
||||||
|
RequestPtr operator()() {
|
||||||
|
return Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& Get() { return Method(methods::kGet); }
|
||||||
|
RequestBuilder& Head() { return Method(methods::kHead); }
|
||||||
|
RequestBuilder& Post() { return Method(methods::kPost); }
|
||||||
|
RequestBuilder& Put() { return Method(methods::kPut); }
|
||||||
|
RequestBuilder& Delete() { return Method(methods::kDelete); }
|
||||||
|
RequestBuilder& Patch() { return Method(methods::kPatch); }
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// The naming convention doesn't follow Google C++ Style for
|
||||||
|
// consistency and simplicity.
|
||||||
|
|
||||||
|
RequestBuilder& Method(const std::string& method) {
|
||||||
|
method_ = method;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& Url(const std::string& url) {
|
||||||
|
url_ = url;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& Query(const std::string& key, const std::string& value) {
|
||||||
|
parameters_.push_back(key);
|
||||||
|
parameters_.push_back(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& Data(const std::string& data) {
|
||||||
|
data_ = data;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& Data(std::string&& data) {
|
||||||
|
data_ = std::move(data);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& Json(bool json = true) {
|
||||||
|
json_ = json;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload a file.
|
||||||
|
RequestBuilder& File(const std::string& name, const Path& path,
|
||||||
|
const std::string& media_type = "");
|
||||||
|
|
||||||
|
RequestBuilder& Form(FormPart&& part) {
|
||||||
|
form_parts_.push_back(std::move(part));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& Form(const std::string& name, std::string&& data,
|
||||||
|
const std::string& media_type = "");
|
||||||
|
|
||||||
|
RequestBuilder& Gzip(bool gzip = true) {
|
||||||
|
gzip_ = gzip;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& Header(const std::string& key, const std::string& value) {
|
||||||
|
headers_.push_back(key);
|
||||||
|
headers_.push_back(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& KeepAlive(bool keep_alive = true) {
|
||||||
|
keep_alive_ = keep_alive;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBuilder& Auth(const std::string& type, const std::string& credentials);
|
||||||
|
|
||||||
|
RequestBuilder& AuthBasic(const std::string& login,
|
||||||
|
const std::string& password);
|
||||||
|
|
||||||
|
RequestBuilder& AuthToken(const std::string& token);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetContent(RequestPtr request, std::string&& data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string method_;
|
||||||
|
|
||||||
|
std::string url_;
|
||||||
|
|
||||||
|
// URL query parameters.
|
||||||
|
std::vector<std::string> parameters_;
|
||||||
|
|
||||||
|
// Data to send in the body of the request.
|
||||||
|
std::string data_;
|
||||||
|
|
||||||
|
// Is the data to send a JSON string?
|
||||||
|
bool json_ = false;
|
||||||
|
|
||||||
|
// Files to upload for a POST request.
|
||||||
|
std::vector<FormPart> form_parts_;
|
||||||
|
|
||||||
|
// Compress the request content.
|
||||||
|
// NOTE: Most servers don't support compressed requests.
|
||||||
|
// Even the requests module from Python doesn't have a built-in support.
|
||||||
|
// See: https://github.com/kennethreitz/requests/issues/1753
|
||||||
|
bool gzip_ = false;
|
||||||
|
|
||||||
|
// Additional request headers.
|
||||||
|
std::vector<std::string> headers_;
|
||||||
|
|
||||||
|
// Persistent connection.
|
||||||
|
bool keep_alive_ = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_REQUEST_BUILDER_H_
|
@ -0,0 +1,48 @@
|
|||||||
|
#ifndef WEBCC_REQUEST_HANDLER_H_
|
||||||
|
#define WEBCC_REQUEST_HANDLER_H_
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webcc/connection.h"
|
||||||
|
#include "webcc/queue.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class Request;
|
||||||
|
class Response;
|
||||||
|
|
||||||
|
// The common handler for all incoming requests.
|
||||||
|
class RequestHandler {
|
||||||
|
public:
|
||||||
|
RequestHandler() = default;
|
||||||
|
virtual ~RequestHandler() = default;
|
||||||
|
|
||||||
|
RequestHandler(const RequestHandler&) = delete;
|
||||||
|
RequestHandler& operator=(const RequestHandler&) = delete;
|
||||||
|
|
||||||
|
// Put the connection into the queue.
|
||||||
|
void Enqueue(ConnectionPtr connection);
|
||||||
|
|
||||||
|
// Start worker threads.
|
||||||
|
void Start(std::size_t count);
|
||||||
|
|
||||||
|
// Clear pending connections from the queue and stop worker threads.
|
||||||
|
void Stop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void WorkerRoutine();
|
||||||
|
|
||||||
|
// Called by the worker routine.
|
||||||
|
virtual void HandleConnection(ConnectionPtr connection) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Queue<ConnectionPtr> queue_;
|
||||||
|
|
||||||
|
std::vector<std::thread> workers_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_REQUEST_HANDLER_H_
|
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef WEBCC_RESPONSE_H_
|
||||||
|
#define WEBCC_RESPONSE_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "webcc/message.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class Response;
|
||||||
|
using ResponsePtr = std::shared_ptr<Response>;
|
||||||
|
|
||||||
|
class Response : public Message {
|
||||||
|
public:
|
||||||
|
explicit Response(Status status = Status::kOK)
|
||||||
|
: status_(status) {
|
||||||
|
}
|
||||||
|
|
||||||
|
~Response() override = default;
|
||||||
|
|
||||||
|
int status() const {
|
||||||
|
return status_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_status(int status) {
|
||||||
|
status_ = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set start line according to status code.
|
||||||
|
void Prepare() final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int status_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_RESPONSE_H_
|
@ -1,22 +1,22 @@
|
|||||||
#include "webcc/http_response_parser.h"
|
#include "webcc/response_parser.h"
|
||||||
|
|
||||||
#include "boost/algorithm/string.hpp"
|
#include "boost/algorithm/string.hpp"
|
||||||
|
|
||||||
#include "webcc/http_response.h"
|
#include "webcc/response.h"
|
||||||
#include "webcc/logger.h"
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
HttpResponseParser::HttpResponseParser(HttpResponse* response)
|
ResponseParser::ResponseParser(Response* response)
|
||||||
: HttpParser(response), response_(response) {
|
: Parser(response), response_(response) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpResponseParser::Init(HttpResponse* response) {
|
void ResponseParser::Init(Response* response) {
|
||||||
HttpParser::Init(response);
|
Parser::Init(response);
|
||||||
response_ = response;
|
response_ = response;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpResponseParser::ParseStartLine(const std::string& line) {
|
bool ResponseParser::ParseStartLine(const std::string& line) {
|
||||||
std::vector<std::string> parts;
|
std::vector<std::string> parts;
|
||||||
boost::split(parts, line, boost::is_any_of(" "), boost::token_compress_on);
|
boost::split(parts, line, boost::is_any_of(" "), boost::token_compress_on);
|
||||||
|
|
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef WEBCC_RESPONSE_PARSER_H_
|
||||||
|
#define WEBCC_RESPONSE_PARSER_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "webcc/parser.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class Response;
|
||||||
|
|
||||||
|
class ResponseParser : public Parser {
|
||||||
|
public:
|
||||||
|
explicit ResponseParser(Response* response = nullptr);
|
||||||
|
|
||||||
|
~ResponseParser() override = default;
|
||||||
|
|
||||||
|
void Init(Response* response);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Parse HTTP start line; E.g., "HTTP/1.1 200 OK".
|
||||||
|
bool ParseStartLine(const std::string& line) final;
|
||||||
|
|
||||||
|
// The result response message.
|
||||||
|
Response* response_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_RESPONSE_PARSER_H_
|
Loading…
Reference in New Issue