Support persistent connection for server.
This commit is contained in:
@@ -19,6 +19,7 @@ set(HEADERS
|
||||
http_client_pool.h
|
||||
http_client_session.h
|
||||
http_connection.h
|
||||
http_connection_pool.h
|
||||
http_message.h
|
||||
http_parser.h
|
||||
http_request.h
|
||||
@@ -46,6 +47,7 @@ set(SOURCES
|
||||
http_client_pool.cc
|
||||
http_client_session.cc
|
||||
http_connection.cc
|
||||
http_connection_pool.cc
|
||||
http_message.cc
|
||||
http_parser.cc
|
||||
http_request.cc
|
||||
|
||||
+15
-16
@@ -22,8 +22,8 @@ HttpClient::HttpClient()
|
||||
bool HttpClient::Request(HttpRequestPtr request, bool connect) {
|
||||
io_context_.restart();
|
||||
|
||||
response_.reset(new HttpResponse());
|
||||
response_parser_.reset(new HttpResponseParser(response_.get()));
|
||||
response_.reset(new HttpResponse{});
|
||||
response_parser_.Init(response_.get());
|
||||
|
||||
closed_ = false;
|
||||
timer_canceled_ = false;
|
||||
@@ -166,7 +166,7 @@ void HttpClient::DoReadResponse(Error* error) {
|
||||
|
||||
LOG_VERB("Socket async read handler.");
|
||||
|
||||
// Stop the deadline timer once the read has started.
|
||||
// Stop the deadline timer once the read has started (or failed).
|
||||
CancelTimer();
|
||||
|
||||
if (ec || length == 0) {
|
||||
@@ -179,20 +179,17 @@ void HttpClient::DoReadResponse(Error* error) {
|
||||
LOG_INFO("Read data, length: %u.", length);
|
||||
|
||||
// Parse the response piece just read.
|
||||
if (!response_parser_->Parse(buffer_.data(), length)) {
|
||||
//CancelTimer();
|
||||
if (!response_parser_.Parse(buffer_.data(), length)) {
|
||||
Close();
|
||||
*error = kHttpError;
|
||||
LOG_ERRO("Failed to parse HTTP response.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (response_parser_->finished()) {
|
||||
if (response_parser_.finished()) {
|
||||
// Stop trying to read once all content has been received, because
|
||||
// some servers will block extra call to read_some().
|
||||
|
||||
//CancelTimer();
|
||||
|
||||
if (response_->IsConnectionKeepAlive()) {
|
||||
// Close the timer but keep the socket connection.
|
||||
LOG_INFO("Keep the socket connection alive.");
|
||||
@@ -220,22 +217,24 @@ void HttpClient::DoReadResponse(Error* error) {
|
||||
}
|
||||
|
||||
void HttpClient::DoWaitTimer() {
|
||||
LOG_VERB("Wait timer asynchronously.");
|
||||
timer_.async_wait(
|
||||
std::bind(&HttpClient::OnTimer, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void HttpClient::OnTimer(boost::system::error_code ec) {
|
||||
if (closed_) {
|
||||
LOG_VERB("On deadline timer.");
|
||||
|
||||
// timer_.cancel() was called.
|
||||
if (ec == boost::asio::error::operation_aborted) {
|
||||
LOG_VERB("Deadline timer canceled.");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_VERB("On deadline timer.");
|
||||
|
||||
// NOTE: Can't check this:
|
||||
// if (ec == boost::asio::error::operation_aborted) {
|
||||
// LOG_VERB("Deadline timer canceled.");
|
||||
// return;
|
||||
// }
|
||||
if (closed_) {
|
||||
LOG_VERB("Socket has been closed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (timer_.expires_at() <= boost::asio::deadline_timer::traits_type::now()) {
|
||||
// The deadline has passed.
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ private:
|
||||
std::unique_ptr<HttpSocketBase> socket_;
|
||||
|
||||
HttpResponsePtr response_;
|
||||
std::unique_ptr<HttpResponseParser> response_parser_;
|
||||
HttpResponseParser response_parser_;
|
||||
|
||||
// Timer for the timeout control.
|
||||
boost::asio::deadline_timer timer_;
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
#ifndef WEBCC_HTTP_CLIENT_SESSION_H_
|
||||
#define WEBCC_HTTP_CLIENT_SESSION_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "boost/optional.hpp"
|
||||
|
||||
#include "webcc/http_client_pool.h"
|
||||
#include "webcc/http_request_builder.h"
|
||||
#include "webcc/http_response.h"
|
||||
|
||||
+23
-10
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "boost/asio/write.hpp"
|
||||
|
||||
#include "webcc/http_connection_pool.h"
|
||||
#include "webcc/http_request_handler.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
@@ -11,14 +12,17 @@ using boost::asio::ip::tcp;
|
||||
|
||||
namespace webcc {
|
||||
|
||||
HttpConnection::HttpConnection(tcp::socket socket, HttpRequestHandler* handler)
|
||||
HttpConnection::HttpConnection(tcp::socket socket, HttpConnectionPool* pool,
|
||||
HttpRequestHandler* handler)
|
||||
: socket_(std::move(socket)),
|
||||
pool_(pool),
|
||||
buffer_(kBufferSize),
|
||||
request_handler_(handler),
|
||||
request_parser_(&request_) {
|
||||
request_handler_(handler) {
|
||||
}
|
||||
|
||||
void HttpConnection::Start() {
|
||||
request_.reset(new HttpRequest{});
|
||||
request_parser_.Init(request_.get());
|
||||
DoRead();
|
||||
}
|
||||
|
||||
@@ -37,8 +41,11 @@ void HttpConnection::SendResponse(HttpResponsePtr response) {
|
||||
|
||||
response_ = response;
|
||||
|
||||
// TODO: Support keep-alive.
|
||||
response_->SetHeader(http::headers::kConnection, "Close");
|
||||
if (request_->IsConnectionKeepAlive()) {
|
||||
response_->SetHeader(http::headers::kConnection, "Keep-Alive");
|
||||
} else {
|
||||
response_->SetHeader(http::headers::kConnection, "Close");
|
||||
}
|
||||
|
||||
response_->Prepare();
|
||||
|
||||
@@ -60,7 +67,7 @@ void HttpConnection::OnRead(boost::system::error_code ec, std::size_t length) {
|
||||
if (ec) {
|
||||
LOG_ERRO("Socket read error (%s).", ec.message().c_str());
|
||||
if (ec != boost::asio::error::operation_aborted) {
|
||||
Close();
|
||||
pool_->Close(shared_from_this());
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -79,7 +86,7 @@ void HttpConnection::OnRead(boost::system::error_code ec, std::size_t length) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_VERB("HTTP request:\n%s", request_.Dump(4, "> ").c_str());
|
||||
LOG_VERB("HTTP request:\n%s", request_->Dump(4, "> ").c_str());
|
||||
|
||||
// Enqueue this connection.
|
||||
// Some worker thread will handle it later.
|
||||
@@ -104,13 +111,19 @@ void HttpConnection::OnWrite(boost::system::error_code ec, std::size_t length) {
|
||||
LOG_ERRO("Socket write error (%s).", ec.message().c_str());
|
||||
|
||||
if (ec != boost::asio::error::operation_aborted) {
|
||||
Close();
|
||||
pool_->Close(shared_from_this());
|
||||
}
|
||||
} else {
|
||||
LOG_INFO("Response has been sent back, length: %u.", length);
|
||||
|
||||
Shutdown();
|
||||
Close(); // Necessary even after shutdown!
|
||||
if (request_->IsConnectionKeepAlive()) {
|
||||
LOG_INFO("The client asked for keep-alive connection.");
|
||||
LOG_INFO("Continue to read next request...");
|
||||
Start();
|
||||
} else {
|
||||
Shutdown();
|
||||
pool_->Close(shared_from_this());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,13 +15,14 @@
|
||||
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,
|
||||
HttpConnection(boost::asio::ip::tcp::socket socket, HttpConnectionPool* pool,
|
||||
HttpRequestHandler* handler);
|
||||
|
||||
~HttpConnection() = default;
|
||||
@@ -29,7 +30,7 @@ public:
|
||||
HttpConnection(const HttpConnection&) = delete;
|
||||
HttpConnection& operator=(const HttpConnection&) = delete;
|
||||
|
||||
const HttpRequest& request() const {
|
||||
HttpRequestPtr request() const {
|
||||
return request_;
|
||||
}
|
||||
|
||||
@@ -57,6 +58,9 @@ private:
|
||||
// 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_;
|
||||
|
||||
@@ -64,7 +68,7 @@ private:
|
||||
HttpRequestHandler* request_handler_;
|
||||
|
||||
// The incoming request.
|
||||
HttpRequest request_;
|
||||
HttpRequestPtr request_;
|
||||
|
||||
// The parser for the incoming request.
|
||||
HttpRequestParser request_parser_;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "webcc/http_connection_pool.h"
|
||||
|
||||
#include "webcc/logger.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
HttpConnectionPool::HttpConnectionPool() {
|
||||
}
|
||||
|
||||
void HttpConnectionPool::Start(HttpConnectionPtr c) {
|
||||
LOG_VERB("Starting connection...");
|
||||
connections_.insert(c);
|
||||
c->Start();
|
||||
}
|
||||
|
||||
void HttpConnectionPool::Close(HttpConnectionPtr c) {
|
||||
LOG_VERB("Closing connection...");
|
||||
connections_.erase(c);
|
||||
c->Close();
|
||||
}
|
||||
|
||||
void HttpConnectionPool::CloseAll() {
|
||||
LOG_VERB("Closing all (%u) connections...", connections_.size());
|
||||
for (auto& c : connections_) {
|
||||
c->Close();
|
||||
}
|
||||
connections_.clear();
|
||||
}
|
||||
|
||||
} // namespace webcc
|
||||
@@ -0,0 +1,33 @@
|
||||
#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_
|
||||
@@ -48,6 +48,10 @@ public:
|
||||
// optional |existed| parameter will be set to false.
|
||||
const std::string& Get(const std::string& key, bool* existed = nullptr) const;
|
||||
|
||||
void Clear() {
|
||||
headers_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<HttpHeader>::iterator Find(const std::string& key);
|
||||
|
||||
|
||||
@@ -33,6 +33,11 @@ HttpParser::HttpParser(HttpMessage* message)
|
||||
finished_(false) {
|
||||
}
|
||||
|
||||
void HttpParser::Init(HttpMessage* message) {
|
||||
Reset();
|
||||
message_ = message;
|
||||
}
|
||||
|
||||
bool HttpParser::Parse(const char* data, std::size_t length) {
|
||||
// Append the new data to the pending data.
|
||||
pending_data_.append(data, length);
|
||||
@@ -63,6 +68,19 @@ bool HttpParser::Parse(const char* data, std::size_t length) {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpParser::Reset() {
|
||||
pending_data_.clear();
|
||||
content_.clear();
|
||||
|
||||
content_length_ = kInvalidLength;
|
||||
start_line_parsed_ = false;
|
||||
content_length_parsed_ = false;
|
||||
header_ended_ = false;
|
||||
chunked_ = false;
|
||||
chunk_size_ = kInvalidLength;
|
||||
finished_ = false;
|
||||
}
|
||||
|
||||
bool HttpParser::ParseHeaders() {
|
||||
std::size_t off = 0;
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ public:
|
||||
HttpParser(const HttpParser&) = delete;
|
||||
HttpParser& operator=(const HttpParser&) = delete;
|
||||
|
||||
void Init(HttpMessage* message);
|
||||
|
||||
bool finished() const { return finished_; }
|
||||
|
||||
std::size_t content_length() const { return content_length_; }
|
||||
@@ -26,6 +28,9 @@ public:
|
||||
bool Parse(const char* data, std::size_t length);
|
||||
|
||||
protected:
|
||||
// Reset for next parse.
|
||||
void Reset();
|
||||
|
||||
// Parse headers from pending data.
|
||||
// Return false only on syntax errors.
|
||||
bool ParseHeaders();
|
||||
|
||||
@@ -64,10 +64,12 @@ public:
|
||||
return port().empty() ? default_port : port();
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
std::size_t buffer_size() const {
|
||||
return buffer_size_;
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
bool ssl_verify() const {
|
||||
return ssl_verify_;
|
||||
}
|
||||
|
||||
@@ -24,11 +24,10 @@ void HttpRequestHandler::Start(std::size_t count) {
|
||||
void HttpRequestHandler::Stop() {
|
||||
LOG_INFO("Stopping workers...");
|
||||
|
||||
// Close pending connections.
|
||||
for (HttpConnectionPtr s = queue_.Pop(); s; s = queue_.Pop()) {
|
||||
LOG_INFO("Closing pending connection...");
|
||||
s->Close();
|
||||
}
|
||||
// Clear pending connections.
|
||||
// The connections will be closed later (see HttpServer::DoAwaitStop).
|
||||
LOG_INFO("Clear pending connections...");
|
||||
queue_.Clear();
|
||||
|
||||
// Enqueue a null connection to trigger the first worker to stop.
|
||||
queue_.Push(HttpConnectionPtr());
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
// Start worker threads.
|
||||
void Start(std::size_t count);
|
||||
|
||||
// Close pending connections and stop worker threads.
|
||||
// Clear pending connections from the queue and stop worker threads.
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
|
||||
@@ -11,6 +11,11 @@ HttpRequestParser::HttpRequestParser(HttpRequest* request)
|
||||
: HttpParser(request), request_(request) {
|
||||
}
|
||||
|
||||
void HttpRequestParser::Init(HttpRequest* request) {
|
||||
HttpParser::Init(request);
|
||||
request_ = request;
|
||||
}
|
||||
|
||||
bool HttpRequestParser::ParseStartLine(const std::string& line) {
|
||||
std::vector<std::string> strs;
|
||||
boost::split(strs, line, boost::is_any_of(" "), boost::token_compress_on);
|
||||
|
||||
@@ -11,12 +11,14 @@ class HttpRequest;
|
||||
|
||||
class HttpRequestParser : public HttpParser {
|
||||
public:
|
||||
explicit HttpRequestParser(HttpRequest* request);
|
||||
explicit HttpRequestParser(HttpRequest* request = nullptr);
|
||||
|
||||
~HttpRequestParser() override = default;
|
||||
|
||||
void Init(HttpRequest* request);
|
||||
|
||||
private:
|
||||
bool ParseStartLine(const std::string& line) override;
|
||||
bool ParseStartLine(const std::string& line) final;
|
||||
|
||||
HttpRequest* request_;
|
||||
};
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
#include "boost/algorithm/string.hpp"
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/http_response.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
@@ -11,6 +11,11 @@ HttpResponseParser::HttpResponseParser(HttpResponse* response)
|
||||
: HttpParser(response), response_(response) {
|
||||
}
|
||||
|
||||
void HttpResponseParser::Init(HttpResponse* response) {
|
||||
HttpParser::Init(response);
|
||||
response_ = response;
|
||||
}
|
||||
|
||||
bool HttpResponseParser::ParseStartLine(const std::string& line) {
|
||||
std::vector<std::string> parts;
|
||||
boost::split(parts, line, boost::is_any_of(" "), boost::token_compress_on);
|
||||
|
||||
@@ -11,13 +11,15 @@ class HttpResponse;
|
||||
|
||||
class HttpResponseParser : public HttpParser {
|
||||
public:
|
||||
explicit HttpResponseParser(HttpResponse* response);
|
||||
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) override;
|
||||
bool ParseStartLine(const std::string& line) final;
|
||||
|
||||
// The result response message.
|
||||
HttpResponse* response_;
|
||||
|
||||
+10
-2
@@ -96,8 +96,10 @@ void HttpServer::DoAccept() {
|
||||
if (!ec) {
|
||||
LOG_INFO("Accepted a connection.");
|
||||
|
||||
std::make_shared<HttpConnection>(std::move(socket),
|
||||
GetRequestHandler())->Start();
|
||||
auto connection = std::make_shared<HttpConnection>(
|
||||
std::move(socket), &pool_, GetRequestHandler());
|
||||
|
||||
pool_.Start(connection);
|
||||
}
|
||||
|
||||
DoAccept();
|
||||
@@ -111,8 +113,14 @@ void HttpServer::DoAwaitStop() {
|
||||
// operations. Once all operations have finished the io_context::run()
|
||||
// call will exit.
|
||||
LOG_INFO("On signal %d, stopping the server...", signo);
|
||||
|
||||
acceptor_.close();
|
||||
|
||||
// Stop worker threads.
|
||||
GetRequestHandler()->Stop();
|
||||
|
||||
// Close all connections.
|
||||
pool_.CloseAll();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "webcc/globals.h"
|
||||
#include "webcc/http_connection.h"
|
||||
#include "webcc/http_connection_pool.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
@@ -47,6 +48,9 @@ private:
|
||||
// Acceptor used to listen for incoming connections.
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
|
||||
// The connection pool which owns all live connections.
|
||||
HttpConnectionPool pool_;
|
||||
|
||||
// The signal_set is used to register for process termination notifications.
|
||||
boost::asio::signal_set signals_;
|
||||
|
||||
|
||||
@@ -41,6 +41,11 @@ public:
|
||||
return message;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
message_list_.clear();
|
||||
}
|
||||
|
||||
void Push(const T& message) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
@@ -15,12 +15,13 @@ bool RestRequestHandler::Bind(RestServicePtr service, const std::string& url,
|
||||
}
|
||||
|
||||
void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) {
|
||||
const HttpRequest& http_request = connection->request();
|
||||
HttpRequestPtr http_request = connection->request();
|
||||
assert(http_request);
|
||||
|
||||
const Url& url = http_request.url();
|
||||
const Url& url = http_request->url();
|
||||
|
||||
RestRequest rest_request{
|
||||
http_request.method(), http_request.content(), url.query()
|
||||
http_request->method(), http_request->content(), url.query()
|
||||
};
|
||||
|
||||
// Get service by URL path.
|
||||
@@ -46,7 +47,7 @@ void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) {
|
||||
|
||||
// Only support gzip for response compression.
|
||||
if (rest_response.content.size() > kGzipThreshold &&
|
||||
http_request.AcceptEncodingGzip()) {
|
||||
http_request->AcceptEncodingGzip()) {
|
||||
std::string compressed;
|
||||
if (Compress(rest_response.content, &compressed)) {
|
||||
http_response->SetHeader(http::headers::kContentEncoding, "gzip");
|
||||
|
||||
@@ -16,12 +16,15 @@ bool SoapRequestHandler::Bind(SoapServicePtr service, const std::string& url) {
|
||||
}
|
||||
|
||||
void SoapRequestHandler::HandleConnection(HttpConnectionPtr connection) {
|
||||
HttpRequestPtr http_request = connection->request();
|
||||
assert(http_request);
|
||||
|
||||
auto http_response = std::make_shared<HttpResponse>();
|
||||
|
||||
// TODO: Support keep-alive.
|
||||
http_response->SetHeader(http::headers::kConnection, "Close");
|
||||
|
||||
std::string path = "/" + connection->request().url().path();
|
||||
std::string path = "/" + http_request->url().path();
|
||||
SoapServicePtr service = GetServiceByUrl(path);
|
||||
if (!service) {
|
||||
http_response->set_status(http::Status::kBadRequest);
|
||||
@@ -31,7 +34,7 @@ void SoapRequestHandler::HandleConnection(HttpConnectionPtr connection) {
|
||||
|
||||
// Parse the SOAP request XML.
|
||||
SoapRequest soap_request;
|
||||
if (!soap_request.FromXml(connection->request().content())) {
|
||||
if (!soap_request.FromXml(http_request->content())) {
|
||||
http_response->set_status(http::Status::kBadRequest);
|
||||
connection->SendResponse(http_response);
|
||||
return;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "boost/asio/ip/tcp.hpp"
|
||||
|
||||
@@ -23,25 +22,6 @@ std::string EndpointToString(const TcpEndpoint& endpoint);
|
||||
// See: https://tools.ietf.org/html/rfc7231#section-7.1.1.2
|
||||
std::string GetHttpDateTimestamp();
|
||||
|
||||
// Resize a buffer in ctor and restore its original size in dtor.
|
||||
struct BufferResizer {
|
||||
BufferResizer(std::vector<char>* buffer, std::size_t new_size)
|
||||
: buffer_(buffer), old_size_(buffer->size()) {
|
||||
if (new_size != 0 && new_size != old_size_) {
|
||||
buffer_->resize(new_size);
|
||||
}
|
||||
}
|
||||
|
||||
~BufferResizer() {
|
||||
if (buffer_->size() != old_size_) {
|
||||
buffer_->resize(old_size_);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<char>* buffer_;
|
||||
std::size_t old_size_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_UTILITY_H_
|
||||
|
||||
Reference in New Issue
Block a user