upgrade core add header check and incrace upload speed
This commit is contained in:
+214
-178
@@ -9,241 +9,277 @@
|
||||
#include "webcc/fs.h"
|
||||
#include "webcc/globals.h"
|
||||
|
||||
namespace webcc {
|
||||
namespace webcc
|
||||
{
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using Header = std::pair<std::string, std::string>;
|
||||
using Header = std::pair<std::string, std::string>;
|
||||
|
||||
class Headers {
|
||||
public:
|
||||
std::size_t size() const {
|
||||
return headers_.size();
|
||||
}
|
||||
class Headers
|
||||
{
|
||||
//fix core dumped
|
||||
public:
|
||||
Headers() : headers_()
|
||||
{
|
||||
|
||||
bool empty() const {
|
||||
return headers_.empty();
|
||||
}
|
||||
}
|
||||
public:
|
||||
std::size_t size() const
|
||||
{
|
||||
return headers_.size();
|
||||
}
|
||||
|
||||
const std::vector<Header>& data() const {
|
||||
return headers_;
|
||||
}
|
||||
bool empty() const
|
||||
{
|
||||
return headers_.empty();
|
||||
}
|
||||
|
||||
bool Set(string_view key, string_view value);
|
||||
const std::vector<Header>& data() const
|
||||
{
|
||||
return headers_;
|
||||
}
|
||||
|
||||
bool Has(string_view key) const;
|
||||
bool Set(string_view key, string_view value);
|
||||
|
||||
// Get header by index.
|
||||
const Header& Get(std::size_t index) const {
|
||||
assert(index < size());
|
||||
return headers_[index];
|
||||
}
|
||||
bool Has(string_view key) const;
|
||||
|
||||
// Get header value by key.
|
||||
// If there's no such header with the given key, besides return empty, the
|
||||
// optional |existed| parameter will be set to false.
|
||||
const std::string& Get(string_view key, bool* existed = nullptr) const;
|
||||
// Get header by index.
|
||||
const Header& Get(std::size_t index) const
|
||||
{
|
||||
assert(index < size());
|
||||
return headers_[index];
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
headers_.clear();
|
||||
}
|
||||
// Get header value by key.
|
||||
// If there's no such header with the given key, besides return empty, the
|
||||
// optional |existed| parameter will be set to false.
|
||||
const std::string& Get(string_view key, bool* existed = nullptr) const;
|
||||
|
||||
private:
|
||||
std::vector<Header>::iterator Find(string_view key);
|
||||
void Clear()
|
||||
{
|
||||
headers_.clear();
|
||||
}
|
||||
|
||||
std::vector<Header> headers_;
|
||||
};
|
||||
private:
|
||||
std::vector<Header>::iterator Find(string_view key);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
std::vector<Header> headers_;
|
||||
};
|
||||
|
||||
// Content-Type header.
|
||||
// Syntax:
|
||||
// Content-Type: text/html; charset=utf-8
|
||||
// Content-Type: multipart/form-data; boundary=something
|
||||
class ContentType {
|
||||
public:
|
||||
explicit ContentType(string_view str = "");
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void Parse(string_view str);
|
||||
// Content-Type header.
|
||||
// Syntax:
|
||||
// Content-Type: text/html; charset=utf-8
|
||||
// Content-Type: multipart/form-data; boundary=something
|
||||
class ContentType
|
||||
{
|
||||
public:
|
||||
explicit ContentType(string_view str = "");
|
||||
|
||||
void Reset();
|
||||
void Parse(string_view str);
|
||||
|
||||
bool Valid() const;
|
||||
void Reset();
|
||||
|
||||
bool multipart() const {
|
||||
return multipart_;
|
||||
}
|
||||
bool Valid() const;
|
||||
|
||||
const std::string& media_type() const {
|
||||
return media_type_;
|
||||
}
|
||||
bool multipart() const
|
||||
{
|
||||
return multipart_;
|
||||
}
|
||||
|
||||
const std::string& charset() const {
|
||||
assert(!multipart_);
|
||||
return additional_;
|
||||
}
|
||||
const std::string& media_type() const
|
||||
{
|
||||
return media_type_;
|
||||
}
|
||||
|
||||
const std::string& boundary() const {
|
||||
assert(multipart_);
|
||||
return additional_;
|
||||
}
|
||||
const std::string& charset() const
|
||||
{
|
||||
assert(!multipart_);
|
||||
return additional_;
|
||||
}
|
||||
|
||||
private:
|
||||
void Init(string_view str);
|
||||
const std::string& boundary() const
|
||||
{
|
||||
assert(multipart_);
|
||||
return additional_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string media_type_;
|
||||
std::string additional_;
|
||||
bool multipart_ = false;
|
||||
};
|
||||
private:
|
||||
void Init(string_view str);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
private:
|
||||
std::string media_type_;
|
||||
std::string additional_;
|
||||
bool multipart_ = false;
|
||||
};
|
||||
|
||||
// Content-Disposition header.
|
||||
// Syntax:
|
||||
// Content-Disposition: form-data
|
||||
// Content-Disposition: form-data; name="fieldName"
|
||||
// Content-Disposition: form-data; name="fieldName"; filename="filename.jpg"
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
|
||||
class ContentDisposition {
|
||||
public:
|
||||
explicit ContentDisposition(string_view str) {
|
||||
valid_ = Init(str);
|
||||
}
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool valid() const {
|
||||
return valid_;
|
||||
}
|
||||
// Content-Disposition header.
|
||||
// Syntax:
|
||||
// Content-Disposition: form-data
|
||||
// Content-Disposition: form-data; name="fieldName"
|
||||
// Content-Disposition: form-data; name="fieldName"; filename="filename.jpg"
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
|
||||
class ContentDisposition
|
||||
{
|
||||
public:
|
||||
explicit ContentDisposition(string_view str)
|
||||
{
|
||||
valid_ = Init(str);
|
||||
}
|
||||
|
||||
const std::string& name() const {
|
||||
return name_;
|
||||
}
|
||||
bool valid() const
|
||||
{
|
||||
return valid_;
|
||||
}
|
||||
|
||||
const std::string& file_name() const {
|
||||
return file_name_;
|
||||
}
|
||||
const std::string& name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool Init(string_view str);
|
||||
const std::string& file_name() const
|
||||
{
|
||||
return file_name_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::string file_name_;
|
||||
bool valid_ = false;
|
||||
};
|
||||
private:
|
||||
bool Init(string_view str);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
private:
|
||||
std::string name_;
|
||||
std::string file_name_;
|
||||
bool valid_ = false;
|
||||
};
|
||||
|
||||
class FormPart;
|
||||
using FormPartPtr = std::shared_ptr<FormPart>;
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// A part of the multipart form data.
|
||||
class FormPart {
|
||||
public:
|
||||
FormPart() = default;
|
||||
class FormPart;
|
||||
using FormPartPtr = std::shared_ptr<FormPart>;
|
||||
|
||||
FormPart(const FormPart&) = delete;
|
||||
FormPart& operator=(const FormPart&) = delete;
|
||||
// A part of the multipart form data.
|
||||
class FormPart
|
||||
{
|
||||
public:
|
||||
FormPart() = default;
|
||||
|
||||
// Construct a non-file part.
|
||||
// The data will be moved, no file name is needed.
|
||||
// The media type is optional. If the data is a JSON string, you can specify
|
||||
// media type as "application/json".
|
||||
static FormPartPtr New(string_view name, std::string&& data,
|
||||
string_view media_type = "");
|
||||
FormPart(const FormPart&) = delete;
|
||||
FormPart& operator=(const FormPart&) = delete;
|
||||
|
||||
// Construct a file part.
|
||||
// The file name will be extracted from path.
|
||||
// The media type, if not provided, will be inferred from file extension.
|
||||
static FormPartPtr NewFile(string_view name, const fs::path& path,
|
||||
string_view media_type = "");
|
||||
// Construct a non-file part.
|
||||
// The data will be moved, no file name is needed.
|
||||
// The media type is optional. If the data is a JSON string, you can specify
|
||||
// media type as "application/json".
|
||||
static FormPartPtr New(string_view name, std::string&& data,
|
||||
string_view media_type = "");
|
||||
|
||||
// API: SERVER
|
||||
const std::string& name() const {
|
||||
return name_;
|
||||
}
|
||||
// Construct a file part.
|
||||
// The file name will be extracted from path.
|
||||
// The media type, if not provided, will be inferred from file extension.
|
||||
static FormPartPtr NewFile(string_view name, const fs::path& path,
|
||||
string_view media_type = "");
|
||||
|
||||
// API: SERVER/PARSER
|
||||
void set_name(const std::string& name) {
|
||||
name_ = name;
|
||||
}
|
||||
void ReserveData(std::size_t capacity);
|
||||
|
||||
// API: SERVER
|
||||
const std::string& file_name() const {
|
||||
return file_name_;
|
||||
}
|
||||
// API: SERVER
|
||||
const std::string& name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
// API: SERVER/PARSER
|
||||
void set_file_name(const std::string& file_name) {
|
||||
file_name_ = file_name;
|
||||
}
|
||||
// API: SERVER/PARSER
|
||||
void set_name(const std::string& name)
|
||||
{
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
// API: SERVER
|
||||
const std::string& media_type() const {
|
||||
return media_type_;
|
||||
}
|
||||
// API: SERVER
|
||||
const std::string& file_name() const
|
||||
{
|
||||
return file_name_;
|
||||
}
|
||||
|
||||
// API: SERVER
|
||||
const std::string& data() const {
|
||||
return data_;
|
||||
}
|
||||
// API: SERVER/PARSER
|
||||
void set_file_name(const std::string& file_name)
|
||||
{
|
||||
file_name_ = file_name;
|
||||
}
|
||||
|
||||
// API: SERVER/PARSER
|
||||
void AppendData(const std::string& data) {
|
||||
data_.append(data);
|
||||
}
|
||||
// API: SERVER
|
||||
const std::string& media_type() const
|
||||
{
|
||||
return media_type_;
|
||||
}
|
||||
|
||||
// API: SERVER/PARSER
|
||||
void AppendData(const char* data, std::size_t count) {
|
||||
data_.append(data, count);
|
||||
}
|
||||
// API: SERVER
|
||||
const std::string& data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
// API: CLIENT
|
||||
void Prepare(Payload* payload);
|
||||
// API: SERVER/PARSER
|
||||
void AppendData(const std::string& data)
|
||||
{
|
||||
data_.append(data);
|
||||
}
|
||||
|
||||
// Free the memory of the data.
|
||||
void Free();
|
||||
// API: SERVER/PARSER
|
||||
void AppendData(const char* data, std::size_t count)
|
||||
{
|
||||
data_.append(data, count);
|
||||
}
|
||||
|
||||
// Get the size of the whole payload.
|
||||
// Used by the request to calculate content length.
|
||||
std::size_t GetSize();
|
||||
// API: CLIENT
|
||||
void Prepare(Payload* payload);
|
||||
|
||||
// Get the size of the data.
|
||||
std::size_t GetDataSize();
|
||||
// Free the memory of the data.
|
||||
void Free();
|
||||
|
||||
// Dump to output stream for logging purpose.
|
||||
void Dump(std::ostream& os, string_view prefix) const;
|
||||
// Get the size of the whole payload.
|
||||
// Used by the request to calculate content length.
|
||||
std::size_t GetSize();
|
||||
|
||||
private:
|
||||
// Generate headers from properties.
|
||||
void SetHeaders();
|
||||
// Get the size of the data.
|
||||
std::size_t GetDataSize();
|
||||
|
||||
private:
|
||||
// The <input> name within the original HTML form.
|
||||
// E.g., given HTML form:
|
||||
// <input name="file1" type="file">
|
||||
// the name will be "file1".
|
||||
std::string name_;
|
||||
// Dump to output stream for logging purpose.
|
||||
void Dump(std::ostream& os, string_view prefix) const;
|
||||
|
||||
// The path of the file to post.
|
||||
fs::path path_;
|
||||
private:
|
||||
// Generate headers from properties.
|
||||
void SetHeaders();
|
||||
|
||||
// The original local file name.
|
||||
// E.g., "baby.jpg".
|
||||
std::string file_name_;
|
||||
private:
|
||||
// The <input> name within the original HTML form.
|
||||
// E.g., given HTML form:
|
||||
// <input name="file1" type="file">
|
||||
// the name will be "file1".
|
||||
std::string name_;
|
||||
|
||||
// The content-type if the media type is known (e.g., inferred from the file
|
||||
// extension or operating system typing information) or as
|
||||
// application/octet-stream.
|
||||
// E.g., "image/jpeg".
|
||||
std::string media_type_;
|
||||
// The path of the file to post.
|
||||
fs::path path_;
|
||||
|
||||
// Headers generated from the above properties.
|
||||
// Only Used to prepare payload.
|
||||
Headers headers_;
|
||||
// The original local file name.
|
||||
// E.g., "baby.jpg".
|
||||
std::string file_name_;
|
||||
|
||||
std::string data_;
|
||||
};
|
||||
// The content-type if the media type is known (e.g., inferred from the file
|
||||
// extension or operating system typing information) or as
|
||||
// application/octet-stream.
|
||||
// E.g., "image/jpeg".
|
||||
std::string media_type_;
|
||||
|
||||
// Headers generated from the above properties.
|
||||
// Only Used to prepare payload.
|
||||
Headers headers_;
|
||||
|
||||
std::string data_;
|
||||
};
|
||||
|
||||
inline std::size_t g_max_multipart_size = 1024 * 1024 * 1024;
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef WEBCC_CONNECTION_H_
|
||||
#ifndef WEBCC_CONNECTION_H_
|
||||
#define WEBCC_CONNECTION_H_
|
||||
|
||||
#include <memory>
|
||||
@@ -101,6 +101,9 @@ private:
|
||||
|
||||
// The response to be sent back to the client.
|
||||
ResponsePtr response_;
|
||||
|
||||
// 标识是否检查过请求头
|
||||
bool header_validated_ = false;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef WEBCC_GLOBALS_H_
|
||||
#ifndef WEBCC_GLOBALS_H_
|
||||
#define WEBCC_GLOBALS_H_
|
||||
|
||||
#include <cassert>
|
||||
@@ -175,7 +175,7 @@ enum Status {
|
||||
kProxyAuthenticationRequired = 407,
|
||||
// 某些服务器会在空闲连接上发送此响应,即使客户端之前没有任何请求。这意味着服务器希望关闭此未使用的连接。
|
||||
kRequestTimeout = 408,
|
||||
// 当请求与服务器的当前状态冲突时,发送此响应。
|
||||
// 当请求与服务器的当前状态冲突时,发送此响应。在
|
||||
kConflict = 409,
|
||||
// 当请求的内容已从服务器永久删除,且没有转发地址时,发送此响应。
|
||||
kGone = 410,
|
||||
|
||||
@@ -1,70 +1,91 @@
|
||||
#ifndef WEBCC_REQUEST_PARSER_H_
|
||||
#ifndef WEBCC_REQUEST_PARSER_H_
|
||||
#define WEBCC_REQUEST_PARSER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "webcc/parser.h"
|
||||
#include "webcc/view.h"
|
||||
|
||||
namespace webcc {
|
||||
namespace webcc
|
||||
{
|
||||
|
||||
using ViewMatcher =
|
||||
std::function<bool(const std::string&, const std::string&, bool*)>;
|
||||
using ViewMatcher =
|
||||
std::function<bool(const std::string&, const std::string&, bool*, ViewPtr*)>;
|
||||
|
||||
class Request;
|
||||
class Request;
|
||||
|
||||
class RequestParser : public Parser {
|
||||
public:
|
||||
RequestParser();
|
||||
class RequestParser : public Parser
|
||||
{
|
||||
public:
|
||||
RequestParser();
|
||||
|
||||
~RequestParser() override = default;
|
||||
~RequestParser() override = default;
|
||||
|
||||
void Init(Request* request, ViewMatcher view_matcher);
|
||||
void Init(Request* request, ViewMatcher view_matcher);
|
||||
|
||||
private:
|
||||
// Override to match the URL against views and check if the matched view
|
||||
// asks for data streaming.
|
||||
bool OnHeadersEnd() override;
|
||||
// 【新增】:检查 Header 是否已解析完毕
|
||||
bool IsHeaderParsed() const
|
||||
{
|
||||
return header_parsed_;
|
||||
}
|
||||
|
||||
bool ParseStartLine(const std::string& line) override;
|
||||
ViewPtr MatchedView() const
|
||||
{
|
||||
return matched_view_;
|
||||
}
|
||||
|
||||
// Override to handle multipart form data which is request only.
|
||||
bool ParseContent(const char* data, std::size_t length) override;
|
||||
private:
|
||||
// Override to match the URL against views and check if the matched view
|
||||
// asks for data streaming.
|
||||
bool OnHeadersEnd() override;
|
||||
|
||||
// Multipart specific parsing helpers.
|
||||
bool ParseStartLine(const std::string& line) override;
|
||||
|
||||
bool ParseMultipartContent(const char* data, std::size_t length);
|
||||
bool ParsePartHeaders(bool* need_more_data);
|
||||
bool GetNextBoundaryLine(std::size_t* b_off, std::size_t* b_len, bool* ended);
|
||||
// Override to handle multipart form data which is request only.
|
||||
bool ParseContent(const char* data, std::size_t length) override;
|
||||
|
||||
// Check if the str.substr(off, count) is a boundary.
|
||||
bool IsBoundary(const std::string& str, std::size_t off,
|
||||
std::size_t count, bool* end = nullptr) const;
|
||||
// Multipart specific parsing helpers.
|
||||
|
||||
private:
|
||||
// The result request message.
|
||||
Request* request_ = nullptr;
|
||||
bool ParseMultipartContent(const char* data, std::size_t length);
|
||||
bool ParsePartHeaders(bool* need_more_data);
|
||||
bool GetNextBoundaryLine(std::size_t* b_off, std::size_t* b_len, bool* ended);
|
||||
|
||||
// A function for matching view once the headers of a request has been
|
||||
// received. The parsing will stop and fail if no view can be matched.
|
||||
ViewMatcher view_matcher_;
|
||||
// Check if the str.substr(off, count) is a boundary.
|
||||
bool IsBoundary(const std::string& str, std::size_t off,
|
||||
std::size_t count, bool* end = nullptr) const;
|
||||
|
||||
// Form data parsing steps.
|
||||
enum class Step {
|
||||
kStart,
|
||||
kBoundaryParsed,
|
||||
kHeadersParsed,
|
||||
kEnded,
|
||||
};
|
||||
private:
|
||||
// The result request message.
|
||||
Request* request_ = nullptr;
|
||||
|
||||
Step step_ = Step::kStart;
|
||||
// A function for matching view once the headers of a request has been
|
||||
// received. The parsing will stop and fail if no view can be matched.
|
||||
ViewMatcher view_matcher_;
|
||||
|
||||
// The current form part being parsed.
|
||||
FormPartPtr part_;
|
||||
// Form data parsing steps.
|
||||
enum class Step
|
||||
{
|
||||
kStart,
|
||||
kBoundaryParsed,
|
||||
kHeadersParsed,
|
||||
kEnded,
|
||||
};
|
||||
|
||||
// All form parts parsed.
|
||||
std::vector<FormPartPtr> form_parts_;
|
||||
};
|
||||
Step step_ = Step::kStart;
|
||||
|
||||
// The current form part being parsed.
|
||||
FormPartPtr part_;
|
||||
|
||||
// All form parts parsed.
|
||||
std::vector<FormPartPtr> form_parts_;
|
||||
|
||||
// 【新增】:Header 解析完成标志
|
||||
bool header_parsed_ = false;
|
||||
|
||||
// View映射(用于请求头检查)
|
||||
ViewPtr matched_view_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
// Return if a view is matched or not.
|
||||
// If the view asks for data streaming, |stream| will be set to true.
|
||||
bool MatchView(const std::string& method, const std::string& url,
|
||||
bool* stream);
|
||||
bool* stream, ViewPtr* out_view = nullptr);
|
||||
|
||||
// Direct access for ViewPtr pointers.
|
||||
// This can be used as server-based ip block/condiction checks
|
||||
|
||||
+106
-99
@@ -16,138 +16,145 @@
|
||||
#include "webcc/router.h"
|
||||
#include "webcc/url.h"
|
||||
|
||||
namespace webcc {
|
||||
namespace webcc
|
||||
{
|
||||
|
||||
class Server : public Router {
|
||||
public:
|
||||
Server(boost::asio::ip::tcp protocol, std::uint16_t port,
|
||||
const fs::path& doc_root = {});
|
||||
using HeaderValidator = std::function<bool(const RequestPtr&)>;
|
||||
|
||||
Server(const Server&) = delete;
|
||||
Server& operator=(const Server&) = delete;
|
||||
class Server : public Router
|
||||
{
|
||||
public:
|
||||
Server(boost::asio::ip::tcp protocol, std::uint16_t port,
|
||||
const fs::path& doc_root = {});
|
||||
|
||||
~Server() = default;
|
||||
Server(const Server&) = delete;
|
||||
Server& operator=(const Server&) = delete;
|
||||
|
||||
void set_buffer_size(std::size_t buffer_size) {
|
||||
if (buffer_size > 0) {
|
||||
buffer_size_ = buffer_size;
|
||||
}
|
||||
}
|
||||
~Server() = default;
|
||||
|
||||
void set_file_chunk_size(std::size_t file_chunk_size) {
|
||||
assert(file_chunk_size > 0);
|
||||
file_chunk_size_ = file_chunk_size;
|
||||
}
|
||||
void set_buffer_size(std::size_t buffer_size)
|
||||
{
|
||||
if (buffer_size > 0)
|
||||
{
|
||||
buffer_size_ = buffer_size;
|
||||
}
|
||||
}
|
||||
|
||||
// Start and run the server.
|
||||
// This method is blocking so will not return until Stop() is called (from
|
||||
// another thread) or a signal like SIGINT is caught.
|
||||
// When the request of a connection has been read, the connection is put into
|
||||
// a queue waiting for some worker thread to process. Normally, the more
|
||||
// |workers| you have, the more concurrency you gain (the concurrency also
|
||||
// depends on the number of CPU cores). The worker thread pops connections
|
||||
// from the queue one by one, prepares the response by the user provided View,
|
||||
// then sends it back to the client.
|
||||
// Meanwhile, the (event) loop, i.e., io_context, is also running in a number
|
||||
// (|loops|) of threads. Normally, one thread for the loop is good enough, but
|
||||
// it could be more than that.
|
||||
void Run(std::size_t workers = 1, std::size_t loops = 1);
|
||||
void set_file_chunk_size(std::size_t file_chunk_size)
|
||||
{
|
||||
assert(file_chunk_size > 0);
|
||||
file_chunk_size_ = file_chunk_size;
|
||||
}
|
||||
|
||||
// Stop the server.
|
||||
// This should be called from another thread since the Run() is blocking.
|
||||
void Stop();
|
||||
// Start and run the server.
|
||||
// This method is blocking so will not return until Stop() is called (from
|
||||
// another thread) or a signal like SIGINT is caught.
|
||||
// When the request of a connection has been read, the connection is put into
|
||||
// a queue waiting for some worker thread to process. Normally, the more
|
||||
// |workers| you have, the more concurrency you gain (the concurrency also
|
||||
// depends on the number of CPU cores). The worker thread pops connections
|
||||
// from the queue one by one, prepares the response by the user provided View,
|
||||
// then sends it back to the client.
|
||||
// Meanwhile, the (event) loop, i.e., io_context, is also running in a number
|
||||
// (|loops|) of threads. Normally, one thread for the loop is good enough, but
|
||||
// it could be more than that.
|
||||
void Run(std::size_t workers = 1, std::size_t loops = 1);
|
||||
|
||||
// Is the server running?
|
||||
bool IsRunning() const;
|
||||
// Stop the server.
|
||||
// This should be called from another thread since the Run() is blocking.
|
||||
void Stop();
|
||||
|
||||
// For High-Level api deleloper: to set the default server name insteaed of
|
||||
// webcc Added by UnknownObject at 2022-09-04
|
||||
void SetDefaultServerName(std::string server_name);
|
||||
// Is the server running?
|
||||
bool IsRunning() const;
|
||||
|
||||
private:
|
||||
// Register signals which indicate when the server should exit.
|
||||
void AddSignals();
|
||||
// For High-Level api deleloper: to set the default server name insteaed of
|
||||
// webcc Added by UnknownObject at 2022-09-04
|
||||
void SetDefaultServerName(std::string server_name);
|
||||
|
||||
// Wait for a signal to stop the server.
|
||||
void AsyncWaitSignals();
|
||||
private:
|
||||
// Register signals which indicate when the server should exit.
|
||||
void AddSignals();
|
||||
|
||||
// Listen on the given port.
|
||||
bool Listen(std::uint16_t port);
|
||||
// Wait for a signal to stop the server.
|
||||
void AsyncWaitSignals();
|
||||
|
||||
// Accept connections asynchronously.
|
||||
void AsyncAccept();
|
||||
// Listen on the given port.
|
||||
bool Listen(std::uint16_t port);
|
||||
|
||||
// Stop acceptor and worker threads, close all pending connections, and
|
||||
// finally stop the event loop.
|
||||
void DoStop();
|
||||
// Accept connections asynchronously.
|
||||
void AsyncAccept();
|
||||
|
||||
// Worker thread routine.
|
||||
void WorkerRoutine();
|
||||
// Stop acceptor and worker threads, close all pending connections, and
|
||||
// finally stop the event loop.
|
||||
void DoStop();
|
||||
|
||||
// Clear pending connections from the queue and stop worker threads.
|
||||
void StopWorkers();
|
||||
// Worker thread routine.
|
||||
void WorkerRoutine();
|
||||
|
||||
// Handle a connection (or more precisely, the request inside it).
|
||||
// Get the request from the connection, process it, prepare the response,
|
||||
// then send the response back to the client.
|
||||
// The connection will keep alive if it's a persistent connection. When next
|
||||
// request comes, this connection will be put back to the queue again.
|
||||
virtual void Handle(ConnectionPtr connection);
|
||||
// Clear pending connections from the queue and stop worker threads.
|
||||
void StopWorkers();
|
||||
|
||||
// Match the view by HTTP method and URL (path).
|
||||
// Return if a view or static file is matched or not.
|
||||
// If the view asks for data streaming, |stream| will be set to true.
|
||||
bool MatchViewOrStatic(const std::string& method, const std::string& url,
|
||||
bool* stream);
|
||||
// Handle a connection (or more precisely, the request inside it).
|
||||
// Get the request from the connection, process it, prepare the response,
|
||||
// then send the response back to the client.
|
||||
// The connection will keep alive if it's a persistent connection. When next
|
||||
// request comes, this connection will be put back to the queue again.
|
||||
virtual void Handle(ConnectionPtr connection);
|
||||
|
||||
// Serve static files from the doc root.
|
||||
ResponsePtr ServeStatic(RequestPtr request);
|
||||
// Match the view by HTTP method and URL (path).
|
||||
// Return if a view or static file is matched or not.
|
||||
// If the view asks for data streaming, |stream| will be set to true.
|
||||
bool MatchViewOrStatic(const std::string& method, const std::string& url,
|
||||
bool* stream, ViewPtr* out_view = nullptr);
|
||||
|
||||
private:
|
||||
// tcp::v4() or tcp::v6()
|
||||
boost::asio::ip::tcp protocol_;
|
||||
// Serve static files from the doc root.
|
||||
ResponsePtr ServeStatic(RequestPtr request);
|
||||
|
||||
// Port number.
|
||||
std::uint16_t port_ = 0;
|
||||
private:
|
||||
// tcp::v4() or tcp::v6()
|
||||
boost::asio::ip::tcp protocol_;
|
||||
|
||||
// The directory with the static files to be served.
|
||||
fs::path doc_root_;
|
||||
// Port number.
|
||||
std::uint16_t port_ = 0;
|
||||
|
||||
// The size of the buffer for reading request.
|
||||
std::size_t buffer_size_ = kBufferSize;
|
||||
// The directory with the static files to be served.
|
||||
fs::path doc_root_;
|
||||
|
||||
// The size of the chunk loaded into memory each time when serving a
|
||||
// static file.
|
||||
std::size_t file_chunk_size_ = 1024;
|
||||
// The size of the buffer for reading request.
|
||||
std::size_t buffer_size_ = kBufferSize;
|
||||
|
||||
// Is the server running?
|
||||
bool running_ = false;
|
||||
// The size of the chunk loaded into memory each time when serving a
|
||||
// static file.
|
||||
std::size_t file_chunk_size_ = 1024;
|
||||
|
||||
// The mutex for guarding the state of the server.
|
||||
std::mutex state_mutex_;
|
||||
// Is the server running?
|
||||
bool running_ = false;
|
||||
|
||||
// The io_context used to perform asynchronous operations.
|
||||
boost::asio::io_context io_context_;
|
||||
// The mutex for guarding the state of the server.
|
||||
std::mutex state_mutex_;
|
||||
|
||||
// Acceptor used to listen for incoming connections.
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
// The io_context used to perform asynchronous operations.
|
||||
boost::asio::io_context io_context_;
|
||||
|
||||
// The connection pool which owns all live connections.
|
||||
ConnectionPool pool_;
|
||||
// Acceptor used to listen for incoming connections.
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
|
||||
// The signals for processing termination notifications.
|
||||
boost::asio::signal_set signals_;
|
||||
// The connection pool which owns all live connections.
|
||||
ConnectionPool pool_;
|
||||
|
||||
// Worker threads.
|
||||
std::vector<std::thread> worker_threads_;
|
||||
// The signals for processing termination notifications.
|
||||
boost::asio::signal_set signals_;
|
||||
|
||||
// The queue with connection waiting for the workers to process.
|
||||
Queue<ConnectionPtr> queue_;
|
||||
// Worker threads.
|
||||
std::vector<std::thread> worker_threads_;
|
||||
|
||||
// For High-Level api deleloper: to set the default server name insteaed of webcc
|
||||
// Added by UnknownObject at 2022-09-04
|
||||
std::string server_name__;
|
||||
};
|
||||
// The queue with connection waiting for the workers to process.
|
||||
Queue<ConnectionPtr> queue_;
|
||||
|
||||
// For High-Level api deleloper: to set the default server name insteaed of webcc
|
||||
// Added by UnknownObject at 2022-09-04
|
||||
std::string server_name__;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
|
||||
+9
-1
@@ -1,4 +1,4 @@
|
||||
#ifndef WEBCC_VIEW_H_
|
||||
#ifndef WEBCC_VIEW_H_
|
||||
#define WEBCC_VIEW_H_
|
||||
|
||||
#include <memory>
|
||||
@@ -25,6 +25,14 @@ public:
|
||||
virtual bool Stream(const std::string& /*method*/) {
|
||||
return false; // No streaming by default
|
||||
}
|
||||
|
||||
// 【核心新增】:请求头预校验虚函数
|
||||
// 当 Header 刚解析完毕、尚未接收 Body 时触发。
|
||||
// 返回 true 继续接收数据;返回 false 强行 RST 切断连接。
|
||||
virtual bool ValidateHeader(RequestPtr /*request*/)
|
||||
{
|
||||
return true; // 默认不校验,全放行
|
||||
}
|
||||
};
|
||||
|
||||
using ViewPtr = std::shared_ptr<View>;
|
||||
|
||||
Reference in New Issue
Block a user