From cf6cfb860835866f80e54f5643397ad4ad8abb6a Mon Sep 17 00:00:00 2001 From: Rootive <81627800+Rootive@users.noreply.github.com> Date: Wed, 29 Jun 2022 19:43:36 +0800 Subject: [PATCH] begin --- Basics/Noncopyable.h | 18 + Basics/StringView.h | 187 ++++++++ Basics/Types.h | 42 ++ CMakeLists.txt | 32 ++ Database/SQLConnection.h | 29 ++ Database/SQLConnectionPool.h | 43 ++ Database/SQLQuery.h | 66 +++ ForumServer/ForumServer.cpp | 750 +++++++++++++++++++++++++++++++++ ForumServer/ForumServer.h | 99 +++++ HTTP/HTTPBuilder.h | 57 +++ HTTP/HTTPParse.cpp | 208 +++++++++ HTTP/HTTPParse.h | 120 ++++++ HTTP/HTTPServer.cpp | 89 ++++ HTTP/HTTPServer.h | 72 ++++ HTTP/HTTPServerBoot.cpp | 144 +++++++ HTTP/HTTPServerBoot.h | 75 ++++ Hash/MD5.cpp | 235 +++++++++++ Hash/MD5.h | 33 ++ IO/DoubleBufferingOutput.cpp | 55 +++ IO/DoubleBufferingOutput.h | 44 ++ IO/IOBuffer.cpp | 37 ++ IO/IOBuffer.h | 93 ++++ IO/OutputFile.h | 42 ++ IO/OutputFileInterface.h | 25 ++ IO/OutputInterface.h | 27 ++ Json/SQLToJson.cpp | 27 ++ Json/SQLToJson.h | 84 ++++ Log/LogFile.cpp | 13 + Log/LogFile.h | 51 +++ Log/LogLine.cpp | 27 ++ Log/LogLine.h | 91 ++++ Log/LogLineParse.cpp | 100 +++++ Log/LogLineParse.h | 39 ++ Network/InetAddress.cpp | 83 ++++ Network/InetAddress.h | 44 ++ Network/NetworkBuffer.h | 108 +++++ Network/PathSegmentView.cpp | 51 +++ Network/PathSegmentView.h | 20 + Network/Socket.cpp | 78 ++++ Network/Socket.h | 41 ++ TCP/TCPConnection.cpp | 221 ++++++++++ TCP/TCPConnection.h | 101 +++++ TCP/TCPServer.cpp | 68 +++ TCP/TCPServer.h | 60 +++ Thread/Acceptor.cpp | 55 +++ Thread/Acceptor.h | 37 ++ Thread/Channel.cpp | 51 +++ Thread/Channel.h | 70 +++ Thread/EventLoop.cpp | 105 +++++ Thread/EventLoop.h | 63 +++ Thread/EventLoopThread.cpp | 46 ++ Thread/EventLoopThread.h | 42 ++ Thread/EventLoopThreadPool.cpp | 57 +++ Thread/EventLoopThreadPool.h | 36 ++ Thread/Poller.cpp | 76 ++++ Thread/Poller.h | 31 ++ Thread/ThisThread.h | 15 + main.cpp | 26 ++ 58 files changed, 4639 insertions(+) create mode 100644 Basics/Noncopyable.h create mode 100644 Basics/StringView.h create mode 100644 Basics/Types.h create mode 100644 CMakeLists.txt create mode 100644 Database/SQLConnection.h create mode 100644 Database/SQLConnectionPool.h create mode 100644 Database/SQLQuery.h create mode 100644 ForumServer/ForumServer.cpp create mode 100644 ForumServer/ForumServer.h create mode 100644 HTTP/HTTPBuilder.h create mode 100644 HTTP/HTTPParse.cpp create mode 100644 HTTP/HTTPParse.h create mode 100644 HTTP/HTTPServer.cpp create mode 100644 HTTP/HTTPServer.h create mode 100644 HTTP/HTTPServerBoot.cpp create mode 100644 HTTP/HTTPServerBoot.h create mode 100644 Hash/MD5.cpp create mode 100644 Hash/MD5.h create mode 100644 IO/DoubleBufferingOutput.cpp create mode 100644 IO/DoubleBufferingOutput.h create mode 100644 IO/IOBuffer.cpp create mode 100644 IO/IOBuffer.h create mode 100644 IO/OutputFile.h create mode 100644 IO/OutputFileInterface.h create mode 100644 IO/OutputInterface.h create mode 100644 Json/SQLToJson.cpp create mode 100644 Json/SQLToJson.h create mode 100644 Log/LogFile.cpp create mode 100644 Log/LogFile.h create mode 100644 Log/LogLine.cpp create mode 100644 Log/LogLine.h create mode 100644 Log/LogLineParse.cpp create mode 100644 Log/LogLineParse.h create mode 100644 Network/InetAddress.cpp create mode 100644 Network/InetAddress.h create mode 100644 Network/NetworkBuffer.h create mode 100644 Network/PathSegmentView.cpp create mode 100644 Network/PathSegmentView.h create mode 100644 Network/Socket.cpp create mode 100644 Network/Socket.h create mode 100644 TCP/TCPConnection.cpp create mode 100644 TCP/TCPConnection.h create mode 100644 TCP/TCPServer.cpp create mode 100644 TCP/TCPServer.h create mode 100644 Thread/Acceptor.cpp create mode 100644 Thread/Acceptor.h create mode 100644 Thread/Channel.cpp create mode 100644 Thread/Channel.h create mode 100644 Thread/EventLoop.cpp create mode 100644 Thread/EventLoop.h create mode 100644 Thread/EventLoopThread.cpp create mode 100644 Thread/EventLoopThread.h create mode 100644 Thread/EventLoopThreadPool.cpp create mode 100644 Thread/EventLoopThreadPool.h create mode 100644 Thread/Poller.cpp create mode 100644 Thread/Poller.h create mode 100644 Thread/ThisThread.h create mode 100644 main.cpp diff --git a/Basics/Noncopyable.h b/Basics/Noncopyable.h new file mode 100644 index 0000000..100e220 --- /dev/null +++ b/Basics/Noncopyable.h @@ -0,0 +1,18 @@ +#ifndef ROOTIVE_NONCOPYABLE_H +#define ROOTIVE_NONCOPYABLE_H + +namespace Rootive +{ + +class Noncopyable +{ +public: + Noncopyable() = default; + ~Noncopyable() = default; + Noncopyable(const Noncopyable &) = delete; + Noncopyable &operator=(const Noncopyable &) = delete; +}; + +} + +#endif \ No newline at end of file diff --git a/Basics/StringView.h b/Basics/StringView.h new file mode 100644 index 0000000..7db2e92 --- /dev/null +++ b/Basics/StringView.h @@ -0,0 +1,187 @@ +#ifndef ROOTIVE_STRINGVIEW_H +#define ROOTIVE_STRINGVIEW_H + +#include +#include +#include +#include + +namespace Rootive +{ + +class StringView +{ +protected: + const char *ptr_; + int length_; +public: + StringView() : ptr_(nullptr), length_(0) {} + StringView(const char *str) : ptr_(str), length_(static_cast(strlen(str))) {} + StringView(const char *offset, int len) : ptr_(offset), length_(len) {} + StringView(const std::string &str) : ptr_(str.data()), length_(str.size()) {} + + inline bool operator==(const StringView &another) const + { + return (length_ == another.length_) && !memcmp(ptr_, another.ptr_, length_); + } + inline bool operator!=(const StringView &another) const { return !(*this == another); } +#define STRINGVIEW_BINARY_PREDICATE(cmp,auxcmp) \ + bool operator cmp (const StringView& another) const \ + { \ + int ret = memcmp(ptr_, another.ptr_, length_ < another.length_ ? length_ : another.length_);\ + return (ret auxcmp 0) || (ret == 0) && (length_ cmp another.length_); \ + } + STRINGVIEW_BINARY_PREDICATE(<, <); + STRINGVIEW_BINARY_PREDICATE(<=, <); + STRINGVIEW_BINARY_PREDICATE(>=, >); + STRINGVIEW_BINARY_PREDICATE(>, >); +#undef STRINGVIEW_BINARY_PREDICATE + + int compare(const StringView& another) const + { + int ret = memcmp(ptr_, another.ptr_, length_ < another.length_ ? length_ : another.length_); + if (!ret) { if (length_ < another.length_) { ret = -1; } else if (length_ > another.length_) { ret = 1; } } + return ret; + } + inline const char *begin() const { return ptr_; } + inline int getLength() const { return length_; } + inline bool bEmpty() const { return 0 == length_; } + inline const char *end() const { return ptr_ + length_; } + + inline void clear() { ptr_ = nullptr, length_ = 0; } + inline void set(const char *str) { ptr_ = str; length_ = static_cast(strlen(str)); } + inline void set(const char *offset, int len) { ptr_ = offset; length_ = len; } + inline char operator[](int index) const { return ptr_[index]; } + + inline void append(int length) { length_ += length; } + + std::string toString() const { return std::string(ptr_, length_); } + + inline StringView mid(int left, int right) const { return StringView(ptr_ + left, right - left); } + inline StringView mid(int left) const { return mid(left, length_); } + + inline void removePrefix(int length) { ptr_ += length; length_ -= length; } + inline void removePrefix(char ch) { while (length_ && ptr_[0] == ch) { removePrefix(1); } } + inline void removeSuffix(int length) { length_ -= length; } + inline void removeSuffix(char ch) { while (length_ && ptr_[length_ - 1] == ch) { removeSuffix(1); } } + + inline bool bPrefix(const StringView &arg) const { return (length_ >= arg.length_) && !memcmp(ptr_, arg.ptr_, arg.length_); } + + inline int indexOf(char ch, const int index = 0) const + { + int ret; + for (ret = index; ret < length_; ++ret) + { + if (ptr_[ret] == ch) { break; } + } + return ret; + } + inline int indexOfNot(char ch, const int index = 0) const + { + int ret; + for (ret = index; ret < length_; ++ret) + { + if (ptr_[ret] != ch) { break; } + } + return ret; + } + inline int indexOfCRLF() const + { + int ret; + for (ret = 1; ret < length_; ++ret) + { + if (ptr_[ret - 1] == '\r' && ptr_[ret] == '\n') + { ret = ret - 1; break; } + } + return ret; + } + + inline int lastIndexOf(char ch) const + { + int ret; + for (ret = length_ - 1; ret >= 0; --ret) + { + if (ptr_[ret] == ch) { break; } + } + return ret; + } + inline int lastIndexOf(char ch, const int index) const + { + int ret; + for (ret = index; ret >= 0; --ret) + { + if (ptr_[ret] == ch) { break; } + } + return ret; + } + inline int lastIndexOfNot(char ch) const + { + int ret; + for (ret = length_ - 1; ret >= 0; --ret) + { + if (ptr_[ret] != ch) { break; } + } + return ret; + } + inline int lastIndexOfNot(char ch, const int index) const + { + int ret; + for (ret = index; ret >= 0; --ret) + { + if (ptr_[ret] != ch) { break; } + } + return ret; + } + + inline StringView cutPrefix(int index) + { + StringView ret(ptr_, index); + removePrefix(index); + return ret; + } + inline bool cutPrefix(char ch, StringView &out) + { + int index = indexOf(ch); + if (index == length_) { return false; } + out = cutPrefix(index); + return true; + } + inline StringView splitPrefix(int index) + { + StringView ret = cutPrefix(index); + removePrefix(1); + return ret; + } + inline bool splitPrefix(char ch, StringView &out) + { + bool ret = cutPrefix(ch, out); + if (ret) { removePrefix(1); } + return ret; + } + + std::vector split(char ch) const + { + StringView cpy(*this); + int index; + std::vector ret; + while ((index = cpy.indexOf(ch)) < cpy.getLength()) + { + ret.push_back(cpy.splitPrefix(index)); + } + ret.push_back(cpy); + return ret; + } + inline int toInt() const + { + int ret = 0; + if (length_ && (isdigit(ptr_[0]) || ptr_[0] == '-' || ptr_[0] == '.')) + { + ret = std::stoi(std::string(ptr_, length_)); + } + return ret; + } +}; + +} + +#endif diff --git a/Basics/Types.h b/Basics/Types.h new file mode 100644 index 0000000..367a41f --- /dev/null +++ b/Basics/Types.h @@ -0,0 +1,42 @@ +#ifndef ROOTIVE_TYPES_H +#define ROOTIVE_TYPES_H + +#include + +namespace Rootive +{ + +namespace Types +{ + +template +inline To implicitCast(const From &arg) +{ + return arg; +} + +template +inline To downCast(const From &arg) +{ + if (false) + { + implicitCast(0); + } + return static_cast(arg); +} + +template +inline ::std::shared_ptr down_pointer_cast(const ::std::shared_ptr &arg) +{ + if (false) + { + implicitCast(0); + } + return ::std::static_pointer_cast(arg); +} + +} + +}; + +#endif diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1dca1b6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.5) + +project(Rootive LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +include_directories(.) +add_executable(Rootive + main.cpp + Basics/Types.h Basics/StringView.h Basics/Noncopyable.h Network/PathSegmentView.h Network/PathSegmentView.cpp + IO/IOBuffer.h IO/IOBuffer.cpp + Thread/EventLoop.h Thread/EventLoop.cpp Thread/EventLoopThread.h Thread/EventLoopThread.cpp + Thread/Channel.h Thread/Channel.cpp Thread/Poller.h Thread/Poller.cpp + Thread/Acceptor.h Thread/Acceptor.cpp Thread/EventLoopThreadPool.h Thread/EventLoopThreadPool.cpp + Network/InetAddress.h Network/InetAddress.cpp + Network/Socket.h Network/Socket.cpp Network/NetworkBuffer.h + TCP/TCPServer.h TCP/TCPServer.cpp TCP/TCPConnection.h TCP/TCPConnection.cpp + Database/SQLConnection.h Database/SQLQuery.h Database/SQLConnectionPool.h + HTTP/HTTPServer.h HTTP/HTTPServer.cpp HTTP/HTTPServerBoot.h HTTP/HTTPServerBoot.cpp + HTTP/HTTPBuilder.h + #Example/SportsServer.h Example/SportsServer.cpp + Json/SQLToJson.h Json/SQLToJson.cpp + ForumServer/ForumServer.h ForumServer/ForumServer.cpp + IO/DoubleBufferingOutput.h IO/DoubleBufferingOutput.cpp Log/LogFile.h IO/OutputFileInterface.h IO/OutputInterface.h + Log/LogLine.h Log/LogLine.cpp + Thread/ThisThread.h Log/LogFile.cpp IO/OutputFile.h + Log/LogLineParse.h Log/LogLineParse.cpp + HTTP/HTTPParse.h HTTP/HTTPParse.cpp +) +target_link_libraries(Rootive pthread) +target_link_libraries(Rootive mariadbclient) + + diff --git a/Database/SQLConnection.h b/Database/SQLConnection.h new file mode 100644 index 0000000..f0046b7 --- /dev/null +++ b/Database/SQLConnection.h @@ -0,0 +1,29 @@ +#ifndef ROOTIVE_SQLCONNECTION_H +#define ROOTIVE_SQLCONNECTION_H + +#include +#include +#include "Basics/Noncopyable.h" +#include "Network/InetAddress.h" + +namespace Rootive +{ +class SQLQuery; +class SQLConnection : Noncopyable +{ + friend class SQLQuery; + + MYSQL *mysql_; + std::mutex mutex_; +public: + SQLConnection(const InetAddress &host, const char *user, const char *pw, const char *db, const char *unixSocket, unsigned long clientFlag) + { + mysql_ = mysql_init(nullptr); + if (!mysql_real_connect(mysql_, host.toIPString().c_str(), user, pw, db, host.getPort_HostEndian(), unixSocket, clientFlag)) { throw ; } + } + ~SQLConnection() { mysql_close(mysql_); } + +}; +} + +#endif \ No newline at end of file diff --git a/Database/SQLConnectionPool.h b/Database/SQLConnectionPool.h new file mode 100644 index 0000000..a3a702f --- /dev/null +++ b/Database/SQLConnectionPool.h @@ -0,0 +1,43 @@ +#ifndef ROOTIVE_SQLCONNECTIONPOOL_H +#define ROOTIVE_SQLCONNECTIONPOOL_H + +#include +#include +#include +#include "Database/SQLConnection.h" +#include "Basics/Noncopyable.h" + +namespace Rootive +{ +class SQLConnectionPool : Noncopyable +{ + std::vector > connectionVector_; + int next_; + std::mutex mutex_; +public: + SQLConnectionPool(const InetAddress &host, const char *user, const char *pw, const char *db, int numConnection = 1) : + next_(0) + { + for (int _i = 0; _i < numConnection; ++_i) + { + connectionVector_.push_back(std::unique_ptr(new SQLConnection(host, user, pw, db, nullptr, CLIENT_MULTI_STATEMENTS))); + } + } + + SQLConnection *getConnection() + { + SQLConnection *ret = nullptr; + { + std::lock_guard guard(mutex_); + if (!connectionVector_.empty()) + { + ret = connectionVector_[next_++].get(); + next_ %= connectionVector_.size(); + } + } + return ret; + } +}; +} + +#endif \ No newline at end of file diff --git a/Database/SQLQuery.h b/Database/SQLQuery.h new file mode 100644 index 0000000..4b0c500 --- /dev/null +++ b/Database/SQLQuery.h @@ -0,0 +1,66 @@ +#ifndef ROOTIVE_SQLQUERY +#define ROOTIVE_SQLQUERY + +#include + +#include "Basics/Noncopyable.h" +#include "Basics/StringView.h" +#include "Database/SQLConnection.h" +#include "Log/LogLine.h" + +#include + +namespace Rootive +{ +class SQLQuery : Noncopyable +{ + MYSQL_RES *res_; + my_ulonglong affected_; +public: + + SQLQuery(SQLConnection *connection, const char *sql, unsigned long length) : + affected_(0) + { + ROOTIVE_LOG_DEBUG << StringView(sql, length); + std::lock_guard lock(connection->mutex_); + mysql_real_query(connection->mysql_, sql, length); + res_ = mysql_store_result(connection->mysql_); + auto tmp = mysql_affected_rows(connection->mysql_); + affected_ += (tmp == (my_ulonglong)-1 ? 0 : tmp); + while (mysql_more_results(connection->mysql_)) + { + mysql_free_result(res_); + mysql_next_result(connection->mysql_); + res_ = mysql_store_result(connection->mysql_); + tmp = mysql_affected_rows(connection->mysql_); + affected_ += (tmp == (my_ulonglong)-1 ? 0 : tmp); + } + } + SQLQuery(SQLConnection *connection, const StringView &sql) : + SQLQuery(connection, sql.begin(), sql.getLength()) + {} + + ~SQLQuery() { mysql_free_result(res_); } + + inline my_ulonglong numRow() const { return res_ ? mysql_num_rows(res_) : 0; } + inline my_ulonglong numCol() const { return res_ ? mysql_num_fields(res_) : 0; } + inline my_ulonglong numAffected() const { return affected_; } + inline const char *colName(unsigned long long index) const + { + mysql_field_seek(res_, index); + return mysql_fetch_field(res_)->name; + } + + MYSQL_ROW operator[](unsigned long long index) + { + mysql_data_seek(res_, index); + return mysql_fetch_row(res_); + } + operator bool() const + { + return numAffected() + numRow(); + } +}; +} + +#endif \ No newline at end of file diff --git a/ForumServer/ForumServer.cpp b/ForumServer/ForumServer.cpp new file mode 100644 index 0000000..4c8c5e5 --- /dev/null +++ b/ForumServer/ForumServer.cpp @@ -0,0 +1,750 @@ +#include "ForumServer.h" +#include +#include +#include +#include "Json/SQLToJson.h" +#include "IO/OutputFile.h" + +namespace Rootive +{ +bool ForumServer::authentication(const std::shared_ptr &connection) +{ + const auto &account = HTTPServerBoot::getAccount(connection); + const auto &password = HTTPServerBoot::getPassword(connection); + const auto &type = HTTPServerBoot::getIT(connection)->second.type_; + std::string sql = "SELECT DISTINCT * FROM Account WHERE `id` = "; + sql.append(account.begin(), account.getLength()); + sql += " AND `password` = '"; + sql.append(password.begin(), password.getLength()); + sql += "' AND `type` >= "; + sql += type; + sql += " LIMIT 1;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + return query; +} +void ForumServer::updatePostPushQuery() +{ + std::string sql = "SELECT id FROM Post WHERE `visible` = 'True' " + "ORDER BY (`favourites` * 2 + `like` - `dislike`) / TIMESTAMPDIFF(DAY, `time`, NOW()) DESC, `time` DESC;"; + postPushQuery.reset(new SQLQuery(sqlConnectionPool_.getConnection(), sql)); + ROOTIVE_LOG_TRACE << SQLToJson::columnToJson(*postPushQuery); +} +void ForumServer::login(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT DISTINCT * FROM Account WHERE id = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " AND `type` <> 'Black' AND password = '"; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += "' LIMIT 1;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::file(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string path = contextPath_; + path.append(argumentVector[0].begin(), argumentVector[0].getLength()); + struct stat fileStat; + if (!stat(path.c_str(), &fileStat)) + { + HTTPBuilder builder(fileStat.st_size + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeEntityBody(path.c_str(), fileStat.st_size); + connection->send(builder.getBuffer()); + } + else { HTTPServerBoot::cold(connection, "404"); } +} +void ForumServer::uploadPostImageAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + fileCount_ = fileCount_ % 100; + std::string path = "PostImage/" + std::to_string(std::chrono::system_clock::now().time_since_epoch().count()) + std::to_string(fileCount_); + ssize_t written = 0; + int fd = ::open((contextPath_ + path).c_str(), O_WRONLY | O_CREAT, 0777); + while (written < argumentVector[0].getLength()) + { + written += ::write(fd, argumentVector[0].begin() + written, argumentVector[0].getLength() - written); + } + ::close(fd); + HTTPBuilder builder(path.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(path); + connection->send(builder.getBuffer()); +} + +void ForumServer::accountClothing(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT DISTINCT * FROM AccountClothing WHERE id = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " LIMIT 1;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::accountClothingAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT DISTINCT * FROM AccountClothing WHERE id = "; + const auto &account = HTTPServerBoot::getAccount(connection); + sql.append(account.begin(), account.getLength()); + sql += " LIMIT 1;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::accountFollowAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT `another` FROM Account_Account WHERE `follow` = '1' AND account = "; + const auto &account = HTTPServerBoot::getAccount(connection); + sql.append(account.begin(), account.getLength()); + sql += ";"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::columnToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::accountFollowedAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT `account` FROM Account_Account WHERE `follow` = '1' AND another = "; + const auto &account = HTTPServerBoot::getAccount(connection); + sql.append(account.begin(), account.getLength()); + sql += " LIMIT "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " OFFSET "; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += ';'; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordsToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::setAccountFollowAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + const auto &account = HTTPServerBoot::getAccount(connection); + std::string res; + if (argumentVector[1] == "0") + { + std::string sql = "DELETE FROM Account_Account WHERE account = "; + sql.append(account.begin(), account.getLength()); + sql += " AND another = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " LIMIT 1;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + res = std::to_string(query.numAffected()); + } + else + { + std::string sql = "INSERT INTO Account_Account VALUE("; + sql.append(account.begin(), account.getLength()); + sql += ", "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += ", "; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += ") ON DUPLICATE KEY UPDATE `follow` = "; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += ";"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + res = std::to_string(query.numAffected()); + } + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); + +} +void ForumServer::commentPost(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT * FROM Comment WHERE post = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " LIMIT "; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += " OFFSET "; + sql.append(argumentVector[2].begin(), argumentVector[2].getLength()); + sql += ';'; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordsToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::setCommentAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "INSERT INTO Comment(post, body, account, time) VALUE('"; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += "', '"; + std::string body(argumentVector[1].begin(), argumentVector[1].getLength()); + SQLToJson::escapeCharacter(body); + sql += body; + sql += "', '"; + const auto &account = HTTPServerBoot::getAccount(connection); + sql.append(account.begin(), account.getLength()); + sql += "', NOW());"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = std::to_string(query.numAffected()); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::post(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT DISTINCT "; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += ", `visible` AS b FROM Post WHERE id = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " LIMIT 1;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res; + if (query) + { + StringView visible(query[0][query.numCol() - 1]); + if (visible != "True") + { + res = "{ \"visible\": \""; + res.append(visible.begin(), visible.getLength()); + res += "\" }"; + } + } + if (res.empty()) { res = SQLToJson::recordToJson(query); } + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::postPush(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + int limit = argumentVector[0].toInt(); + int offset = 0; + offset = argumentVector[1].toInt(); + auto res = SQLToJson::columnToJson(*postPushQuery, limit, offset); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::postFollowAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT Post.* FROM Post, Account_Account " + "WHERE Post.visible = 'True' AND Post.account = Account_Account.another " + "AND Account_Account.account = "; + const auto &account = HTTPServerBoot::getAccount(connection); + sql.append(account.begin(), account.getLength()); + sql += " LIMIT "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " OFFSET "; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += ";"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordsToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::postLikeAndFavouritesAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT DISTINCT * FROM Account_Post WHERE account = "; + const auto &account = HTTPServerBoot::getAccount(connection); + sql.append(account.begin(), account.getLength()); + sql += " AND post = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " LIMIT 1;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::setPostLikeAndFavouritesAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + const auto &account = HTTPServerBoot::getAccount(connection); + std::string sql; + if (argumentVector[1] == "0" && argumentVector[2] == "0" && argumentVector[3] == "0") + { + sql = "DELETE FROM Account_Post WHERE account = "; + sql.append(account.begin(), account.getLength()); + sql += " AND post = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " LIMIT 1;"; + } + else + { + sql = "INSERT INTO Account_Post VALUE('"; + sql.append(account.begin(), account.getLength()); + sql += "', '"; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += "', '"; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += "', '"; + sql.append(argumentVector[2].begin(), argumentVector[2].getLength()); + sql += "', '"; + sql.append(argumentVector[3].begin(), argumentVector[3].getLength()); + sql += "') ON DUPLICATE KEY UPDATE `like` = '"; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += "', `dislike` = '"; + sql.append(argumentVector[2].begin(), argumentVector[2].getLength()); + sql += "', `favourites` = '"; + sql.append(argumentVector[3].begin(), argumentVector[3].getLength()); + sql += "';"; + } + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = std::to_string(query.numAffected()); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::postFavouritesAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT post FROM Account_Post WHERE account = "; + const auto &account = HTTPServerBoot::getAccount(connection); + sql.append(account.begin(), account.getLength()); + sql += " AND favourites = 1 LIMIT "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " OFFSET "; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += ";"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::columnToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::postAccount(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT * FROM Post " + "WHERE visible = 'True' AND account = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " ORDER BY time DESC LIMIT "; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += " OFFSET "; + sql.append(argumentVector[2].begin(), argumentVector[2].getLength()); + sql += ";"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordsToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::postInvisibleAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT * FROM Post " + "WHERE visible <> 'True' && visible <> 'Checking' AND account = "; + const auto &account = HTTPServerBoot::getAccount(connection); + sql.append(account.begin(), account.getLength()); + sql += " LIMIT "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " OFFSET "; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += ";"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordsToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::postCheckingAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT * FROM Post " + "WHERE visible = 'Checking' AND account = "; + const auto &account = HTTPServerBoot::getAccount(connection); + sql.append(account.begin(), account.getLength()); + sql += " LIMIT "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " OFFSET "; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += ";"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordsToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::setPostVisibleAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string res; + if (argumentVector[1] == "True" || argumentVector[1] == "Account") + { + std::string sql = "UPDATE Post SET `visible` = '"; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += "' WHERE id = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " AND (`visible` = 'Account' || `visible` = 'True') AND account = "; + const auto &account = HTTPServerBoot::getAccount(connection); + sql.append(account.begin(), account.getLength()); + sql += " LIMIT 1;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + res = std::to_string(query.numAffected()); + } + else + { + res += '0'; + } + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::setPostVisibleAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string res; + if (argumentVector[1] == "True" || argumentVector[1] == "Administrator") + { + std::string sql = "UPDATE Post SET `visible` = '"; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += "' WHERE id = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " AND `visible` <> 'Account' LIMIT 1;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + res = std::to_string(query.numAffected()); + } + else + { + res += '0'; + } + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::setPostAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string res; + if (argumentVector[2] == "Text" || argumentVector[2] == "Image" || argumentVector[2] == "ImageAndTitle") + { + std::string sql = "INSERT INTO Post(`title`, `body`, `type`, `image`, `account`, `time`) VALUES('"; + std::string title(argumentVector[0].begin(), argumentVector[0].getLength()); + SQLToJson::escapeCharacter(title); + sql += title; sql += "', '"; + std::string body(argumentVector[1].begin(), argumentVector[1].getLength()); + SQLToJson::escapeCharacter(body); + sql += body; sql += "', '"; + sql.append(argumentVector[2].begin(), argumentVector[2].getLength()); + sql += "', '"; + std::string image(argumentVector[3].begin(), argumentVector[3].getLength()); + SQLToJson::escapeCharacter(image); + sql += image; sql += "', '"; + const auto &account = HTTPServerBoot::getAccount(connection); + sql.append(account.begin(), account.getLength()); + sql += "', NOW());"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + if (query) + { + std::string sql = "SELECT LAST_INSERT_ID();"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + if (query) + { + auto split = argumentVector[4].split(','); + for (const auto &s : split) + { + if (!s.bEmpty()) + { + std::string sql = "INSERT INTO Post_Tag VALUE('"; + sql += query[0][0]; sql += "', '"; + sql.append(s.begin(), s.getLength()); sql += "');"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + } + } + } + } + res = std::to_string(query.numAffected()); + } + else + { + res += '0'; + } + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::postTag(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT `post` FROM Post_Tag WHERE `tag` = (SELECT `id` FROM Tag WHERE `id` = '"; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += "' AND `visible` = 'True') LIMIT "; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += " OFFSET "; + sql.append(argumentVector[2].begin(), argumentVector[2].getLength()); + sql += ";"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::columnToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::tag(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT DISTINCT * FROM TagName WHERE `tag` = (SELECT `id` FROM Tag WHERE `id` = '"; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += "' AND `visible` = 'True') LIMIT 1;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::tagName(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT TagName.`tag`, TagName.`name` " + "FROM TagName, Tag WHERE `name` LIKE '%"; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += "%' AND Tag.`visible` = 'True' AND Tag.`id` = TagName.`tag` LIMIT 3;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordsToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::tagPost(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT tag FROM Post_Tag, Tag WHERE Tag.id = Post_Tag.`tag` " + "AND Tag.visible = 'True' AND post = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += ";"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::columnToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::setTagAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT DISTINCT * FROM TagName WHERE `name` = '"; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += "' LIMIT 1;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res; + if (query) + { + res = SQLToJson::recordToJson(query); + } + else + { + std::string sql = "INSERT INTO Tag() VALUE();"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + if (query) + { + std::string sql = "SELECT LAST_INSERT_ID();"; + SQLQuery idQuery(sqlConnectionPool_.getConnection(), sql); + if (idQuery) + { + std::string sql = "INSERT INTO TagName(`name`, `tag`) VALUE('"; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += "', "; + sql += idQuery[0][0]; + sql += ");"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + if (query) + { + res = "{ \"tag\": \""; + int rest = 10 - strlen(idQuery[0][0]); + for (int _i = 0; _i < rest; ++_i) + { + res += '0'; + } + res += idQuery[0][0]; + res += "\", \"name\": \""; + res.append(argumentVector[0].begin(), argumentVector[0].getLength()); + res += "\" }"; + } + } + } + } + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::postCheckingAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT * FROM Post WHERE `visible` = 'Checking' LIMIT 1;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::joinTagAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "START TRANSACTION; UPDATE TagName SET tag = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += " WHERE tag = "; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += "; UPDATE Post_Tag a SET tag = '"; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += "' WHERE tag = '"; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += "' AND NOT EXISTS (SELECT * FROM Post_Tag b WHERE b.post = a.post AND b.tag = '";//"); DELETE FROM Tag WHERE id = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += "'); DELETE FROM Post_Tag WHERE tag = '";//" COMMIT;"; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += "';DELETE FROM Tag WHERE id = '"; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += "'; COMMIT;"; + std::cout << sql << std::endl; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = std::to_string(query.numAffected()); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::setTagVisibleAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string res; + if (argumentVector[1] == "True" || argumentVector[1] == "Administrator") + { + std::string sql = "UPDATE Tag SET `visible` = '"; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += "' WHERE id = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += ";"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + res = std::to_string(query.numAffected()); + } + else + { + res += '0'; + } + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::tagPostAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT tag FROM Post_Tag, Tag WHERE Tag.id = Post_Tag.`tag` AND post = "; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += ";"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::columnToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::tagAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "SELECT DISTINCT TagName.tag, TagName.name, Tag.visible FROM TagName, Tag WHERE TagName.`tag` = '"; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += "' AND Tag.`id` = TagName.`tag` LIMIT 1;"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = SQLToJson::recordToJson(query); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +void ForumServer::setPostSizeAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector) +{ + std::string sql = "UPDATE Post SET `size` = '"; + sql.append(argumentVector[1].begin(), argumentVector[1].getLength()); + sql += "' WHERE `id` = '"; + sql.append(argumentVector[0].begin(), argumentVector[0].getLength()); + sql += "';"; + SQLQuery query(sqlConnectionPool_.getConnection(), sql); + std::string res = std::to_string(query.numAffected()); + HTTPBuilder builder(res.size() + 128); + builder.writeStartLine("HTTP/1.1", "200", "Omitted."); + builder.writeHeader("Content-Type", "text/plain; charset=UTF-8"); + builder.writeEntityBody(res); + connection->send(builder.getBuffer()); +} +ForumServer::ForumServer(EventLoop *eventLoop, const InetAddress &listenAddr, +const InetAddress &sqlHost, const char *user, const char *pw, const char *db, +const std::string &name, int numThread, int numSQLConnection, TCPServer::EOption option) : +server_(eventLoop, listenAddr, name, numThread, option), +sqlConnectionPool_(sqlHost, user, pw, db, numSQLConnection) +{ + server_.functionMap2_["login"] = HTTPServerBoot::FunctionControl(2, "", std::bind(&ForumServer::login, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["file"] = HTTPServerBoot::FunctionControl(1, "", std::bind(&ForumServer::file, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["uploadPostImageAuthentication"] = HTTPServerBoot::FunctionControl(1, "2", std::bind(&ForumServer::uploadPostImageAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["accountClothing"] = HTTPServerBoot::FunctionControl(1, "", std::bind(&ForumServer::accountClothing, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["accountClothingAuthentication"] = HTTPServerBoot::FunctionControl(0, "2", std::bind(&ForumServer::accountClothingAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["accountFollowAuthentication"] = HTTPServerBoot::FunctionControl(0, "2", std::bind(&ForumServer::accountFollowAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["accountFollowedAuthentication"] = HTTPServerBoot::FunctionControl(2, "2", std::bind(&ForumServer::accountFollowedAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["setAccountFollowAuthentication"] = HTTPServerBoot::FunctionControl(2, "2", std::bind(&ForumServer::setAccountFollowAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["commentPost"] = HTTPServerBoot::FunctionControl(3, "", std::bind(&ForumServer::commentPost, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["setCommentAuthentication"] = HTTPServerBoot::FunctionControl(2, "2", std::bind(&ForumServer::setCommentAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["post"] = HTTPServerBoot::FunctionControl(2, "", std::bind(&ForumServer::post, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["postPush"] = HTTPServerBoot::FunctionControl(2, "", std::bind(&ForumServer::postPush, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["postFollowAuthentication"] = HTTPServerBoot::FunctionControl(2, "2", std::bind(&ForumServer::postFollowAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["postLikeAndFavouritesAuthentication"] = HTTPServerBoot::FunctionControl(1, "2", std::bind(&ForumServer::postLikeAndFavouritesAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["setPostLikeAndFavouritesAuthentication"] = HTTPServerBoot::FunctionControl(4, "2", std::bind(&ForumServer::setPostLikeAndFavouritesAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["postFavouritesAuthentication"] = HTTPServerBoot::FunctionControl(2, "2", std::bind(&ForumServer::postFavouritesAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["postAccount"] = HTTPServerBoot::FunctionControl(3, "", std::bind(&ForumServer::postAccount, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["postInvisibleAuthentication"] = HTTPServerBoot::FunctionControl(2, "2", std::bind(&ForumServer::postInvisibleAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["postCheckingAuthentication"] = HTTPServerBoot::FunctionControl(2, "2", std::bind(&ForumServer::postCheckingAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["setPostVisibleAuthentication"] = HTTPServerBoot::FunctionControl(2, "2", std::bind(&ForumServer::setPostVisibleAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["setPostVisibleAdministrator"] = HTTPServerBoot::FunctionControl(2, "3", std::bind(&ForumServer::setPostVisibleAdministrator, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["setPostAuthentication"] = HTTPServerBoot::FunctionControl(5, "2", std::bind(&ForumServer::setPostAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["postTag"] = HTTPServerBoot::FunctionControl(3, "", std::bind(&ForumServer::postTag, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["tag"] = HTTPServerBoot::FunctionControl(1, "", std::bind(&ForumServer::tag, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["tagName"] = HTTPServerBoot::FunctionControl(1, "", std::bind(&ForumServer::tagName, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["tagPost"] = HTTPServerBoot::FunctionControl(1, "", std::bind(&ForumServer::tagPost, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["setTagAuthentication"] = HTTPServerBoot::FunctionControl(1, "2", std::bind(&ForumServer::setTagAuthentication, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["postCheckingAdministrator"] = HTTPServerBoot::FunctionControl(0, "3", std::bind(&ForumServer::postCheckingAdministrator, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["tagPostAdministrator"] = HTTPServerBoot::FunctionControl(1, "3", std::bind(&ForumServer::tagPostAdministrator, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["tagAdministrator"] = HTTPServerBoot::FunctionControl(1, "3", std::bind(&ForumServer::tagAdministrator, this, std::placeholders::_1, std::placeholders::_2)); + + server_.functionMap2_["joinTagAdministrator"] = HTTPServerBoot::FunctionControl(2, "3", std::bind(&ForumServer::joinTagAdministrator, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["setTagVisibleAdministrator"] = HTTPServerBoot::FunctionControl(2, "3", std::bind(&ForumServer::setTagVisibleAdministrator, this, std::placeholders::_1, std::placeholders::_2)); + server_.functionMap2_["setPostSizeAdministrator"] = HTTPServerBoot::FunctionControl(2, "3", std::bind(&ForumServer::setPostSizeAdministrator, this, std::placeholders::_1, std::placeholders::_2)); + updatePostPushQuery(); + server_.setAuthentication(std::bind(&ForumServer::authentication, this, std::placeholders::_1));//setCommentAuthentication +} +} diff --git a/ForumServer/ForumServer.h b/ForumServer/ForumServer.h new file mode 100644 index 0000000..53d6497 --- /dev/null +++ b/ForumServer/ForumServer.h @@ -0,0 +1,99 @@ +#ifndef ROOTIVE_FORUMSERVER_H +#define ROOTIVE_FORUMSERVER_H + +#include "HTTP/HTTPServerBoot.h" +#include +#include +#include "Database/SQLConnectionPool.h" +#include "Database/SQLQuery.h" + +namespace Rootive +{ +class ForumServer +{ + HTTPServerBoot server_; + SQLConnectionPool sqlConnectionPool_; + std::string contextPath_; + std::unique_ptr postPushQuery; + std::atomic fileCount_; + + bool authentication(const std::shared_ptr &connection); + void updatePostPushQuery(); + + /* argument(2): account, password return: { } */ + void login(const std::shared_ptr &connection, const std::vector &argumentVector); + /* argument(1): filename return: file */ + void file(const std::shared_ptr &connection, const std::vector &argumentVector); + /* authentication argument(1): file return: path */ + void uploadPostImageAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* argument(1): account return: { } */ + void accountClothing(const std::shared_ptr &connection, const std::vector &argumentVector); + /* authentication argument(0) return: { } */ + void accountClothingAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* authentication argument(0) return: [ ] */ + void accountFollowAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* authentication argument(2): limit, offset return: [ { } ] */ + void accountFollowedAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* authentication argument(2): another, follow return: affected */ + void setAccountFollowAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* argument(3): post, limit, offset return: [ { } ] */ + void commentPost(const std::shared_ptr &connection, const std::vector &argumentVector); + /* authentication argument(2); post body return: affected*/ + void setCommentAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* argument(2): post, column return: { } */ + void post(const std::shared_ptr &connection, const std::vector &argumentVector); + /* argument(2): limit, offset return: [ post ] */ + void postPush(const std::shared_ptr &connection, const std::vector &argumentVector); + /* authentication argument(2): limit, offset return: [ { } ] */ + void postFollowAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* authentication argument(1): post return: { } */ + void postLikeAndFavouritesAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* authentication argument(4): post like dislike favourites return: affected */ + void setPostLikeAndFavouritesAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* authentication argument(2): limit, offset return: [ ] */ + void postFavouritesAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* argument(3): account, limit, offset return: [ { } ] */ + void postAccount(const std::shared_ptr &connection, const std::vector &argumentVector); + /* argument(2): limit, offset return: [ { } ] */ + void postInvisibleAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* argument(2): limit, offset return: [ { } ]*/ + void postCheckingAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* argument(2): post, visible return: affected */ + void setPostVisibleAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* administrator argument(2): post, visible return: affected */ + void setPostVisibleAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector); + /* authentication argument(5): title, body, type, image, tag return: { } */ + void setPostAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + /* argument(3): tag, limit, offset return: [ ] */ + void postTag(const std::shared_ptr &connection, const std::vector &argumentVector); + /* argument(1): tag return: { } */ + void tag(const std::shared_ptr &connection, const std::vector &argumentVector); + /* argument(1): name return: [ { } ] */ + void tagName(const std::shared_ptr &connection, const std::vector &argumentVector); + /* argument(1): post return: [ ] */ + void tagPost(const std::shared_ptr &connection, const std::vector &argumentVector); + /* authentication argument(1): name return: { } */ + void setTagAuthentication(const std::shared_ptr &connection, const std::vector &argumentVector); + + /* administrator argument(0) return: { } */ + void postCheckingAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector); + /* administrator argument(2): a, b return: affected */ + void joinTagAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector); + /* administrator argument(2): tag, visible return: affected */ + void setTagVisibleAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector); + void tagPostAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector); + void tagAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector); + + /* administrator argument(2): post, size return: affected */ + void setPostSizeAdministrator(const std::shared_ptr &connection, const std::vector &argumentVector); +public: + ForumServer(EventLoop *eventLoop, const InetAddress &listenAddr, + const InetAddress &sqlAddr, const char *user, const char *pw, const char *db, + const std::string &name = "Forum", int numThread = 0, int numSQLConnection = 1, + TCPServer::EOption option = TCPServer::EOption::NonReusePort); + void run() { server_.run(); } + inline void setContextPath(const std::string &argument) { contextPath_ = argument; } +}; +} + +#endif diff --git a/HTTP/HTTPBuilder.h b/HTTP/HTTPBuilder.h new file mode 100644 index 0000000..203a617 --- /dev/null +++ b/HTTP/HTTPBuilder.h @@ -0,0 +1,57 @@ +#ifndef ROOTIVE_HTTPBUILDER_H +#define ROOTIVE_HTTPBUILDER_H + +#include +#include +#include "Basics/Noncopyable.h" +#include "IO/IOBuffer.h" +#include "Basics/StringView.h" + +#include + +namespace Rootive +{ +class HTTPBuilder : Noncopyable +{ + IOBuffer buffer_; +public: + HTTPBuilder(size_t initialSize = 1024) : buffer_(initialSize) {} + IOBuffer &getBuffer() { return buffer_; } + void writeStartLine(const StringView &l, const StringView &m, const StringView &r) + { + buffer_.write(l); buffer_.write(' '); + buffer_.write(m); buffer_.write(' '); + buffer_.write(r); buffer_.write("\r\n"); + } + void writeHeader(const StringView &l, const StringView &r) + { + buffer_.write(l); buffer_.write(": "); + buffer_.write(r); buffer_.write("\r\n"); + } + void finish() { buffer_.write("\r\n"); } + void writeEntityBody(const StringView &l) + { + char sizeBuffer[16]; + sprintf(sizeBuffer, "%d", l.getLength()); + writeHeader("Content-Length", sizeBuffer); + buffer_.write("\r\n"); + buffer_.write(l); + } + int writeEntityBody(const char *file, size_t fileSize) + { + char sizeBuffer[16]; + sprintf(sizeBuffer, "%lu", fileSize); + writeHeader("Content-Length", sizeBuffer); + + buffer_.write("\r\n"); + + int fd = open(file, O_RDONLY); + int _errno = 0; + buffer_.write(fd, fileSize, &_errno); + close(fd); + return _errno; + } +}; +} + +#endif \ No newline at end of file diff --git a/HTTP/HTTPParse.cpp b/HTTP/HTTPParse.cpp new file mode 100644 index 0000000..58a2e2e --- /dev/null +++ b/HTTP/HTTPParse.cpp @@ -0,0 +1,208 @@ +#include "HTTPParse.h" +#include +#include + +namespace Rootive +{ +void HTTPParse::update(const char *data, int size) +{ + int before = unparsedLength(); + while (true) + { + if (state_ == StartLine) + { + if (size - unparsedOffset_ > 1) + { + int begin; + int end = size - 1; + for (begin = unparsedOffset_; begin != end; ++begin) + { + if (data[begin] == '\r' && data[begin + 1] == '\n') + { + updateStartLine(data, begin); + unparsedOffset_ = offset_ = begin + 2; + break; + } + } + if (begin == end) + { + unparsedOffset_ = end; + } + } + else + { + break; + } + } + else if (state_ == Headers) + { + if (size - unparsedOffset_ > 1) + { + int begin; + int end = size - 1; + for (begin = unparsedOffset_; begin != end; ++begin) + { + if (data[begin] == '\r' && data[begin + 1] == '\n') + { + if (begin == offset_) + { + if (contentLength_) + { + state_ = EntityBody; + unparsedOffset_ = offset_ = entityBody_.first = begin + 2; + if (headersFinishedCallback_) { headersFinishedCallback_(this); } + } + else + { + state_ = Finished; + unparsedOffset_ = offset_ = begin + 2; + if (headersFinishedCallback_) { headersFinishedCallback_(this); } + if (finishedCallback_) { finishedCallback_(this); } + } + } + else + { + updateHeader(data, begin); + unparsedOffset_ = offset_ = begin + 2; + } + break; + } + } + if (begin == end) + { + unparsedOffset_ = end; + } + } + else + { + break; + } + } + else if (state_ == EntityBody) + { + if (size - unparsedOffset_ > 0) + { + int endExpected = entityBody_.first + contentLength_; + if (endExpected <= size) + { + unparsedOffset_ = offset_ = entityBody_.second = endExpected; + state_ = Finished; + if (finishedCallback_) { finishedCallback_(this); } + } + else + { + unparsedOffset_ = size; + } + } + else + { + break; + } + } + else + { + break; + } + } + if (unparsedLength() - before) + { + if (unparsedLengthIncreaseCallback_) { unparsedLengthIncreaseCallback_(this); } + } +} +void HTTPParse::clear(EType type) +{ + state_ = StartLine; + type_ = type; + contentLength_ = 0; + offset_ = 0; + unparsedOffset_ = 0; + clearOffset(method_); + clearOffset(url_); + clearOffset(version_); + clearOffset(status_); + clearOffset(reasonPhrase_); + clearOffset(entityBody_); +} +void HTTPParse::updateStartLine(const char *data, int end) +{ + int ll = indexOfNot(' ', data, offset_, end); + int lr = indexOf(' ', data, ll, end); + int ml = indexOfNot(' ', data, lr, end); + int mr = indexOf(' ', data, ml, end); + int rl = indexOfNot(' ', data, mr, end); + int rr = lastIndexOfNot(' ', data, offset_, end) + 1; + if (rl == end) + { + if (errorOccurredCallback_) { errorOccurredCallback_(this, EError::StartLineParseError, offset_, end); } + } + else + { + switch (type_) + { + case Request: + setOffset(method_, ll, lr); + setOffset(url_, ml, mr); + setOffset(version_, rl, rr); + break; + case Response: + case HeadResponse: + setOffset(version_, ll, lr); + setOffset(status_, ml, mr); + setOffset(reasonPhrase_, rl, rr); + break; + } + state_ = Headers; + if (startLineFinishedCallback_) { startLineFinishedCallback_(this); } + } +} +void HTTPParse::updateHeader(const char *data, int end) +{ + int m = indexOf(':', data, offset_, end); + if (m == end) + { + if (errorOccurredCallback_) + { errorOccurredCallback_(this, EError::HeaderParseError, offset_, end); } + } + else + { + int ll = indexOfNot(' ', data, offset_, end); + int lr = lastIndexOfNot(' ', data, ll, m - 1) + 1; + if (ll < m) + { + int rl = indexOfNot(' ', data, m + 1, end); + int rr = lastIndexOfNot(' ', data, rl, end - 1) + 1; + if (rl < end) + { + if (lr - ll == 14) + { + if (std::equal(data + ll, data + lr, "content-length", [](char a, char b)->bool { return ::tolower(a) == b; })) + { + try + { + contentLength_ = std::stoul(std::string(data + rl, data + rr)); + } + catch(const std::exception& e) + { + contentLength_ = 0; + if (errorOccurredCallback_) + { errorOccurredCallback_(this, EError::ContentLengthInvalid, offset_, end); } + } + } + } + headersList_.push_back(std::pair(Offset(ll, lr), Offset(rl, rr))); + if (headersChangedCallback_) { headersChangedCallback_(this); } + } + else + { + if (errorOccurredCallback_) + { errorOccurredCallback_(this, EError::HeaderValueLost, offset_, end); } + } + } + else + { + if (errorOccurredCallback_) + { errorOccurredCallback_(this, EError::HeaderKeyLost, offset_, end); } + } + } +} +} diff --git a/HTTP/HTTPParse.h b/HTTP/HTTPParse.h new file mode 100644 index 0000000..50a0f35 --- /dev/null +++ b/HTTP/HTTPParse.h @@ -0,0 +1,120 @@ +#ifndef ROOTIVE_HTTPPARSE_H +#define ROOTIVE_HTTPPARSE_H + +#include +#include + + +namespace Rootive +{ +struct HTTPParse +{ + enum EState + { + StartLine, Headers, EntityBody, Finished, Paused + }; + enum EError + { + StartLineParseError, ContentLengthInvalid, HeaderParseError, HeaderKeyLost, HeaderValueLost + }; + enum EType + { + Request, Response, HeadResponse + }; + typedef std::pair Offset; + typedef std::function StartLineFinishedCallback; // Omitted + typedef std::function HeadersChangedCallback; // mean the back of headersList_ is new; will not call when blank line + typedef std::function HeadersFinishedCallback; // call when blank line + typedef std::function FinishedCallback; // mean entitybody finished + typedef std::function UnparsedLengthIncreaseCallback; // have got new data but no crlf or entitybody.size < content-length + typedef std::function ErrorOccurredCallback; // error line will be ignored if continued + Offset method_; + Offset url_; + Offset version_; + Offset status_; + Offset reasonPhrase_; + std::list > headersList_; + + HTTPParse(EType type) : + state_(StartLine), type_(type), contentLength_(0), offset_(0), unparsedOffset_(0) + { + clearOffset(method_); + clearOffset(url_); + clearOffset(version_); + clearOffset(status_); + clearOffset(reasonPhrase_); + clearOffset(entityBody_); + } + + static inline void clearOffset(Offset &offset) { offset.first = 0; offset.second = 0; } + static inline void setOffset(Offset &offset, int first, int second) { offset.first = first; offset.second = second; } + + inline bool bFinished() const { return state_ == Finished; } + inline bool bPaused() const { return state_ == Paused; } + inline EState getState() const { return state_; } + inline EType getType() const { return type_; } + inline int getContentLength() const { return contentLength_; } + inline int getOffset() const { return offset_; } + inline int getUnparsedOffset() const { return unparsedOffset_; } + inline const Offset &getEntityBody() const { return entityBody_; } + + inline void setStartLineFinishedCallback(const StartLineFinishedCallback &callback) { startLineFinishedCallback_ = callback; } + inline void setHeadersChangedCallback(const HeadersChangedCallback &callback) { headersChangedCallback_ = callback; } + inline void setHeadersFinishedCallback(const HeadersFinishedCallback &callback) { headersFinishedCallback_ = callback; } + inline void setFinishedCallback(const FinishedCallback &callback) { finishedCallback_ = callback; } + inline void setUnparsedLengthIncreaseCallback(const UnparsedLengthIncreaseCallback &callback) { unparsedLengthIncreaseCallback_ = callback; } + inline void setErrorOccurredCallback(const ErrorOccurredCallback &callback) { errorOccurredCallback_ = callback; } + + inline int unparsedLength() const { return unparsedOffset_ - offset_; } + + inline void pause() { state_ = Paused; } + void update(const char *data, int size); + void clear(EType type); +private: + EState state_; + EType type_; + int contentLength_; + int offset_; + int unparsedOffset_; + Offset entityBody_; + + StartLineFinishedCallback startLineFinishedCallback_; + HeadersChangedCallback headersChangedCallback_; + HeadersFinishedCallback headersFinishedCallback_; + FinishedCallback finishedCallback_; + UnparsedLengthIncreaseCallback unparsedLengthIncreaseCallback_; + ErrorOccurredCallback errorOccurredCallback_; + + static inline int indexOf(char ch, const char *data, const int begin, const int end) + { + int ret; + for (ret = begin; ret < end; ++ret) + { + if (data[ret] == ch) { break; } + } + return ret; + } + static inline int indexOfNot(char ch, const char *data, const int begin, const int end) + { + int ret; + for (ret = begin; ret < end; ++ret) + { + if (data[ret] != ch) { break; } + } + return ret; + } + static inline int lastIndexOfNot(char ch, const char *data, const int begin, const int end) + { + int ret; + for (ret = end; ret >= begin; --ret) + { + if (data[ret] != ch) { break; } + } + return ret; + } + void updateStartLine(const char *data, int end); + void updateHeader(const char *data, int end); +}; +} + +#endif \ No newline at end of file diff --git a/HTTP/HTTPServer.cpp b/HTTP/HTTPServer.cpp new file mode 100644 index 0000000..7070ac6 --- /dev/null +++ b/HTTP/HTTPServer.cpp @@ -0,0 +1,89 @@ +#include "HTTPServer.h" +#include "Log/LogLine.h" + +#include + +namespace Rootive +{ +void HTTPServer::onConnection(const std::shared_ptr &conn) +{ + if (conn->bConnected() && !conn->context_) + { + conn->setTCPNodelay(true); + conn->context_ = new ConnectionContext; + auto &parse = getContext(conn)->parse_; + parse.setStartLineFinishedCallback(std::bind(&HTTPServer::onStartLineFinished, this, conn, std::placeholders::_1)); + parse.setHeadersChangedCallback(std::bind(&HTTPServer::onHeadersChanged, this, conn, std::placeholders::_1)); + parse.setHeadersFinishedCallback(std::bind(&HTTPServer::onHeadersFinished, this, conn, std::placeholders::_1)); + parse.setFinishedCallback(std::bind(&HTTPServer::onFinished, this, conn, std::placeholders::_1)); + parse.setUnparsedLengthIncreaseCallback(std::bind(&HTTPServer::onUnparsedLengthIncrease, this, conn, std::placeholders::_1)); + parse.setErrorOccurredCallback(std::bind(&HTTPServer::onErrorOccurred, this, conn, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + if (connectionCallback_) { connectionCallback_(conn); } + } + if (conn->bDisconnected() && conn->context_) + { + if (connectionCallback_) { connectionCallback_(conn); } + delete getContext(conn); + conn->context_ = nullptr; + } +} +void HTTPServer::onMessage(const std::shared_ptr &conn, std::chrono::system_clock::time_point time) +{ + auto buffer = conn->inputBuffer(); + ConnectionContext *context = getContext(conn); + auto &parse = context->parse_; + while (buffer->readableLength()) + { + context->time_ = time; + parse.update(buffer->readBegin(), buffer->readableLength()); + if (parse.bFinished()) + { + buffer->hasRead(parse.getOffset()); + context->clear(); + } + else { break; } + } +} +void HTTPServer::onWriteFinished(const std::shared_ptr &conn) +{ + ROOTIVE_LOG_INFO << conn->getName(); +} +void HTTPServer::onStartLineFinished(const std::shared_ptr &conn, const HTTPParse *placeholder) +{ + if (startLineFinishedCallback_) + { startLineFinishedCallback_(conn); } +} +void HTTPServer::onHeadersChanged(const std::shared_ptr &conn, const HTTPParse *placeholder) +{ + if (headersChangedCallback_) + { headersChangedCallback_(conn); } +} +void HTTPServer::onHeadersFinished(const std::shared_ptr &conn, const HTTPParse *placeholder) +{ + if (headersFinishedCallback_) + { headersFinishedCallback_(conn); } +} +void HTTPServer::onFinished(const std::shared_ptr &conn, const HTTPParse *placeholder) +{ + if (finishedCallback_) + { finishedCallback_(conn); } +} +void HTTPServer::onUnparsedLengthIncrease(const std::shared_ptr &conn, const HTTPParse *placeholder) +{ + if (unparsedLengthIncreaseCallback_) + { unparsedLengthIncreaseCallback_(conn); } +} +void HTTPServer::onErrorOccurred(const std::shared_ptr &conn, const HTTPParse *placeholder, HTTPParse::EError _errno, int begin, int end) +{ + if (errorOccurredCallback_) + { errorOccurredCallback_(conn, _errno, begin, end); } +} +HTTPServer::HTTPServer(EventLoop *eventLoop, const InetAddress &listenAddr, +const std::string &name, int numThread, TCPServer::EOption option) : +server_(eventLoop, listenAddr, name, numThread, option) +{ + server_.setConnectionCallback(std::bind(&HTTPServer::onConnection, this, std::placeholders::_1)); + server_.setMessageCallback(std::bind(&HTTPServer::onMessage, this, std::placeholders::_1, std::placeholders::_2)); + server_.setWriteFinishedCallback(&HTTPServer::onWriteFinished); +} +} \ No newline at end of file diff --git a/HTTP/HTTPServer.h b/HTTP/HTTPServer.h new file mode 100644 index 0000000..5e4d014 --- /dev/null +++ b/HTTP/HTTPServer.h @@ -0,0 +1,72 @@ +#ifndef ROOTIVE_HTTPSERVER_H +#define ROOTIVE_HTTPSERVER_H + +#include +#include "Basics/Noncopyable.h" +#include "TCP/TCPServer.h" +#include "HTTP/HTTPParse.h" + +namespace Rootive +{ +class HTTPServer : Noncopyable +{ +public: + class ConnectionContext + { + friend class HTTPServer; + HTTPParse parse_; + std::chrono::system_clock::time_point time_; + public: + void *context_; + ConnectionContext() : + parse_(HTTPParse::EType::Request), context_(nullptr) {} + void clear() + { + parse_.clear(HTTPParse::EType::Request); + } + }; + typedef std::function &)> Callback; + typedef std::function &, HTTPParse::EError, int, int)> ErrorOccurredCallback; +private: + TCPServer server_; + Callback connectionCallback_; + Callback startLineFinishedCallback_; + Callback headersChangedCallback_; + Callback headersFinishedCallback_; + Callback finishedCallback_; + Callback unparsedLengthIncreaseCallback_; + ErrorOccurredCallback errorOccurredCallback_; + void onConnection(const std::shared_ptr &conn); + void onMessage(const std::shared_ptr &conn, std::chrono::system_clock::time_point time); + static void onWriteFinished(const std::shared_ptr &conn); + void onStartLineFinished(const std::shared_ptr &conn, const HTTPParse *placeholder); + void onHeadersChanged(const std::shared_ptr &conn, const HTTPParse *placeholder); + void onHeadersFinished(const std::shared_ptr &conn, const HTTPParse *placeholder); + void onFinished(const std::shared_ptr &conn, const HTTPParse *placeholder); + void onUnparsedLengthIncrease(const std::shared_ptr &conn, const HTTPParse *placeholder); + void onErrorOccurred(const std::shared_ptr &conn, const HTTPParse *placeholder, HTTPParse::EError _errno, int begin, int end); +public: + HTTPServer(EventLoop *eventLoop, const InetAddress &listenAddr, + const std::string &name = "HTTPServer", int numThread = 0, TCPServer::EOption option = TCPServer::EOption::NonReusePort); + + static inline ConnectionContext *getContext(const std::shared_ptr &conn) + { return static_cast(conn->context_); } + static inline HTTPParse &getHTTPParse(const std::shared_ptr &conn) + { return getContext(conn)->parse_; } + static inline std::chrono::system_clock::time_point getTime(const std::shared_ptr &conn) + { return getContext(conn)->time_; } + + inline const std::string &getName() const { return server_.getName(); } + void setConnectionCallback(const Callback &callback) { connectionCallback_ = callback; } + void setStartLineFinishedCallback(const Callback &callback) { startLineFinishedCallback_ = callback; } + void setHeadersChangedCallback(const Callback &callback) { headersChangedCallback_ = callback; } + void setHeadersFinishedCallback(const Callback &callback) { headersFinishedCallback_ = callback; } + void setFinishedCallback(const Callback &callback) { finishedCallback_ = callback; } + void setUnparsedLengthIncreaseCallback_(const Callback &callback) { unparsedLengthIncreaseCallback_ = callback; }; + void setErrorOccurredCallback(const ErrorOccurredCallback &callback) { errorOccurredCallback_ = callback; } + inline void run() { server_.run(); } +}; +} + + +#endif \ No newline at end of file diff --git a/HTTP/HTTPServerBoot.cpp b/HTTP/HTTPServerBoot.cpp new file mode 100644 index 0000000..8459b87 --- /dev/null +++ b/HTTP/HTTPServerBoot.cpp @@ -0,0 +1,144 @@ +#include "HTTPServerBoot.h" +#include +#include "Database/SQLQuery.h" +#include "Log/LogLine.h" + +#include + +namespace Rootive +{ +void HTTPServerBoot::defaultUnauthorizedCallback(const std::shared_ptr &conn) +{ + cold(conn, "401"); + HTTPServer::getHTTPParse(conn).pause(); +} +void HTTPServerBoot::onConnection(const std::shared_ptr &conn) +{ + HTTPServer::ConnectionContext *context = HTTPServer::getContext(conn); + if (conn->bConnected() && !context->context_) + { + context->context_ = new ConnectionContext; + } + if (conn->bDisconnected() && context->context_) + { + delete static_cast(context->context_); + context->context_ = nullptr; + } +} +void HTTPServerBoot::onStartLineFinished(const std::shared_ptr &conn) +{ + bool bFinished = false; + auto context = getContext(conn); + auto &parse = HTTPServer::getHTTPParse(conn); + StringView url(parse.url_.first + conn->inputBuffer()->readBegin(), parse.url_.second - parse.url_.first); + int ll = 1; + int lr = url.indexOf('/', 1); + int rl = lr + 1; + int rr = url.indexOf('/', rl); + if (ll < lr && rl < rr) + { + if (url.mid(ll, lr) == getName()) + { + const auto it = functionMap2_.find(url.mid(rl, rr).toString()); + if (it != functionMap2_.cend()) + { + if (context->url_.find("/..") == std::string::npos) + { + context->it_ = it; + context->url_ = url.mid(std::min(rr + 1, url.getLength())).toString(); + bFinished = true; + } + } + } + } + if (!bFinished) + { + cold(conn, "404"); + parse.pause(); + ROOTIVE_LOG_INFO << conn->getName() << ": 404"; + } +} +void HTTPServerBoot::onHeadersChanged(const std::shared_ptr &conn) +{ + auto context = getContext(conn); + if (!context->it_->second.type_.empty()) + { + auto &parse = HTTPServer::getHTTPParse(conn); + const auto &readBegin = conn->inputBuffer()->readBegin(); + const auto &keyOffset = parse.headersList_.back().first; + const auto &valueOffset = parse.headersList_.back().second; + StringView key(keyOffset.first + readBegin, keyOffset.second - keyOffset.first); + if (key == "username") + { + context->account_.set(valueOffset.first + readBegin, valueOffset.second - valueOffset.first); + } + else if (key == "password") + { + context->password_.set(valueOffset.first + readBegin, valueOffset.second - valueOffset.first); + } + } +} +void HTTPServerBoot::onHeadersFinished(const std::shared_ptr &conn) +{ + auto context = getContext(conn); + if (!context->it_->second.type_.empty() && authentication_ && !authentication_(conn)) + { + if (unauthorizedCallback_) { unauthorizedCallback_(conn); } + HTTPServer::getHTTPParse(conn).pause(); + ROOTIVE_LOG_INFO << conn->getName() << ": unauthorized"; + } +} +void HTTPServerBoot::onFinished(const std::shared_ptr &conn) +{ + auto context = getContext(conn); + auto &parse = HTTPServer::getHTTPParse(conn); + const auto &function = context->it_->second; + const auto &_entityBody = parse.getEntityBody(); + StringView entityBody(conn->inputBuffer()->readBegin() + _entityBody.first, _entityBody.second - _entityBody.first); + std::vector argumentVector; + if (!context->url_.empty()) { argumentVector.push_back(context->url_); } + int index; + while (function.argument_ - argumentVector.size() > 1 && (index = entityBody.indexOfCRLF()) < entityBody.getLength()) + { + argumentVector.push_back(entityBody.cutPrefix(index)); + entityBody.removePrefix(2); + } + if (!entityBody.bEmpty()) { argumentVector.push_back(entityBody); } + if (function.argument_ <= argumentVector.size()) { function.function_(conn, argumentVector); } + else + { + cold(conn, "400"); + parse.pause(); + ROOTIVE_LOG_INFO << conn->getName() << " give " << argumentVector.size() << " instead of " << function.argument_; + } + getContext(conn)->clear(); +} +void HTTPServerBoot::onErrorOccurred(const std::shared_ptr &conn, HTTPParse::EError _errno, int begin, int end) +{ + auto &parse = HTTPServer::getHTTPParse(conn); + ROOTIVE_LOG_INFO << conn->getName(); + parse.pause(); + cold(conn, "400"); +} +HTTPServerBoot::HTTPServerBoot(EventLoop *eventLoop, const InetAddress &listenAddr, +const std::string &name, int numThread, TCPServer::EOption option) : +server_(eventLoop, listenAddr, name, numThread, option), unauthorizedCallback_(defaultUnauthorizedCallback) +{ + server_.setConnectionCallback(std::bind(&HTTPServerBoot::onConnection, this, std::placeholders::_1)); + server_.setStartLineFinishedCallback(std::bind(&HTTPServerBoot::onStartLineFinished, this, std::placeholders::_1)); + server_.setHeadersChangedCallback(std::bind(&HTTPServerBoot::onHeadersChanged, this, std::placeholders::_1)); + server_.setHeadersFinishedCallback(std::bind(&HTTPServerBoot::onHeadersFinished, this, std::placeholders::_1)); + server_.setFinishedCallback(std::bind(&HTTPServerBoot::onFinished, this, std::placeholders::_1)); + server_.setErrorOccurredCallback(std::bind(&HTTPServerBoot::onErrorOccurred, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); +} +void HTTPServerBoot::cold(const std::shared_ptr &conn, +const StringView &status, const StringView &reason, const StringView &version) +{ + ROOTIVE_LOG_TRACE << conn->getName(); + HTTPBuilder builder(64); + builder.writeStartLine(version, status, reason); + builder.finish(); + conn->send(builder.getBuffer()); + conn->forceClose(); +} +} diff --git a/HTTP/HTTPServerBoot.h b/HTTP/HTTPServerBoot.h new file mode 100644 index 0000000..a90b1ef --- /dev/null +++ b/HTTP/HTTPServerBoot.h @@ -0,0 +1,75 @@ +#ifndef ROOTIVE_HTTPSERVERBOOT_H +#define ROOTIVE_HTTPSERVERBOOT_H + +#include +#include "HTTP/HTTPServer.h" +#include "HTTP/HTTPBuilder.h" + +namespace Rootive +{ +class HTTPServerBoot +{ +public: + typedef std::function &, const std::vector &)> Function; + typedef std::function &)> Authentication; + typedef std::function &)> UnauthorizedCallback; + struct FunctionControl + { + int argument_; + std::string type_; + Function function_; + }; + class ConnectionContext + { + friend class HTTPServerBoot; + std::string url_; + std::map::const_iterator it_; + StringView account_; + StringView password_; + public: + void *context_; + void clear() + { + url_.clear(); + account_.clear(); + password_.clear(); + } + }; +private: + HTTPServer server_; + Authentication authentication_; + UnauthorizedCallback unauthorizedCallback_; + static void defaultUnauthorizedCallback(const std::shared_ptr &conn); + void onConnection(const std::shared_ptr &conn); + void onStartLineFinished(const std::shared_ptr &conn); + void onHeadersChanged(const std::shared_ptr &conn); + void onHeadersFinished(const std::shared_ptr &conn); + void onFinished(const std::shared_ptr &conn); + void onErrorOccurred(const std::shared_ptr &conn, HTTPParse::EError _errno, int begin, int end); +public: + std::map functionMap2_; + + HTTPServerBoot(EventLoop *eventLoop, const InetAddress &listenAddr, + const std::string &name = "HTTPServerBoot", int numThread = 0, TCPServer::EOption option = TCPServer::EOption::NonReusePort); + + static inline ConnectionContext *getContext(const std::shared_ptr &conn) + { return static_cast(HTTPServer::getContext(conn)->context_); } + static inline const StringView &getAccount(const std::shared_ptr &conn) + { return getContext(conn)->account_; } + static inline const StringView &getPassword(const std::shared_ptr &conn) + { return getContext(conn)->password_; } + static inline const std::map::const_iterator &getIT(const std::shared_ptr &conn) + { return getContext(conn)->it_; } + static inline std::chrono::system_clock::time_point getTime(const std::shared_ptr &conn) + { return HTTPServer::getTime(conn); } + const std::string &getName() const { return server_.getName(); } + + static void cold(const std::shared_ptr &conn, const StringView &status, + const StringView &reason = "Omitted", const StringView &version = "HTTP/1.1"); + inline void setAuthentication(Authentication function) { authentication_ = function; } + inline void setUnauthorizedCallback(UnauthorizedCallback callback) { unauthorizedCallback_ = callback; } + inline void run() { server_.run(); } +}; +} + +#endif \ No newline at end of file diff --git a/Hash/MD5.cpp b/Hash/MD5.cpp new file mode 100644 index 0000000..c5e0e9b --- /dev/null +++ b/Hash/MD5.cpp @@ -0,0 +1,235 @@ + +#include "MD5.h" +#include + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) +#define ROTATE_LEFT(x, n) (((x) << (n)) | (( (unsigned int) x) >> (32-(n)))) + +#define FF(a, b, c, d, x, s, ac) \ +{ \ + (a) += F ((b), (c), (d)) + (x) + (unsigned long int)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ +} +#define GG(a, b, c, d, x, s, ac) \ +{ \ + (a) += G ((b), (c), (d)) + (x) + (unsigned long int)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ +} +#define HH(a, b, c, d, x, s, ac) \ +{ \ + (a) += H ((b), (c), (d)) + (x) + (unsigned long int)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ +} +#define II(a, b, c, d, x, s, ac) \ +{ \ + (a) += I ((b), (c), (d)) + (x) + (unsigned long int)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ +} + +namespace Rootive +{ +const unsigned char MD5::padding_[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +void MD5::_transform(unsigned long int state[4], const unsigned char block[64]) +{ + unsigned long int a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + _decode(x, block, 64); + + FF(a, b, c, d, x[0], S11, 0xd76aa478); + FF(d, a, b, c, x[1], S12, 0xe8c7b756); + FF(c, d, a, b, x[2], S13, 0x242070db); + FF(b, c, d, a, x[3], S14, 0xc1bdceee); + FF(a, b, c, d, x[4], S11, 0xf57c0faf); + FF(d, a, b, c, x[5], S12, 0x4787c62a); + FF(c, d, a, b, x[6], S13, 0xa8304613); + FF(b, c, d, a, x[7], S14, 0xfd469501); + FF(a, b, c, d, x[8], S11, 0x698098d8); + FF(d, a, b, c, x[9], S12, 0x8b44f7af); + FF(c, d, a, b, x[10], S13, 0xffff5bb1); + FF(b, c, d, a, x[11], S14, 0x895cd7be); + FF(a, b, c, d, x[12], S11, 0x6b901122); + FF(d, a, b, c, x[13], S12, 0xfd987193); + FF(c, d, a, b, x[14], S13, 0xa679438e); + FF(b, c, d, a, x[15], S14, 0x49b40821); + + GG(a, b, c, d, x[1], S21, 0xf61e2562); + GG(d, a, b, c, x[6], S22, 0xc040b340); + GG(c, d, a, b, x[11], S23, 0x265e5a51); + GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); + GG(a, b, c, d, x[5], S21, 0xd62f105d); + GG(d, a, b, c, x[10], S22, 0x2441453); + GG(c, d, a, b, x[15], S23, 0xd8a1e681); + GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); + GG(a, b, c, d, x[9], S21, 0x21e1cde6); + GG(d, a, b, c, x[14], S22, 0xc33707d6); + GG(c, d, a, b, x[3], S23, 0xf4d50d87); + GG(b, c, d, a, x[8], S24, 0x455a14ed); + GG(a, b, c, d, x[13], S21, 0xa9e3e905); + GG(d, a, b, c, x[2], S22, 0xfcefa3f8); + GG(c, d, a, b, x[7], S23, 0x676f02d9); + GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); + + HH(a, b, c, d, x[5], S31, 0xfffa3942); + HH(d, a, b, c, x[8], S32, 0x8771f681); + HH(c, d, a, b, x[11], S33, 0x6d9d6122); + HH(b, c, d, a, x[14], S34, 0xfde5380c); + HH(a, b, c, d, x[1], S31, 0xa4beea44); + HH(d, a, b, c, x[4], S32, 0x4bdecfa9); + HH(c, d, a, b, x[7], S33, 0xf6bb4b60); + HH(b, c, d, a, x[10], S34, 0xbebfbc70); + HH(a, b, c, d, x[13], S31, 0x289b7ec6); + HH(d, a, b, c, x[0], S32, 0xeaa127fa); + HH(c, d, a, b, x[3], S33, 0xd4ef3085); + HH(b, c, d, a, x[6], S34, 0x4881d05); + HH(a, b, c, d, x[9], S31, 0xd9d4d039); + HH(d, a, b, c, x[12], S32, 0xe6db99e5); + HH(c, d, a, b, x[15], S33, 0x1fa27cf8); + HH(b, c, d, a, x[2], S34, 0xc4ac5665); + + II(a, b, c, d, x[0], S41, 0xf4292244); + II(d, a, b, c, x[7], S42, 0x432aff97); + II(c, d, a, b, x[14], S43, 0xab9423a7); + II(b, c, d, a, x[5], S44, 0xfc93a039); + II(a, b, c, d, x[12], S41, 0x655b59c3); + II(d, a, b, c, x[3], S42, 0x8f0ccc92); + II(c, d, a, b, x[10], S43, 0xffeff47d); + II(b, c, d, a, x[1], S44, 0x85845dd1); + II(a, b, c, d, x[8], S41, 0x6fa87e4f); + II(d, a, b, c, x[15], S42, 0xfe2ce6e0); + II(c, d, a, b, x[6], S43, 0xa3014314); + II(b, c, d, a, x[13], S44, 0x4e0811a1); + II(a, b, c, d, x[4], S41, 0xf7537e82); + II(d, a, b, c, x[11], S42, 0xbd3af235); + II(c, d, a, b, x[2], S43, 0x2ad7d2bb); + II(b, c, d, a, x[9], S44, 0xeb86d391); + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + memset((unsigned char*)x, 0, sizeof(x)); +} +void MD5::_encode(unsigned char* output, const unsigned long int* input, unsigned int len) +{ + for (unsigned int _i = 0, _j = 0; _j < len; ++_i, _j += 4) + { + output[_j] = (unsigned char)(input[_i] & 0xff); + output[_j + 1] = (unsigned char)((input[_i] >> 8) & 0xff); + output[_j + 2] = (unsigned char)((input[_i] >> 16) & 0xff); + output[_j + 3] = (unsigned char)((input[_i] >> 24) & 0xff); + } +} +void MD5::_decode(unsigned long int* output, const unsigned char* input, unsigned int len) +{ + for (unsigned int _i = 0, _j = 0; _j < len; _i++, _j += 4) + { + output[_i] = ((unsigned long int)input[_j]) | (((unsigned long int)input[_j + 1]) << 8) | + (((unsigned long int)input[_j + 2]) << 16) | (((unsigned long int)input[_j + 3]) << 24); + } +} +void MD5::_memcpy(unsigned char* output, const unsigned char* input, unsigned int len) +{ + for (unsigned int _i = 0; _i < len; _i++) { output[_i] = input[_i]; } +} +void MD5::_memset(unsigned char* output, int value, unsigned int len) +{ + for (unsigned int _i = 0; _i < len; _i++) { ((char*)output)[_i] = (char)value; } +} +void MD5::_init(Context* context) +{ + context->count[0] = context->count[1] = 0; + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} +void MD5::_update(Context* context, const unsigned char* input, unsigned int inputLen) +{ + unsigned int _i, index, partLen; + + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + if ((context->count[0] += ((unsigned long int)inputLen << 3)) < ((unsigned long int)inputLen << 3)) + { context->count[1]++; } + + context->count[1] += ((unsigned long int)inputLen >> 29); + partLen = 64 - index; + + if (inputLen >= partLen) + { + _memcpy((unsigned char*)&context->buffer[index], (unsigned char*)input, partLen); + _transform(context->state, context->buffer); + + for (_i = partLen; _i + 63 < inputLen; _i += 64) + { _transform(context->state, &input[_i]); } + + index = 0; + } + else + { _i = 0; } + + _memcpy((unsigned char*)&context->buffer[index], (unsigned char*)&input[_i], inputLen - _i); +} +void MD5::_final(unsigned char digest[16], Context* context) +{ + unsigned char bits[8]; + unsigned int index, padLen; + + _encode(bits, context->count, 8); + + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + _update(context, padding_, padLen); + + _update(context, bits, 8); + + _encode(digest, context->state, 16); + + memset((unsigned char*)context, 0, sizeof(*context)); +} +std::string MD5::get(const char* data, size_t length) +{ + _init(&context_); + _update(&context_, (unsigned char*)data, length); + unsigned char buffer[16] = ""; + _final(buffer, &context_); + std::ostringstream ostream; + for (int _i = 0; _i < sizeof(buffer); ++_i) + { + ostream.width(2); ostream.fill('0'); + ostream << std::hex << static_cast(buffer[_i]); + } + return ostream.str(); +} +} \ No newline at end of file diff --git a/Hash/MD5.h b/Hash/MD5.h new file mode 100644 index 0000000..a061780 --- /dev/null +++ b/Hash/MD5.h @@ -0,0 +1,33 @@ +#ifndef ROOTIVE_MD5_H +#define ROOTIVE_MD5_H + +#include +#include + +namespace Rootive +{ +class MD5 +{ + struct Context + { + unsigned long int state[4]; + unsigned long int count[2]; + unsigned char buffer[64]; + }; + Context context_; + static const unsigned char padding_[64]; + void _transform(unsigned long int state[4], const unsigned char block[64]); + void _encode(unsigned char* output, const unsigned long int* input, unsigned int len); + void _decode(unsigned long int* output, const unsigned char* input, unsigned int len); + void _memcpy(unsigned char* output, const unsigned char* input, unsigned int len); + void _memset(unsigned char* output, int value, unsigned int len); + void _init(Context* context); + void _update(Context* context, const unsigned char* input, unsigned int inputLen); + void _final(unsigned char digest[16], Context* context); +public: + std::string get(const char* data, size_t length); + inline std::string get(const char* data) { return get(data, strlen(data)); } +}; +} + +#endif \ No newline at end of file diff --git a/IO/DoubleBufferingOutput.cpp b/IO/DoubleBufferingOutput.cpp new file mode 100644 index 0000000..ecf81bf --- /dev/null +++ b/IO/DoubleBufferingOutput.cpp @@ -0,0 +1,55 @@ +#include "DoubleBufferingOutput.h" +#include +#include +#include +#include + +using namespace Rootive; + +DoubleBufferingOutput::DoubleBufferingOutput(OutputFileInterface *file, const std::chrono::seconds &interval) : +file_(file), interval_(interval), bRun_(false) +{ + thread_ = new std::thread(&DoubleBufferingOutput::threadFunc, this); + while (!bRun_); +} + +DoubleBufferingOutput::~DoubleBufferingOutput() +{ + bRun_ = false; + thread_->join(); + delete thread_; +} + +void DoubleBufferingOutput::write(const void *data, size_t size) +{ + { + std::lock_guard lock(mutexAvailable_); + available_->append(static_cast(data), size); + } + condAvailable_.notify_one(); +} + +void DoubleBufferingOutput::threadFunc() +{ + available_ = new std::string; + fulled_ = new std::string; + bRun_ = true; + do + { + { + std::unique_lock lock(mutexAvailable_); + condAvailable_.wait_for(lock, interval_, [this]()-> bool { return available_->size(); } ); + std::swap(available_, fulled_); + } + file_->write(fulled_->data(), fulled_->size()); + fulled_->clear(); + file_->flush(); + } while (bRun_); + file_->write(fulled_->data(), fulled_->size()); + { + std::lock_guard lock(mutexAvailable_); + file_->write(available_->data(), available_->size()); + } + delete available_; + delete fulled_; +} \ No newline at end of file diff --git a/IO/DoubleBufferingOutput.h b/IO/DoubleBufferingOutput.h new file mode 100644 index 0000000..d7ff9f0 --- /dev/null +++ b/IO/DoubleBufferingOutput.h @@ -0,0 +1,44 @@ +#ifndef ROOTIVE_DOUBLEBUFFERINGOUTPUT_H +#define ROOTIVE_DOUBLEBUFFERINGOUTPUT_H + +#include +#include +#include +#include +#include +#include +#include +#include "IO/OutputFileInterface.h" +#include "IO/OutputInterface.h" + +namespace Rootive +{ +class DoubleBufferingOutput : public OutputInterface +{ +public: + + DoubleBufferingOutput(OutputFileInterface *file = new DefaultOutputFile, const std::chrono::seconds &interval = std::chrono::seconds(3)); + virtual ~DoubleBufferingOutput(); + virtual void write(const void *data, size_t size) override; + +private: + + std::unique_ptr file_; + + std::string *available_; + std::string *fulled_; + std::chrono::seconds interval_; + + std::mutex mutexAvailable_; + std::condition_variable condAvailable_; + + std::atomic bRun_; + std::thread *thread_; + + void threadFunc(); + +}; + +} + +#endif \ No newline at end of file diff --git a/IO/IOBuffer.cpp b/IO/IOBuffer.cpp new file mode 100644 index 0000000..b63005d --- /dev/null +++ b/IO/IOBuffer.cpp @@ -0,0 +1,37 @@ +#include "IOBuffer.h" + +#include + +namespace Rootive +{ + +ssize_t IOBuffer::write(int fd, const size_t max, int* out_errno) +{ + char *extra = (char *)malloc(max); + iovec vec[2]; + const size_t writable = writableLength(); + vec[0].iov_base = writeBegin(); + vec[0].iov_len = writable; + vec[1].iov_base = extra; + vec[1].iov_len = max; + const int iovcnt = (writable >= max) ? 1 : 2; + const ssize_t ret = ::readv(fd, vec, iovcnt); + if (ret < 0) { *out_errno = errno; } + else if (static_cast(ret) <= writable) { hasWritten(ret); } + else { writeIndex_ = buffer_.size(); write(extra, ret - writable); } + delete extra; + return ret; +} +void IOBuffer::makeSpace(size_t len) +{ + size_t _readableLength = readableLength(); + int _i = constexpr_initialPrependSize + _readableLength + len; + if (_i > buffer_.size()) { buffer_.resize(_i); } + std::copy(begin() + readIndex_, writeBegin(), begin() + constexpr_initialPrependSize); + readIndex_ = constexpr_initialPrependSize; + writeIndex_ = readIndex_ + _readableLength; +} + +} + + diff --git a/IO/IOBuffer.h b/IO/IOBuffer.h new file mode 100644 index 0000000..21197dc --- /dev/null +++ b/IO/IOBuffer.h @@ -0,0 +1,93 @@ +#ifndef ROOTIVE_IOBUFFER_H +#define ROOTIVE_IOBUFFER_H + +#include +#include +#include + +#include "Basics/StringView.h" + +namespace Rootive +{ + +class IOBuffer +{ + std::vector buffer_; + size_t readIndex_; + size_t writeIndex_; + + inline const char *begin() const { return &*buffer_.begin(); } + inline char *begin() { return &*buffer_.begin(); } +public: + static constexpr size_t constexpr_initialPrependSize = 8; + explicit IOBuffer(size_t initialSize) + : buffer_(constexpr_initialPrependSize + initialSize), readIndex_(constexpr_initialPrependSize), writeIndex_(constexpr_initialPrependSize) {} + + inline void swap(IOBuffer &another) + { + buffer_.swap(another.buffer_); + std::swap(readIndex_, another.readIndex_); + std::swap(writeIndex_, another.writeIndex_); + } + inline size_t readableLength() const { return writeIndex_ - readIndex_; } + inline size_t writableLength() const { return buffer_.size() - writeIndex_; } + inline size_t prependableLength() const { return readIndex_; } + + inline const char *readBegin() const { return begin() + readIndex_; } + inline void hasRead(size_t len) + { + if (len < readableLength()) { readIndex_ += len; } + else { hasReadAll(); } + } + inline void hasReadAll() { readIndex_ = constexpr_initialPrependSize; writeIndex_ = constexpr_initialPrependSize; } + inline StringView toStringView(int length) const + { + if (length < readableLength()) + { + return StringView(readBegin(), length); + } + else + { + return StringView(readBegin(), static_cast(readableLength())); + } + } + inline StringView toStringView() const { return StringView(readBegin(), static_cast(readableLength())); } + inline std::string readAsString(size_t len) { std::string ret(readBegin(), len); hasRead(len); return ret; } + inline std::string readAllAsString() { return readAsString(readableLength()); } + + inline char *writeBegin() { return begin() + writeIndex_; } + inline const char *writeBegin() const { return begin() + writeIndex_; } + inline void hasWritten(size_t len) { writeIndex_ += len; } + inline void unWrite(size_t len) { writeIndex_ -= len; } + void makeSpace(size_t len); + inline void ensureWritableLength(size_t len) { if (writableLength() < len) { makeSpace(len); } } + inline void write(const char *data, size_t len) + { + ensureWritableLength(len); + std::copy(data, data + len, writeBegin()); + hasWritten(len); + } + inline void write(const void *data, size_t len) { write(static_cast(data), len); } + inline void write(const StringView &str) { write(str.begin(), str.getLength()); } + ssize_t write(int fd, const size_t max, int* out_errno); + inline void write(char ch) { write(&ch, 1); } + + void prepend(const void *data, size_t len) + { + readIndex_ -= len; + const char *_data = static_cast(data); + std::copy(_data, _data + len, begin() + readIndex_); + } + + void shrink(size_t reserve) + { + makeSpace(reserve); + buffer_.shrink_to_fit(); + } + size_t capacity() const { return buffer_.capacity(); } + +}; + +} + +#endif diff --git a/IO/OutputFile.h b/IO/OutputFile.h new file mode 100644 index 0000000..2374b1b --- /dev/null +++ b/IO/OutputFile.h @@ -0,0 +1,42 @@ +#ifndef ROOTIVE_OUTPUTFILE_H +#define ROOTIVE_OUTPUTFILE_H + +#include "IO/OutputFileInterface.h" +#include "Basics/StringView.h" +#include "Log/LogLine.h" +#include +#include +#include + +namespace Rootive +{ +class OutputFile : public OutputFileInterface +{ + std::string filename_; + int fd_; + int writtenSize_; +public: + OutputFile(const StringView &filename) : + filename_(filename.begin(), filename.getLength()), + fd_(::open(filename_.c_str(), O_WRONLY | O_APPEND | O_CREAT)), writtenSize_(0) + { + if (fd_ < 0) + { + ROOTIVE_LOG_SYSTEMERROR; + } + } + ~OutputFile() { ::close(fd_); } + int getWrittenSize() const { return writtenSize_; } + virtual void write(const void *data, size_t size) override + { + auto res = ::write(fd_, data, size); + if (res < 0) { ROOTIVE_LOG_SYSTEMERROR; } + else { writtenSize_ += res; } + + } + virtual void flush() override + { ::fsync(fd_); } +}; +} + +#endif diff --git a/IO/OutputFileInterface.h b/IO/OutputFileInterface.h new file mode 100644 index 0000000..e0f519f --- /dev/null +++ b/IO/OutputFileInterface.h @@ -0,0 +1,25 @@ +#ifndef ROOTIVE_OUTPUTFILEINTERFACE_H +#define ROOTIVE_OUTPUTFILEINTERFACE_H + +#include +#include + +namespace Rootive +{ +class OutputFileInterface +{ +public: + virtual void write(const void *data, size_t size) = 0; + virtual void flush() = 0; + virtual ~OutputFileInterface() = default; +}; +class DefaultOutputFile : public OutputFileInterface +{ +public: + virtual void write(const void *data, size_t size) override + { ::write(1, data, size); } + virtual void flush() override { ::fsync(1); } +}; +} + +#endif \ No newline at end of file diff --git a/IO/OutputInterface.h b/IO/OutputInterface.h new file mode 100644 index 0000000..3f73ecf --- /dev/null +++ b/IO/OutputInterface.h @@ -0,0 +1,27 @@ +#ifndef ROOTIVE_OUTPUTINTERFACE_H +#define ROOTIVE_OUTPUTINTERFACE_H + +#include +#include "IO/OutputFileInterface.h" + +namespace Rootive +{ +class OutputInterface +{ +public: + virtual ~OutputInterface() = default; + virtual void write(const void *data, size_t size) = 0; +}; +class DefaultOutput : public OutputInterface +{ + std::unique_ptr file_; +public: + DefaultOutput(OutputFileInterface *file = new DefaultOutputFile) : + file_(file) + {} + virtual void write(const void *data, size_t size) override + { file_->write(data, size); } +}; +} + +#endif \ No newline at end of file diff --git a/Json/SQLToJson.cpp b/Json/SQLToJson.cpp new file mode 100644 index 0000000..e636852 --- /dev/null +++ b/Json/SQLToJson.cpp @@ -0,0 +1,27 @@ +#include "SQLToJson.h" + +namespace Rootive +{ +void SQLToJson::escapeCharacter(::std::string &arg) +{ + size_t index, begin = 0; + while ((index = arg.find('\n', begin)) != std::string::npos) + { + arg.replace(index, 1, "\\\\n"); + begin = index + 3; + } + begin = 0; + while ((index = arg.find('\"', begin)) != std::string::npos) + { + arg.replace(index, 1, "\\\\\\\""); + begin = index + 4; + } + begin = 0; + while ((index = arg.find('\'', begin)) != std::string::npos) + { + arg.replace(index, 1, "\\\'"); + begin = index + 2; + } + +} +} \ No newline at end of file diff --git a/Json/SQLToJson.h b/Json/SQLToJson.h new file mode 100644 index 0000000..21ef967 --- /dev/null +++ b/Json/SQLToJson.h @@ -0,0 +1,84 @@ +#ifndef ROOTIVE_SQLTOJSON_H +#define ROOTIVE_SQLTOJSON_H + +#include +#include "Database/SQLQuery.h" + +namespace Rootive +{ +namespace SQLToJson +{ +inline std::string recordToJson(SQLQuery &query, int record = 0) +{ + std::string ret = "{ "; + if (query) + { + for (int _i = 0; _i < query.numCol(); ++_i) + { + ret += '\"'; + ret += query.colName(_i); + ret += "\": \""; + ret += query[record][_i] ? query[record][_i] : ""; + ret += "\", "; + } + ret.pop_back(); + ret.pop_back(); + } + ret += " }"; + return ret; +} +inline std::string recordsToJson(SQLQuery &query) +{ + std::string ret = "[ "; + if (query) + { + for (int _i = 0; _i < query.numRow(); ++_i) + { + ret += recordToJson(query, _i); + ret += ", "; + } + ret.pop_back(); + ret.pop_back(); + } + ret += " ]"; + return ret; +} +inline std::string columnToJson(SQLQuery &query, int column = 0) +{ + std::string ret = "[ "; + if (query) + { + for (int _i = 0; _i < query.numRow(); ++_i) + { + ret += "\""; + ret += query[_i][column] ? query[_i][column] : ""; + ret += "\", "; + } + ret.pop_back(); + ret.pop_back(); + } + ret += " ]"; + return std::move(ret); +} +inline std::string columnToJson(SQLQuery &query, int limit, int offset = 0, int column = 0) +{ + std::string ret = "[ "; + for (int _i = 0; offset < query.numRow() && _i < limit; ++_i, ++offset) + { + ret += "\""; + ret += query[offset][column] ? query[offset][column] : ""; + ret += "\", "; + } + if (ret.size() > 2) + { + ret.pop_back(); + ret.pop_back(); + } + ret += " ]"; + return std::move(ret); +} +void escapeCharacter(::std::string &arg); +} +} + +#endif \ No newline at end of file diff --git a/Log/LogFile.cpp b/Log/LogFile.cpp new file mode 100644 index 0000000..7ff4b8a --- /dev/null +++ b/Log/LogFile.cpp @@ -0,0 +1,13 @@ +#include "LogFile.h" + +namespace Rootive +{ +LogFile::LogFile(const char *filename, int fileSizeExpected) : +fileSizeExpected_(fileSizeExpected) +{ + size_t filenameSize = strlen(filename); + filenameSize_ = std::min(filenameSize, static_cast(constexpr_maxFilenameSize)); + memcpy(filename_, filename, filenameSize); + _roll(); +} +} \ No newline at end of file diff --git a/Log/LogFile.h b/Log/LogFile.h new file mode 100644 index 0000000..5c9f789 --- /dev/null +++ b/Log/LogFile.h @@ -0,0 +1,51 @@ +#ifndef ROOTIVE_LOGFILE_H +#define ROOTIVE_LOGFILE_H + +#include +#include +#include +#include +#include +#include +#include "IO/OutputFileInterface.h" +#include "Log/LogLine.h" + +namespace Rootive +{ +class LogFile : public OutputFileInterface +{ + int fd_; + char filename_[128]; + size_t filenameSize_; + + time_t time_; + struct tm tm_; + + int fileSize_; + const int fileSizeExpected_; + + void _roll() + { + fileSize_ = 0; + sprintf(filename_ + filenameSize_, "%lld\0", std::chrono::system_clock::now().time_since_epoch().count()); + do { fd_ = open(filename_, O_WRONLY | O_APPEND | O_CREAT); } + while (fd_ == -1); + ROOTIVE_LOG_INFO << filename_; + } +public: + static constexpr size_t constexpr_maxFilenameSize = sizeof (filename_) - 20 - 1; + + LogFile(const char *filename, int fileSizeExpected); + ~LogFile() { ::close(fd_); } + virtual void write(const void *data, size_t size) override + { + if (fileSize_ > fileSizeExpected_) { roll(); } + fileSize_ += ::write(fd_, data, size); + } + virtual void flush() override { ::fsync(fd_); } + void roll() + { close(fd_); _roll(); } +}; +} + +#endif \ No newline at end of file diff --git a/Log/LogLine.cpp b/Log/LogLine.cpp new file mode 100644 index 0000000..2789cd3 --- /dev/null +++ b/Log/LogLine.cpp @@ -0,0 +1,27 @@ +#include "LogLine.h" + +#include +#include "Basics/StringView.h" +#include "Thread/ThisThread.h" + +namespace Rootive +{ +std::unique_ptr LogLine::output_(new DefaultOutput); +const char *LogLine::levelToStringArray[] = { "Trace", "Debug", "Info", "Warn", "Error", "Fatal" }; +thread_local char LogLine::strerror_[512]; +LogLine::LogLine(Level level, const char *function, bool bSystem) +{ + data_ += std::to_string(std::chrono::system_clock::now().time_since_epoch().count()); + data_ += "|"; + data_ += levelToStringArray[level]; data_ += "|"; + data_ += std::to_string(ThisThread::pid_); data_ += "|"; + data_ += std::to_string(ThisThread::tid_); data_ += "|"; + data_ += function; data_ += "|"; + if (bSystem) { data_ += "errno: "; data_ += std::to_string(errno); } +} +LogLine::~LogLine() +{ + data_ += "\n"; + output_->write(data_.data(), data_.size()); +} +} \ No newline at end of file diff --git a/Log/LogLine.h b/Log/LogLine.h new file mode 100644 index 0000000..1087a55 --- /dev/null +++ b/Log/LogLine.h @@ -0,0 +1,91 @@ +#ifndef ROOTIVE_LOGSTREAM_H +#define ROOTIVE_LOGSTREAM_H + +#ifndef ROOTIVE_LOG_LEVEL +#define ROOTIVE_LOG_LEVEL ::Rootive::LogLine::Level::Trace +#endif + +#include +#include +#include +#include "IO/OutputInterface.h" +#include "Basics/StringView.h" + +namespace Rootive +{ +class StringView; +class LogLine +{ +public: + enum Level + { + Trace, Debug, Info, Warn, Error, Fatal + }; +private: + std::string data_; + static std::unique_ptr output_; + static const char *levelToStringArray[]; + static thread_local char strerror_[512]; +public: + LogLine(Level level, const char *function, bool bSystem = false); + ~LogLine(); + + static inline const char *errorString(int _errno) + { return strerror_r(_errno, strerror_, sizeof(strerror_)); } + + static inline void setOutput(OutputInterface *output) + { output_.reset(output); } + + inline LogLine &operator<<(const StringView &data) + { data_.append(data.begin(), data.getLength()); return *this; } + + inline LogLine &operator<<(const char *data) + { return operator<<(StringView(data)); } + + inline LogLine &operator<<(char data) + { data_ += data; return *this; } + + inline LogLine &operator<<(int data) + { data_ += std::to_string(data); return *this; } + + inline LogLine &operator<<(long data) + { data_ += std::to_string(data); return *this; } + + inline LogLine &operator<<(unsigned long data) + { data_ += std::to_string(data); return *this; } + + inline LogLine &operator<<(const void *data) + { data_ += std::to_string(reinterpret_cast(data)); return *this; } + + +}; + + +#define ROOTIVE_LOG_TRACE \ +if constexpr (ROOTIVE_LOG_LEVEL <= ::Rootive::LogLine::Level::Trace) \ +::Rootive::LogLine(::Rootive::LogLine::Level::Trace, __PRETTY_FUNCTION__) +#define ROOTIVE_LOG_DEBUG \ +if constexpr (ROOTIVE_LOG_LEVEL <= ::Rootive::LogLine::Level::Debug) \ +::Rootive::LogLine(::Rootive::LogLine::Level::Debug, __PRETTY_FUNCTION__) +#define ROOTIVE_LOG_INFO \ +if constexpr (ROOTIVE_LOG_LEVEL <= ::Rootive::LogLine::Level::Info) \ +::Rootive::LogLine(::Rootive::LogLine::Level::Info, __PRETTY_FUNCTION__) +#define ROOTIVE_LOG_WARN \ +if constexpr (ROOTIVE_LOG_LEVEL <= ::Rootive::LogLine::Level::Warn) \ +::Rootive::LogLine(::Rootive::LogLine::Level::Warn, __PRETTY_FUNCTION__) +#define ROOTIVE_LOG_ERROR \ +if constexpr (ROOTIVE_LOG_LEVEL <= ::Rootive::LogLine::Level::Error) \ +::Rootive::LogLine(::Rootive::LogLine::Level::Error, __PRETTY_FUNCTION__) +#define ROOTIVE_LOG_FATAL \ +if constexpr (ROOTIVE_LOG_LEVEL <= ::Rootive::LogLine::Level::Fatal) \ +::Rootive::LogLine(::Rootive::LogLine::Level::Fatal, __PRETTY_FUNCTION__) + +#define ROOTIVE_LOG_SYSTEMERROR \ +if constexpr (ROOTIVE_LOG_LEVEL <= ::Rootive::LogLine::Level::Error) \ +::Rootive::LogLine(::Rootive::LogLine::Level::Error, __PRETTY_FUNCTION__, true); +#define ROOTIVE_LOG_SYSTEMFATAL \ +if constexpr (ROOTIVE_LOG_LEVEL <= ::Rootive::LogLine::Level::Fatal) \ +::Rootive::LogLine(::Rootive::LogLine::Level::Fatal, __PRETTY_FUNCTION__, true); +} + +#endif \ No newline at end of file diff --git a/Log/LogLineParse.cpp b/Log/LogLineParse.cpp new file mode 100644 index 0000000..962bc9e --- /dev/null +++ b/Log/LogLineParse.cpp @@ -0,0 +1,100 @@ +#include "LogLineParse.h" +#include + +namespace Rootive +{ +LogLine::Level LogLineParse::stringToLevel(const StringView &str) +{ + LogLine::Level ret; + if (str == "Trace") { ret = LogLine::Level::Trace; } + else if (str == "Debug") { ret = LogLine::Level::Debug; } + else if (str == "Info") { ret = LogLine::Level::Info; } + else if (str == "Warn") { ret = LogLine::Level::Warn; } + else if (str == "Error") { ret = LogLine::Level::Error; } + else { ret = LogLine::Level::Fatal; } + return ret; +} +const int LogLineParse::levelToColorArray_[6]{ 44, 46, 42, 43, 41, 41 }; +const char *LogLineParse::pidStyleArray_[6] = { "\e[31m", "\e[32m", "\e[33m", "\e[34m", "\e[35m", "\e[36m" }; +void SingleProcessLogLineToConsole::parseLine(std::vector &splitResult) +{ + int l = splitResult[4].lastIndexOf(' ', splitResult[4].indexOf('(')) + 1; + int r = splitResult[4].indexOf('('); + if (l < r) + { + std::string format = "\e[0m%02d:%02d:%02d \e[37;%dm %-5.*s \e[0m %s%.*s\e[0m %s%.*s\e[0m "; + auto level = LogLineParse::stringToLevel(splitResult[1]); + + int tid = splitResult[3].toInt(); + const char *tidStyle; + if (tid == pid_) + { + tidStyle = "\e[8m"; + } + else + { + auto it = tidStyleMap_.find(tid); + if (it == tidStyleMap_.cend()) + { + tidStyleMap_[tid] = LogLineParse::pidStyleArray_[tidCount_]; + tidStyle = LogLineParse::pidStyleArray_[tidCount_]; + tidCount_ = (tidCount_ + 1) % 6; + } + else + { + tidStyle = it->second; + } + } + + auto _time = std::chrono::system_clock::time_point(std::chrono::system_clock::time_point::duration(std::stol(std::string(splitResult[0].begin(), splitResult[0].getLength())))); + auto time = std::chrono::system_clock::to_time_t(_time); + struct tm local; + localtime_r(&time, &local); + + auto functionSplitResult = splitResult[4].mid(l, r).split(':'); + format.append(functionSplitResult[0].begin(), functionSplitResult[0].getLength()); + for (int _i = 1; _i < functionSplitResult.size(); ++_i) + { + if (!functionSplitResult[_i].bEmpty()) + { + format += "\e[4m::\e[0m"; + format.append(functionSplitResult[_i].begin(), functionSplitResult[_i].getLength()); + } + } + format += " \e[4m"; + format.append(splitResult[5].begin(), splitResult[5].getLength()); + format += "\e[0m\n"; + printf(format.c_str(), local.tm_hour, local.tm_min, local.tm_sec, + LogLineParse::levelToColorArray_[level], splitResult[1].getLength(), splitResult[1].begin(), + pidStyle_, splitResult[2].getLength(), splitResult[2].begin(), + tidStyle, splitResult[3].getLength(), splitResult[3]); + } + else + { + std::cout << "function parse failed." << std::endl; + } +} +void LogLineToConsole::run() +{ + while (std::cin.getline(buffer_, sizeof(buffer_))) + { + StringView line(buffer_); + auto splitResult = line.split('|'); + if (splitResult.size() == 6) + { + int pid = splitResult[2].toInt(); + auto it = map_.find(pid); + if (it == map_.cend()) + { + it = map_.emplace(std::pair(pid, SingleProcessLogLineToConsole(pid, LogLineParse::pidStyleArray_[count_])) ).first; + count_ = (count_ + 1) % 6; + } + it->second.parseLine(splitResult); + } + else + { + std::cout << "size parse failed." << std::endl; + } + } +} +} \ No newline at end of file diff --git a/Log/LogLineParse.h b/Log/LogLineParse.h new file mode 100644 index 0000000..77ca590 --- /dev/null +++ b/Log/LogLineParse.h @@ -0,0 +1,39 @@ +#ifndef ROOTIVE_LOGLINEPARSE_H +#define ROOTIVE_LOGLINEPARSE_H + +#include +#include +#include "Basics/StringView.h" +#include "Log/LogLine.h" + +namespace Rootive +{ +namespace LogLineParse +{ +LogLine::Level stringToLevel(const StringView &str); +extern const int levelToColorArray_[6]; +extern const char *pidStyleArray_[6]; +} +class SingleProcessLogLineToConsole +{ + std::map<__pid_t, const char *> tidStyleMap_; + int tidCount_; + int pid_; + const char *pidStyle_; +public: + SingleProcessLogLineToConsole(int pid, const char *pidStyle) : + tidCount_(0), pid_(pid), pidStyle_(pidStyle) + {} + void parseLine(std::vector &splitResult); +}; +class LogLineToConsole +{ + char buffer_[1024]; + int count_ = 0; + std::map<__pid_t, SingleProcessLogLineToConsole> map_; +public: + void run(); +}; +} + +#endif \ No newline at end of file diff --git a/Network/InetAddress.cpp b/Network/InetAddress.cpp new file mode 100644 index 0000000..a2bb996 --- /dev/null +++ b/Network/InetAddress.cpp @@ -0,0 +1,83 @@ +#include "InetAddress.h" + +#include +#include +#include +#include + +namespace Rootive +{ + +static thread_local char threadLocal_resolveBuffer[64 * 1024]; + +bool InetAddress::resolve(const char *hostname, InetAddress *out) +{ + hostent hent, *he = nullptr; + memset(&hent, 0, sizeof(hent)); + int herrno; + int ret = gethostbyname_r(hostname, &hent, threadLocal_resolveBuffer, sizeof(threadLocal_resolveBuffer), &he, &herrno); + if (!ret && he != nullptr) + { + out->addr_.sin_addr = *reinterpret_cast(he->h_addr); + return true; + } + return false; +} + +InetAddress::InetAddress(uint16_t port, bool loopbackOnly, bool ipv6) +{ + if (ipv6) + { + memset(&addr6_, 0, sizeof(addr6_)); + addr6_.sin6_family = AF_INET6; + in6_addr ip = loopbackOnly ? in6addr_loopback : in6addr_any; + addr6_.sin6_addr = ip; + addr6_.sin6_port = ::htobe16(port); + } + else + { + memset(&addr_, 0, sizeof(addr_)); + addr_.sin_family = AF_INET; + in_addr_t ip = loopbackOnly ? INADDR_LOOPBACK : INADDR_ANY; + addr_.sin_addr.s_addr = ::htobe32(ip); + addr_.sin_port = ::htobe16(port); + } +} + +InetAddress::InetAddress(const char *ip, uint16_t port, bool ipv6) +{ + if (ipv6) + { + memset(&addr6_, 0, sizeof(addr6_)); + addr6_.sin6_family = AF_INET6; + addr6_.sin6_port = htobe16(port); + ::inet_pton(AF_INET6, ip, &addr6_.sin6_addr); + } + else + { + memset(&addr_, 0, sizeof(addr_)); + addr_.sin_family = AF_INET; + addr_.sin_port = htobe16(port); + ::inet_pton(AF_INET, ip, &addr_.sin_addr); + } +} + +std::string InetAddress::toIPPortString() const +{ + char buf[64] = ""; + if (addr_.sin_family == AF_INET) { ::inet_ntop(AF_INET, &addr_.sin_addr, buf, static_cast(sizeof(buf))); } + else if (addr_.sin_family == AF_INET6) { ::inet_ntop(AF_INET6, &addr6_.sin6_addr, buf, static_cast(sizeof(buf))); } + size_t end = ::strlen(buf); + snprintf(buf + end, sizeof(buf) - end, ":%u", be16toh(addr_.sin_port)); + return buf; +} + +std::string InetAddress::toIPString() const +{ + char buf[64] = ""; + if (addr_.sin_family == AF_INET) { ::inet_ntop(AF_INET, &addr_.sin_addr, buf, static_cast(sizeof(buf))); } + else if (addr_.sin_family == AF_INET6) { ::inet_ntop(AF_INET6, &addr6_.sin6_addr, buf, static_cast(sizeof(buf))); } + return buf; +} + +} diff --git a/Network/InetAddress.h b/Network/InetAddress.h new file mode 100644 index 0000000..838cffe --- /dev/null +++ b/Network/InetAddress.h @@ -0,0 +1,44 @@ +#ifndef ROOTIVE_INETADDRESS_H +#define ROOTIVE_INETADDRESS_H + +#include +#include +#include + +#include "Basics/Types.h" + +namespace Rootive +{ + +class InetAddress +{ +private: + union { sockaddr_in addr_; sockaddr_in6 addr6_; }; +public: + template + inline static To *sockaddrPtrCast(From *addr) + { return static_cast(Types::implicitCast::value, const void *, void *>::type >(addr)); } + + explicit InetAddress(uint16_t port = 0, bool loopbackOnly = false, bool ipv6 = false); + InetAddress(const char *ip, uint16_t port, bool ipv6 = false); + explicit InetAddress(const sockaddr_in &addr) : addr_(addr) {} + explicit InetAddress(const sockaddr_in6 &addr) : addr6_(addr) {} + + static bool resolve(const char *hostname, InetAddress *result); + + inline sa_family_t getFamily() const { return addr_.sin_family; } + inline const sockaddr *getSockaddr() const { return sockaddrPtrCast(&addr_); } + inline uint16_t getPort_NetEndian() const { return addr_.sin_port; } + inline uint16_t getPort_HostEndian() const { return be16toh(addr_.sin_port); } + inline uint32_t getIP_NetEndian() const { return addr_.sin_addr.s_addr; } + + std::string toIPString() const; + std::string toIPPortString() const; + + inline void setSockaddrIn6(const sockaddr_in6 &addr6) { addr6_ = addr6; } + inline void setScopeID(uint32_t scopeID) { if (addr_.sin_family == AF_INET6) { addr6_.sin6_scope_id = scopeID; } } +}; + +} + +#endif diff --git a/Network/NetworkBuffer.h b/Network/NetworkBuffer.h new file mode 100644 index 0000000..951e83a --- /dev/null +++ b/Network/NetworkBuffer.h @@ -0,0 +1,108 @@ +#ifndef ROOTIVE_NETWORKBUFFER_H +#define ROOTIVE_NETWORKBUFFER_H + +#include +#include "IO/IOBuffer.h" + +namespace Rootive +{ +class NetworkBuffer : public IOBuffer +{ +public: + NetworkBuffer(size_t initialSize = 1024) : + IOBuffer(initialSize) + {} + + inline void hasReadInt64() { hasRead(sizeof(int64_t)); } + inline void hasReadInt32() { hasRead(sizeof(int32_t)); } + inline void hasReadInt16() { hasRead(sizeof(int16_t)); } + inline void hasReadInt8() { hasRead(sizeof(int8_t)); } + int64_t peekInt64() const + { + int64_t net64; + ::memcpy(&net64, readBegin(), sizeof(net64)); + return be64toh(net64); + } + int32_t peekInt32() const + { + int32_t net32; + ::memcpy(&net32, readBegin(), sizeof(net32)); + return be32toh(net32); + } + int16_t peekInt16() const + { + int16_t net16; + ::memcpy(&net16, readBegin(), sizeof(net16)); + return be16toh(net16); + } + int8_t peekInt8() const + { + return *(readBegin()); + } + int64_t readInt64() + { + int64_t ret = peekInt64(); + hasReadInt64(); + return ret; + } + int32_t readInt32() + { + int32_t ret = peekInt32(); + hasReadInt32(); + return ret; + } + int16_t readInt16() + { + int16_t ret = peekInt16(); + hasReadInt16(); + return ret; + } + int8_t readInt8() + { + int8_t ret = peekInt8(); + hasReadInt8(); + return ret; + } + inline void writeInt64(int64_t data) + { + int64_t net64 = htole64(data); + write(&net64, sizeof(net64)); + } + inline void writeInt32(int32_t data) + { + int32_t net32 = htobe32(data); + write(&net32, sizeof(net32)); + } + inline void writeInt16(int16_t data) + { + int16_t net16 = htobe16(data); + write(&net16, sizeof(net16)); + } + inline void writeInt8(int8_t data) + { + write(&data, sizeof(data)); + } + + void prependInt64(int64_t data) + { + int64_t net64 = htobe64(data); + prepend(&net64, sizeof(net64)); + } + void prependInt32(int32_t data) + { + int32_t net32 = htobe32(data); + prepend(&net32, sizeof(net32)); + } + void prependInt16(int16_t data) + { + int16_t net16 = htobe16(data); + prepend(&net16, sizeof(net16)); + } + void prependInt8(int8_t data) + { + prepend(&data, sizeof(data)); + } +}; +} + +#endif \ No newline at end of file diff --git a/Network/PathSegmentView.cpp b/Network/PathSegmentView.cpp new file mode 100644 index 0000000..2620c2b --- /dev/null +++ b/Network/PathSegmentView.cpp @@ -0,0 +1,51 @@ +#include "PathSegmentView.h" + +namespace Rootive +{ +bool PathSegmentView::set(const StringView &data) +{ + clear(); + int pIndex = data.indexOf(';'); + int qIndex = data.indexOf('?'); + if (pIndex == data.getLength()) { pIndex = qIndex; } + else if (qIndex < pIndex) { return false; } + path_ = data.mid(0, pIndex); + + std::vector vector; + + if (pIndex < qIndex) + { + vector = data.mid(pIndex + 1, qIndex).split('&'); + for (const StringView &p : vector) + { + int eIndex = p.indexOf('='); + if (eIndex != p.getLength()) + { + StringView key = p.mid(0, eIndex); + StringView value = p.mid(eIndex + 1, p.getLength()); + if (!key.bEmpty() && !value.bEmpty()) { paramsMap_[key] = value; } + else { return false; } + } + else { return false; } + } + } + + if (qIndex != data.getLength()) + { + vector = data.mid(qIndex, data.getLength()).split('&'); + for (const StringView &q : vector) + { + int eIndex = q.indexOf('='); + if (eIndex != q.getLength()) + { + StringView key = q.mid(0, eIndex); + StringView value = q.mid(eIndex + 1, q.getLength()); + if (!key.bEmpty() && !value.bEmpty()) { queryMap_[key] = value; } + else { return false; } + } + else { return false; } + } + } + return true; +} +} \ No newline at end of file diff --git a/Network/PathSegmentView.h b/Network/PathSegmentView.h new file mode 100644 index 0000000..174a12e --- /dev/null +++ b/Network/PathSegmentView.h @@ -0,0 +1,20 @@ +#ifndef ROOTIVE_PATHSEGMENTVIEW_H +#define ROOTIVE_PATHSEGMENTVIEW_H + +#include "Basics/StringView.h" +#include + +namespace Rootive +{ +struct PathSegmentView +{ + StringView path_; + std::map paramsMap_; + std::map queryMap_; + + inline void clear() { path_.clear(); paramsMap_.clear(); queryMap_.clear(); } + bool set(const StringView &data); +}; +} + +#endif \ No newline at end of file diff --git a/Network/Socket.cpp b/Network/Socket.cpp new file mode 100644 index 0000000..448039f --- /dev/null +++ b/Network/Socket.cpp @@ -0,0 +1,78 @@ +#include "Socket.h" + +#include +#include + +#include "Network/InetAddress.h" + +namespace Rootive +{ + +void Socket::setTCPNodelay(bool bOn) +{ + int optval = bOn ? 1 : 0; + ::setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, &optval, static_cast(sizeof(optval))); +} +void Socket::setReuseAddr(bool bOn) +{ + int optval = bOn ? 1 : 0; + ::setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &optval, static_cast(sizeof(optval))); +} +void Socket::setReusePort(bool bOn) +{ + int optval = bOn ? 1 : 0; + ::setsockopt(fd_, SOL_SOCKET, SO_REUSEPORT, &optval, static_cast(sizeof(optval))); +} +void Socket::setKeepAlive(bool bOn) +{ + int optval = bOn ? 1 : 0; + ::setsockopt(fd_, SOL_SOCKET, SO_KEEPALIVE, &optval, static_cast(sizeof(optval))); +} +bool Socket::getTCPInfo(tcp_info *out) const +{ + socklen_t size = sizeof(*out); + memset(out, 0, size); + return ::getsockopt(fd_, SOL_TCP, TCP_INFO, out, &size) == 0; +} +bool Socket::getTCPInfoString(char *buffer, int size) const +{ + tcp_info info; + bool ret = getTCPInfo(&info); + if (ret) + { + snprintf(buffer, size, "unrecovered=%u rto=%u ato=%u snd_mss=%u rcv_mss=%u " + "lost=%u retrans=%u rtt=%u rttvar=%u sshthresh=%u cwnd=%u total_retrans=%u", + info.tcpi_retransmits, info.tcpi_rto, info.tcpi_ato, info.tcpi_snd_mss, + info.tcpi_rcv_mss, info.tcpi_lost, info.tcpi_retrans, info.tcpi_rtt, + info.tcpi_rttvar, info.tcpi_snd_ssthresh, info.tcpi_snd_cwnd, info.tcpi_total_retrans); + } + return ret; +} +int Socket::getError() +{ + int optval; + socklen_t optlen = static_cast(sizeof(optval)); + if (::getsockopt(fd_, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) { return errno; } + else { return optval; } +} +void Socket::bind(const InetAddress &addr) +{ + ::bind(fd_, addr.getSockaddr(), static_cast(sizeof(sockaddr_in6))); +} +void Socket::listen() +{ + if (::listen(fd_, SOMAXCONN)) { throw; } +} +int Socket::accept(InetAddress *out_addr) +{ + sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + socklen_t addrSize = static_cast(sizeof(addr)); + int fd = ::accept4(fd_, InetAddress::sockaddrPtrCast(&addr), &addrSize, SOCK_NONBLOCK | SOCK_CLOEXEC); + if (fd >= 0) { out_addr->setSockaddrIn6(addr); } + return fd; +} +void Socket::shutdown(int how) { ::shutdown(fd_, how); } + + +} \ No newline at end of file diff --git a/Network/Socket.h b/Network/Socket.h new file mode 100644 index 0000000..7f8f1b8 --- /dev/null +++ b/Network/Socket.h @@ -0,0 +1,41 @@ +#ifndef ROOTIVE_SOCKET_H +#define ROOTIVE_SOCKET_H + +#include +#include + + +#include "Basics/Noncopyable.h" + +namespace Rootive +{ + +class InetAddress; + +class Socket : Noncopyable +{ + const int fd_; +public: + explicit Socket(int fd) : fd_(fd) {} + ~Socket() { ::close(fd_); } + + int getFD() { return fd_; } + void setTCPNodelay(bool bOn); + void setReuseAddr(bool bOn); + void setReusePort(bool bOn); + void setKeepAlive(bool bOn); + + bool getTCPInfo(tcp_info *out) const; + bool getTCPInfoString(char *buffer, int size) const; + int getError(); + + void bind(const InetAddress &addr); + void listen(); + int accept(InetAddress *out_addr); + void shutdown(int how); + +}; + +} + +#endif \ No newline at end of file diff --git a/TCP/TCPConnection.cpp b/TCP/TCPConnection.cpp new file mode 100644 index 0000000..a702ee1 --- /dev/null +++ b/TCP/TCPConnection.cpp @@ -0,0 +1,221 @@ +#include "TCPConnection.h" + +#include "Basics/StringView.h" +#include "Thread/EventLoop.h" +#include "Thread/Channel.h" +#include "Log/LogLine.h" +#include +#include + +namespace Rootive +{ +void TCPConnection::_send(const void* message, int size) +{ + if (state_ == EState::Disconnected) + { + ROOTIVE_LOG_WARN << name_ << " id: " << socket_->getFD() << " disconnected"; + return; + } + ssize_t written = 0; + bool fatalError = false; + if (!channel_->bWrite() && !outputBuffer_.readableLength()) + { + written = ::write(channel_->getFD(), message, size); + if (written >= 0) + { + size -= written; + if (!size && writeFinishedCallback_) + { + eventLoop_->queue(std::bind(writeFinishedCallback_, shared_from_this())); + } + } + else + { + written = 0; + if (errno != EWOULDBLOCK) + { + ROOTIVE_LOG_SYSTEMERROR; + if (errno == EPIPE || errno == ECONNRESET) + { + fatalError = true; + } + } + } + } + if (!fatalError && size > 0) + { + size_t oldSize = outputBuffer_.readableLength(); + if (oldSize + size >= hwm_ && oldSize < hwm_ && hwmCallback_) + { + eventLoop_->queue(std::bind(hwmCallback_, shared_from_this(), oldSize + size)); + } + outputBuffer_.write(static_cast(message) + written, size); + if (!channel_->bWrite()) { channel_->enableWrite(); } + } +} +void TCPConnection::_send(const StringView& message) +{ + _send(message.begin(), message.getLength()); +} +void TCPConnection::_forceClose() { handleClose(); } +void TCPConnection::_shutdown() +{ + if (!channel_->bWrite()) + { + socket_->shutdown(SHUT_WR); + } +} +std::string TCPConnection::getTCPInfoString() const +{ + char buffer[1024]; buffer[0] = '\0'; + socket_->getTCPInfoString(buffer, sizeof(buffer)); + return buffer; +} +void TCPConnection::handleRead(std::chrono::system_clock::time_point time) +{ + int savedErrno = 0; + ssize_t n = inputBuffer_.write(channel_->getFD(), 65536, &savedErrno); + if (n > 0) { messageCallback_(shared_from_this(), time); } + else if (n == 0) + { + ROOTIVE_LOG_TRACE << getName() << " read 0"; + handleClose(); + } + else + { + errno = savedErrno; + ROOTIVE_LOG_SYSTEMERROR; + handleError(); + } +} +void TCPConnection::handleWrite() +{ + if (channel_->bWrite()) + { + ssize_t n = ::write(channel_->getFD(), outputBuffer_.readBegin(), outputBuffer_.readableLength()); + if (n > 0) + { + outputBuffer_.hasRead(n); + if (!outputBuffer_.readableLength()) + { + channel_->disableWrite(); + if (writeFinishedCallback_) + { + eventLoop_->queue(std::bind(writeFinishedCallback_, shared_from_this())); + } + if (state_ == Disconnecting) { _shutdown(); } + } + } + else + { + ROOTIVE_LOG_SYSTEMERROR; + } + } + else + { + ROOTIVE_LOG_TRACE << name_ << " down"; + } +} +void TCPConnection::handleClose() //BUG +{ + ROOTIVE_LOG_TRACE << name_ << " state: " << state_; + assert(state_ == Connected || state_ == Disconnecting); + state_ = Disconnected; + channel_->disableAll(); + std::shared_ptr guard(shared_from_this()); + connectionCallback_(guard); + closeCallback_(guard); +} +void TCPConnection::handleError() +{ + auto res = socket_->getError(); + ROOTIVE_LOG_ERROR << name_ << " fd: " << socket_->getFD() << " SO_ERROR: " << res << " " << LogLine::errorString(res); +} +TCPConnection::TCPConnection(EventLoop* eventLoop, int fd, const InetAddress &localAddr, const InetAddress &peerAddr, const std::string& name) : +eventLoop_(eventLoop), name_(name), state_(EState::Connecting), +socket_(new Socket(fd)), channel_(new Channel(eventLoop, fd)), +localAddr_(localAddr), peerAddr_(peerAddr), hwm_(64 * 1024 * 1024), +context_(nullptr) +{ + channel_->setReadCallback(std::bind(&TCPConnection::handleRead, this, std::placeholders::_1)); + channel_->setWriteCallback(std::bind(&TCPConnection::handleWrite, this)); + channel_->setCloseCallback(std::bind(&TCPConnection::handleClose, this)); + channel_->setErrorCallback(std::bind(&TCPConnection::handleError, this)); + socket_->setKeepAlive(true); + ROOTIVE_LOG_INFO << name_ << " fd: " << fd; +} +TCPConnection::~TCPConnection() +{ + ROOTIVE_LOG_DEBUG << name_ << " state: " << state_; + assert(state_ == Disconnected); +} +void TCPConnection::enableRead() +{ + eventLoop_->run([this]()->void{ if (!channel_->bRead()) { channel_->enableRead(); } }); +} +void TCPConnection::disableRead() +{ + eventLoop_->run([this]()->void{ if (channel_->bRead()) { channel_->disableRead(); } }); +} +void TCPConnection::forceClose() +{ + if (state_ == Connected || state_ == Disconnecting) + { + state_ = Disconnecting; + eventLoop_->queue(std::bind(&TCPConnection::_forceClose, shared_from_this())); + } +} +void TCPConnection::shutdown() +{ + if (state_ = Connected) + { + state_ = Disconnecting; + eventLoop_->run(std::bind(&TCPConnection::_shutdown, this)); + } +} +void TCPConnection::run() +{ + state_ = Connected; + channel_->tie(shared_from_this()); + channel_->enableRead(); + connectionCallback_(shared_from_this()); +} +void TCPConnection::close() +{ + if (state_ == Connected) + { + state_ = Disconnected; + channel_->disableAll(); + connectionCallback_(shared_from_this()); + } + channel_->remove(); +} +void TCPConnection::send(const void* message, int size) +{ + if (state_ == Connected) + { + if (eventLoop_->bThread()) { _send(message, size); } + else + { + eventLoop_->run(std::bind(static_cast(&TCPConnection::_send), this, message, size)); + } + } +} +void TCPConnection::send(const StringView& message) { send(message.begin(), message.getLength()); } +void TCPConnection::send(IOBuffer &message) +{ + if (state_ == Connected) + { + if (eventLoop_->bThread()) + { + _send(message.readBegin(), message.readableLength()); + message.hasReadAll(); + } + else + { + eventLoop_->run(std::bind(static_cast(&TCPConnection::_send), this, message.readAllAsString())); + } + } +} + +} \ No newline at end of file diff --git a/TCP/TCPConnection.h b/TCP/TCPConnection.h new file mode 100644 index 0000000..a0c6553 --- /dev/null +++ b/TCP/TCPConnection.h @@ -0,0 +1,101 @@ +#ifndef ROOTIVE_TCPCONNECTION_H +#define ROOTIVE_TCPCONNECTION_H + +#include +#include +#include +#include +#include +#include + +#include "Basics/Noncopyable.h" +#include "Network/InetAddress.h" +#include "Network/Socket.h" +#include "Network/NetworkBuffer.h" + + +namespace Rootive +{ + +class EventLoop; +class Channel; +class StringView; + +class TCPConnection : Noncopyable, public std::enable_shared_from_this +{ + enum EState { Disconnected, Connecting, Connected, Disconnecting }; +public: + typedef std::function&)> ConnectionCallback; + typedef std::function&, std::chrono::system_clock::time_point)> MessageCallback; + typedef std::function&)> WriteFinishedCallback; + typedef std::function&, size_t)> HWMCallback; + typedef std::function&)> CloseCallback; +private: + EventLoop *eventLoop_; + std::string name_; + std::atomic state_; + + std::unique_ptr socket_; + std::unique_ptr channel_; + + InetAddress localAddr_; + InetAddress peerAddr_; + + NetworkBuffer inputBuffer_; + NetworkBuffer outputBuffer_; + + ConnectionCallback connectionCallback_; + MessageCallback messageCallback_; + WriteFinishedCallback writeFinishedCallback_; + HWMCallback hwmCallback_; + CloseCallback closeCallback_; + size_t hwm_; + + void _send(const void* message, int size); + void _send(const StringView& message); + void _forceClose(); + void _shutdown(); + void handleRead(std::chrono::system_clock::time_point time); + void handleWrite(); + void handleClose(); + void handleError(); + +public: + void *context_; + TCPConnection(EventLoop* eventLoop, int fd, const InetAddress &localAddr, const InetAddress &peerAddr, const std::string& name); + ~TCPConnection(); + + inline EventLoop* getEventLoop() const { return eventLoop_; } + inline const std::string& getName() const { return name_; } + inline const InetAddress& getLocalAddr() const { return localAddr_; } + inline const InetAddress& getPeerAddr() const { return peerAddr_; } + inline bool bConnected() const { return state_ == EState::Connected; } + inline bool bDisconnected() const { return state_ == EState::Disconnected; } + inline NetworkBuffer* inputBuffer() { return &inputBuffer_; } + inline NetworkBuffer* outputBuffer() { return &outputBuffer_; } + + inline void setConnectionCallback(const ConnectionCallback& callback) { connectionCallback_ = callback; } + inline void setMessageCallback(const MessageCallback& callback) { messageCallback_ = callback; } + inline void setWriteCompleteCallback(const WriteFinishedCallback& callback) { writeFinishedCallback_ = callback; } + inline void setHighWaterMarkCallback(const HWMCallback& callback, size_t hwm) { hwmCallback_ = callback; hwm_ = hwm; } + inline void setCloseCallback(const CloseCallback& callback) { closeCallback_ = callback; } + + bool getTCPInfo(struct tcp_info *out) const { return socket_->getTCPInfo(out); } + std::string getTCPInfoString() const; + void setTCPNodelay(bool bOn) { socket_->setTCPNodelay(bOn); } + + void enableRead(); + void disableRead(); + void forceClose(); + void shutdown(); + void run(); + void close(); + + void send(const void *message, int size); + void send(const StringView &message); + void send(IOBuffer &message); + +}; +} + +#endif \ No newline at end of file diff --git a/TCP/TCPServer.cpp b/TCP/TCPServer.cpp new file mode 100644 index 0000000..d6c06bd --- /dev/null +++ b/TCP/TCPServer.cpp @@ -0,0 +1,68 @@ +#include "TCPServer.h" + +#include + +#include "Network/InetAddress.h" +#include "Thread/EventLoop.h" +#include "Thread/Acceptor.h" +#include "Thread/EventLoopThreadPool.h" +#include "Log/LogLine.h" + +#include + +namespace Rootive +{ +void TCPServer::newConnection(int fd, const InetAddress &peerAddr) +{ + char buffer[32]; + snprintf(buffer, sizeof(buffer), "Connection%d", nextConnID_++); + std::string connName = name_ + buffer; + ROOTIVE_LOG_INFO << connName << ": " << peerAddr.toIPPortString(); + sockaddr_in6 _localAddr; + memset(&_localAddr, 0, sizeof(_localAddr)); + socklen_t addrSize = static_cast(sizeof(_localAddr)); + ::getsockname(fd, InetAddress::sockaddrPtrCast(&_localAddr), &addrSize); + EventLoop *eventLoop = threadPool_->getEventLoop(); + std::shared_ptr conn(new TCPConnection(eventLoop, fd, InetAddress(_localAddr), peerAddr, connName)); + connectionMap_[connName] = conn; + conn->setConnectionCallback(connectionCallback_); + conn->setMessageCallback(messageCallback_); + conn->setWriteCompleteCallback(writeFinishedCallback_); + conn->setCloseCallback(std::bind(&TCPServer::removeConnection, this, std::placeholders::_1)); + eventLoop->run(std::bind(&TCPConnection::run, conn)); +} +void TCPServer::removeConnection(const std::shared_ptr &conn) +{ + eventLoop_->run(std::bind(&TCPServer::_removeConnection, this, conn)); +} +void TCPServer::_removeConnection(const std::shared_ptr &conn) +{ + ROOTIVE_LOG_INFO << conn->getName(); + connectionMap_.erase(conn->getName()); + conn->getEventLoop()->queue(std::bind(&TCPConnection::close, conn)); +} +TCPServer::TCPServer(EventLoop *eventLoop, const InetAddress &listenAddr, const std::string &name, int numThread, EOption option) : +eventLoop_(eventLoop), ipPort(listenAddr.toIPPortString()), name_(name), nextConnID_(1), +acceptor_(new Acceptor(eventLoop, listenAddr, option)), +threadPool_(new EventLoopThreadPool(eventLoop, numThread, name)) +{ + acceptor_->setNewConnectionCallback(std::bind(&TCPServer::newConnection, this, std::placeholders::_1, std::placeholders::_2)); +} +TCPServer::~TCPServer() +{ + ROOTIVE_LOG_TRACE << name_; + for (std::pair > &item : connectionMap_) + { + std::shared_ptr conn(item.second); + item.second.reset(); + conn->getEventLoop()->run(std::bind(&TCPConnection::close, conn)); + } +} +void TCPServer::run() +{ + threadPool_->setThreadInitFunc(threadInitFunc_); + threadPool_->run(); + eventLoop_->run(std::bind(&Acceptor::listen, acceptor_.get())); +} + +} diff --git a/TCP/TCPServer.h b/TCP/TCPServer.h new file mode 100644 index 0000000..6c74593 --- /dev/null +++ b/TCP/TCPServer.h @@ -0,0 +1,60 @@ +#ifndef ROOTIVE_TCPSERVER_H +#define ROOTIVE_TCPSERVER_H + +#include +#include +#include +#include + +#include "TCP/TCPConnection.h" +#include "Thread/EventLoopThread.h" + +namespace Rootive +{ + +class InetAddress; +class EventLoop; +class Acceptor; +class EventLoopThreadPool; + +class TCPServer +{ +public: +enum EOption +{ + NonReusePort, + ReusePort +}; +private: + EventLoop *eventLoop_; + const std::string ipPort; + const std::string name_; + int nextConnID_; + std::unique_ptr acceptor_; + std::map > connectionMap_; + std::shared_ptr threadPool_; + + TCPConnection::ConnectionCallback connectionCallback_; + TCPConnection::MessageCallback messageCallback_; + TCPConnection::WriteFinishedCallback writeFinishedCallback_; + EventLoopThread::ThreadInitFunc threadInitFunc_; + + void newConnection(int fd, const InetAddress &peerAddr); + void removeConnection(const std::shared_ptr &conn); + void _removeConnection(const std::shared_ptr &conn); +public: + TCPServer(EventLoop *eventLoop, const InetAddress &listenAddr, const std::string &name, int numThread = 0, EOption option = NonReusePort); + ~TCPServer(); + + inline const std::string &getName() const { return name_; } + std::shared_ptr getEventLoopThreadPool() { return threadPool_; } + void setConnectionCallback(const TCPConnection::ConnectionCallback &callback) { connectionCallback_ = callback; } + void setMessageCallback(const TCPConnection::MessageCallback &callback) { messageCallback_ = callback; } + void setWriteFinishedCallback(const TCPConnection::WriteFinishedCallback &callback) { writeFinishedCallback_ = callback; } + void setThreadInitFunc(const EventLoopThread::ThreadInitFunc &callback) { threadInitFunc_ = callback; } + + void run(); +}; +} + +#endif \ No newline at end of file diff --git a/Thread/Acceptor.cpp b/Thread/Acceptor.cpp new file mode 100644 index 0000000..00cfe5e --- /dev/null +++ b/Thread/Acceptor.cpp @@ -0,0 +1,55 @@ +#include "Acceptor.h" + +#include +#include + +#include "Network/InetAddress.h" +#include "Log/LogLine.h" + +namespace Rootive +{ + +void Acceptor::handleRead() +{ + InetAddress addr; + int fd = socket_.accept(&addr); + if (fd >= 0) + { + ROOTIVE_LOG_TRACE << "accpet " << addr.toIPPortString(); + if (newConnectionCallback_) { newConnectionCallback_(fd, addr); } + else { ::close(fd); } + } + else + { + ROOTIVE_LOG_SYSTEMERROR; + if (errno == EMFILE) + { + ::close(idleFD_); + idleFD_ = ::accept(socket_.getFD(), nullptr, nullptr); + ::close(idleFD_); + idleFD_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC); + } + } +} +Acceptor::Acceptor(EventLoop *eventLoop, const InetAddress &listenAddr, bool reusePort) : +socket_(::socket(listenAddr.getFamily(), SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP)), +channel_(eventLoop, socket_.getFD()), +idleFD_(::open("/dev/null", O_RDONLY | O_CLOEXEC)) +{ + socket_.setReuseAddr(true); + socket_.setReusePort(reusePort); + socket_.bind(listenAddr); + channel_.setReadCallback(std::bind(&Acceptor::handleRead, this)); +} +Acceptor::~Acceptor() +{ + channel_.remove(); + ::close(idleFD_); +} +void Acceptor::listen() +{ + socket_.listen(); + channel_.enableRead(); +} + +} \ No newline at end of file diff --git a/Thread/Acceptor.h b/Thread/Acceptor.h new file mode 100644 index 0000000..6ccc0fc --- /dev/null +++ b/Thread/Acceptor.h @@ -0,0 +1,37 @@ +#ifndef ROOTIVE_ACCEPTOR_H +#define ROOTIVE_ACCEPTOR_H + +#include + +#include "Basics/Noncopyable.h" +#include "Network/Socket.h" +#include "Thread/Channel.h" + +namespace Rootive +{ + +class EventLoop; +class InetAddress; + +class Acceptor : Noncopyable +{ +public: + typedef std::function NewConnectionCallback; +private: + Socket socket_; + Channel channel_; + NewConnectionCallback newConnectionCallback_; + int idleFD_; + + void handleRead(); +public: + Acceptor(EventLoop *eventLoop, const InetAddress &listenAddr, bool reusePort); + ~Acceptor(); + + inline void setNewConnectionCallback(NewConnectionCallback callback) { newConnectionCallback_ = callback; } + void listen(); +}; + +} + +#endif \ No newline at end of file diff --git a/Thread/Channel.cpp b/Thread/Channel.cpp new file mode 100644 index 0000000..599081d --- /dev/null +++ b/Thread/Channel.cpp @@ -0,0 +1,51 @@ +#include "Channel.h" +#include "Thread/EventLoop.h" +#include "Log/LogLine.h" + +namespace Rootive +{ +void Channel::update() { eventLoop_->updateChannel(this); } +void Channel::_handleEvent(std::chrono::system_clock::time_point time) const +{ + if ((revents_ & POLLHUP) && !(revents_ & POLLIN)) + { + ROOTIVE_LOG_WARN << "fd: " << fd_ << " POLLHUP"; + if (closeCallback_) closeCallback_(); + } + if (revents_ & POLLNVAL) + { + ROOTIVE_LOG_WARN << "fd: " << fd_ << " POLLNVAL"; + } + if (revents_ & (POLLERR | POLLNVAL)) + { + if (errorCallback_) { errorCallback_(); } + } + if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) + { + if (readCallback_) { readCallback_(time); } + } + if (revents_ & POLLOUT) + { + if (writeCallback_) { writeCallback_(); } + } +} +void Channel::enableRead() { events_ |= constexpr_readEvent; update(); } +void Channel::enableWrite() { events_ |= constexpr_writeEvent; update(); } +void Channel::disableRead() { events_ &= ~constexpr_readEvent; update(); } +void Channel::disableWrite() { events_ &= ~constexpr_writeEvent; update(); } +void Channel::disableAll() { events_ = constexpr_noneEvent; update(); } +Channel::Channel(EventLoop *eventLoop, int fd) : +eventLoop_(eventLoop), fd_(fd), events_(0), revents_(0), bTied_(false), index_(-1) {} +void Channel::handleEvent(std::chrono::system_clock::time_point time) const +{ + if (bTied_) + { + std::shared_ptr guard = tie_.lock(); + if (guard) { _handleEvent(time); } + } + else { _handleEvent(time); } +} +void Channel::remove(){ eventLoop_->removeChannel(this); } +void Channel::tie(const std::shared_ptr &obj) { tie_ = obj; bTied_ = true; } + +} \ No newline at end of file diff --git a/Thread/Channel.h b/Thread/Channel.h new file mode 100644 index 0000000..475af7a --- /dev/null +++ b/Thread/Channel.h @@ -0,0 +1,70 @@ +#ifndef ROOTIVE_CHANNEL_H +#define ROOTIVE_CHANNEL_H + +#include +#include +#include +#include + +#include "Thread/Poller.h" +#include "Basics/Noncopyable.h" + +namespace Rootive +{ + +class EventLoop; + +class Channel : Noncopyable +{ +public: + typedef std::function EventCallback; + typedef std::function ReadCallback; +private: + static constexpr int constexpr_noneEvent = 0; + static constexpr int constexpr_readEvent = POLLIN | POLLPRI; + static constexpr int constexpr_writeEvent = POLLOUT; + + EventLoop *eventLoop_; + const int fd_; + int events_; + int revents_; + + ReadCallback readCallback_; + EventCallback writeCallback_; + EventCallback closeCallback_; + EventCallback errorCallback_; + + std::weak_ptr tie_; + bool bTied_; + + void update(); + void _handleEvent(std::chrono::system_clock::time_point time) const; +public: + int index_; + + Channel(EventLoop *eventLoop, int fd); + + inline int getFD() const { return fd_; } + inline int getEvents() const { return events_; } + inline int getRevents() const { return revents_; } + inline bool bNoneEvent() const { return events_ == constexpr_noneEvent; } + inline bool bWrite() const { return events_ & constexpr_writeEvent; } + inline bool bRead() const { return events_ & constexpr_readEvent; } + void enableRead(); + void enableWrite(); + void disableRead(); + void disableWrite(); + void disableAll(); + inline void setReadCallback(ReadCallback callback) { readCallback_ = std::move(callback); } + inline void setWriteCallback(EventCallback callback) { writeCallback_ = std::move(callback); } + inline void setCloseCallback(EventCallback callback) { closeCallback_ = std::move(callback); } + inline void setErrorCallback(EventCallback callback) { errorCallback_ = std::move(callback); } + inline void setRevents(int revents) { revents_ = revents; } + void handleEvent(std::chrono::system_clock::time_point time) const; + void remove(); + void tie(const std::shared_ptr &obj); + +}; +} + +#endif \ No newline at end of file diff --git a/Thread/EventLoop.cpp b/Thread/EventLoop.cpp new file mode 100644 index 0000000..284d46b --- /dev/null +++ b/Thread/EventLoop.cpp @@ -0,0 +1,105 @@ +#include "EventLoop.h" + +#include +#include "Log/LogLine.h" +#include + +namespace Rootive +{ +void EventLoop::wakeup() +{ + uint64_t buffer; + auto res = ::write(wakeupChannel_.getFD(), &buffer, sizeof(buffer)); + if (res != sizeof(buffer)) + { + ROOTIVE_LOG_ERROR << "write " << res << " instead of " << sizeof(buffer); + } +} +void EventLoop::availableLogTrace(const std::vector &available) +{ + if constexpr (ROOTIVE_LOG_LEVEL <= LogLine::Level::Trace) + { + LogLine stream(LogLine::Level::Trace, __PRETTY_FUNCTION__); + stream << "[ "; + int _i = 0; + int size = available.size(); + while (_i < size) + { + stream << "{ " << available[_i]->getFD() << ": " << available[_i]->getRevents() << " }"; + ++_i; + if (_i < size) { stream << ", "; } + } + stream << " ]"; + } +} +EventLoop::EventLoop() : +bRunning_(false), tid_(ThisThread::tid_), poller_(new Poller), +wakeupChannel_(this, ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)), bFuncCalling(false) +{ + if (wakeupChannel_.getFD() < 0) + { + ROOTIVE_LOG_SYSTEMERROR; + throw; + } + ROOTIVE_LOG_DEBUG << this << " in thread: " << ThisThread::tid_; + wakeupChannel_.setReadCallback( + [&](std::chrono::system_clock::time_point time)-> void + { + uint64_t buffer; + auto res = ::read(wakeupChannel_.getFD(), &buffer, sizeof(buffer)); + if (res != sizeof(buffer)) + { + ROOTIVE_LOG_ERROR << "read " << res << " instead of " << sizeof(buffer); + } + }); + wakeupChannel_.enableRead(); +} +EventLoop::~EventLoop() +{ + ROOTIVE_LOG_DEBUG << this << " of thread: " << tid_ << " in thread: " << ThisThread::tid_; + wakeupChannel_.disableAll(); + wakeupChannel_.remove(); + ::close(wakeupChannel_.getFD()); +} +void EventLoop::run() +{ + assert(bThread()); + bRunning_ = true; + bquit_ = false; + ROOTIVE_LOG_TRACE << this << " start"; + while (!bquit_) + { + std::chrono::system_clock::time_point time = poller_->poll(constexpr_pollTimeout); + const auto &available = poller_->getAvailableVector(); + availableLogTrace(available); + for (Channel *ch : available) + { + ch->handleEvent(time); + } + std::vector funcVector; + { + std::lock_guard lock(funcVectorMutex_); + funcVector.swap(funcVector_); + } + bFuncCalling = true; + for (const Func &func : funcVector) { func(); } + bFuncCalling = false; + } + ROOTIVE_LOG_TRACE << this << " stop"; + bRunning_ = false; +} +void EventLoop::run(Func func) +{ + if (bThread()) { func(); } + else { queue(func); } +} +void EventLoop::queue(Func func) +{ + { + std::lock_guard lock(funcVectorMutex_); + funcVector_.push_back(func); + } + if (!bThread() || bFuncCalling) { wakeup(); } +} + +} \ No newline at end of file diff --git a/Thread/EventLoop.h b/Thread/EventLoop.h new file mode 100644 index 0000000..4701535 --- /dev/null +++ b/Thread/EventLoop.h @@ -0,0 +1,63 @@ +#ifndef ROOTIVE_EVENTLOOP_H +#define ROOTIVE_EVENTLOOP_H + +#include +#include +#include +#include +#include +#include +#include + +#include "Basics/Noncopyable.h" +#include "Thread/Poller.h" +#include "Thread/Channel.h" +#include "Thread/ThisThread.h" + +namespace Rootive +{ + +class EventLoop : Noncopyable +{ + class IgnoreSIGPIPE + { + public: + IgnoreSIGPIPE() + { ::signal(SIGPIPE, SIG_IGN); } + }; +public: + typedef std::function Func; +private: + static const IgnoreSIGPIPE ignore_; + static constexpr int constexpr_pollTimeout = 10000; + + const __pid_t tid_; + std::atomic bRunning_; + std::atomic bquit_; + std::unique_ptr poller_; + Channel wakeupChannel_; + std::vector funcVector_; + std::mutex funcVectorMutex_; + bool bFuncCalling; + + void wakeup(); + void availableLogTrace(const std::vector &available); +public: + EventLoop(); + ~EventLoop(); + + inline const __pid_t &getTID() { return tid_; } + inline bool bThread() { return ThisThread::tid_ == tid_; } + + inline void updateChannel(Channel *ch) { poller_->updateChannel(ch); } + inline void removeChannel(Channel *ch) { poller_->removeChannel(ch); } + + void run(); + void run(Func func); + void queue(Func func); + inline void quit() { bquit_ = true; wakeup(); } +}; + +} + +#endif \ No newline at end of file diff --git a/Thread/EventLoopThread.cpp b/Thread/EventLoopThread.cpp new file mode 100644 index 0000000..43b477a --- /dev/null +++ b/Thread/EventLoopThread.cpp @@ -0,0 +1,46 @@ +#include "EventLoopThread.h" +#include +#include "Thread/EventLoop.h" + +namespace Rootive +{ + +void EventLoopThread::threadFunc() +{ + EventLoop eventLoop; + if (threadInitFunc_) + { + threadInitFunc_(&eventLoop); + } + { + std::lock_guard lock(eventLoopMutex_); + eventLoop_ = &eventLoop; + } + eventLoopCond_.notify_one(); + eventLoop.run(); + std::lock_guard lock(eventLoopMutex_); + eventLoop_ = nullptr; +} +EventLoopThread::EventLoopThread(const std::string &name) : +eventLoop_(nullptr), thread_(nullptr), name_(name) +{} +EventLoopThread::~EventLoopThread() +{ + if (eventLoop_) + { + eventLoop_->quit(); + } + if (thread_) + { + thread_->join(); + delete thread_; + } +} +void EventLoopThread::run() +{ + thread_ = new std::thread(&EventLoopThread::threadFunc, this); + std::unique_lock lock(eventLoopMutex_); + while (!eventLoop_) { eventLoopCond_.wait(lock); } +} + +} \ No newline at end of file diff --git a/Thread/EventLoopThread.h b/Thread/EventLoopThread.h new file mode 100644 index 0000000..d3f91e6 --- /dev/null +++ b/Thread/EventLoopThread.h @@ -0,0 +1,42 @@ +#ifndef ROOTIVE_EVENTLOOPTHREAD_H +#define ROOTIVE_EVENTLOOPTHREAD_H + +#include +#include +#include +#include + +namespace std +{ +class thread; +} + +namespace Rootive +{ +class EventLoop; +class EventLoopThread +{ +public: + typedef std::function ThreadInitFunc; +private: + EventLoop *eventLoop_; + std::mutex eventLoopMutex_; + std::condition_variable eventLoopCond_; + ThreadInitFunc threadInitFunc_; + std::thread *thread_; + std::string name_; + + void threadFunc(); +public: + EventLoopThread(const std::string &name); + ~EventLoopThread(); + + inline EventLoop *getEventLoop() { return eventLoop_; } + inline void setThreadInitFunc(ThreadInitFunc threadInitFunc) { threadInitFunc_ = threadInitFunc; } + + void run(); +}; + +} + +#endif \ No newline at end of file diff --git a/Thread/EventLoopThreadPool.cpp b/Thread/EventLoopThreadPool.cpp new file mode 100644 index 0000000..ecb5885 --- /dev/null +++ b/Thread/EventLoopThreadPool.cpp @@ -0,0 +1,57 @@ +#include "EventLoopThreadPool.h" + +namespace Rootive +{ +EventLoopThreadPool::EventLoopThreadPool(EventLoop *eventLoop, int numThread, const std::string &name) : +eventLoop_(eventLoop), numThread_(numThread), name_(name), next_(0) +{} + +void EventLoopThreadPool::run() +{ + for (int _i = 0; _i < numThread_; ++_i) + { + const int bufferLength = name_.size() + 8; + char *buffer = new char[bufferLength]; + snprintf(buffer, bufferLength * sizeof(char), "%sThread%d", name_.c_str(), _i); + threadVector_.push_back(std::unique_ptr(new EventLoopThread(buffer))); + std::unique_ptr &_thread(threadVector_.back()); + _thread->setThreadInitFunc(threadInitFunc_); + _thread->run(); + eventLoopVector_.push_back(_thread->getEventLoop()); + } + if (!numThread_ && threadInitFunc_) + { + threadInitFunc_(eventLoop_); + } +} +EventLoop *EventLoopThreadPool::getEventLoop() +{ + EventLoop *ret = eventLoop_; + if (!eventLoopVector_.empty()) + { + ret = eventLoopVector_[next_++]; + next_ %= eventLoopVector_.size(); + } + return ret; +} +EventLoop *EventLoopThreadPool::getEventLoop(int hashcode) +{ + EventLoop *ret = eventLoop_; + if (!eventLoopVector_.empty()) + { + ret = eventLoopVector_[hashcode % eventLoopVector_.size()]; + } + return ret; +} +std::vector EventLoopThreadPool::getEventLoopVector() +{ + if (eventLoopVector_.empty()) + { + return std::vector(1, eventLoop_); + } + else + { + return eventLoopVector_; + } +} +} \ No newline at end of file diff --git a/Thread/EventLoopThreadPool.h b/Thread/EventLoopThreadPool.h new file mode 100644 index 0000000..807b683 --- /dev/null +++ b/Thread/EventLoopThreadPool.h @@ -0,0 +1,36 @@ +#ifndef ROOTIVE_EVENTLOOPTHREADPOOL_H +#define ROOTIVE_EVENTLOOPTHREADPOOL_H + +#include "Basics/Noncopyable.h" + +#include +#include + +#include "Thread/EventLoopThread.h" + +namespace Rootive +{ +class EventLoopThreadPool : Noncopyable +{ + EventLoop *eventLoop_; + int numThread_; + std::string name_; + std::vector > threadVector_; + std::vector eventLoopVector_; + EventLoopThread::ThreadInitFunc threadInitFunc_; + int next_; +public: + EventLoopThreadPool(EventLoop *eventLoop, int numThread, const std::string &name); + + inline const std::string &getName() const { return name_; } + + void setThreadInitFunc(EventLoopThread::ThreadInitFunc threadInitFunc) { threadInitFunc_ = threadInitFunc; } + void run(); + + EventLoop *getEventLoop(); + EventLoop *getEventLoop(int hashcode); + std::vector getEventLoopVector(); +}; +} + +#endif \ No newline at end of file diff --git a/Thread/Poller.cpp b/Thread/Poller.cpp new file mode 100644 index 0000000..53719ff --- /dev/null +++ b/Thread/Poller.cpp @@ -0,0 +1,76 @@ +#include "Poller.h" + +#include "Thread/Channel.h" +#include "Log/LogLine.h" + +namespace Rootive +{ + +std::chrono::system_clock::time_point Poller::poll(int timeout) +{ + availableVector_.clear(); + int numActive = ::poll(pollFDVector_.data(), pollFDVector_.size(), timeout); + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + if (numActive > 0) + { + ROOTIVE_LOG_TRACE << numActive << " events"; + for (std::vector::const_iterator pfd = pollFDVector_.cbegin(); pfd != pollFDVector_.cend() && numActive > 0; ++pfd) + { + if (pfd->revents > 0) + { + --numActive; + Channel *ch = channelMap_.find(pfd->fd)->second; + ch->setRevents(pfd->revents); + availableVector_.push_back(ch); + } + } + } + else if (!numActive) + { + ROOTIVE_LOG_TRACE << "nothing"; + } + else if (errno != EINTR) + { + ROOTIVE_LOG_SYSTEMERROR; + } + return now; +} +void Poller::updateChannel(Channel *ch) +{ + ROOTIVE_LOG_TRACE << "fd: " << ch->getFD() << " events: " << ch->getEvents(); + if (ch->index_ < 0) + { + ch->index_ = pollFDVector_.size(); + pollfd pfd = { ch->getFD(), static_cast(ch->getEvents()), 0 }; + pollFDVector_.push_back(pfd); + channelMap_[pfd.fd] = ch; + } + else + { + pollfd &pfd = pollFDVector_[ch->index_]; + pfd.fd = ch->getFD(); + pfd.events = ch->getEvents(); + pfd.revents = 0; + if (ch->bNoneEvent()) { pfd.fd = -pfd.fd - 1; } + } +} +void Poller::removeChannel(Channel *ch) +{ + ROOTIVE_LOG_TRACE << "To remove fd: " << ch->getFD(); + channelMap_.erase(ch->getFD()); + ROOTIVE_LOG_TRACE << "pollFDVector_.size: " << pollFDVector_.size(); + if (ch->index_ != pollFDVector_.size() - 1) + { + int endFD = pollFDVector_.back().fd; + iter_swap(pollFDVector_.begin() + ch->index_, pollFDVector_.end() - 1); + if (endFD < 0) + { + endFD = -endFD - 1; + } + ROOTIVE_LOG_TRACE << "swap fd: " << endFD; + channelMap_[endFD]->index_ = ch->index_; + } + pollFDVector_.pop_back(); +} + +} \ No newline at end of file diff --git a/Thread/Poller.h b/Thread/Poller.h new file mode 100644 index 0000000..89947e3 --- /dev/null +++ b/Thread/Poller.h @@ -0,0 +1,31 @@ +#ifndef ROOTIVE_POLLER_H +#define ROOTIVE_POLLER_H + +#include +#include +#include +#include + +#include "Basics/Noncopyable.h" + +namespace Rootive +{ + +class Channel; +class EventLoop; + +class Poller : Noncopyable +{ + std::vector pollFDVector_; + std::map channelMap_; + std::vector availableVector_; +public: + std::chrono::system_clock::time_point poll(int timeout); + void updateChannel(Channel *ch); + void removeChannel(Channel *ch); + const std::vector &getAvailableVector() const { return availableVector_; } +}; + +} + +#endif \ No newline at end of file diff --git a/Thread/ThisThread.h b/Thread/ThisThread.h new file mode 100644 index 0000000..9143907 --- /dev/null +++ b/Thread/ThisThread.h @@ -0,0 +1,15 @@ +#ifndef ROOTIVE_THISTHREAD_H +#define ROOTIVE_THISTHREAD_H + +#include + +namespace Rootive +{ +namespace ThisThread +{ + const __pid_t pid_ = getpid(); + thread_local const __pid_t tid_ = gettid();; +} +} + +#endif \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..78d37e3 --- /dev/null +++ b/main.cpp @@ -0,0 +1,26 @@ +#include "Thread/EventLoop.h" +#include "Network/InetAddress.h" +#include "Log/LogLine.h" +#include "Log/LogFile.h" +#include "IO/DoubleBufferingOutput.h" +#include "Log/LogLineParse.h" +#include "IO/OutputFile.h" +#include "ForumServer/ForumServer.h" + +using namespace Rootive; + +int main() +{ + LogLine::setOutput(new DoubleBufferingOutput(new OutputFile("/home/dmtsai/bak/Test"))); + EventLoop eventLoop; + const char user[] = "Rootive"; + const char pw[] = ""; // BUG + ForumServer server(&eventLoop, InetAddress(11116), + InetAddress("127.0.0.1", 3306), user, pw, "Forum"); // 47.111.114.171 + server.setContextPath("/home/dmtsai/Sports/"); + server.run(); + eventLoop.run(); + return 0; + // LogLineToConsole c; + // c.run(); +}