Initial commit. Added the current rcpp_cms as a base.

This commit is contained in:
Relintai 2021-02-05 15:24:58 +01:00
commit 54df3653a8
173 changed files with 164964 additions and 0 deletions

128
.clang-format Normal file
View File

@ -0,0 +1,128 @@
# Commented out parameters are those with the same value as base LLVM style
# We can uncomment them if we want to change their value, or enforce the
# chosen value in case the base style changes (last sync: Clang 6.0.1).
---
### General config, applies to all languages ###
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
# AlignConsecutiveAssignments: false
# AlignConsecutiveDeclarations: false
# AlignEscapedNewlines: Right
# AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
# AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: true
# AllowShortLoopsOnASingleLine: false
# AlwaysBreakAfterDefinitionReturnType: None
# AlwaysBreakAfterReturnType: None
# AlwaysBreakBeforeMultilineStrings: false
# AlwaysBreakTemplateDeclarations: false
# BinPackArguments: true
# BinPackParameters: true
# BraceWrapping:
# AfterClass: false
# AfterControlStatement: false
# AfterEnum: false
# AfterFunction: false
# AfterNamespace: false
# AfterObjCDeclaration: false
# AfterStruct: false
# AfterUnion: false
# AfterExternBlock: false
# BeforeCatch: false
# BeforeElse: false
# IndentBraces: false
# SplitEmptyFunction: true
# SplitEmptyRecord: true
# SplitEmptyNamespace: true
# BreakBeforeBinaryOperators: None
# BreakBeforeBraces: Attach
# BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: false
# BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: AfterColon
# BreakStringLiterals: true
ColumnLimit: 0
# CommentPragmas: '^ IWYU pragma:'
# CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
# DerivePointerAlignment: false
# DisableFormat: false
# ExperimentalAutoDetectBinPacking: false
# FixNamespaceComments: true
# ForEachMacros:
# - foreach
# - Q_FOREACH
# - BOOST_FOREACH
# IncludeBlocks: Preserve
IncludeCategories:
- Regex: '".*"'
Priority: 1
- Regex: '^<.*\.h>'
Priority: 2
- Regex: '^<.*'
Priority: 3
# IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: true
# IndentPPDirectives: None
IndentWidth: 4
# IndentWrappedFunctionNames: false
# JavaScriptQuotes: Leave
# JavaScriptWrapImports: true
# KeepEmptyLinesAtTheStartOfBlocks: true
# MacroBlockBegin: ''
# MacroBlockEnd: ''
# MaxEmptyLinesToKeep: 1
# NamespaceIndentation: None
# PenaltyBreakAssignment: 2
# PenaltyBreakBeforeFirstCallParameter: 19
# PenaltyBreakComment: 300
# PenaltyBreakFirstLessLess: 120
# PenaltyBreakString: 1000
# PenaltyExcessCharacter: 1000000
# PenaltyReturnTypeOnItsOwnLine: 60
# PointerAlignment: Right
# RawStringFormats:
# - Delimiter: pb
# Language: TextProto
# BasedOnStyle: google
# ReflowComments: true
# SortIncludes: true
# SortUsingDeclarations: true
# SpaceAfterCStyleCast: false
# SpaceAfterTemplateKeyword: true
# SpaceBeforeAssignmentOperators: true
# SpaceBeforeParens: ControlStatements
# SpaceInEmptyParentheses: false
# SpacesBeforeTrailingComments: 1
# SpacesInAngles: false
# SpacesInContainerLiterals: true
# SpacesInCStyleCastParentheses: false
# SpacesInParentheses: false
# SpacesInSquareBrackets: false
TabWidth: 4
UseTab: Always
---
### C++ specific config ###
Language: Cpp
Standard: Cpp03
---
### ObjC specific config ###
Language: ObjC
Standard: Cpp03
ObjCBlockIndentWidth: 4
# ObjCSpaceAfterProperty: false
# ObjCSpaceBeforeProtocolList: true
---
### Java specific config ###
Language: Java
# BreakAfterJavaFieldAnnotations: false
JavaImportGroups: ['org.godotengine', 'android', 'androidx', 'com.android', 'com.google', 'java', 'javax']
...

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
bin/**
*.dblite
*.o
*.a
*.pyc
settings.json

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2020 Péter Magyar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

14
Readme.md Normal file
View File

@ -0,0 +1,14 @@
# rcpp_cms
This is an experimental project, to implement a html framework in c++, because I'm dissatisfied with
every single framework that I've seen or used so far.
Developing websites nowadays feels more like creating a bunch of oneliner files all over the place,
which I find incredibly annoying and distracting. Not only that I'm also dissatisfied with the way most
ORMs are built, they always come off as strange to use, especially with joins. At this point I'm so fed up,
I'd rather use raw SQl. And let's not forget about auth, I have to think about implementing auth way too
much. Every single time.
I think things can be done better. At lest from my perspective.
Whether this stupid and moronic attempt will end up working or not, we'll see.

198
SConstruct Normal file
View File

@ -0,0 +1,198 @@
#!/usr/bin/env python
EnsureSConsVersion(0, 98, 1)
# System
import glob
import os
import pickle
import sys
import re
import subprocess
from collections import OrderedDict
#from compat import iteritems, isbasestring, open_utf8, decode_utf8, qualname
from SCons import Node
from SCons.Script import Glob
def isbasestring(s):
return isinstance(s, (str, bytes))
def add_source_files(self, sources, files, warn_duplicates=True):
# Convert string to list of absolute paths (including expanding wildcard)
if isbasestring(files):
# Keep SCons project-absolute path as they are (no wildcard support)
if files.startswith("#"):
if "*" in files:
print("ERROR: Wildcards can't be expanded in SCons project-absolute path: '{}'".format(files))
return
files = [files]
else:
dir_path = self.Dir(".").abspath
files = sorted(glob.glob(dir_path + "/" + files))
# Add each path as compiled Object following environment (self) configuration
for path in files:
obj = self.Object(path)
if obj in sources:
if warn_duplicates:
print('WARNING: Object "{}" already included in environment sources.'.format(obj))
else:
continue
sources.append(obj)
def add_library(env, name, sources, **args):
library = env.Library(name, sources, **args)
env.NoCache(library)
return library
def add_program(env, name, sources, **args):
program = env.Program(name, sources, **args)
env.NoCache(program)
return program
env_base = Environment()
env_base.__class__.add_source_files = add_source_files
env_base.__class__.add_library = add_library
env_base.__class__.add_program = add_program
if "TERM" in os.environ:
env_base["ENV"]["TERM"] = os.environ["TERM"]
env_base.AppendENVPath("PATH", os.getenv("PATH"))
env_base.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
env_base.disabled_modules = []
env_base.use_ptrcall = False
env_base.module_version_string = ""
env_base.msvc = False
# avoid issues when building with different versions of python out of the same directory
env_base.SConsignFile(".sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL))
database_list = []
for x in sorted(glob.glob("database/*")):
if not os.path.isdir(x) or not os.path.exists(x + "/detect.py"):
continue
tmppath = "./" + x
sys.path.insert(0, tmppath)
import detect
if detect.is_active() and detect.can_build():
x = x.replace("database/", "") # rest of world
x = x.replace("database\\", "") # win32
database_list += [x]
sys.path.remove(tmppath)
sys.modules.pop("detect")
module_list = []
for x in sorted(glob.glob("modules/*")):
if not os.path.isdir(x) or not os.path.exists(x + "/detect.py"):
continue
tmppath = "./" + x
sys.path.insert(0, tmppath)
import detect
if detect.is_active() and detect.can_build():
x = x.replace("modules/", "") # rest of world
x = x.replace("modules\\", "") # win32
module_list += [x]
sys.path.remove(tmppath)
sys.modules.pop("detect")
# Build options
opts = Variables([], ARGUMENTS)
opts.Add(EnumVariable("target", "Compilation target", "debug", ("debug", "release_debug", "release")))
# Compilation environment setup
opts.Add("CXX", "C++ compiler")
opts.Add("CC", "C compiler")
opts.Add("LINK", "Linker")
opts.Add("CCFLAGS", "Custom flags for both the C and C++ compilers")
opts.Add("CFLAGS", "Custom flags for the C compiler")
opts.Add("CXXFLAGS", "Custom flags for the C++ compiler")
opts.Add("LINKFLAGS", "Custom flags for the linker")
# add default include paths
env_base.Prepend(CPPPATH=["#", "libs"])
env_base.Prepend(CPPPATH=["#libs"])
env_base.Prepend(LINKFLAGS=["-lpthread"])
env_base.Append(CXX=["-o3"])
#env_base.Append(CXX=["-g2"])
env = env_base.Clone()
Export("env")
SConscript("core/SCsub")
for d in database_list:
tmppath = "./database/" + d
sys.path.insert(0, tmppath)
import detect
env_db = env_base.Clone()
# Compilation DB requires SCons 3.1.1+.
from SCons import __version__ as scons_raw_version
scons_ver = env_db._get_major_minor_revision(scons_raw_version)
if scons_ver >= (4, 0, 0):
env_db.Tool("compilation_db")
env_db.Alias("compiledb", env.CompilationDatabase())
detect.configure(env_db)
detect.configure(env)
Export("env_db")
SConscript("database/" + d + "/SCsub")
sys.path.remove(tmppath)
sys.modules.pop("detect")
for m in module_list:
tmppath = "./modules/" + m
sys.path.insert(0, tmppath)
import detect
env_mod = env_base.Clone()
# Compilation DB requires SCons 3.1.1+.
from SCons import __version__ as scons_raw_version
scons_ver = env_mod._get_major_minor_revision(scons_raw_version)
if scons_ver >= (4, 0, 0):
env_mod.Tool("compilation_db")
env_mod.Alias("compiledb", env.CompilationDatabase())
detect.configure(env_mod)
detect.configure(env)
Export("env_mod")
SConscript("modules/" + m + "/SCsub")
sys.path.remove(tmppath)
sys.modules.pop("detect")
env.prg_sources = [ "rdn_application.cpp" ]
libapp = env.add_library("application", env.prg_sources)
env.Prepend(LIBS=[libapp])
prog = env.add_program("#bin/server", ["main.cpp"])

6
compile_linux.sh Executable file
View File

@ -0,0 +1,6 @@
g++ -o3 -g main.cpp ./core/settings.cpp ./core/request.cpp ./core/http_server.cpp ./core/file_cache.cpp ./core/application.cpp ./rdn_application.cpp \
./core/theme.cpp \
-o ./bin/server -Ilibs -lpthread -std=c++11

14
compile_vs.bat Normal file
View File

@ -0,0 +1,14 @@
@echo off
if not defined DevEnvDir (
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64
)
rem debug: /Zi (== -g)
cl main.cpp ./core/settings.cpp ./core/request.cpp ./core/http_server.cpp ./core/file_cache.cpp ./core/application.cpp ./rdn_application.cpp ./core/theme.cpp ^
/Febin/game-vc.exe ^
/EHsc /std:c++17 ^
/Ilibs ^
/link WSock32.lib Ws2_32.lib

4
compile_win.bat Normal file
View File

@ -0,0 +1,4 @@
g++ -o3 -g main.cpp ./core/settings.cpp ./core/request.cpp ./core/http_server.cpp ./core/file_cache.cpp ./core/application.cpp ./rdn_application.cpp ./core/theme.cpp -o ./bin/server.exe -Ilibs -lWsock32 -lWs2_32 -lpthread -std=c++14

11
core/SCsub Normal file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env python
Import("env")
env.core_sources = []
env.add_source_files(env.core_sources, "*.cpp")
# Build it all as a library
lib = env.add_library("core", env.core_sources)
env.Prepend(LIBS=[lib])

153
core/application.cpp Normal file
View File

@ -0,0 +1,153 @@
#include "application.h"
#include <functional>
#include <string>
#include "request.h"
#include "file_cache.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
void Application::load_settings() {
}
void Application::setup_routes() {
default_error_handler_func = Application::default_fallback_error_handler;
error_handler_map[404] = Application::default_404_error_handler;
}
void Application::setup_middleware() {
middlewares.push_back(HandlerInstance(Application::default_routing_middleware));
}
void Application::default_routing_middleware(Object *instance, Request *request) {
std::string path = request->http_parser->getPath();
if (FileCache::get_singleton()->wwwroot_has_file(path)) {
send_file(path, request);
return;
}
HandlerInstance handler_data;
//std::function<void(Object *, Request *)> func;
if (path == "/") {
//quick shortcut
handler_data = index_func;
} else {
std::string main_route = "";
uint32_t endpos = 1;
for (; endpos < path.size(); ++endpos) {
if (path[endpos] == '/') {
break;
}
}
main_route = path.substr(1, endpos - 1);
handler_data = main_route_map[main_route];
}
if (!handler_data.handler_func) {
send_error(404, request);
return;
}
request->handler_instance = handler_data;
request->next_stage();
}
void Application::default_fallback_error_handler(int error_code, Request *request) {
request->response->setBody(default_generic_error_body);
request->send();
}
void Application::default_404_error_handler(int error_code, Request *request) {
request->response->setBody(default_error_404_body);
request->send();
}
void Application::handle_request(Request *request) {
request->middleware_stack = &middlewares;
//note that middlewares handle the routing -> Application::default_routing_middleware by default
request->next_stage();
}
void Application::send_error(int error_code, Request *request) {
std::function<void(int, Request *)> func = error_handler_map[error_code];
if (!func) {
default_error_handler_func(error_code, request);
return;
}
func(error_code, request);
}
void Application::send_file(const std::string &path, Request *request) {
std::string fp = FileCache::get_singleton()->wwwroot + path;
FILE *f = fopen(fp.c_str(), "rb");
if (!f) {
printf("Error: Registered file doesn't exists anymore! %s\n", path.c_str());
send_error(404, request);
return;
}
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET); /* same as rewind(f); */
std::string body;
body.resize(fsize);
fread(&body[0], 1, fsize, f);
fclose(f);
//TODO set mimetype?
request->response->setBody(body);
request->send();
}
void Application::migrate() {
}
Application::Application() {
_instance = this;
}
Application::~Application() {
main_route_map.clear();
error_handler_map.clear();
middlewares.clear();
}
Application *Application::get_instance() {
return _instance;
}
HandlerInstance Application::index_func;
std::map<std::string, HandlerInstance> Application::main_route_map;
std::vector<HandlerInstance> Application::middlewares;
std::map<int, std::function<void(int, Request *)> > Application::error_handler_map;
std::function<void(int, Request *)> Application::default_error_handler_func = nullptr;
Application *Application::_instance = nullptr;
std::string Application::default_error_404_body = "<html><body>404 :(</body></html>";
std::string Application::default_generic_error_body = "<html><body>Internal server error! :(</body></html>";

51
core/application.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef APPLICATION_H
#define APPLICATION_H
#include "object.h"
#include <functional>
#include <map>
#include <string>
#include <vector>
#include "handler_instance.h"
class Request;
class Application {
public:
static std::string default_error_404_body;
static std::string default_generic_error_body;
static void handle_request(Request *request);
static void send_error(int error_code, Request *request);
static void send_file(const std::string &path, Request *request);
static void default_fallback_error_handler(int error_code, Request *request);
static void default_404_error_handler(int error_code, Request *request);
virtual void load_settings();
virtual void setup_routes();
virtual void setup_middleware();
static void default_routing_middleware(Object *instance, Request *request);
virtual void migrate();
Application();
virtual ~Application();
static Application *get_instance();
public:
static HandlerInstance index_func;
static std::map<std::string, HandlerInstance> main_route_map;
static std::vector<HandlerInstance> middlewares;
static std::map<int, std::function<void(int, Request *)> > error_handler_map;
static std::function<void(int, Request *)> default_error_handler_func;
private:
static Application *_instance;
};
#endif

28
core/database.cpp Normal file
View File

@ -0,0 +1,28 @@
#include "database.h"
#include "query_builder.h"
#include "table_builder.h"
#include "query_result.h"
void Database::connect(const std::string &connection_str) {
}
QueryResult *Database::query(const std::string &query) {
return nullptr;
}
void Database::query_run(const std::string &query) {
}
QueryBuilder *Database::get_query_builder() {
return new QueryBuilder();
}
TableBuilder *Database::get_table_builder() {
return new TableBuilder();
}
Database::Database() {
}
Database::~Database() {
}

45
core/database.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef DATABASE_H
#define DATABASE_H
#include <string>
#include <memory>
enum QueryErrorCode {
OK,
ERROR
};
class QueryBuilder;
class TableBuilder;
class QueryResult;
class Database {
public:
//probably needs refcount, depending on what db engines do, todo
//virtual QueryResult *query(const std::string &sql);
//virtual QueryResult *query_async(const std::string &sql);
//or
//virtual QueryErrorCode query(QueryResult *result, const std::string &sql);
//virtual QueryErrorCode query_async(QueryResult *result, const std::string &sql);
//also
//virtual QueryResult *query_prepared(const std::string &sql, param1, param2, ...);
//query interface (codeigniter 3 style)
//virtual void where(""); etc
virtual void connect(const std::string &connection_str);
virtual QueryResult *query(const std::string &query);
virtual void query_run(const std::string &query);
virtual QueryBuilder *get_query_builder();
virtual TableBuilder *get_table_builder();
Database();
~Database();
private:
//std::vector<QueryBuilder *> _builders;
};
#endif

68
core/database_manager.cpp Normal file
View File

@ -0,0 +1,68 @@
#include "database_manager.h"
void DatabaseManager::load() {
//go thourgh settings, and create all the defined db backends
}
uint32_t DatabaseManager::create_database(const std::string &name) {
Database *db = _create_database(name);
if (!db) {
printf("create_database: %s, returned db is null!", name.c_str());
return -1;
}
databases.push_back(db);
return databases.size() - 1;
}
DatabaseManager *DatabaseManager::get_singleton() {
return _instance;
}
void DatabaseManager::_register_db_creation_func(const std::string &name, std::function<Database *()> func) {
if (!func) {
printf("_register_db_creation_func: %s, func is wrong!", name.c_str());
return;
}
_db_creation_func_map[name] = func;
}
void DatabaseManager::_unregister_db_creation_func(const std::string &name) {
_db_creation_func_map.erase(name);
}
Database *DatabaseManager::_create_database(const std::string &name) {
std::function<Database *()> func = _db_creation_func_map[name];
if (!func) {
printf("_create_database: %s, func is wrong!", name.c_str());
return nullptr;
}
Database *db = func();
if (!db) {
printf("_create_database: %s, returned db is null!", name.c_str());
}
return db;
}
DatabaseManager::DatabaseManager() {
_instance = this;
}
DatabaseManager::~DatabaseManager() {
_instance = nullptr;
for (uint32_t i = 0; i < databases.size(); ++i) {
delete databases[i];
}
}
DatabaseManager *DatabaseManager::_instance = nullptr;
std::map<std::string, std::function<Database *()> > DatabaseManager::_db_creation_func_map;

35
core/database_manager.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef DATABASE_MANAGER_H
#define DATABASE_MANAGER_H
#include <vector>
#include <map>
#include <functional>
#include "database.h"
class DatabaseManager {
public:
std::vector<Database *> databases;
void load();
static DatabaseManager *get_singleton();
//note: not threadsafe, create these at the start of your program!
uint32_t create_database(const std::string &name);
static void _register_db_creation_func(const std::string &name, std::function<Database*()> func);
static void _unregister_db_creation_func(const std::string &name);
static Database *_create_database(const std::string &name);
DatabaseManager();
~DatabaseManager();
private:
static DatabaseManager * _instance;
static std::map<std::string, std::function<Database *()> > _db_creation_func_map;
};
#endif

124
core/file_cache.cpp Normal file
View File

@ -0,0 +1,124 @@
#include "file_cache.h"
#include <tinydir/tinydir.h>
#include <iostream>
void FileCache::wwwroot_register_file(const std::string &file_path) {
registered_files.insert(file_path);
}
void FileCache::wwwroot_deregister_file(const std::string &file_path) {
registered_files.erase(file_path);
}
bool FileCache::wwwroot_has_file(const std::string &file_path) {
return registered_files.find(file_path) != registered_files.end();
}
void FileCache::wwwroot_refresh_cache() {
registered_files.clear();
wwwroot_evaluate_dir(wwwroot.c_str());
}
void FileCache::wwwroot_evaluate_dir(const char *path) {
tinydir_dir dir;
if (tinydir_open(&dir, path) == -1) {
printf("Error opening wwwroot!\n");
return;
}
while (dir.has_next) {
tinydir_file file;
if (tinydir_readfile(&dir, &file) == -1) {
tinydir_next(&dir);
continue;
}
if (!file.is_dir) {
std::string np = file.path;
np = np.substr(wwwroot.size(), np.size() - wwwroot.size());
registered_files.insert(np);
} else {
if (file.name[0] == '.' && file.name[1] == '\0' || file.name[0] == '.' && file.name[1] == '.') {
tinydir_next(&dir);
continue;
}
wwwroot_evaluate_dir(file.path);
}
tinydir_next(&dir);
}
tinydir_close(&dir);
}
bool FileCache::get_cached_body(const std::string &path, std::string *body) {
//TODO ERROR MACRO body == null
//this shouldn't need mutexes
CacheEntry *e = cache_map[path];
if (!e) {
return false;
}
int64_t current_timestamp = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
int64_t diff = current_timestamp - e->timestamp;
if (diff > cache_invalidation_time) {
return false;
}
body->append(e->body);
return true;
}
void FileCache::set_cached_body(const std::string &path, const std::string &body) {
cache_mutex.lock();
CacheEntry *e = cache_map[path];
if (!e) {
e = new CacheEntry();
cache_map[path] = e;
}
int64_t current_timestamp = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
e->timestamp = current_timestamp;
e->body = body;
cache_mutex.unlock();
}
FileCache::FileCache(bool singleton) {
if (singleton) {
if (_instance) {
printf("FileCache: Filecache instance is set as singleton, but an another FileCache instance is already set up as singleton! Ignoring setting!\n");
} else {
_instance = this;
}
}
cache_invalidation_time = 1;
}
FileCache::~FileCache() {
registered_files.clear();
if (_instance == this)
_instance = nullptr;
}
FileCache *FileCache::get_singleton() {
return _instance;
}
FileCache *FileCache::_instance = nullptr;

52
core/file_cache.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef FILE_CACHE_H
#define FILE_CACHE_H
#include <string>
#include <set>
#include <map>
#include <chrono>
#include <mutex>
class FileCache {
public:
std::string wwwroot;
int cache_invalidation_time;
//Note: file path should be the url you want to access the file with, inculding lead slash
//e.g. http://127.0.0.1/a/b/d.jpg -> /a/b/d.jpg
void wwwroot_register_file(const std::string &file_path);
void wwwroot_deregister_file(const std::string &file_path);
bool wwwroot_has_file(const std::string &file_path);
void wwwroot_refresh_cache();
void wwwroot_evaluate_dir(const char *path);
bool get_cached_body(const std::string &path, std::string *body);
void set_cached_body(const std::string &path, const std::string &body);
FileCache(bool singleton = false);
virtual ~FileCache();
static FileCache *get_singleton();
std::set<std::string> registered_files;
protected:
struct CacheEntry {
int64_t timestamp;
std::string body;
CacheEntry() {
timestamp = 0;
}
};
std::mutex cache_mutex;
std::map<std::string, CacheEntry *> cache_map;
private:
static FileCache *_instance;
};
#endif

1
core/form_validator.cpp Normal file
View File

@ -0,0 +1 @@
#include "form_validator.h"

51
core/form_validator.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef FORM_H
#define FORM_H
#include <map>
#include <string>
#include <vector>
class Request;
//This might be converted to a form validator
class FormField {
public:
std::string name;
std::string label;
std::map<std::string, std::string> attribues;
virtual std::string render();
virtual bool validate(Request *request, std::vector<std::string> *errors);
FormField();
virtual ~FormField();
};
class InputFormField : public FormField {
std::string render();
};
class InputTextFormField : public InputFormField {
std::string render();
};
class InputPasswordFormField : public InputFormField {
std::string render();
};
class Form {
public:
std::string name;
std::map<std::string, std::string> attribues;
std::vector<FormField *> fields;
//call Theme->render(); in it, and that will go though all attribs and call their renders
virtual std::string render();
Form();
virtual ~Form();
};
#endif

13
core/handler_instance.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "handler_instance.h"
#include "request.h"
#include "object.h"
HandlerInstance::HandlerInstance() {
instance = nullptr;
}
HandlerInstance::HandlerInstance(std::function<void(Object *, Request *)> p_handler_func, Object *p_instance) {
handler_func = p_handler_func;
instance = p_instance;
}

17
core/handler_instance.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef HANDLER_INSTANCE_H
#define HANDLER_INSTANCE_H
#include <functional>
class Object;
class Request;
struct HandlerInstance {
std::function<void(Object *, Request *)> handler_func;
Object *instance;
HandlerInstance();
HandlerInstance(std::function<void(Object *, Request *)> p_handler_func, Object *p_instance = nullptr);
};
#endif

1456
core/html_builder.cpp Normal file

File diff suppressed because it is too large Load Diff

315
core/html_builder.h Normal file
View File

@ -0,0 +1,315 @@
#ifndef HTML_BUILDER_H
#define HTML_BUILDER_H
#include <map>
#include <string>
#include <vector>
class Request;
//This might be converted to a form validator
class HTMLTag {
public:
bool simple;
std::string result;
HTMLTag *str(const std::string &str);
HTMLTag *style(const std::string &val);
HTMLTag *href(const std::string &val);
HTMLTag *cls(const std::string &val);
HTMLTag *id(const std::string &val);
HTMLTag *name(const std::string &val);
HTMLTag *method(const std::string &val);
HTMLTag *type(const std::string &val);
HTMLTag *attrib(const std::string &attr, const std::string &val);
HTMLTag *start(const std::string &p_new_tag, const bool p_simple = false);
HTMLTag *reset();
HTMLTag *close();
bool has_data();
HTMLTag();
};
class HTMLBuilder {
public:
std::string result;
void comment(const std::string &val);
HTMLTag *doctype();
void doctype(const std::string &val);
HTMLTag *a();
HTMLTag *abbr();
HTMLTag *acronym(); //Not supported in HTML5.
HTMLTag *address();
HTMLTag *applet(); //Not supported in HTML5.
HTMLTag *area();
HTMLTag *article();
HTMLTag *aside();
HTMLTag *audio();
HTMLTag *b();
HTMLTag *basefont(); //Not supported in HTML5.
HTMLTag *bdi();
HTMLTag *bdo();
HTMLTag *big(); //Not supported in HTML5.
HTMLTag *blockquote();
HTMLTag *body();
HTMLTag *br();
HTMLTag *button();
HTMLTag *canvas();
HTMLTag *caption();
HTMLTag *center(); //Not supported in HTML5.
HTMLTag *cite();
HTMLTag *code();
HTMLTag *col();
HTMLTag *colgroup();
HTMLTag *data();
HTMLTag *datalist();
HTMLTag *dd();
HTMLTag *del();
HTMLTag *details();
HTMLTag *dfn();
HTMLTag *dialog();
HTMLTag *dir(); // Not supported in HTML5.
HTMLTag *div();
HTMLTag *dl();
HTMLTag *dt();
HTMLTag *em();
HTMLTag *embed();
HTMLTag *fieldset();
HTMLTag *figcaption();
HTMLTag *figure();
HTMLTag *font(); //Not supported in HTML5.
HTMLTag *footer();
HTMLTag *form();
HTMLTag *frame(); //Not supported in HTML5.
HTMLTag *frameset(); //Not supported in HTML5.
HTMLTag *h1();
HTMLTag *h2();
HTMLTag *h3();
HTMLTag *h4();
HTMLTag *h5();
HTMLTag *h6();
HTMLTag *head();
HTMLTag *header();
HTMLTag *hr();
HTMLTag *html();
HTMLTag *i();
HTMLTag *iframe();
HTMLTag *img();
HTMLTag *input();
HTMLTag *ins();
HTMLTag *kbd();
HTMLTag *label();
HTMLTag *legend();
HTMLTag *li();
HTMLTag *link();
HTMLTag *main();
HTMLTag *map();
HTMLTag *mark();
HTMLTag *meta();
HTMLTag *meter();
HTMLTag *nav();
HTMLTag *noframes(); //Not supported in HTML5.
HTMLTag *noscript();
HTMLTag *object();
HTMLTag *ol();
HTMLTag *optgroup();
HTMLTag *option();
HTMLTag *output();
HTMLTag *p();
HTMLTag *param();
HTMLTag *picture();
HTMLTag *pre();
HTMLTag *progress();
HTMLTag *q();
HTMLTag *rp();
HTMLTag *rt();
HTMLTag *ruby();
HTMLTag *s();
HTMLTag *samp();
HTMLTag *script();
HTMLTag *section();
HTMLTag *select();
HTMLTag *small();
HTMLTag *source();
HTMLTag *span();
HTMLTag *strike(); //Not supported in HTML5
HTMLTag *strong();
HTMLTag *style();
HTMLTag *sub();
HTMLTag *summary();
HTMLTag *sup();
HTMLTag *svg();
HTMLTag *table();
HTMLTag *tbody();
HTMLTag *td();
HTMLTag *templateh();
HTMLTag *textarea();
HTMLTag *tfoot();
HTMLTag *th();
HTMLTag *thead();
HTMLTag *time();
HTMLTag *title();
HTMLTag *tr();
HTMLTag *track();
HTMLTag *tt(); //Not supported in HTML5.
HTMLTag *u();
HTMLTag *ul();
HTMLTag *var();
HTMLTag *video();
HTMLTag *wbr();
//closing tags c prefix means close
//Note simple tags should not have these like <br>
//Note that I might have a few that shouldn't be here, those will be removed as I find them
void ca();
void cabbr();
void cacronym();
void caddress();
void capplet();
void carea();
void carticle();
void caside();
void caudio();
void cb();
void cbasefont();
void cbdi();
void cbdo();
void cbig();
void cblockquote();
void cbody();
void cbutton();
void ccanvas();
void ccaption();
void ccenter();
void ccite();
void ccode();
void ccol();
void ccolgroup();
void cdata();
void cdatalist();
void cdd();
void cdel();
void cdetails();
void cdfn();
void cdialog();
void cdir();
void cdiv();
void cdl();
void cdt();
void cem();
void cembed();
void cfieldset();
void cfigcaption();
void cfigure();
void cfont();
void cfooter();
void cform();
void cframe();
void cframeset();
void ch1();
void ch2();
void ch3();
void ch4();
void ch5();
void ch6();
void chead();
void cheader();
void chr();
void chtml();
void ci();
void ciframe();
void cimg();
void cinput();
void cins();
void ckbd();
void clabel();
void clegend();
void cli();
void clink();
void cmain();
void cmap();
void cmark();
void cmeta();
void cmeter();
void cnav();
void cnoframes();
void cnoscript();
void cobject();
void c_ol();
void coptgroup();
void coption();
void coutput();
void cp();
void cparam();
void cpicture();
void cpre();
void cprogress();
void cq();
void crp();
void crt();
void cruby();
void cs();
void csamp();
void cscript();
void csection();
void cselect();
void csmall();
void csource();
void cspan();
void cstrike();
void cstrong();
void cstyle();
void csub();
void csummary();
void csup();
void csvg();
void ctable();
void ctbody();
void ctd();
void ctemplateh();
void ctextarea();
void ctfoot();
void cth();
void cthead();
void ctime();
void ctitle();
void ctr();
void ctrack();
void ctt();
void cu();
void cul();
void cvar();
void cvideo();
void cwbr();
//write
void w(const std::string &val);
//write_escaped
void we(const std::string &val);
void write_tag();
HTMLBuilder();
virtual ~HTMLBuilder();
protected:
HTMLTag tag;
};
#endif

96
core/http_server.cpp Normal file
View File

@ -0,0 +1,96 @@
#include "http_server.h"
#include "request.h"
#include "application.h"
#define LOG_VERBOSE 0
void HTTPServer::http_callback_handler(Request *request) {
Application::handle_request(request);
}
void HTTPServer::httpEnterCallbackDefault(const HTTPParser &httpParser, const HttpSession::Ptr &session) {
Request *request = RequestPool::get_request();
request->http_parser = &httpParser;
request->session = &session;
#if LOG_VERBOSE
std::cout << "method:" << http_method_str(static_cast<http_method>(httpParser.method())) << std::endl;
#endif
http_callback_handler(request);
}
void HTTPServer::wsEnterCallbackDefault(const HttpSession::Ptr &httpSession, WebSocketFormat::WebSocketFrameType opcode, const std::string &payload) {
std::cout << "frame enter of type:" << int(opcode) << std::endl;
std::cout << "payload is:" << payload << std::endl;
// echo frame
auto frame = std::make_shared<std::string>();
WebSocketFormat::wsFrameBuild(payload.c_str(), payload.size(), *frame, WebSocketFormat::WebSocketFrameType::TEXT_FRAME, true, false);
httpSession->send(frame);
}
void HTTPServer::configure() {
}
void HTTPServer::initialize() {
if (service)
return;
configure();
service = TcpService::Create();
service->startWorkerThread(threads);
int p_port = port;
//!
if (listenBuilder)
delete listenBuilder;
listenBuilder = new wrapper::HttpListenerBuilder();
listenBuilder->configureService(service);
listenBuilder->configureSocketOptions({
[](TcpSocket &socket) {
socket.setNodelay();
},
});
listenBuilder->configureConnectionOptions({ AddSocketOption::WithMaxRecvBufferSize(1024) });
listenBuilder->configureListen([p_port](wrapper::BuildListenConfig builder) {
builder.setAddr(false, "0.0.0.0", p_port);
});
listenBuilder->configureEnterCallback([](const HttpSession::Ptr &httpSession, HttpSessionHandlers &handlers) {
handlers.setHttpCallback(HTTPServer::httpEnterCallbackDefault);
handlers.setWSCallback(HTTPServer::wsEnterCallbackDefault);
});
listenBuilder->asyncRun();
}
void HTTPServer::main_loop() {
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
if (brynet::base::app_kbhit()) {
break;
}
}
}
HTTPServer::HTTPServer() {
port = 80;
threads = 4;
listenBuilder = nullptr;
}
HTTPServer::~HTTPServer() {
delete listenBuilder;
}

42
core/http_server.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef HTTP_SERVER_H
#define HTTP_SERVER_H
#include <condition_variable>
#include <iostream>
#include <string>
#include <brynet/base/AppStatus.hpp>
#include <brynet/net/http/HttpFormat.hpp>
#include <brynet/net/http/HttpService.hpp>
#include <brynet/net/http/WebSocketFormat.hpp>
#include <brynet/net/wrapper/HttpServiceBuilder.hpp>
#include <brynet/net/wrapper/ServiceBuilder.hpp>
using namespace brynet;
using namespace brynet::net;
using namespace brynet::net::http;
class Request;
class HTTPServer {
public:
int port;
int threads;
std::shared_ptr<TcpService> service;
wrapper::HttpListenerBuilder *listenBuilder;
static void http_callback_handler(Request *response);
static void httpEnterCallbackDefault(const HTTPParser &httpParser, const HttpSession::Ptr &session);
static void wsEnterCallbackDefault(const HttpSession::Ptr &httpSession, WebSocketFormat::WebSocketFrameType opcode, const std::string &payload);
virtual void configure();
virtual void initialize();
void main_loop();
HTTPServer();
virtual ~HTTPServer();
};
#endif

15
core/object.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "object.h"
#include "database.h"
void Object::migrate() {
}
Object::Object() {
db = nullptr;
}
Object::~Object() {
}

80
core/object.h Normal file
View File

@ -0,0 +1,80 @@
#ifndef OBJECT_H
#define OBJECT_H
#include <list>
#include <string>
#include <vector>
class Database;
//taken from GodotEngine's object.h
#define RCPP_OBJECT(m_class, m_inherits) \
private: \
void operator=(const m_class &p_rval) {} \
\
public: \
virtual std::string get_class() const override { \
return std::string(#m_class); \
} \
static void *get_class_ptr_static() { \
static int ptr; \
return &ptr; \
} \
static std::string get_class_static() { \
return std::string(#m_class); \
} \
static std::string get_parent_class_static() { \
return m_inherits::get_class_static(); \
} \
static void get_inheritance_list_static(std::list<std::string> *p_inheritance_list) { \
m_inherits::get_inheritance_list_static(p_inheritance_list); \
p_inheritance_list->push_back(std::string(#m_class)); \
} \
static std::string inherits_static() { \
return std::string(#m_inherits); \
} \
virtual bool is_class(const std::string &p_class) const override { return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); } \
virtual bool is_class_ptr(void *p_ptr) const override { return (p_ptr == get_class_ptr_static()) ? true : m_inherits::is_class_ptr(p_ptr); } \
\
static void get_valid_parents_static(std::list<std::string> *p_parents) { \
if (m_class::_get_valid_parents_static != m_inherits::_get_valid_parents_static) { \
m_class::_get_valid_parents_static(p_parents); \
} \
\
m_inherits::get_valid_parents_static(p_parents); \
} \
\
private:
class Object {
public:
Database *db;
virtual std::string get_class() const { return "Object"; }
static void *get_class_ptr_static() {
static int ptr;
return &ptr;
}
static std::string get_class_static() { return "Object"; }
static std::string get_parent_class_static() { return std::string(); }
static void get_inheritance_list_static(std::list<std::string> *p_inheritance_list) { p_inheritance_list->push_back("Object"); }
virtual bool is_class(const std::string &p_class) const { return (p_class == "Object"); }
virtual bool is_class_ptr(void *p_ptr) const { return get_class_ptr_static() == p_ptr; }
static void get_valid_parents_static(std::list<std::string> *p_parents) {}
static void _get_valid_parents_static(std::list<std::string> *p_parents) {}
//dbconnection
//setting object?
//FileCache? -> set it to the global singleton by default?
virtual void migrate();
Object();
virtual ~Object();
};
#endif

30
core/query_builder.cpp Normal file
View File

@ -0,0 +1,30 @@
#include "query_builder.h"
QueryBuilder *QueryBuilder::select(const std::string &params) {
return this;
}
QueryBuilder *QueryBuilder::where(const std::string &params) {
return this;
}
QueryBuilder *QueryBuilder::from(const std::string &params) {
return this;
}
QueryBuilder *QueryBuilder::limit(const int min, const int max) {
return this;
}
QueryBuilder *QueryBuilder::insert(const std::string &table_name, const std::string &params_str) {
return this;
}
void QueryBuilder::finalize() {
}
QueryBuilder::QueryBuilder() {
}
QueryBuilder::~QueryBuilder() {
}

21
core/query_builder.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef QUERY_BUILDER_H
#define QUERY_BUILDER_H
#include <string>
class QueryBuilder {
public:
virtual QueryBuilder *select(const std::string &params);
virtual QueryBuilder *where(const std::string &params);
virtual QueryBuilder *from(const std::string &params);
virtual QueryBuilder *limit(const int min, const int max);
virtual QueryBuilder *insert(const std::string &table_name, const std::string &params_str);
virtual void finalize();
QueryBuilder();
virtual ~QueryBuilder();
std::string query_result;
};
#endif

15
core/query_result.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "query_result.h"
bool QueryResult::next_row() {
return false;
}
const char *QueryResult::get_cell(const int index) {
return "";
}
QueryResult::QueryResult() {
}
QueryResult::~QueryResult() {
}

13
core/query_result.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef QUERY_RESULT_H
#define QUERY_RESULT_H
class QueryResult {
public:
virtual bool next_row();
virtual const char*get_cell(const int index);
QueryResult();
virtual ~QueryResult();
};
#endif

128
core/request.cpp Normal file
View File

@ -0,0 +1,128 @@
#include "request.h"
void Request::compile_body() {
compiled_body.reserve(body.size() + head.size() + 13 + 14 + 15);
//13
compiled_body += "<html>"
"<head>";
compiled_body += head;
//14
compiled_body += "</head>"
"<body>";
compiled_body += body;
//15
compiled_body += "</body>"
"</html>";
response->setBody(compiled_body);
}
void Request::compile_and_send_body() {
compile_body();
send();
}
void Request::next_stage() {
if (current_middleware_index == (*middleware_stack).size()) {
handler_instance.handler_func(handler_instance.instance, this);
return;
}
const HandlerInstance &hi = (*middleware_stack)[current_middleware_index++];
hi.handler_func(hi.instance, this);
}
void Request::send() {
if (http_parser->isKeepAlive()) {
response->addHeadValue("Connection", "Keep-Alive");
std::string result = response->getResult();
(*session)->send(result.c_str(), result.size());
} else {
response->addHeadValue("Connection", "Close");
std::string result = response->getResult();
const HttpSession::Ptr lsession = (*session);
(*session)->send(result.c_str(), result.size(), [lsession]() { lsession->postShutdown(); });
}
RequestPool::return_request(this);
}
void Request::reset() {
http_parser = nullptr;
session = nullptr;
current_middleware_index = 0;
middleware_stack = nullptr;
head.clear();
body.clear();
compiled_body.clear();
if (response)
delete response;
response = new HttpResponse();
}
Request::Request() {
response = nullptr;
reset();
}
Request::~Request() {
delete response;
}
Request *RequestPool::get_request() {
_mutex.lock();
Request *request;
if (_requests.size() == 0) {
_mutex.unlock();
request = new Request();
return request;
}
request = _requests[_requests.size() - 1];
_requests.pop_back();
_mutex.unlock();
return request;
}
void RequestPool::return_request(Request *request) {
request->reset();
_mutex.lock();
_requests.push_back(request);
_mutex.unlock();
}
RequestPool::RequestPool() {
}
RequestPool::~RequestPool() {
for (uint32_t i = 0; i < _requests.size(); ++i) {
delete _requests[i];
}
_requests.clear();
}
std::mutex RequestPool::_mutex;
std::vector<Request *> RequestPool::_requests;

53
core/request.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef REQUEST_H
#define REQUEST_H
#include <mutex>
#include <vector>
#include <brynet/net/http/HttpFormat.hpp>
#include <brynet/net/http/HttpService.hpp>
#include "handler_instance.h"
using namespace brynet;
using namespace brynet::net;
using namespace brynet::net::http;
class Request {
public:
const HTTPParser *http_parser;
const HttpSession::Ptr *session;
HttpResponse *response;
uint32_t current_middleware_index;
HandlerInstance handler_instance;
std::vector<HandlerInstance> *middleware_stack;
std::string head;
std::string body;
std::string compiled_body;
void compile_body();
void compile_and_send_body();
void next_stage();
void send();
void reset();
Request();
~Request();
};
class RequestPool {
public:
static Request *get_request();
static void return_request(Request *request);
RequestPool();
~RequestPool();
protected:
static std::mutex _mutex;
static std::vector<Request *> _requests;
};
#endif

47
core/settings.cpp Normal file
View File

@ -0,0 +1,47 @@
#include "settings.h"
#include <stdio.h>
#include <stdlib.h>
void Settings::parse_file(const std::string &path) {
FILE *f = fopen(path.c_str(), "r");
if (!f) {
printf("Settings::parse_file: Error opening file!");
return;
}
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET); /* same as rewind(f); */
std::string config_str;
config_str.resize(fsize);
fread(&config_str[0], 1, fsize, f);
fclose(f);
settings.Parse(config_str.c_str());
}
Settings *Settings::get_singleton() {
return _singleton;
}
Settings::Settings(const bool singleton) {
if (singleton) {
if (_singleton) {
printf("Settings singleton overridden!\n");
}
_singleton = this;
}
}
Settings::~Settings() {
if (_singleton == this) {
_singleton = nullptr;
}
}
Settings *Settings::_singleton = nullptr;

24
core/settings.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef SETTINGS_H
#define SETTINGS_H
#include <string>
#include "rapidjson/document.h"
class Settings {
public:
rapidjson::Document settings;
void parse_file(const std::string &path);
static Settings *get_singleton();
Settings(const bool singleton = false);
virtual ~Settings();
protected:
static Settings *_singleton;
};
#endif

54
core/table_builder.cpp Normal file
View File

@ -0,0 +1,54 @@
#include "table_builder.h"
TableBuilder *TableBuilder::create_table(const std::string &name) {
return this;
}
TableBuilder *TableBuilder::integer(const std::string &name) {
return this;
}
TableBuilder *TableBuilder::date(const std::string &name) {
return this;
}
TableBuilder *TableBuilder::varchar(const std::string &name, const int length) {
return this;
}
TableBuilder *TableBuilder::not_null() {
return this;
}
TableBuilder *TableBuilder::null() {
return this;
}
TableBuilder *TableBuilder::auto_increment() {
return this;
}
TableBuilder *TableBuilder::primary_key(const std::string &name) {
return this;
}
TableBuilder *TableBuilder::primary_key() {
return this;
}
TableBuilder *TableBuilder::next_row() {
return this;
}
TableBuilder *TableBuilder::drop_table(const std::string &name) {
}
void TableBuilder::finalize() {
}
TableBuilder::TableBuilder() {
}
TableBuilder::~TableBuilder() {
}

29
core/table_builder.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef TABLE_BUILDER_H
#define TABLE_BUILDER_H
#include <string>
class TableBuilder {
public:
virtual TableBuilder *create_table(const std::string &name);
virtual TableBuilder *integer(const std::string &name);
virtual TableBuilder *date(const std::string &name);
virtual TableBuilder *varchar(const std::string &name, const int length);
virtual TableBuilder *not_null();
virtual TableBuilder *null();
virtual TableBuilder *auto_increment();
virtual TableBuilder *primary_key(const std::string &name);
virtual TableBuilder *primary_key();
virtual TableBuilder *next_row();
virtual TableBuilder *drop_table(const std::string &name);
virtual void finalize();
TableBuilder();
virtual ~TableBuilder();
std::string result;
};
#endif

30
database/db_init.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef DB_INIT_H
#define DB_INIT_H
#if MYSQL_PRESENT
#include "mysql/mysql_database.h"
#endif
#if PGSQL_PRESENT
#include "postgres/pgsql_database.h"
#endif
#if SQLITE_PRESENT
#include "sqlite/sqlite3_database.h"
#endif
void initialize_database_backends() {
#if MYSQL_PRESENT
MysqlDatabase::_register();
#endif
#if PGSQL_PRESENT
PGSQLDatabase::_register();
#endif
#if SQLITE_PRESENT
SQLite3Database::_register();
#endif
}
#endif

12
database/mysql/SCsub Normal file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
Import("env_db")
Import("env")
env_db.core_sources = []
env_db.add_source_files(env_db.core_sources, "*.cpp")
# Build it all as a library
lib = env_db.add_library("database_mysql", env_db.core_sources)
env.Prepend(LIBS=[lib])

71
database/mysql/detect.py Normal file
View File

@ -0,0 +1,71 @@
import os
import platform
import sys
def is_active():
return True
def get_name():
return "mysql"
def can_build():
if os.name == "posix" or sys.platform == "darwin":
x11_error = os.system("pkg-config --version > /dev/null")
if x11_error:
return False
mariadb_error = os.system("pkg-config mariadb --modversion --silence-errors > /dev/null ")
mysql_error = os.system("pkg-config mysql --modversion --silence-errors > /dev/null ")
if mariadb_error and mysql_error:
print("mysql and mariadb not found..")
return False
if not mariadb_error:
print("mariadb found!")
return True
if not mysql_error:
print("mysql found!")
return True
#todo
return False
def get_opts():
from SCons.Variables import BoolVariable, EnumVariable
return [
EnumVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", "yes", ("yes", "no")),
]
def get_flags():
return []
def configure(env):
mariadb_error = os.system("pkg-config mariadb --modversion --silence-errors > /dev/null ")
mysql_error = os.system("pkg-config mysql --modversion --silence-errors > /dev/null ")
if not mariadb_error:
env.ParseConfig("pkg-config mariadb --cflags --libs")
env.Append(CPPDEFINES=["MYSQL_PRESENT"])
return
if not mysql_error:
env.ParseConfig("pkg-config mysql --cflags --libs")
env.Append(CPPDEFINES=["MYSQL_PRESENT"])
# Link those statically for portability
#if env["use_static_cpp"]:
#env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"])

View File

@ -0,0 +1,120 @@
#include "mysql_database.h"
#include <cstdio>
#include "core/database_manager.h"
#include "mysql_query_builder.h"
#include "mysql_table_builder.h"
#include "mysql_query_result.h"
void MysqlDatabase::connect(const std::string &connection_str) {
mysql = mysql_init(mysql);
mysql_options(mysql, MYSQL_OPT_NONBLOCK, 0);
std::string host = "127.0.0.1";
std::string user = "";
std::string password = "";
std::string dbname = "testappdb";
int port = 3306;
mysql = mysql_real_connect(mysql, host.c_str(), user.c_str(), password.c_str(), dbname.c_str(), port, NULL, 0);
if (mysql) {
printf("mysql connected\n");
}
}
QueryResult *MysqlDatabase::query(const std::string &query) {
if (!mysql)
return nullptr;
//printf("%s\n", query.c_str());
int error = mysql_real_query(mysql, query.c_str(), query.length());
if (error) {
const char *merr = mysql_error(mysql);
printf("MySQL error: %s\n", merr);
return nullptr;
}
MYSQL_RES *result = mysql_use_result(mysql);
MysqlQueryResult *res = new MysqlQueryResult();
res->result = result;
//res->next_row();
return res;
}
void MysqlDatabase::query_run(const std::string &query) {
if (!mysql)
return;
//printf("%s\n", query.c_str());
int error = mysql_real_query(mysql, query.c_str(), query.length());
if (error) {
const char *merr = mysql_error(mysql);
printf("MySQL error: %s\n", merr);
return;
}
//printf("query OK\n");
//printf("----------------\n");
/*
printf("----------------\n");
MYSQL_RES *result = mysql_use_result(mysql);
//MYSQL_RES *result = mysql_store_result(mysql);
MYSQL_ROW row;
while (row = mysql_fetch_row(result)) {
printf("%s\n", row[0]);
//printf("%s\n", row[1]);
}
printf("----------------\n");
mysql_free_result(result);
*/
}
QueryBuilder *MysqlDatabase::get_query_builder() {
return new MysqlQueryBuilder();
}
TableBuilder *MysqlDatabase::get_table_builder() {
return new MysqlTableBuilder();
}
MysqlDatabase::MysqlDatabase() :
Database() {
mysql = nullptr;
}
MysqlDatabase::~MysqlDatabase() {
mysql_close(mysql);
delete mysql;
}
Database *MysqlDatabase::_creation_func() {
return new MysqlDatabase();
}
void MysqlDatabase::_register() {
DatabaseManager::_register_db_creation_func("mysql", MysqlDatabase::_creation_func);
}
void MysqlDatabase::_unregister() {
DatabaseManager::_unregister_db_creation_func("mysql");
}

View File

@ -0,0 +1,37 @@
#ifndef MYSQL_CONNECTION
#define MYSQL_CONNECTION
#include "core/database.h"
#include <memory>
//Brynet has it aswell, and because of using namespace it is defined here aswell
//later this will be fixed better
#ifdef IS_NUM
#undef IS_NUM
#endif
#include <mysql.h>
class MysqlDatabase : public Database {
public:
void connect(const std::string &connection_str);
QueryResult *query(const std::string &query);
void query_run(const std::string &query);
QueryBuilder *get_query_builder();
TableBuilder *get_table_builder();
static Database *_creation_func();
static void _register();
static void _unregister();
MysqlDatabase();
~MysqlDatabase();
MYSQL *mysql;
};
#undef IS_NUM
#endif

View File

@ -0,0 +1,41 @@
#include "mysql_query_builder.h"
QueryBuilder *MysqlQueryBuilder::select(const std::string &params) {
query_result += "SELECT " + params;
return this;
}
QueryBuilder *MysqlQueryBuilder::where(const std::string &params) {
query_result += " WHERE " + params;
return this;
}
QueryBuilder *MysqlQueryBuilder::from(const std::string &params) {
query_result += " FROM " + params;
return this;
}
QueryBuilder *MysqlQueryBuilder::limit(const int min, const int max) {
return this;
}
QueryBuilder *MysqlQueryBuilder::insert(const std::string &table_name, const std::string &params_str) {
query_result += " INSERT INTO " + table_name + " VALUES( " + params_str + " );";
return this;
}
void MysqlQueryBuilder::finalize() {
query_result += ";";
}
MysqlQueryBuilder::MysqlQueryBuilder() {
}
MysqlQueryBuilder::~MysqlQueryBuilder() {
}

View File

@ -0,0 +1,22 @@
#ifndef MYSQL_QUERY_BUILDER_H
#define MYSQL_QUERY_BUILDER_H
#include <memory>
#include <string>
#include "core/query_builder.h"
class MysqlQueryBuilder : public QueryBuilder {
public:
QueryBuilder *select(const std::string &params);
QueryBuilder *where(const std::string &params);
QueryBuilder *from(const std::string &params);
QueryBuilder *limit(const int min, const int max);
QueryBuilder *insert(const std::string &table_name, const std::string &params_str);
void finalize();
MysqlQueryBuilder();
~MysqlQueryBuilder();
};
#endif

View File

@ -0,0 +1,27 @@
#include "mysql_query_result.h"
#include <cstdio>
bool MysqlQueryResult::next_row() {
current_row = mysql_fetch_row(result);
//null if no result
return current_row;
}
const char* MysqlQueryResult::get_cell(const int index) {
if (!current_row)
return "";
return current_row[index];
}
MysqlQueryResult::MysqlQueryResult() : QueryResult() {
result = nullptr;
}
MysqlQueryResult::~MysqlQueryResult() {
if(result) {
mysql_free_result(result);
}
}

View File

@ -0,0 +1,20 @@
#ifndef MYSQL_QUERY_RESULT_H
#define MYSQL_QUERY_RESULT_H
#include "core/query_result.h"
#include <mysql.h>
class MysqlQueryResult : public QueryResult {
public:
bool next_row();
const char* get_cell(const int index);
MysqlQueryResult();
~MysqlQueryResult();
MYSQL_ROW current_row;
MYSQL_RES *result;
};
#endif

View File

@ -0,0 +1,77 @@
#include "mysql_table_builder.h"
TableBuilder *MysqlTableBuilder::create_table(const std::string &name) {
result += "CREATE TABLE " + name + " ( ";
return this;
}
TableBuilder *MysqlTableBuilder::integer(const std::string &name) {
result += name + " INTEGER ";
return this;
}
TableBuilder *MysqlTableBuilder::date(const std::string &name) {
result += name + " DATE ";
return this;
}
TableBuilder *MysqlTableBuilder::varchar(const std::string &name, const int length) {
result += name + " VARCHAR(" + std::to_string(length) + ")";
return this;
}
TableBuilder *MysqlTableBuilder::not_null() {
result += "NOT NULL ";
return this;
}
TableBuilder *MysqlTableBuilder::null() {
result += "NULL ";
return this;
}
TableBuilder *MysqlTableBuilder::auto_increment() {
result += "AUTO_INCREMENT ";
return this;
}
TableBuilder *MysqlTableBuilder::primary_key(const std::string &name) {
result += "PRIMARY KEY (" + name + ") ";
return this;
}
TableBuilder *MysqlTableBuilder::primary_key() {
result += "PRIMARY KEY ";
return this;
}
TableBuilder *MysqlTableBuilder::next_row() {
result += ", ";
return this;
}
void MysqlTableBuilder::finalize() {
result += ");";
}
TableBuilder *MysqlTableBuilder::drop_table(const std::string &name) {
result += "DROP TABLE " + name + ";";
return this;
}
MysqlTableBuilder::MysqlTableBuilder() {
}
MysqlTableBuilder::~MysqlTableBuilder() {
}

View File

@ -0,0 +1,29 @@
#ifndef MYSQL_TABLE_BUILDER_H
#define MYSQL_TABLE_BUILDER_H
#include <string>
#include "core/table_builder.h"
class MysqlTableBuilder : public TableBuilder {
public:
TableBuilder *create_table(const std::string &name);
TableBuilder *integer(const std::string &name);
TableBuilder *date(const std::string &name);
TableBuilder *varchar(const std::string &name, const int length);
TableBuilder *not_null();
TableBuilder *null();
TableBuilder *auto_increment();
TableBuilder *primary_key(const std::string &name);
TableBuilder *primary_key();
TableBuilder *next_row();
TableBuilder *drop_table(const std::string &name);
void finalize();
MysqlTableBuilder();
virtual ~MysqlTableBuilder();
};
#endif

12
database/postgres/SCsub Normal file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
Import("env_db")
Import("env")
env_db.core_sources = []
env_db.add_source_files(env_db.core_sources, "*.cpp")
# Build it all as a library
lib = env_db.add_library("database_pgsql", env_db.core_sources)
env.Prepend(LIBS=[lib])

View File

@ -0,0 +1,55 @@
import os
import platform
import sys
def is_active():
return True
def get_name():
return "pgsql"
def can_build():
if os.name == "posix" or sys.platform == "darwin":
x11_error = os.system("pkg-config --version > /dev/null")
if x11_error:
return False
libpg_error = os.system("pkg-config libpq --modversion --silence-errors > /dev/null ")
if libpg_error:
print("postgres not found!")
return False
print("postgres found!")
return True
#todo
return False
def get_opts():
from SCons.Variables import BoolVariable, EnumVariable
return [
EnumVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", "yes", ("yes", "no")),
]
def get_flags():
return []
def configure(env):
env.ParseConfig("pkg-config libpq --cflags --libs")
env.Append(CPPDEFINES=["PGSQL_PRESENT"])
# Link those statically for portability
#if env["use_static_cpp"]:
#env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"])

View File

@ -0,0 +1,15 @@
#include "pgsql_database.h"
#include "core/database_manager.h"
Database *PGSQLDatabase::_creation_func() {
return new PGSQLDatabase();
}
void PGSQLDatabase::_register() {
DatabaseManager::_register_db_creation_func("pgsql", PGSQLDatabase::_creation_func);
}
void PGSQLDatabase::_unregister() {
DatabaseManager::_unregister_db_creation_func("pgsql");
}

View File

@ -0,0 +1,33 @@
#ifndef PGSQL_CONNECTION
#define PGSQL_CONNECTION
#include "core/database.h"
//Brynet has it aswell, and because of using namespace it is defined here aswell
//later this will be fixed better
//#ifdef IS_NUM
//#undef IS_NUM
//#endif
#include <libpq-fe.h>
class PGSQLDatabase : public Database {
public:
static Database *_creation_func();
static void _register();
static void _unregister();
PGSQLDatabase() :
Database() {
conn = PQconnectStart("");
}
~PGSQLDatabase() {
PQfinish(conn);
}
PGconn *conn;
};
//#undef IS_NUM
#endif

13
database/sqlite/SCsub Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env python
Import("env_db")
Import("env")
env_db.core_sources = []
env_db.add_source_files(env_db.core_sources, "*.cpp")
env_db.core_sources.append("./sqlite/sqlite3.c")
# Build it all as a library
lib = env_db.add_library("database_pgsql", env_db.core_sources)
env.Prepend(LIBS=[lib])

58
database/sqlite/detect.py Normal file
View File

@ -0,0 +1,58 @@
import os
import platform
import sys
def is_active():
return True
def get_name():
return "pgsql"
def can_build():
# if os.name == "posix" or sys.platform == "darwin":
# x11_error = os.system("pkg-config --version > /dev/null")
# if x11_error:
# return False
# sqlite_error = os.system("pkg-config sqlite3 --modversion --silence-errors > /dev/null ")
# if sqlite_error:
# print("sqlite3 not found!")
# return False
# print("sqlite3 found!")
# return True
# #todo
# return False
print("sqlite3 built in!")
return True
def get_opts():
from SCons.Variables import BoolVariable, EnumVariable
return [
# EnumVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", "yes", ("yes", "no")),
]
def get_flags():
return []
def configure(env):
#env.ParseConfig("pkg-config sqlite3 --cflags --libs")
env.Append(CPPDEFINES=["SQLITE_PRESENT"])
# Link those statically for portability
#if env["use_static_cpp"]:
#env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"])

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,186 @@
EXPORTS
sqlite3_aggregate_context
sqlite3_aggregate_count
sqlite3_auto_extension
sqlite3_backup_finish
sqlite3_backup_init
sqlite3_backup_pagecount
sqlite3_backup_remaining
sqlite3_backup_step
sqlite3_bind_blob
sqlite3_bind_double
sqlite3_bind_int
sqlite3_bind_int64
sqlite3_bind_null
sqlite3_bind_parameter_count
sqlite3_bind_parameter_index
sqlite3_bind_parameter_name
sqlite3_bind_text
sqlite3_bind_text16
sqlite3_bind_value
sqlite3_bind_zeroblob
sqlite3_blob_bytes
sqlite3_blob_close
sqlite3_blob_open
sqlite3_blob_read
sqlite3_blob_write
sqlite3_busy_handler
sqlite3_busy_timeout
sqlite3_changes
sqlite3_clear_bindings
sqlite3_close
sqlite3_collation_needed
sqlite3_collation_needed16
sqlite3_column_blob
sqlite3_column_bytes
sqlite3_column_bytes16
sqlite3_column_count
sqlite3_column_database_name
sqlite3_column_database_name16
sqlite3_column_decltype
sqlite3_column_decltype16
sqlite3_column_double
sqlite3_column_int
sqlite3_column_int64
sqlite3_column_name
sqlite3_column_name16
sqlite3_column_origin_name
sqlite3_column_origin_name16
sqlite3_column_table_name
sqlite3_column_table_name16
sqlite3_column_text
sqlite3_column_text16
sqlite3_column_type
sqlite3_column_value
sqlite3_commit_hook
sqlite3_compileoption_get
sqlite3_compileoption_used
sqlite3_complete
sqlite3_complete16
sqlite3_config
sqlite3_context_db_handle
sqlite3_create_collation
sqlite3_create_collation16
sqlite3_create_collation_v2
sqlite3_create_function
sqlite3_create_function16
sqlite3_create_module
sqlite3_create_module_v2
sqlite3_data_count
sqlite3_db_config
sqlite3_db_handle
sqlite3_db_mutex
sqlite3_db_status
sqlite3_declare_vtab
sqlite3_enable_load_extension
sqlite3_enable_shared_cache
sqlite3_errcode
sqlite3_errmsg
sqlite3_errmsg16
sqlite3_exec
sqlite3_expired
sqlite3_extended_errcode
sqlite3_extended_result_codes
sqlite3_file_control
sqlite3_finalize
sqlite3_free
sqlite3_free_table
sqlite3_get_autocommit
sqlite3_get_auxdata
sqlite3_get_table
sqlite3_global_recover
sqlite3_initialize
sqlite3_interrupt
sqlite3_last_insert_rowid
sqlite3_libversion
sqlite3_libversion_number
sqlite3_limit
sqlite3_load_extension
sqlite3_log
sqlite3_malloc
sqlite3_memory_alarm
sqlite3_memory_highwater
sqlite3_memory_used
sqlite3_mprintf
sqlite3_mutex_alloc
sqlite3_mutex_enter
sqlite3_mutex_free
sqlite3_mutex_leave
sqlite3_mutex_try
sqlite3_next_stmt
sqlite3_open
sqlite3_open16
sqlite3_open_v2
sqlite3_os_end
sqlite3_os_init
sqlite3_overload_function
sqlite3_prepare
sqlite3_prepare16
sqlite3_prepare16_v2
sqlite3_prepare_v2
sqlite3_profile
sqlite3_progress_handler
sqlite3_randomness
sqlite3_realloc
sqlite3_release_memory
sqlite3_reset
sqlite3_reset_auto_extension
sqlite3_result_blob
sqlite3_result_double
sqlite3_result_error
sqlite3_result_error16
sqlite3_result_error_code
sqlite3_result_error_nomem
sqlite3_result_error_toobig
sqlite3_result_int
sqlite3_result_int64
sqlite3_result_null
sqlite3_result_text
sqlite3_result_text16
sqlite3_result_text16be
sqlite3_result_text16le
sqlite3_result_value
sqlite3_result_zeroblob
sqlite3_rollback_hook
sqlite3_set_authorizer
sqlite3_set_auxdata
sqlite3_shutdown
sqlite3_sleep
sqlite3_snprintf
sqlite3_soft_heap_limit
sqlite3_sourceid
sqlite3_sql
sqlite3_status
sqlite3_step
sqlite3_stmt_status
sqlite3_strnicmp
sqlite3_table_column_metadata
sqlite3_test_control
sqlite3_thread_cleanup
sqlite3_threadsafe
sqlite3_total_changes
sqlite3_trace
sqlite3_transfer_bindings
sqlite3_update_hook
sqlite3_user_data
sqlite3_value_blob
sqlite3_value_bytes
sqlite3_value_bytes16
sqlite3_value_double
sqlite3_value_int
sqlite3_value_int64
sqlite3_value_numeric_type
sqlite3_value_text
sqlite3_value_text16
sqlite3_value_text16be
sqlite3_value_text16le
sqlite3_value_type
sqlite3_version
sqlite3_vfs_find
sqlite3_vfs_register
sqlite3_vfs_unregister
sqlite3_vmprintf
sqlite3_wal_autocheckpoint
sqlite3_wal_checkpoint
sqlite3_wal_hook
sqlite3_win32_mbcs_to_utf8

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,378 @@
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef _SQLITE3EXT_H_
#define _SQLITE3EXT_H_
#include "sqlite3.h"
typedef struct sqlite3_api_routines sqlite3_api_routines;
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each others' shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*snprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*),void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
};
/*
** The following macros redefine the API routines so that they are
** redirected throught the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#ifndef SQLITE_CORE
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->snprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#endif /* SQLITE_CORE */
#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0;
#define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v;
#endif /* _SQLITE3EXT_H_ */

View File

@ -0,0 +1,15 @@
#include "sqlite3_database.h"
#include "core/database_manager.h"
Database *SQLite3Database::_creation_func() {
return new SQLite3Database();
}
void SQLite3Database::_register() {
DatabaseManager::_register_db_creation_func("sqlite", SQLite3Database::_creation_func);
}
void SQLite3Database::_unregister() {
DatabaseManager::_unregister_db_creation_func("sqlite");
}

View File

@ -0,0 +1,42 @@
#ifndef SQLITE3_CONNECTION
#define SQLITE3_CONNECTION
#include "core/database.h"
//Brynet has it aswell, and because of using namespace it is defined here aswell
//later this will be fixed better
//#ifdef IS_NUM
//#undef IS_NUM
//#endif
#include <cstdio>
#include <sqlite3.h>
class SQLite3Database : public Database {
public:
static Database *_creation_func();
static void _register();
static void _unregister();
SQLite3Database() :
Database() {
int ret = sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
if (ret != SQLITE_OK) {
printf("SQLITE3 multithreading is not supported!\n");
}
ret = sqlite3_open("", &conn);
}
~SQLite3Database() {
sqlite3_close(conn);
}
sqlite3 *conn;
};
//#undef IS_NUM
#endif

3
libs/HEADS Normal file
View File

@ -0,0 +1,3 @@
RapidJSON 0ccdbf364c577803e2a751f5aededce935314313
brynet b0d13e7419628d0f7051a2bb310daaf8a506e08b
rapidxml 1.13

21
libs/brynet/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 IronsDu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

3
libs/brynet/Version.hpp Normal file
View File

@ -0,0 +1,3 @@
#pragma once
#define BRYNET_VERSION 1008000

30
libs/brynet/base/Any.hpp Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include <brynet/base/CPP_VERSION.hpp>
#ifdef BRYNET_HAVE_LANG_CXX17
#include <any>
#else
#include <cstdint>
#endif
namespace brynet { namespace base {
#ifdef BRYNET_HAVE_LANG_CXX17
using BrynetAny = std::any;
template<typename T>
auto cast(const BrynetAny& ud)
{
return std::any_cast<T>(&ud);
}
#else
using BrynetAny = int64_t;
template<typename T>
const T* cast(const BrynetAny& ud)
{
return static_cast<const T*>(&ud);
}
#endif
} }

View File

@ -0,0 +1,47 @@
#pragma once
#include <cstdbool>
#include <cstdio>
#include <signal.h>
#include <brynet/base/Platform.hpp>
#ifdef BRYNET_PLATFORM_WINDOWS
#include <conio.h>
#else
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#endif
namespace brynet { namespace base {
static bool app_kbhit()
{
#ifdef BRYNET_PLATFORM_WINDOWS
return _kbhit();
#else
struct termios oldt;
tcgetattr(STDIN_FILENO, &oldt);
auto newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
const auto oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
const auto ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if (ch != EOF)
{
ungetc(ch, stdin);
return true;
}
return false;
#endif
}
} }

129
libs/brynet/base/Array.hpp Normal file
View File

@ -0,0 +1,129 @@
#pragma once
#include <cstdbool>
#include <cstring>
#include <cstdlib>
#include <cassert>
namespace brynet { namespace base {
struct array_s
{
void* buffer;
size_t buffer_size;
size_t element_size;
size_t element_num;
};
static void array_delete(struct array_s* self)
{
if (self == nullptr)
{
return;
}
if (self->buffer != nullptr)
{
free(self->buffer);
self->buffer = nullptr;
}
self->element_num = 0;
free(self);
self = nullptr;
}
static struct array_s* array_new(size_t num, size_t element_size)
{
auto ret = (struct array_s*)malloc(sizeof(struct array_s));
if (ret == nullptr)
{
return nullptr;
}
const auto buffer_size = num * element_size;
ret->buffer_size = 0;
ret->element_size = 0;
ret->element_num = 0;
ret->buffer = malloc(buffer_size);
if (ret->buffer != nullptr)
{
ret->element_size = element_size;
ret->element_num = num;
ret->buffer_size = buffer_size;
}
else
{
array_delete(ret);
ret = nullptr;
}
return ret;
}
static void* array_at(struct array_s* self, size_t index)
{
void* ret = nullptr;
if (index < self->element_num)
{
ret = (char*)(self->buffer) + (index * self->element_size);
}
else
{
assert(false);
}
return ret;
}
static bool array_set(struct array_s* self, size_t index, const void* data)
{
void* old_data = array_at(self, index);
if (old_data != nullptr)
{
memcpy(old_data, data, self->element_size);
return true;
}
else
{
return false;
}
}
static bool array_increase(struct array_s* self, size_t increase_num)
{
if (increase_num == 0)
{
return false;
}
const auto new_buffer_size = self->buffer_size + increase_num * self->element_size;
auto new_buffer = malloc(new_buffer_size);
if (new_buffer != nullptr)
{
memcpy(new_buffer, self->buffer, self->buffer_size);
free(self->buffer);
self->buffer = new_buffer;
self->element_num += increase_num;
self->buffer_size = new_buffer_size;
return true;
}
else
{
assert(false);
return false;
}
}
static size_t array_num(const struct array_s* self)
{
return self->element_num;
}
} }

189
libs/brynet/base/Buffer.hpp Normal file
View File

@ -0,0 +1,189 @@
#pragma once
#include <cstdbool>
#include <cstdint>
#include <cstdlib>
#include <cstring>
namespace brynet { namespace base {
struct buffer_s
{
char* data;
size_t data_len;
size_t write_pos;
size_t read_pos;
};
static void buffer_delete(struct buffer_s* self)
{
if (self == nullptr)
{
return;
}
if (self->data != nullptr)
{
free(self->data);
self->data = nullptr;
}
free(self);
self = nullptr;
}
static struct buffer_s* buffer_new(size_t buffer_size)
{
struct buffer_s* ret = (struct buffer_s*)malloc(sizeof(struct buffer_s));
if (ret == nullptr)
{
return nullptr;
}
ret->data_len = 0;
ret->read_pos = 0;
ret->write_pos = 0;
ret->data = (char*)malloc(sizeof(char) * buffer_size);
if (ret->data != nullptr)
{
ret->data_len = buffer_size;
}
else
{
buffer_delete(ret);
ret = nullptr;
}
return ret;
}
static size_t buffer_getreadvalidcount(struct buffer_s* self)
{
return self->write_pos - self->read_pos;
}
static void buffer_adjustto_head(struct buffer_s* self)
{
if (self->read_pos == 0)
{
return;
}
const auto len = buffer_getreadvalidcount(self);
if (len > 0)
{
memmove(self->data, self->data + self->read_pos, len);
}
self->read_pos = 0;
self->write_pos = len;
}
static void buffer_init(struct buffer_s* self)
{
self->read_pos = 0;
self->write_pos = 0;
}
static size_t buffer_getwritepos(struct buffer_s* self)
{
return self->write_pos;
}
static size_t buffer_getreadpos(struct buffer_s* self)
{
return self->read_pos;
}
static bool buffer_addwritepos(struct buffer_s* self, size_t value)
{
const size_t temp = self->write_pos + value;
if (temp <= self->data_len)
{
self->write_pos = temp;
return true;
}
else
{
return false;
}
}
static bool buffer_addreadpos(struct buffer_s* self, size_t value)
{
const size_t temp = self->read_pos + value;
if (temp <= self->data_len)
{
self->read_pos = temp;
return true;
}
else
{
return false;
}
}
static size_t buffer_getwritevalidcount(struct buffer_s* self)
{
return self->data_len - self->write_pos;
}
static size_t buffer_getsize(struct buffer_s* self)
{
return self->data_len;
}
static char* buffer_getwriteptr(struct buffer_s* self)
{
if (self->write_pos < self->data_len)
{
return self->data + self->write_pos;
}
else
{
return nullptr;
}
}
static char* buffer_getreadptr(struct buffer_s* self)
{
if (self->read_pos < self->data_len)
{
return self->data + self->read_pos;
}
else
{
return nullptr;
}
}
static bool buffer_write(struct buffer_s* self, const char* data, size_t len)
{
bool write_ret = true;
if (buffer_getwritevalidcount(self) >= len)
{
/* Ö±½ÓдÈë */
memcpy(buffer_getwriteptr(self), data, len);
buffer_addwritepos(self, len);
}
else
{
size_t left_len = self->data_len - buffer_getreadvalidcount(self);
if (left_len >= len)
{
buffer_adjustto_head(self);
buffer_write(self, data, len);
}
else
{
write_ret = false;
}
}
return write_ret;
}
} }

View File

@ -0,0 +1,16 @@
#pragma once
#if (__cplusplus >= 201103L || \
(defined(_MSC_VER) && _MSC_VER >= 1800))
#define BRYNET_HAVE_LANG_CXX11 1
#endif
#if (__cplusplus >= 201402L || \
(defined(_MSC_VER) && _MSC_VER >= 1900))
#define BRYNET_HAVE_LANG_CXX14 1
#endif
#if (__cplusplus >= 201703L || \
(defined(_MSC_VER) && _MSC_VER >= 1910))
#define BRYNET_HAVE_LANG_CXX17 1
#endif

View File

@ -0,0 +1,9 @@
#pragma once
#include <brynet/base/CPP_VERSION.hpp>
#ifdef BRYNET_HAVE_LANG_CXX17
#define BRYNET_NOEXCEPT noexcept
#else
#define BRYNET_NOEXCEPT
#endif

View File

@ -0,0 +1,16 @@
#pragma once
namespace brynet { namespace base {
class NonCopyable
{
public:
NonCopyable(const NonCopyable&) = delete;
const NonCopyable& operator=(const NonCopyable&) = delete;
protected:
NonCopyable() = default;
~NonCopyable() = default;
};
} }

398
libs/brynet/base/Packet.hpp Normal file
View File

@ -0,0 +1,398 @@
#pragma once
#include <cstdint>
#include <cassert>
#include <cstdbool>
#include <cstring>
#include <string>
#include <brynet/base/NonCopyable.hpp>
#include <brynet/base/endian/Endian.hpp>
namespace brynet { namespace base {
class BasePacketWriter : public NonCopyable
{
public:
BasePacketWriter(char* buffer,
size_t len,
bool useBigEndian = true,
bool isAutoMalloc = false)
:
mIsAutoMalloc(isAutoMalloc),
mBigEndian(useBigEndian)
{
mMaxLen = len;
mPos = 0;
mBuffer = buffer;
mMallocBuffer = nullptr;
}
virtual ~BasePacketWriter()
{
if (mMallocBuffer != nullptr)
{
free(mMallocBuffer);
mMallocBuffer = nullptr;
}
}
void init()
{
mPos = 0;
}
size_t getMaxLen() const
{
return mMaxLen;
}
size_t getPos() const
{
return mPos;
}
const char* getData() const
{
return mBuffer;
}
bool isAutoGrow() const
{
return mIsAutoMalloc;
}
bool writeBool(bool value)
{
static_assert(sizeof(bool) == sizeof(int8_t), "");
return writeBuffer((char*)&value, sizeof(value));
}
bool writeINT8(int8_t value)
{
return writeBuffer((char*)&value, sizeof(value));
}
bool writeUINT8(uint8_t value)
{
return writeBuffer((char*)&value, sizeof(value));
}
bool writeINT16(int16_t value)
{
value = endian::hostToNetwork16(value, mBigEndian);
return writeBuffer((char*)&value, sizeof(value));
}
bool writeUINT16(uint16_t value)
{
value = endian::hostToNetwork16(value, mBigEndian);
return writeBuffer((char*)&value, sizeof(value));
}
bool writeINT32(int32_t value)
{
value = endian::hostToNetwork32(value, mBigEndian);
return writeBuffer((char*)&value, sizeof(value));
}
bool writeUINT32(uint32_t value)
{
value = endian::hostToNetwork32(value, mBigEndian);
return writeBuffer((char*)&value, sizeof(value));
}
bool writeINT64(int64_t value)
{
value = endian::hostToNetwork64(value, mBigEndian);
return writeBuffer((char*)&value, sizeof(value));
}
bool writeUINT64(uint64_t value)
{
value = endian::hostToNetwork64(value, mBigEndian);
return writeBuffer((char*)&value, sizeof(value));
}
bool writeBinary(const std::string& binary)
{
return writeBuffer(binary.c_str(), binary.size());
}
bool writeBinary(const char* binary, size_t binaryLen)
{
return writeBuffer(binary, binaryLen);
}
bool writeBuffer(const char* buffer, size_t len)
{
growBuffer(len);
if (mMaxLen < (mPos + len))
{
return false;
}
memcpy(mBuffer + mPos, buffer, len);
mPos += len;
return true;
}
BasePacketWriter & operator << (const bool &v)
{
writeBool(v);
return *this;
}
BasePacketWriter & operator << (const uint8_t &v)
{
writeUINT8(v);
return *this;
}
BasePacketWriter & operator << (const int8_t &v)
{
writeINT8(v);
return *this;
}
BasePacketWriter & operator << (const int16_t &v)
{
writeINT16(v);
return *this;
}
BasePacketWriter & operator << (const uint16_t &v)
{
writeUINT16(v);
return *this;
}
BasePacketWriter & operator << (const int32_t &v)
{
writeINT32(v);
return *this;
}
BasePacketWriter & operator << (const uint32_t &v)
{
writeUINT32(v);
return *this;
}
BasePacketWriter & operator << (const int64_t &v)
{
writeINT64(v);
return *this;
}
BasePacketWriter & operator << (const uint64_t &v)
{
writeUINT64(v);
return *this;
}
BasePacketWriter & operator << (const char* const &v)
{
writeBinary(v);
return *this;
}
BasePacketWriter & operator << (const std::string &v)
{
writeBinary(v);
return *this;
}
private:
// 为了避免直接<<导致没有指定字节序导致隐藏BUG,因为此函数设置为私有
template<typename T>
BasePacketWriter & operator << (const T& v)
{
static_assert(!std::is_pointer<T>::value, "T must is't a pointer");
static_assert(std::is_class <T>::value, "T must a class or struct type");
static_assert(std::is_pod <T>::value, "T must a pod type");
writeBuffer((const char*)&v, sizeof(v));
return *this;
}
template<typename Arg1, typename... Args>
void writev(const Arg1& arg1, const Args&... args)
{
this->operator<<(arg1);
writev(args...);
}
void writev()
{
}
protected:
void growBuffer(size_t len)
{
if (!mIsAutoMalloc || (mPos + len) <= mMaxLen)
{
return;
}
auto newBuffer = (char*)malloc(mMaxLen + len);
if (newBuffer == nullptr)
{
return;
}
memcpy(newBuffer, mBuffer, mPos);
if (mMallocBuffer != nullptr)
{
free(mMallocBuffer);
mMallocBuffer = nullptr;
}
mMaxLen += len;
mMallocBuffer = newBuffer;
mBuffer = newBuffer;
}
protected:
const bool mIsAutoMalloc;
bool mBigEndian;
size_t mPos;
size_t mMaxLen;
char* mBuffer;
char* mMallocBuffer;
};
class BasePacketReader
{
public:
BasePacketReader(const char* buffer,
size_t len,
bool useBigEndian = true) :
mBigEndian(useBigEndian),
mMaxLen(len)
{
mPos = 0;
mBuffer = buffer;
}
virtual ~BasePacketReader() = default;
size_t getLeft() const
{
if (mPos > mMaxLen)
{
throw std::out_of_range("current pos is greater than max len");
}
return mMaxLen - mPos;
}
const char* getBuffer() const
{
return mBuffer;
}
void skipAll()
{
mPos = mMaxLen;
}
size_t getPos() const
{
return mPos;
}
size_t getMaxPos() const
{
return mMaxLen;
}
void addPos(size_t diff)
{
const auto tmpPos = mPos + diff;
if (tmpPos > mMaxLen)
{
throw std::out_of_range("diff is to big");
}
mPos = tmpPos;
}
bool readBool()
{
static_assert(sizeof(bool) == sizeof(int8_t), "");
bool value = false;
read(value);
return value;
}
int8_t readINT8()
{
int8_t value = 0;
read(value);
return value;
}
uint8_t readUINT8()
{
uint8_t value = 0;
read(value);
return value;
}
int16_t readINT16()
{
int16_t value = 0;
read(value);
return endian::networkToHost16(value, mBigEndian);
}
uint16_t readUINT16()
{
uint16_t value = 0;
read(value);
return endian::networkToHost16(value, mBigEndian);
}
int32_t readINT32()
{
int32_t value = 0;
read(value);
return endian::networkToHost32(value, mBigEndian);
}
uint32_t readUINT32()
{
uint32_t value = 0;
read(value);
return endian::networkToHost32(value, mBigEndian);
}
int64_t readINT64()
{
int64_t value = 0;
read(value);
return endian::networkToHost64(value, mBigEndian);
}
uint64_t readUINT64()
{
uint64_t value = 0;
read(value);
return endian::networkToHost64(value, mBigEndian);
}
private:
// 为了避免直接read(uintXXX)导致没有指定字节序造成隐患BUG,因为此函数设置为私有
template<typename T>
void read(T& value)
{
static_assert(std::is_same<T, typename std::remove_pointer<T>::type>::value,
"T must a nomal type");
static_assert(std::is_pod<T>::value,
"T must a pod type");
if ((mPos + sizeof(value)) > mMaxLen)
{
throw std::out_of_range("T size is to big");
}
value = *(T*)(mBuffer + mPos);
mPos += sizeof(value);
}
protected:
const bool mBigEndian;
const size_t mMaxLen;
const char* mBuffer;
size_t mPos;
};
template<size_t SIZE>
class AutoMallocPacket : public BasePacketWriter
{
public:
explicit AutoMallocPacket(bool useBigEndian = true,
bool isAutoMalloc = false)
:
BasePacketWriter(mData, SIZE, useBigEndian, isAutoMalloc)
{}
private:
char mData[SIZE];
};
using BigPacket = AutoMallocPacket<32 * 1024>;
} }

View File

@ -0,0 +1,9 @@
#pragma once
#if defined _MSC_VER || defined __MINGW32__
#define BRYNET_PLATFORM_WINDOWS
#elif defined __APPLE_CC__ || defined __APPLE__
#define BRYNET_PLATFORM_DARWIN
#else
#define BRYNET_PLATFORM_LINUX
#endif

175
libs/brynet/base/Stack.hpp Normal file
View File

@ -0,0 +1,175 @@
#pragma once
#include <cstdbool>
#include <cstring>
#include <cstdlib>
#include <cassert>
#include <cstdint>
#include <brynet/base/Array.hpp>
namespace brynet { namespace base {
struct stack_s
{
struct array_s* array;
size_t element_size;
size_t element_num;
size_t front; /* 栈底 */
size_t num; /* 栈有效元素大小 */
};
static void stack_delete(struct stack_s* self)
{
if (self == nullptr)
{
return;
}
if (self->array != nullptr)
{
array_delete(self->array);
self->array = nullptr;
}
self->element_num = 0;
self->front = 0;
self->num = 0;
free(self);
self = nullptr;
}
static struct stack_s* stack_new(size_t num, size_t element_size)
{
struct stack_s* ret = (struct stack_s*)malloc(sizeof(struct stack_s));
if (ret == nullptr)
{
return nullptr;
}
ret->element_size = 0;
ret->element_num = 0;
ret->front = 0;
ret->num = 0;
ret->array = array_new(num, element_size);
if (ret->array != nullptr)
{
ret->element_size = element_size;
ret->element_num = num;
}
else
{
stack_delete(ret);
ret = nullptr;
}
return ret;
}
static void stack_init(struct stack_s* self)
{
self->front = 0;
self->num = 0;
}
static size_t stack_num(struct stack_s* self)
{
return self->num;
}
static bool stack_increase(struct stack_s* self, size_t increase_num)
{
struct array_s* tmp = array_new(self->element_num + increase_num,
self->element_size);
if (tmp == nullptr)
{
return false;
}
{
size_t current_num = self->element_num;
size_t current_stack_num = stack_num(self);
for (size_t i = 0; i < current_stack_num; ++i)
{
array_set(tmp, i, array_at(self->array, (self->front + i) % current_num));
}
self->front = 0;
array_delete(self->array);
self->array = tmp;
self->element_num = array_num(self->array);
}
return true;
}
static size_t stack_size(struct stack_s* self)
{
return self->element_num;
}
static bool stack_isfull(struct stack_s* self)
{
return (self->num == self->element_num);
}
/* stack的stack_push会在空间不足的时候自动增长(通过stack_increase) */
static bool stack_push(struct stack_s* self, const void* data)
{
if (stack_isfull(self))
{
stack_increase(self, stack_size(self));
}
if (stack_isfull(self))
{
return false;
}
array_set(self->array, (self->front + self->num) % self->element_num, data);
self->num++;
return true;
}
static void* stack_front(struct stack_s* self)
{
void* ret = nullptr;
if (stack_num(self) > 0)
{
ret = array_at(self->array, self->front);
}
return ret;
}
static void* stack_popfront(struct stack_s* self)
{
void* ret = stack_front(self);
if (ret != nullptr)
{
self->num--;
self->front++;
self->front %= self->element_num;
}
return ret;
}
static void* stack_popback(struct stack_s* self)
{
void* ret = nullptr;
if (stack_num(self) > 0)
{
self->num--;
ret = array_at(self->array, (self->front + self->num) % self->element_num);
}
return ret;
}
} }

167
libs/brynet/base/Timer.hpp Normal file
View File

@ -0,0 +1,167 @@
#pragma once
#include <functional>
#include <queue>
#include <memory>
#include <vector>
#include <chrono>
#include <mutex>
#include <brynet/base/Noexcept.hpp>
namespace brynet { namespace base {
class TimerMgr;
class Timer final
{
public:
using Ptr = std::shared_ptr<Timer>;
using WeakPtr = std::weak_ptr<Timer>;
using Callback = std::function<void(void)>;
Timer(std::chrono::steady_clock::time_point startTime,
std::chrono::nanoseconds lastTime,
Callback&& callback) BRYNET_NOEXCEPT
:
mCallback(std::move(callback)),
mStartTime(startTime),
mLastTime(lastTime)
{
}
const std::chrono::steady_clock::time_point& getStartTime() const
{
return mStartTime;
}
const std::chrono::nanoseconds& getLastTime() const
{
return mLastTime;
}
std::chrono::nanoseconds getLeftTime() const
{
const auto now = std::chrono::steady_clock::now();
return getLastTime() - (now - getStartTime());
}
void cancel()
{
std::call_once(mExecuteOnceFlag, [this]() {
mCallback = nullptr;
});
}
private:
void operator() ()
{
Callback callback;
std::call_once(mExecuteOnceFlag, [&callback, this]() {
callback = std::move(mCallback);
mCallback = nullptr;
});
if (callback != nullptr)
{
callback();
}
}
private:
std::once_flag mExecuteOnceFlag;
Callback mCallback;
const std::chrono::steady_clock::time_point mStartTime;
const std::chrono::nanoseconds mLastTime;
friend class TimerMgr;
};
class TimerMgr final
{
public:
using Ptr = std::shared_ptr<TimerMgr>;
template<typename F, typename ...TArgs>
Timer::WeakPtr addTimer(
std::chrono::nanoseconds timeout,
F&& callback,
TArgs&& ...args)
{
auto timer = std::make_shared<Timer>(
std::chrono::steady_clock::now(),
std::chrono::nanoseconds(timeout),
std::bind(std::forward<F>(callback), std::forward<TArgs>(args)...));
mTimers.push(timer);
return timer;
}
void addTimer(const Timer::Ptr& timer)
{
mTimers.push(timer);
}
void schedule()
{
while (!mTimers.empty())
{
auto tmp = mTimers.top();
if (tmp->getLeftTime() > std::chrono::nanoseconds::zero())
{
break;
}
mTimers.pop();
(*tmp)();
}
}
bool isEmpty() const
{
return mTimers.empty();
}
// if timer empty, return zero
std::chrono::nanoseconds nearLeftTime() const
{
if (mTimers.empty())
{
return std::chrono::nanoseconds::zero();
}
auto result = mTimers.top()->getLeftTime();
if (result < std::chrono::nanoseconds::zero())
{
return std::chrono::nanoseconds::zero();
}
return result;
}
void clear()
{
while (!mTimers.empty())
{
mTimers.pop();
}
}
private:
class CompareTimer
{
public:
bool operator() (const Timer::Ptr& left,
const Timer::Ptr& right) const
{
const auto startDiff = left->getStartTime() - right->getStartTime();
const auto lastDiff = left->getLastTime() - right->getLastTime();
const auto diff = startDiff.count() + lastDiff.count();
return diff > 0;
}
};
std::priority_queue<Timer::Ptr, std::vector<Timer::Ptr>, CompareTimer> mTimers;
};
} }

View File

@ -0,0 +1,65 @@
#pragma once
#include <mutex>
#include <atomic>
#include <memory>
#include <condition_variable>
#include <chrono>
#include <brynet/base/NonCopyable.hpp>
namespace brynet { namespace base {
class WaitGroup : public NonCopyable
{
public:
typedef std::shared_ptr<WaitGroup> Ptr;
static Ptr Create()
{
struct make_shared_enabler : public WaitGroup {};
return std::make_shared<make_shared_enabler>();
}
public:
void add(int i = 1)
{
mCounter += i;
}
void done()
{
mCounter--;
mCond.notify_all();
}
void wait()
{
std::unique_lock<std::mutex> l(mMutex);
mCond.wait(l, [&] { return mCounter <= 0; });
}
template<class Rep, class Period>
void wait(const std::chrono::duration<Rep, Period>& timeout)
{
std::unique_lock<std::mutex> l(mMutex);
mCond.wait_for(l, timeout, [&] {
return mCounter <= 0;
});
}
private:
WaitGroup()
:
mCounter(0)
{
}
virtual ~WaitGroup() = default;
private:
std::mutex mMutex;
std::atomic<int> mCounter;
std::condition_variable mCond;
};
} }

View File

@ -0,0 +1,105 @@
#ifndef _BRYNET_BASE_BASE64_H
#define _BRYNET_BASE_BASE64_H
#include <string>
namespace brynet { namespace base { namespace crypto {
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static bool is_base64(unsigned char c)
{
return (isalnum(c) || (c == '+') || (c == '/'));
}
static std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len)
{
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; (i < 4); i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for (j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while ((i++ < 3))
ret += '=';
}
return ret;
}
static std::string base64_decode(std::string const& encoded_string)
{
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i == 4) {
for (i = 0; i < 4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++)
char_array_4[j] = 0;
for (j = 0; j < 4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}
} } }
#endif

View File

@ -0,0 +1,496 @@
/*
100% free public domain implementation of the SHA-1 algorithm
by Dominik Reichl <dominik.reichl@t-online.de>
Web: http://www.dominik-reichl.de/
Version 2.1 - 2012-06-19
- Deconstructor (resetting internal variables) is now only
implemented if SHA1_WIPE_VARIABLES is defined (which is the
default).
- Renamed inclusion guard to contain a GUID.
- Demo application is now using C++/STL objects and functions.
- Unicode build of the demo application now outputs the hashes of both
the ANSI and Unicode representations of strings.
- Various other demo application improvements.
Version 2.0 - 2012-06-14
- Added 'limits.h' include.
- Renamed inclusion guard and macros for compliancy (names beginning
with an underscore are reserved).
Version 1.9 - 2011-11-10
- Added Unicode test vectors.
- Improved support for hashing files using the HashFile method that
are larger than 4 GB.
- Improved file hashing performance (by using a larger buffer).
- Disabled unnecessary compiler warnings.
- Internal variables are now private.
Version 1.8 - 2009-03-16
- Converted project files to Visual Studio 2008 format.
- Added Unicode support for HashFile utility method.
- Added support for hashing files using the HashFile method that are
larger than 2 GB.
- HashFile now returns an error code instead of copying an error
message into the output buffer.
- GetHash now returns an error code and validates the input parameter.
- Added ReportHashStl STL utility method.
- Added REPORT_HEX_SHORT reporting mode.
- Improved Linux compatibility of test program.
Version 1.7 - 2006-12-21
- Fixed buffer underrun warning that appeared when compiling with
Borland C Builder (thanks to Rex Bloom and Tim Gallagher for the
patch).
- Breaking change: ReportHash writes the final hash to the start
of the buffer, i.e. it's not appending it to the string anymore.
- Made some function parameters const.
- Added Visual Studio 2005 project files to demo project.
Version 1.6 - 2005-02-07 (thanks to Howard Kapustein for patches)
- You can set the endianness in your files, no need to modify the
header file of the CSHA1 class anymore.
- Aligned data support.
- Made support/compilation of the utility functions (ReportHash and
HashFile) optional (useful when bytes count, for example in embedded
environments).
Version 1.5 - 2005-01-01
- 64-bit compiler compatibility added.
- Made variable wiping optional (define SHA1_WIPE_VARIABLES).
- Removed unnecessary variable initializations.
- ROL32 improvement for the Microsoft compiler (using _rotl).
Version 1.4 - 2004-07-22
- CSHA1 now compiles fine with GCC 3.3 under Mac OS X (thanks to Larry
Hastings).
Version 1.3 - 2003-08-17
- Fixed a small memory bug and made a buffer array a class member to
ensure correct working when using multiple CSHA1 class instances at
one time.
Version 1.2 - 2002-11-16
- Borlands C++ compiler seems to have problems with string addition
using sprintf. Fixed the bug which caused the digest report function
not to work properly. CSHA1 is now Borland compatible.
Version 1.1 - 2002-10-11
- Removed two unnecessary header file includes and changed BOOL to
bool. Fixed some minor bugs in the web page contents.
Version 1.0 - 2002-06-20
- First official release.
================ Test Vectors ================
SHA1("abc" in ANSI) =
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
SHA1("abc" in Unicode LE) =
9F04F41A 84851416 2050E3D6 8C1A7ABB 441DC2B5
SHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
in ANSI) =
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
SHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
in Unicode LE) =
51D7D876 9AC72C40 9C5B0E3F 69C60ADC 9A039014
SHA1(A million repetitions of "a" in ANSI) =
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
SHA1(A million repetitions of "a" in Unicode LE) =
C4609560 A108A0C6 26AA7F2B 38A65566 739353C5
*/
#ifndef SHA1_H_A545E61D43E9404E8D736869AB3CBFE7
#define SHA1_H_A545E61D43E9404E8D736869AB3CBFE7
#if !defined(SHA1_UTILITY_FUNCTIONS) && !defined(SHA1_NO_UTILITY_FUNCTIONS)
#define SHA1_UTILITY_FUNCTIONS
#endif
#if !defined(SHA1_STL_FUNCTIONS) && !defined(SHA1_NO_STL_FUNCTIONS)
#define SHA1_STL_FUNCTIONS
#if !defined(SHA1_UTILITY_FUNCTIONS)
#error STL functions require SHA1_UTILITY_FUNCTIONS.
#endif
#endif
#include <memory.h>
#include <limits.h>
#ifdef SHA1_UTILITY_FUNCTIONS
#include <stdio.h>
#include <string.h>
#endif
#ifdef SHA1_STL_FUNCTIONS
#include <string>
#endif
#ifdef _MSC_VER
#include <stdlib.h>
#endif
// You can define the endian mode in your files without modifying the SHA-1
// source files. Just #define SHA1_LITTLE_ENDIAN or #define SHA1_BIG_ENDIAN
// in your files, before including the SHA1.h header file. If you don't
// define anything, the class defaults to little endian.
#if !defined(SHA1_LITTLE_ENDIAN) && !defined(SHA1_BIG_ENDIAN)
#define SHA1_LITTLE_ENDIAN
#endif
// If you want variable wiping, #define SHA1_WIPE_VARIABLES, if not,
// #define SHA1_NO_WIPE_VARIABLES. If you don't define anything, it
// defaults to wiping.
#if !defined(SHA1_WIPE_VARIABLES) && !defined(SHA1_NO_WIPE_VARIABLES)
#define SHA1_WIPE_VARIABLES
#endif
#if defined(SHA1_HAS_TCHAR)
#include <tchar.h>
#else
#ifdef _MSC_VER
#include <tchar.h>
#else
#ifndef TCHAR
#define TCHAR char
#endif
#ifndef _T
#define _T(__x) (__x)
#define _tmain main
#define _tprintf printf
#define _getts gets
#define _tcslen strlen
#define _tfopen fopen
#define _tcscpy strcpy
#define _tcscat strcat
#define _sntprintf snprintf
#endif
#endif
#endif
///////////////////////////////////////////////////////////////////////////
// Define variable types
#ifndef UINT_8
#ifdef _MSC_VER // Compiling with Microsoft compiler
#define UINT_8 unsigned __int8
#else // !_MSC_VER
#define UINT_8 unsigned char
#endif // _MSC_VER
#endif
#ifndef UINT_32
#ifdef _MSC_VER // Compiling with Microsoft compiler
#define UINT_32 unsigned __int32
#else // !_MSC_VER
#if (ULONG_MAX == 0xFFFFFFFFUL)
#define UINT_32 unsigned long
#else
#define UINT_32 unsigned int
#endif
#endif // _MSC_VER
#endif // UINT_32
#ifndef INT_64
#ifdef _MSC_VER // Compiling with Microsoft compiler
#define INT_64 __int64
#else // !_MSC_VER
#define INT_64 long long
#endif // _MSC_VER
#endif // INT_64
#ifndef UINT_64
#ifdef _MSC_VER // Compiling with Microsoft compiler
#define UINT_64 unsigned __int64
#else // !_MSC_VER
#define UINT_64 unsigned long long
#endif // _MSC_VER
#endif // UINT_64
///////////////////////////////////////////////////////////////////////////
// Declare SHA-1 workspace
typedef union
{
UINT_8 c[64];
UINT_32 l[16];
} SHA1_WORKSPACE_BLOCK;
#define SHA1_MAX_FILE_BUFFER (32 * 20 * 820)
// Rotate p_val32 by p_nBits bits to the left
#ifndef ROL32
#ifdef _MSC_VER
#define ROL32(p_val32,p_nBits) _rotl(p_val32,p_nBits)
#else
#define ROL32(p_val32,p_nBits) (((p_val32)<<(p_nBits))|((p_val32)>>(32-(p_nBits))))
#endif
#endif
#ifdef SHA1_LITTLE_ENDIAN
#define SHABLK0(i) (m_block->l[i] = \
(ROL32(m_block->l[i],24) & 0xFF00FF00) | (ROL32(m_block->l[i],8) & 0x00FF00FF))
#else
#define SHABLK0(i) (m_block->l[i])
#endif
#define SHABLK(i) (m_block->l[i&15] = ROL32(m_block->l[(i+13)&15] ^ \
m_block->l[(i+8)&15] ^ m_block->l[(i+2)&15] ^ m_block->l[i&15],1))
// SHA-1 rounds
#define S_R0(v,w,x,y,z,i) {z+=((w&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5);w=ROL32(w,30);}
#define S_R1(v,w,x,y,z,i) {z+=((w&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5);w=ROL32(w,30);}
#define S_R2(v,w,x,y,z,i) {z+=(w^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5);w=ROL32(w,30);}
#define S_R3(v,w,x,y,z,i) {z+=(((w|x)&y)|(w&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5);w=ROL32(w,30);}
#define S_R4(v,w,x,y,z,i) {z+=(w^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5);w=ROL32(w,30);}
class CSHA1
{
public:
#ifdef SHA1_UTILITY_FUNCTIONS
// Different formats for ReportHash(Stl)
enum REPORT_TYPE
{
REPORT_HEX = 0,
REPORT_DIGIT = 1,
REPORT_HEX_SHORT = 2
};
#endif
// Constructor and destructor
CSHA1()
{
(void)m_reserved0;
(void)m_reserved1;
m_block = (SHA1_WORKSPACE_BLOCK*)m_workspace;
Reset();
}
#ifdef SHA1_WIPE_VARIABLES
~CSHA1()
{
Reset();
}
#endif
void Reset()
{
// SHA1 initialization constants
m_state[0] = 0x67452301;
m_state[1] = 0xEFCDAB89;
m_state[2] = 0x98BADCFE;
m_state[3] = 0x10325476;
m_state[4] = 0xC3D2E1F0;
m_count[0] = 0;
m_count[1] = 0;
}
// Hash in binary data and strings
void Update(const UINT_8* pbData, UINT_32 uLen)
{
UINT_32 j = ((m_count[0] >> 3) & 0x3F);
if ((m_count[0] += (uLen << 3)) < (uLen << 3))
++m_count[1]; // Overflow
m_count[1] += (uLen >> 29);
UINT_32 i;
if ((j + uLen) > 63)
{
i = 64 - j;
memcpy(&m_buffer[j], pbData, i);
Transform(m_state, m_buffer);
for (; (i + 63) < uLen; i += 64)
Transform(m_state, &pbData[i]);
j = 0;
}
else i = 0;
if ((uLen - i) != 0)
memcpy(&m_buffer[j], &pbData[i], uLen - i);
}
#ifdef SHA1_UTILITY_FUNCTIONS
// Hash in file contents
bool HashFile(const TCHAR* tszFileName)
{
if (tszFileName == NULL) return false;
FILE* fpIn = _tfopen(tszFileName, _T("rb"));
if (fpIn == NULL) return false;
UINT_8* pbData = new UINT_8[SHA1_MAX_FILE_BUFFER];
if (pbData == NULL) { fclose(fpIn); return false; }
bool bSuccess = true;
while (true)
{
const size_t uRead = fread(pbData, 1, SHA1_MAX_FILE_BUFFER, fpIn);
if (uRead > 0)
Update(pbData, static_cast<UINT_32>(uRead));
if (uRead < SHA1_MAX_FILE_BUFFER)
{
if (feof(fpIn) == 0) bSuccess = false;
break;
}
}
fclose(fpIn);
delete[] pbData;
return bSuccess;
}
#endif
// Finalize hash; call it before using ReportHash(Stl)
void Final()
{
UINT_32 i;
UINT_8 pbFinalCount[8];
for (i = 0; i < 8; ++i)
pbFinalCount[i] = static_cast<UINT_8>((m_count[((i >= 4) ? 0 : 1)] >>
((3 - (i & 3)) * 8)) & 0xFF); // Endian independent
Update((UINT_8*)"\200", 1);
while ((m_count[0] & 504) != 448)
Update((UINT_8*)"\0", 1);
Update(pbFinalCount, 8); // Cause a Transform()
for (i = 0; i < 20; ++i)
m_digest[i] = static_cast<UINT_8>((m_state[i >> 2] >> ((3 -
(i & 3)) * 8)) & 0xFF);
// Wipe variables for security reasons
#ifdef SHA1_WIPE_VARIABLES
memset(m_buffer, 0, 64);
memset(m_state, 0, 20);
memset(m_count, 0, 8);
memset(pbFinalCount, 0, 8);
Transform(m_state, m_buffer);
#endif
}
#ifdef SHA1_UTILITY_FUNCTIONS
bool ReportHash(TCHAR* tszReport, REPORT_TYPE rtReportType = REPORT_HEX) const
{
if (tszReport == NULL) return false;
TCHAR tszTemp[16];
if ((rtReportType == REPORT_HEX) || (rtReportType == REPORT_HEX_SHORT))
{
_sntprintf(tszTemp, 15, _T("%02X"), m_digest[0]);
_tcscpy(tszReport, tszTemp);
const TCHAR* lpFmt = ((rtReportType == REPORT_HEX) ? _T(" %02X") : _T("%02X"));
for (size_t i = 1; i < 20; ++i)
{
_sntprintf(tszTemp, 15, lpFmt, m_digest[i]);
_tcscat(tszReport, tszTemp);
}
}
else if (rtReportType == REPORT_DIGIT)
{
_sntprintf(tszTemp, 15, _T("%u"), m_digest[0]);
_tcscpy(tszReport, tszTemp);
for (size_t i = 1; i < 20; ++i)
{
_sntprintf(tszTemp, 15, _T(" %u"), m_digest[i]);
_tcscat(tszReport, tszTemp);
}
}
else return false;
return true;
}
#endif
#ifdef SHA1_STL_FUNCTIONS
bool ReportHashStl(std::basic_string<TCHAR>& strOut, REPORT_TYPE rtReportType =
REPORT_HEX) const
{
TCHAR tszOut[84];
const bool bResult = ReportHash(tszOut, rtReportType);
if (bResult) strOut = tszOut;
return bResult;
}
#endif
// Get the raw message digest (20 bytes)
bool GetHash(UINT_8* pbDest20) const
{
if (pbDest20 == NULL) return false;
memcpy(pbDest20, m_digest, 20);
return true;
}
private:
// Private SHA-1 transformation
void Transform(UINT_32* pState, const UINT_8* pBuffer)
{
UINT_32 a = pState[0], b = pState[1], c = pState[2], d = pState[3], e = pState[4];
memcpy(m_block, pBuffer, 64);
// 4 rounds of 20 operations each, loop unrolled
S_R0(a, b, c, d, e, 0); S_R0(e, a, b, c, d, 1); S_R0(d, e, a, b, c, 2); S_R0(c, d, e, a, b, 3);
S_R0(b, c, d, e, a, 4); S_R0(a, b, c, d, e, 5); S_R0(e, a, b, c, d, 6); S_R0(d, e, a, b, c, 7);
S_R0(c, d, e, a, b, 8); S_R0(b, c, d, e, a, 9); S_R0(a, b, c, d, e, 10); S_R0(e, a, b, c, d, 11);
S_R0(d, e, a, b, c, 12); S_R0(c, d, e, a, b, 13); S_R0(b, c, d, e, a, 14); S_R0(a, b, c, d, e, 15);
S_R1(e, a, b, c, d, 16); S_R1(d, e, a, b, c, 17); S_R1(c, d, e, a, b, 18); S_R1(b, c, d, e, a, 19);
S_R2(a, b, c, d, e, 20); S_R2(e, a, b, c, d, 21); S_R2(d, e, a, b, c, 22); S_R2(c, d, e, a, b, 23);
S_R2(b, c, d, e, a, 24); S_R2(a, b, c, d, e, 25); S_R2(e, a, b, c, d, 26); S_R2(d, e, a, b, c, 27);
S_R2(c, d, e, a, b, 28); S_R2(b, c, d, e, a, 29); S_R2(a, b, c, d, e, 30); S_R2(e, a, b, c, d, 31);
S_R2(d, e, a, b, c, 32); S_R2(c, d, e, a, b, 33); S_R2(b, c, d, e, a, 34); S_R2(a, b, c, d, e, 35);
S_R2(e, a, b, c, d, 36); S_R2(d, e, a, b, c, 37); S_R2(c, d, e, a, b, 38); S_R2(b, c, d, e, a, 39);
S_R3(a, b, c, d, e, 40); S_R3(e, a, b, c, d, 41); S_R3(d, e, a, b, c, 42); S_R3(c, d, e, a, b, 43);
S_R3(b, c, d, e, a, 44); S_R3(a, b, c, d, e, 45); S_R3(e, a, b, c, d, 46); S_R3(d, e, a, b, c, 47);
S_R3(c, d, e, a, b, 48); S_R3(b, c, d, e, a, 49); S_R3(a, b, c, d, e, 50); S_R3(e, a, b, c, d, 51);
S_R3(d, e, a, b, c, 52); S_R3(c, d, e, a, b, 53); S_R3(b, c, d, e, a, 54); S_R3(a, b, c, d, e, 55);
S_R3(e, a, b, c, d, 56); S_R3(d, e, a, b, c, 57); S_R3(c, d, e, a, b, 58); S_R3(b, c, d, e, a, 59);
S_R4(a, b, c, d, e, 60); S_R4(e, a, b, c, d, 61); S_R4(d, e, a, b, c, 62); S_R4(c, d, e, a, b, 63);
S_R4(b, c, d, e, a, 64); S_R4(a, b, c, d, e, 65); S_R4(e, a, b, c, d, 66); S_R4(d, e, a, b, c, 67);
S_R4(c, d, e, a, b, 68); S_R4(b, c, d, e, a, 69); S_R4(a, b, c, d, e, 70); S_R4(e, a, b, c, d, 71);
S_R4(d, e, a, b, c, 72); S_R4(c, d, e, a, b, 73); S_R4(b, c, d, e, a, 74); S_R4(a, b, c, d, e, 75);
S_R4(e, a, b, c, d, 76); S_R4(d, e, a, b, c, 77); S_R4(c, d, e, a, b, 78); S_R4(b, c, d, e, a, 79);
// Add the working vars back into state
pState[0] += a;
pState[1] += b;
pState[2] += c;
pState[3] += d;
pState[4] += e;
// Wipe variables
#ifdef SHA1_WIPE_VARIABLES
a = b = c = d = e = 0;
#endif
}
// Member variables
UINT_32 m_state[5];
UINT_32 m_count[2];
UINT_32 m_reserved0[1]; // Memory alignment padding
UINT_8 m_buffer[64];
UINT_8 m_digest[20];
UINT_32 m_reserved1[3]; // Memory alignment padding
UINT_8 m_workspace[64];
SHA1_WORKSPACE_BLOCK* m_block; // SHA1 pointer to the byte array above
};
#endif // SHA1_H_A545E61D43E9404E8D736869AB3CBFE7

View File

@ -0,0 +1,140 @@
#pragma once
#include <cstdint>
#include <cassert>
#include <cstring>
#include <cstdbool>
#include <cstring>
#include <brynet/net/SocketLibTypes.hpp>
#ifdef BRYNET_PLATFORM_LINUX
#include <endian.h>
#elif defined BRYNET_PLATFORM_DARWIN
#include <sys/_endian.h>
#endif
namespace brynet { namespace base { namespace endian {
inline uint64_t hl64ton(uint64_t hostValue)
{
uint64_t ret = 0;
uint32_t high, low;
low = hostValue & 0xFFFFFFFF;
high = (hostValue >> 32) & 0xFFFFFFFF;
low = htonl(low);
high = htonl(high);
ret = low;
ret <<= 32;
ret |= high;
return ret;
}
inline uint64_t ntohl64(uint64_t netValue)
{
uint64_t ret = 0;
uint32_t high, low;
low = netValue & 0xFFFFFFFF;
high = (netValue >> 32) & 0xFFFFFFFF;
low = ntohl(low);
high = ntohl(high);
ret = low;
ret <<= 32;
ret |= high;
return ret;
}
#ifdef BRYNET_PLATFORM_WINDOWS
inline uint64_t hostToNetwork64(uint64_t host64, bool convert = true)
{
return convert ? hl64ton(host64) : host64;
}
inline uint32_t hostToNetwork32(uint32_t host32, bool convert = true)
{
return convert ? htonl(host32) : host32;
}
inline uint16_t hostToNetwork16(uint16_t host16, bool convert = true)
{
return convert ? htons(host16) : host16;
}
inline uint64_t networkToHost64(uint64_t net64, bool convert = true)
{
return convert ? ntohl64(net64) : net64;
}
inline uint32_t networkToHost32(uint32_t net32, bool convert = true)
{
return convert ? ntohl(net32) : net32;
}
inline uint16_t networkToHost16(uint16_t net16, bool convert = true)
{
return convert ? ntohs(net16) : net16;
}
#elif defined BRYNET_PLATFORM_LINUX
inline uint64_t hostToNetwork64(uint64_t host64, bool convert = true)
{
return convert ? htobe64(host64) : host64;
}
inline uint32_t hostToNetwork32(uint32_t host32, bool convert = true)
{
return convert ? htobe32(host32) : host32;
}
inline uint16_t hostToNetwork16(uint16_t host16, bool convert = true)
{
return convert ? htobe16(host16) : host16;
}
inline uint64_t networkToHost64(uint64_t net64, bool convert = true)
{
return convert ? be64toh(net64) : net64;
}
inline uint32_t networkToHost32(uint32_t net32, bool convert = true)
{
return convert ? be32toh(net32) : net32;
}
inline uint16_t networkToHost16(uint16_t net16, bool convert = true)
{
return convert ? be16toh(net16) : net16;
}
#elif defined BRYNET_PLATFORM_DARWIN
inline uint64_t hostToNetwork64(uint64_t host64, bool convert = true)
{
return convert ? hl64ton(host64) : host64;
}
inline uint32_t hostToNetwork32(uint32_t host32, bool convert = true)
{
return convert ? htonl(host32) : host32;
}
inline uint16_t hostToNetwork16(uint16_t host16, bool convert = true)
{
return convert ? htons(host16) : host16;
}
inline uint64_t networkToHost64(uint64_t net64, bool convert = true)
{
return convert ? ntohl64(net64) : net64;
}
inline uint32_t networkToHost32(uint32_t net32, bool convert = true)
{
return convert ? ntohl(net32) : net32;
}
inline uint16_t networkToHost16(uint16_t net16, bool convert = true)
{
return convert ? ntohs(net16) : net16;
}
#endif
} } }

View File

@ -0,0 +1,89 @@
#pragma once
#include <brynet/net/detail/ConnectorDetail.hpp>
namespace brynet { namespace net {
class ConnectOption final
{
public:
using CompletedCallback = std::function<void(TcpSocket::Ptr)>;
using ProcessTcpSocketCallback = std::function<void(TcpSocket&)>;
using FailedCallback = std::function<void()>;
using ConnectOptionFunc = detail::ConnectOptionFunc;
static ConnectOptionFunc WithAddr(const std::string& ip, int port)
{
return [ip, port](detail::ConnectOptionsInfo& option) {
option.ip = ip;
option.port = port;
};
}
static ConnectOptionFunc WithTimeout(std::chrono::nanoseconds timeout)
{
return [timeout](detail::ConnectOptionsInfo& option) {
option.timeout = timeout;
};
}
static ConnectOptionFunc WithCompletedCallback(CompletedCallback callback)
{
return [callback](detail::ConnectOptionsInfo& option) {
option.completedCallback = callback;
};
}
static ConnectOptionFunc AddProcessTcpSocketCallback(ProcessTcpSocketCallback process)
{
return [process](detail::ConnectOptionsInfo& option) {
option.processCallbacks.push_back(process);
};
}
static ConnectOptionFunc WithFailedCallback(FailedCallback callback)
{
return [callback](detail::ConnectOptionsInfo& option) {
option.faledCallback = callback;
};
}
static std::chrono::nanoseconds ExtractTimeout(const std::vector<ConnectOptionFunc>& options)
{
detail::ConnectOptionsInfo option;
for (const auto& func : options)
{
func(option);
}
return option.timeout;
}
};
class AsyncConnector : public detail::AsyncConnectorDetail,
public std::enable_shared_from_this<AsyncConnector>
{
public:
using Ptr = std::shared_ptr<AsyncConnector>;
void startWorkerThread()
{
detail::AsyncConnectorDetail::startWorkerThread();
}
void stopWorkerThread()
{
detail::AsyncConnectorDetail::stopWorkerThread();
}
void asyncConnect(const std::vector<detail::ConnectOptionFunc>& options)
{
detail::AsyncConnectorDetail::asyncConnect(options);
}
static Ptr Create()
{
class make_shared_enabler : public AsyncConnector {};
return std::make_shared<make_shared_enabler>();
}
private:
AsyncConnector() = default;
};
} }

View File

@ -0,0 +1,20 @@
#pragma once
namespace brynet { namespace net {
class EventLoop;
class Channel
{
public:
virtual ~Channel() = default;
private:
virtual void canSend() = 0;
virtual void canRecv() = 0;
virtual void onClose() = 0;
friend class EventLoop;
};
} }

View File

@ -0,0 +1,55 @@
#pragma once
#include <brynet/base/Platform.hpp>
#ifdef BRYNET_PLATFORM_WINDOWS
#include <winsock2.h>
#include <Windows.h>
#elif defined BRYNET_PLATFORM_LINUX
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/unistd.h>
#elif defined BRYNET_PLATFORM_DARWIN
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#endif
namespace brynet { namespace net { namespace current_thread {
#ifdef BRYNET_PLATFORM_WINDOWS
using THREAD_ID_TYPE = DWORD;
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
using THREAD_ID_TYPE = int;
#endif
static THREAD_ID_TYPE& tid()
{
#ifdef BRYNET_PLATFORM_WINDOWS
static __declspec(thread) THREAD_ID_TYPE cachedTid = 0;
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
static __thread THREAD_ID_TYPE cachedTid = 0;
#endif
if (cachedTid == 0)
{
#ifdef BRYNET_PLATFORM_WINDOWS
cachedTid = GetCurrentThreadId();
#elif defined BRYNET_PLATFORM_LINUX
cachedTid = static_cast<pid_t>(::syscall(SYS_gettid));
#elif defined BRYNET_PLATFORM_DARWIN
// warning: 'syscall' is deprecated:
// first deprecated in macOS 10.12 - syscall(2) is unsupported;
// please switch to a supported interface.
uint64_t tid64;
pthread_threadid_np(NULL, &tid64);
cachedTid = (pid_t)tid64;
#endif
}
return cachedTid;
}
} } }

View File

@ -0,0 +1,444 @@
#pragma once
#include <cstdint>
#include <functional>
#include <vector>
#include <mutex>
#include <memory>
#include <atomic>
#include <unordered_map>
#include <cassert>
#include <algorithm>
#include <brynet/base/Timer.hpp>
#include <brynet/base/NonCopyable.hpp>
#include <brynet/base/Noexcept.hpp>
#include <brynet/net/SocketLibFunction.hpp>
#include <brynet/net/CurrentThread.hpp>
#include <brynet/net/Channel.hpp>
#include <brynet/net/Socket.hpp>
#include <brynet/net/Exception.hpp>
#include <brynet/net/detail/WakeupChannel.hpp>
namespace brynet { namespace net {
class Channel;
class TcpConnection;
using TcpConnectionPtr = std::shared_ptr<TcpConnection>;
class EventLoop : public brynet::base::NonCopyable
{
public:
using Ptr = std::shared_ptr<EventLoop>;
using UserFunctor = std::function<void(void)>;
public:
EventLoop()
BRYNET_NOEXCEPT
:
#ifdef BRYNET_PLATFORM_WINDOWS
mIOCP(CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)),
mWakeupChannel(std::make_unique<detail::WakeupChannel>(mIOCP))
#elif defined BRYNET_PLATFORM_LINUX
mEpollFd(epoll_create(1))
#elif defined BRYNET_PLATFORM_DARWIN
mKqueueFd(kqueue())
#endif
{
#ifdef BRYNET_PLATFORM_WINDOWS
mPGetQueuedCompletionStatusEx = NULL;
auto kernel32_module = GetModuleHandleA("kernel32.dll");
if (kernel32_module != NULL) {
mPGetQueuedCompletionStatusEx = reinterpret_cast<sGetQueuedCompletionStatusEx>(GetProcAddress(
kernel32_module,
"GetQueuedCompletionStatusEx"));
FreeLibrary(kernel32_module);
}
#elif defined BRYNET_PLATFORM_LINUX
auto eventfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
mWakeupChannel.reset(new detail::WakeupChannel(eventfd));
linkChannel(eventfd, mWakeupChannel.get());
#elif defined BRYNET_PLATFORM_DARWIN
const int NOTIFY_IDENT = 42; // Magic number we use for our filter ID.
mWakeupChannel.reset(new detail::WakeupChannel(mKqueueFd, NOTIFY_IDENT));
//Add user event
struct kevent ev;
EV_SET(&ev, NOTIFY_IDENT, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, NULL);
struct timespec timeout = { 0, 0 };
kevent(mKqueueFd, &ev, 1, NULL, 0, &timeout);
#endif
mIsAlreadyPostWakeup = false;
mIsInBlock = true;
reallocEventSize(1024);
mSelfThreadID = -1;
mTimer = std::make_shared<brynet::base::TimerMgr>();
}
virtual ~EventLoop() BRYNET_NOEXCEPT
{
#ifdef BRYNET_PLATFORM_WINDOWS
CloseHandle(mIOCP);
mIOCP = INVALID_HANDLE_VALUE;
#elif defined BRYNET_PLATFORM_LINUX
close(mEpollFd);
mEpollFd = -1;
#elif defined BRYNET_PLATFORM_DARWIN
close(mKqueueFd);
mKqueueFd = -1;
#endif
}
void loop(int64_t milliseconds)
{
tryInitThreadID();
#ifndef NDEBUG
assert(isInLoopThread());
#endif
if (!isInLoopThread())
{
throw BrynetCommonException("only loop in io thread");
}
if (!mAfterLoopFunctors.empty())
{
milliseconds = 0;
}
#ifdef BRYNET_PLATFORM_WINDOWS
ULONG numComplete = 0;
if (mPGetQueuedCompletionStatusEx != nullptr)
{
if (!mPGetQueuedCompletionStatusEx(mIOCP,
mEventEntries.data(),
static_cast<ULONG>(mEventEntries.size()),
&numComplete,
static_cast<DWORD>(milliseconds),
false))
{
numComplete = 0;
}
}
else
{
for (auto& e : mEventEntries)
{
const auto timeout = (numComplete == 0) ? static_cast<DWORD>(milliseconds) : 0;
/* don't check the return value of GQCS */
GetQueuedCompletionStatus(mIOCP,
&e.dwNumberOfBytesTransferred,
&e.lpCompletionKey,
&e.lpOverlapped,
timeout);
if (e.lpOverlapped == nullptr)
{
break;
}
++numComplete;
}
}
mIsInBlock = false;
for (ULONG i = 0; i < numComplete; ++i)
{
auto channel = (Channel*)mEventEntries[i].lpCompletionKey;
assert(channel != nullptr);
const auto ovl = reinterpret_cast<const port::Win::OverlappedExt*>(mEventEntries[i].lpOverlapped);
if (ovl->OP == port::Win::OverlappedType::OverlappedRecv)
{
channel->canRecv();
}
else if (ovl->OP == port::Win::OverlappedType::OverlappedSend)
{
channel->canSend();
}
else
{
assert(false);
}
}
#elif defined BRYNET_PLATFORM_LINUX
int numComplete = epoll_wait(mEpollFd, mEventEntries.data(), mEventEntries.size(), milliseconds);
mIsInBlock = false;
for (int i = 0; i < numComplete; ++i)
{
auto channel = (Channel*)(mEventEntries[i].data.ptr);
auto event_data = mEventEntries[i].events;
if (event_data & EPOLLRDHUP)
{
channel->canRecv();
channel->onClose();
continue;
}
if (event_data & EPOLLIN)
{
channel->canRecv();
}
if (event_data & EPOLLOUT)
{
channel->canSend();
}
}
#elif defined BRYNET_PLATFORM_DARWIN
struct timespec timeout = { milliseconds / 1000, (milliseconds % 1000) * 1000 * 1000 };
int numComplete = kevent(mKqueueFd, NULL, 0, mEventEntries.data(), mEventEntries.size(), &timeout);
mIsInBlock = false;
for (int i = 0; i < numComplete; ++i)
{
auto channel = (Channel*)(mEventEntries[i].udata);
const struct kevent& event = mEventEntries[i];
if (event.filter == EVFILT_USER)
{
continue;
}
if (event.filter == EVFILT_READ)
{
channel->canRecv();
}
if (event.filter == EVFILT_WRITE)
{
channel->canSend();
}
}
#endif
mIsAlreadyPostWakeup = false;
mIsInBlock = true;
processAsyncFunctors();
processAfterLoopFunctors();
if (static_cast<size_t>(numComplete) == mEventEntries.size())
{
reallocEventSize(mEventEntries.size() + 128);
}
mTimer->schedule();
}
// loop指定毫秒数,但如果定时器不为空,则loop时间为当前最近定时器的剩余时间和milliseconds的较小值
void loopCompareNearTimer(int64_t milliseconds)
{
tryInitThreadID();
#ifndef NDEBUG
assert(isInLoopThread());
#endif
if (!isInLoopThread())
{
throw BrynetCommonException("only loop in IO thread");
}
if (!mTimer->isEmpty())
{
auto nearTimeout = std::chrono::duration_cast<std::chrono::milliseconds>(mTimer->nearLeftTime());
milliseconds = std::min<int64_t>(milliseconds, nearTimeout.count());
}
loop(milliseconds);
}
// 返回true表示实际发生了wakeup所需的操作(此返回值不代表接口本身操作成功与否,因为此函数永远成功)
bool wakeup()
{
if (!isInLoopThread() && mIsInBlock && !mIsAlreadyPostWakeup.exchange(true))
{
return mWakeupChannel->wakeup();
}
return false;
}
void runAsyncFunctor(UserFunctor&& f)
{
if (isInLoopThread())
{
f();
}
else
{
pushAsyncFunctor(std::move(f));
wakeup();
}
}
void runFunctorAfterLoop(UserFunctor&& f)
{
assert(isInLoopThread());
if (!isInLoopThread())
{
throw BrynetCommonException("only push after functor in io thread");
}
mAfterLoopFunctors.emplace_back(std::move(f));
}
brynet::base::Timer::WeakPtr runAfter(std::chrono::nanoseconds timeout, UserFunctor&& callback)
{
auto timer = std::make_shared<brynet::base::Timer>(
std::chrono::steady_clock::now(),
std::chrono::nanoseconds(timeout),
std::move(callback));
if (isInLoopThread())
{
mTimer->addTimer(timer);
}
else
{
auto timerMgr = mTimer;
runAsyncFunctor([timerMgr, timer]() {
timerMgr->addTimer(timer);
});
}
return timer;
}
inline bool isInLoopThread() const
{
return mSelfThreadID == current_thread::tid();
}
private:
void reallocEventSize(size_t size)
{
mEventEntries.resize(size);
}
void processAfterLoopFunctors()
{
mCopyAfterLoopFunctors.swap(mAfterLoopFunctors);
for (const auto& x : mCopyAfterLoopFunctors)
{
x();
}
mCopyAfterLoopFunctors.clear();
}
void processAsyncFunctors()
{
swapAsyncFunctors();
for (const auto& x : mCopyAsyncFunctors)
{
x();
}
mCopyAsyncFunctors.clear();
}
void swapAsyncFunctors()
{
std::lock_guard<std::mutex> lck(mAsyncFunctorsMutex);
assert(mCopyAsyncFunctors.empty());
mCopyAsyncFunctors.swap(mAsyncFunctors);
}
void pushAsyncFunctor(UserFunctor&& f)
{
std::lock_guard<std::mutex> lck(mAsyncFunctorsMutex);
mAsyncFunctors.emplace_back(std::move(f));
}
#ifdef BRYNET_PLATFORM_LINUX
int getEpollHandle() const
{
return mEpollFd;
}
#elif defined BRYNET_PLATFORM_DARWIN
int getKqueueHandle() const
{
return mKqueueFd;
}
#endif
bool linkChannel(BrynetSocketFD fd, const Channel* ptr) BRYNET_NOEXCEPT
{
#ifdef BRYNET_PLATFORM_WINDOWS
return CreateIoCompletionPort((HANDLE)fd, mIOCP, (ULONG_PTR)ptr, 0) != nullptr;
#elif defined BRYNET_PLATFORM_LINUX
struct epoll_event ev = { 0, { nullptr } };
ev.events = EPOLLET | EPOLLIN | EPOLLOUT | EPOLLRDHUP;
ev.data.ptr = (void*)ptr;
return epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &ev) == 0;
#elif defined BRYNET_PLATFORM_DARWIN
struct kevent ev[2];
memset(&ev, 0, sizeof(ev));
int n = 0;
EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD | EV_CLEAR, NOTE_TRIGGER, 0, (void*)ptr);
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, NOTE_TRIGGER, 0, (void*)ptr);
struct timespec now = { 0, 0 };
return kevent(mKqueueFd, ev, n, NULL, 0, &now) == 0;
#endif
}
TcpConnectionPtr getTcpConnection(BrynetSocketFD fd)
{
auto it = mTcpConnections.find(fd);
if (it != mTcpConnections.end())
{
return (*it).second;
}
return nullptr;
}
void addTcpConnection(BrynetSocketFD fd, TcpConnectionPtr tcpConnection)
{
mTcpConnections[fd] = std::move(tcpConnection);
}
void removeTcpConnection(BrynetSocketFD fd)
{
mTcpConnections.erase(fd);
}
void tryInitThreadID()
{
std::call_once(mOnceInitThreadID, [this]() {
mSelfThreadID = current_thread::tid();
});
}
private:
#ifdef BRYNET_PLATFORM_WINDOWS
std::vector<OVERLAPPED_ENTRY> mEventEntries;
typedef BOOL(WINAPI *sGetQueuedCompletionStatusEx) (HANDLE, LPOVERLAPPED_ENTRY, ULONG, PULONG, DWORD, BOOL);
sGetQueuedCompletionStatusEx mPGetQueuedCompletionStatusEx;
HANDLE mIOCP;
#elif defined BRYNET_PLATFORM_LINUX
std::vector<epoll_event> mEventEntries;
int mEpollFd;
#elif defined BRYNET_PLATFORM_DARWIN
std::vector<struct kevent> mEventEntries;
int mKqueueFd;
#endif
std::unique_ptr<detail::WakeupChannel> mWakeupChannel;
std::atomic_bool mIsInBlock;
std::atomic_bool mIsAlreadyPostWakeup;
std::mutex mAsyncFunctorsMutex;
std::vector<UserFunctor> mAsyncFunctors;
std::vector<UserFunctor> mCopyAsyncFunctors;
std::vector<UserFunctor> mAfterLoopFunctors;
std::vector<UserFunctor> mCopyAfterLoopFunctors;
std::once_flag mOnceInitThreadID;
current_thread::THREAD_ID_TYPE mSelfThreadID;
brynet::base::TimerMgr::Ptr mTimer;
std::unordered_map<BrynetSocketFD, TcpConnectionPtr> mTcpConnections;
friend class TcpConnection;
};
} }

View File

@ -0,0 +1,41 @@
#pragma once
#include <string>
#include <exception>
#include <stdexcept>
namespace brynet { namespace net {
class ConnectException : public std::runtime_error
{
public:
explicit ConnectException(const std::string& message)
:
std::runtime_error(message)
{
}
explicit ConnectException(const char* message)
:
std::runtime_error(message)
{
}
};
class BrynetCommonException : public std::runtime_error
{
public:
explicit BrynetCommonException(const std::string& message)
:
std::runtime_error(message)
{
}
explicit BrynetCommonException(const char* message)
:
std::runtime_error(message)
{
}
};
} }

View File

@ -0,0 +1,58 @@
#pragma once
#include <brynet/net/detail/ListenThreadDetail.hpp>
namespace brynet { namespace net {
class ListenThread : public detail::ListenThreadDetail,
public std::enable_shared_from_this<ListenThread>
{
public:
using Ptr = std::shared_ptr<ListenThread>;
using AccepCallback = std::function<void(TcpSocket::Ptr)>;;
using TcpSocketProcessCallback = std::function<void(TcpSocket&)>;
void startListen()
{
detail::ListenThreadDetail::startListen();
}
void stopListen()
{
detail::ListenThreadDetail::stopListen();
}
public:
static Ptr Create(bool isIPV6,
const std::string& ip,
int port,
const AccepCallback& callback,
const std::vector<TcpSocketProcessCallback> & processCallbacks = {})
{
class make_shared_enabler : public ListenThread
{
public:
make_shared_enabler(bool isIPV6,
const std::string& ip,
int port,
const AccepCallback& callback,
const std::vector<TcpSocketProcessCallback>& processCallbacks)
:
ListenThread(isIPV6, ip, port, callback, processCallbacks)
{}
};
return std::make_shared<make_shared_enabler>(isIPV6, ip, port, callback, processCallbacks);
}
protected:
ListenThread(bool isIPV6,
const std::string& ip,
int port,
const AccepCallback& callback,
const std::vector<TcpSocketProcessCallback>& processCallbacks)
:
detail::ListenThreadDetail(isIPV6, ip, port, callback, processCallbacks)
{}
};
} }

266
libs/brynet/net/Poller.hpp Normal file
View File

@ -0,0 +1,266 @@
#pragma once
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <cstdbool>
#include <string>
#include <brynet/net/SocketLibTypes.hpp>
#include <brynet/base/Stack.hpp>
#if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
#include <poll.h>
#endif
namespace brynet { namespace base {
#ifdef BRYNET_PLATFORM_WINDOWS
const static int CHECK_READ_FLAG = (POLLIN | POLLRDNORM | POLLRDBAND);
const static int CHECK_WRITE_FLAG = (POLLOUT | POLLWRNORM);
const static int CHECK_ERROR_FLAG = (POLLERR | POLLHUP);
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
const static int CHECK_READ_FLAG = (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI);
const static int CHECK_WRITE_FLAG = (POLLOUT | POLLWRNORM | POLLWRBAND);
const static int CHECK_ERROR_FLAG = (POLLERR | POLLHUP);
#endif
enum CheckType
{
ReadCheck = 0x1,
WriteCheck = 0x2,
ErrorCheck = 0x4,
};
struct poller_s
{
struct pollfd* pollFds;
int nfds;
int limitSize;
};
static void upstep_pollfd(struct poller_s* self, int upSize)
{
if (upSize <= 0)
{
return;
}
struct pollfd* newPollfds = (struct pollfd*)malloc(
sizeof(struct pollfd) * (self->limitSize + upSize));
if (newPollfds == nullptr)
{
return;
}
if (self->pollFds != nullptr)
{
memcpy(newPollfds, self->pollFds, sizeof(struct pollfd) * self->nfds);
free(self->pollFds);
self->pollFds = nullptr;
}
self->pollFds = newPollfds;
self->limitSize += upSize;
}
static struct pollfd* find_pollfd(struct poller_s* self, BrynetSocketFD fd)
{
for (int i = 0; i < self->nfds; i++)
{
if (self->pollFds[i].fd == fd)
{
return self->pollFds + i;
}
}
return nullptr;
}
static void try_remove_pollfd(struct poller_s* self, BrynetSocketFD fd)
{
int pos = -1;
for (int i = 0; i < self->nfds; i++)
{
if (self->pollFds[i].fd == fd)
{
pos = i;
break;
}
}
if (pos != -1)
{
memmove(self->pollFds + pos,
self->pollFds + pos + 1,
sizeof(struct pollfd) * (self->nfds - pos - 1));
self->nfds--;
assert(self->nfds >= 0);
}
}
static struct poller_s* poller_new(void)
{
struct poller_s* ret = (struct poller_s*)malloc(sizeof(struct poller_s));
if (ret != nullptr)
{
ret->pollFds = NULL;
ret->limitSize = 0;
ret->nfds = 0;
upstep_pollfd(ret, 1024);
}
return ret;
}
static void poller_delete(struct poller_s* self)
{
free(self->pollFds);
self->pollFds = nullptr;
self->nfds = 0;
self->limitSize = 0;
free(self);
self = nullptr;
}
static void poller_add(struct poller_s* self, BrynetSocketFD fd, int type)
{
if (self->limitSize == self->nfds)
{
upstep_pollfd(self, 128);
}
if (self->limitSize <= self->nfds)
{
return;
}
struct pollfd* pf = find_pollfd(self, fd);
if (pf == nullptr)
{
/*real add*/
pf = self->pollFds + self->nfds;
pf->events = 0;
pf->fd = fd;
self->nfds++;
}
if (type & ReadCheck)
{
pf->events |= CHECK_READ_FLAG;
}
if (type & WriteCheck)
{
pf->events |= CHECK_WRITE_FLAG;
}
if (type & ErrorCheck)
{
//pf->events |= CHECK_ERROR_FLAG; TODO::on windows, not supports
}
}
static void poller_del(struct poller_s* self, BrynetSocketFD fd, int type)
{
struct pollfd* pf = find_pollfd(self, fd);
if (pf == nullptr)
{
return;
}
if (type & ReadCheck)
{
pf->events &= ~CHECK_READ_FLAG;
}
if (type & WriteCheck)
{
pf->events &= ~CHECK_WRITE_FLAG;
}
if (type & ErrorCheck)
{
pf->events &= ~CHECK_ERROR_FLAG;
}
if (pf->events == 0)
{
try_remove_pollfd(self, fd);
}
}
static void poller_remove(struct poller_s* self, BrynetSocketFD fd)
{
try_remove_pollfd(self, fd);
}
static bool check_event(const struct pollfd* pf, enum CheckType type)
{
if (pf == nullptr)
{
return false;
}
if ((type & ReadCheck) &&
(pf->revents & CHECK_READ_FLAG))
{
return true;
}
else if ((type & WriteCheck) &&
(pf->revents & CHECK_WRITE_FLAG))
{
return true;
}
else if ((type & ErrorCheck) &&
(pf->revents & CHECK_ERROR_FLAG))
{
return true;
}
else
{
return false;
}
}
static void poller_visitor(struct poller_s* self,
enum CheckType type,
struct stack_s* result)
{
for (int i = 0; i < self->nfds; i++)
{
if (check_event(self->pollFds + i, type))
{
stack_push(result, &self->pollFds[i].fd);
}
}
}
static int poller_poll(struct poller_s* self, long overtime)
{
#ifdef BRYNET_PLATFORM_WINDOWS
int ret = WSAPoll(&self->pollFds[0], self->nfds, overtime);
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
int ret = poll(self->pollFds, self->nfds, overtime);
#endif
if (ret == BRYNET_SOCKET_ERROR)
{
ret = (BRYNET_ERRNO != BRYNET_EINTR) ? -1 : 0;
}
return ret;
}
static bool poller_check(struct poller_s* self, BrynetSocketFD fd, enum CheckType type)
{
const struct pollfd* pf = find_pollfd(self, fd);
if (pf == NULL)
{
return false;
}
return check_event(pf, type);
}
} }

View File

@ -0,0 +1,155 @@
#pragma once
#include <brynet/net/TcpService.hpp>
namespace brynet { namespace net {
/* binary search in memory */
void memsearch(const char *hay, size_t haysize, const char *needle, size_t needlesize, size_t& result, bool& isOK)
{
size_t haypos, needlepos;
haysize -= needlesize;
for (haypos = 0; haypos <= haysize; haypos++)
{
for (needlepos = 0; needlepos < needlesize; needlepos++)
{
if (hay[haypos + needlepos] != needle[needlepos])
{
// Next character in haystack.
break;
}
}
if (needlepos == needlesize)
{
result = haypos;
isOK = true;
return;
}
}
isOK = false;
}
class PromiseReceive;
std::shared_ptr<PromiseReceive> setupPromiseReceive(const TcpConnection::Ptr& session);
class PromiseReceive : public std::enable_shared_from_this<PromiseReceive>
{
public:
using Ptr = std::shared_ptr<PromiseReceive>;
using Handle = std::function<bool(const char* buffer, size_t len)>;
PromiseReceive::Ptr receive(size_t len, Handle handle)
{
return receive(std::make_shared<size_t>(len), std::move(handle));
}
PromiseReceive::Ptr receive(std::shared_ptr<size_t> len, Handle handle)
{
return helpReceive(std::move(len), "", std::move(handle));
}
PromiseReceive::Ptr receiveUntil(std::string str, Handle handle)
{
if (str.empty())
{
throw std::runtime_error("str is empty");
}
return helpReceive(nullptr, std::move(str), std::move(handle));
}
private:
PromiseReceive::Ptr helpReceive(std::shared_ptr<size_t> len, std::string str, Handle handle)
{
auto pr = std::make_shared<PendingReceive>();
pr->len = std::move(len);
pr->str = std::move(str);
pr->handle = std::move(handle);
mPendingReceives.push_back(std::move(pr));
return shared_from_this();
}
size_t process(const char* buffer, const size_t len)
{
size_t procLen = 0;
while (!mPendingReceives.empty() && len >= procLen)
{
auto pendingReceive = mPendingReceives.front();
if (pendingReceive->len != nullptr)
{
const auto tryReceiveLen = *pendingReceive->len;
if ((len - procLen) < tryReceiveLen)
{
break;
}
mPendingReceives.pop_front();
procLen += tryReceiveLen;
if (pendingReceive->handle(buffer + procLen - tryReceiveLen, tryReceiveLen) && tryReceiveLen > 0)
{
mPendingReceives.push_front(pendingReceive);
}
}
else if (!pendingReceive->str.empty())
{
size_t pos = 0;
bool isOK = false;
auto data = buffer + procLen;
memsearch(buffer + procLen,
len - procLen,
pendingReceive->str.c_str(),
pendingReceive->str.size(),
pos,
isOK);
if (!isOK)
{
break;
}
mPendingReceives.pop_front();
procLen += (pos + pendingReceive->str.size());
if (pendingReceive->handle(data, pos))
{
mPendingReceives.push_front(pendingReceive);
}
}
else
{
break;
}
}
return procLen;
}
private:
struct PendingReceive
{
std::shared_ptr<size_t> len;
std::string str;
Handle handle;
};
std::deque<std::shared_ptr<PendingReceive>> mPendingReceives;
friend std::shared_ptr<PromiseReceive> setupPromiseReceive(const TcpConnection::Ptr& session);
};
std::shared_ptr<PromiseReceive> setupPromiseReceive(const TcpConnection::Ptr& session)
{
auto promiseReceive = std::make_shared<PromiseReceive>();
session->setDataCallback([promiseReceive](const char* buffer,
size_t len) {
return promiseReceive->process(buffer, len);
});
return promiseReceive;
}
} }

View File

@ -0,0 +1,174 @@
#pragma once
#include <string>
#include <memory>
#include <unordered_map>
#include <mutex>
#include <thread>
#include <brynet/base/NonCopyable.hpp>
#include <brynet/base/Platform.hpp>
#include <brynet/base/Noexcept.hpp>
#ifdef BRYNET_USE_OPENSSL
#ifdef __cplusplus
extern "C" {
#endif
#include <openssl/ssl.h>
#include <openssl/err.h>
#ifdef __cplusplus
}
#endif
#endif
namespace brynet { namespace net {
#ifdef BRYNET_USE_OPENSSL
#ifndef CRYPTO_THREADID_set_callback
static void cryptoSetThreadIDCallback(CRYPTO_THREADID* id)
{
#ifdef BRYNET_PLATFORM_WINDOWS
CRYPTO_THREADID_set_numeric(id,
static_cast<unsigned long>(GetCurrentThreadId()));
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
CRYPTO_THREADID_set_numeric(id,
static_cast<unsigned long>(pthread_self()));
#endif
}
#endif
#ifndef CRYPTO_set_locking_callback
static std::unordered_map<int, std::shared_ptr<std::mutex>> cryptoLocks;
static void cryptoLockingCallback(int mode,
int type,
const char* file, int line)
{
(void)file;
(void)line;
if (mode & CRYPTO_LOCK)
{
cryptoLocks[type]->lock();
}
else if (mode & CRYPTO_UNLOCK)
{
cryptoLocks[type]->unlock();
}
}
#endif
static std::once_flag initCryptoThreadSafeSupportOnceFlag;
static void InitCryptoThreadSafeSupport()
{
#ifndef CRYPTO_THREADID_set_callback
CRYPTO_THREADID_set_callback(cryptoSetThreadIDCallback);
#endif
#ifndef CRYPTO_set_locking_callback
for (int i = 0; i < CRYPTO_num_locks(); i++)
{
cryptoLocks[i] = std::make_shared<std::mutex>();
}
CRYPTO_set_locking_callback(cryptoLockingCallback);
#endif
}
#endif
class SSLHelper : public brynet::base::NonCopyable,
public std::enable_shared_from_this<SSLHelper>
{
public:
using Ptr = std::shared_ptr<SSLHelper>;
#ifdef BRYNET_USE_OPENSSL
bool initSSL(const std::string& certificate,
const std::string& privatekey)
{
std::call_once(initCryptoThreadSafeSupportOnceFlag,
InitCryptoThreadSafeSupport);
if (mOpenSSLCTX != nullptr)
{
return false;
}
if (certificate.empty() || privatekey.empty())
{
return false;
}
mOpenSSLCTX = SSL_CTX_new(SSLv23_method());
SSL_CTX_set_client_CA_list(mOpenSSLCTX,
SSL_load_client_CA_file(certificate.c_str()));
SSL_CTX_set_verify_depth(mOpenSSLCTX, 10);
if (SSL_CTX_use_certificate_chain_file(mOpenSSLCTX,
certificate.c_str()) <= 0)
{
SSL_CTX_free(mOpenSSLCTX);
mOpenSSLCTX = nullptr;
return false;
}
if (SSL_CTX_use_PrivateKey_file(mOpenSSLCTX,
privatekey.c_str(),
SSL_FILETYPE_PEM) <= 0)
{
SSL_CTX_free(mOpenSSLCTX);
mOpenSSLCTX = nullptr;
return false;
}
if (!SSL_CTX_check_private_key(mOpenSSLCTX))
{
SSL_CTX_free(mOpenSSLCTX);
mOpenSSLCTX = nullptr;
return false;
}
return true;
}
void destroySSL()
{
if (mOpenSSLCTX != nullptr)
{
SSL_CTX_free(mOpenSSLCTX);
mOpenSSLCTX = nullptr;
}
}
SSL_CTX* getOpenSSLCTX()
{
return mOpenSSLCTX;
}
#endif
static Ptr Create()
{
class make_shared_enabler : public SSLHelper {};
return std::make_shared<make_shared_enabler>();
}
protected:
SSLHelper() BRYNET_NOEXCEPT
{
#ifdef BRYNET_USE_OPENSSL
mOpenSSLCTX = nullptr;
#endif
}
virtual ~SSLHelper() BRYNET_NOEXCEPT
{
#ifdef BRYNET_USE_OPENSSL
destroySSL();
#endif
}
private:
#ifdef BRYNET_USE_OPENSSL
SSL_CTX* mOpenSSLCTX;
#endif
};
} }

230
libs/brynet/net/Socket.hpp Normal file
View File

@ -0,0 +1,230 @@
#pragma once
#include <memory>
#include <string>
#include <exception>
#include <stdexcept>
#include <brynet/base/NonCopyable.hpp>
#include <brynet/net/SocketLibFunction.hpp>
namespace brynet { namespace net {
class TcpConnection;
class UniqueFd final : public brynet::base::NonCopyable
{
public:
explicit UniqueFd(BrynetSocketFD fd)
:
mFD(fd)
{}
~UniqueFd()
{
brynet::net::base::SocketClose(mFD);
}
UniqueFd(const UniqueFd& other) = delete;
UniqueFd& operator=(const UniqueFd& other) = delete;
BrynetSocketFD getFD() const
{
return mFD;
}
private:
BrynetSocketFD mFD;
};
class TcpSocket : public brynet::base::NonCopyable
{
private:
class TcpSocketDeleter
{
public:
void operator()(TcpSocket* ptr) const
{
delete ptr;
}
};
public:
using Ptr = std::unique_ptr<TcpSocket, TcpSocketDeleter>;
public:
static Ptr Create(BrynetSocketFD fd, bool serverSide)
{
class make_unique_enabler : public TcpSocket
{
public:
make_unique_enabler(BrynetSocketFD fd, bool serverSide)
:
TcpSocket(fd, serverSide)
{}
};
return Ptr(new make_unique_enabler(fd, serverSide));
}
public:
void setNodelay() const
{
brynet::net::base::SocketNodelay(mFD);
}
bool setNonblock() const
{
return brynet::net::base::SocketNonblock(mFD);
}
void setSendSize(int sdSize) const
{
brynet::net::base::SocketSetSendSize(mFD, sdSize);
}
void setRecvSize(int rdSize) const
{
brynet::net::base::SocketSetRecvSize(mFD, rdSize);
}
std::string getRemoteIP() const
{
return brynet::net::base::GetIPOfSocket(mFD);
}
bool isServerSide() const
{
return mServerSide;
}
protected:
TcpSocket(BrynetSocketFD fd, bool serverSide)
:
mFD(fd),
mServerSide(serverSide)
{
}
virtual ~TcpSocket()
{
brynet::net::base::SocketClose(mFD);
}
BrynetSocketFD getFD() const
{
return mFD;
}
private:
const BrynetSocketFD mFD;
const bool mServerSide;
friend class TcpConnection;
};
class EintrError : public std::exception
{
};
class AcceptError : public std::runtime_error
{
public:
explicit AcceptError(int errorCode)
:
std::runtime_error(std::to_string(errorCode)),
mErrorCode(errorCode)
{}
int getErrorCode() const
{
return mErrorCode;
}
private:
int mErrorCode;
};
class ListenSocket : public brynet::base::NonCopyable
{
private:
class ListenSocketDeleter
{
public:
void operator()(ListenSocket* ptr) const
{
delete ptr;
}
};
public:
using Ptr = std::unique_ptr<ListenSocket, ListenSocketDeleter>;
public:
TcpSocket::Ptr accept()
{
const auto clientFD = brynet::net::base::Accept(mFD, nullptr, nullptr);
if (clientFD == BRYNET_INVALID_SOCKET)
{
#if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
if (BRYNET_ERRNO == EMFILE)
{
// Thanks libev and muduo.
// Read the section named "The special problem of
// accept()ing when you can't" in libev's doc.
// By Marc Lehmann, author of libev.
mIdle.reset();
TcpSocket::Create(brynet::net::base::Accept(mFD, nullptr, nullptr), true);
mIdle = brynet::net::TcpSocket::Create(::open("/dev/null", O_RDONLY | O_CLOEXEC), true);
}
#endif
if (BRYNET_ERRNO == EINTR)
{
throw EintrError();
}
else
{
throw AcceptError(BRYNET_ERRNO);
}
}
return TcpSocket::Create(clientFD, true);
}
public:
static Ptr Create(BrynetSocketFD fd)
{
class make_unique_enabler : public ListenSocket
{
public:
explicit make_unique_enabler(BrynetSocketFD fd)
: ListenSocket(fd)
{}
};
return Ptr(new make_unique_enabler(fd));
}
protected:
explicit ListenSocket(BrynetSocketFD fd)
:
mFD(fd)
{
#if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
mIdle = brynet::net::TcpSocket::Create(::open("/dev/null", O_RDONLY | O_CLOEXEC), true);
#endif
}
virtual ~ListenSocket()
{
brynet::net::base::SocketClose(mFD);
}
private:
const BrynetSocketFD mFD;
#if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
brynet::net::TcpSocket::Ptr mIdle;
#endif
friend class TcpConnection;
};
} }

View File

@ -0,0 +1,329 @@
#pragma once
#include <string.h>
#include <string>
#include <brynet/net/SocketLibTypes.hpp>
namespace brynet { namespace net { namespace base {
static bool InitSocket()
{
bool ret = true;
#ifdef BRYNET_PLATFORM_WINDOWS
static WSADATA g_WSAData;
static bool WinSockIsInit = false;
if (WinSockIsInit)
{
return true;
}
if (WSAStartup(MAKEWORD(2, 2), &g_WSAData) == 0)
{
WinSockIsInit = true;
}
else
{
ret = false;
}
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
signal(SIGPIPE, SIG_IGN);
#endif
return ret;
}
static void DestroySocket()
{
#ifdef BRYNET_PLATFORM_WINDOWS
WSACleanup();
#endif
}
static int SocketNodelay(BrynetSocketFD fd)
{
const int flag = 1;
return ::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&flag, sizeof(flag));
}
static bool SocketBlock(BrynetSocketFD fd)
{
int err;
unsigned long ul = false;
#ifdef BRYNET_PLATFORM_WINDOWS
err = ioctlsocket(fd, FIONBIO, &ul);
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
err = ioctl(fd, FIONBIO, &ul);
#endif
return err != BRYNET_SOCKET_ERROR;
}
static bool SocketNonblock(BrynetSocketFD fd)
{
int err;
unsigned long ul = true;
#ifdef BRYNET_PLATFORM_WINDOWS
err = ioctlsocket(fd, FIONBIO, &ul);
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
err = ioctl(fd, FIONBIO, &ul);
#endif
return err != BRYNET_SOCKET_ERROR;
}
static int SocketSetSendSize(BrynetSocketFD fd, int sd_size)
{
return ::setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const char*)&sd_size, sizeof(sd_size));
}
static int SocketSetRecvSize(BrynetSocketFD fd, int rd_size)
{
return ::setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&rd_size, sizeof(rd_size));
}
static BrynetSocketFD SocketCreate(int af, int type, int protocol)
{
return ::socket(af, type, protocol);
}
static void SocketClose(BrynetSocketFD fd)
{
#ifdef BRYNET_PLATFORM_WINDOWS
::closesocket(fd);
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
::close(fd);
#endif
}
static BrynetSocketFD Connect(bool isIPV6, const std::string& server_ip, int port)
{
InitSocket();
struct sockaddr_in ip4Addr = sockaddr_in();
struct sockaddr_in6 ip6Addr = sockaddr_in6();
struct sockaddr_in* paddr = &ip4Addr;
int addrLen = sizeof(ip4Addr);
BrynetSocketFD clientfd = isIPV6 ?
SocketCreate(AF_INET6, SOCK_STREAM, 0) :
SocketCreate(AF_INET, SOCK_STREAM, 0);
if (clientfd == BRYNET_INVALID_SOCKET)
{
return clientfd;
}
bool ptonResult = false;
if (isIPV6)
{
ip6Addr.sin6_family = AF_INET6;
ip6Addr.sin6_port = htons(port);
ptonResult = inet_pton(AF_INET6,
server_ip.c_str(),
&ip6Addr.sin6_addr) > 0;
paddr = (struct sockaddr_in*) & ip6Addr;
addrLen = sizeof(ip6Addr);
}
else
{
ip4Addr.sin_family = AF_INET;
ip4Addr.sin_port = htons(port);
ptonResult = inet_pton(AF_INET,
server_ip.c_str(),
&ip4Addr.sin_addr) > 0;
}
if (!ptonResult)
{
SocketClose(clientfd);
return BRYNET_INVALID_SOCKET;
}
while (::connect(clientfd, (struct sockaddr*)paddr, addrLen) < 0)
{
if (EINTR == BRYNET_ERRNO)
{
continue;
}
SocketClose(clientfd);
return BRYNET_INVALID_SOCKET;
}
return clientfd;
}
static BrynetSocketFD Listen(bool isIPV6, const char* ip, int port, int back_num)
{
InitSocket();
struct sockaddr_in ip4Addr = sockaddr_in();
struct sockaddr_in6 ip6Addr = sockaddr_in6();
struct sockaddr_in* paddr = &ip4Addr;
int addrLen = sizeof(ip4Addr);
const auto socketfd = isIPV6 ?
socket(AF_INET6, SOCK_STREAM, 0) :
socket(AF_INET, SOCK_STREAM, 0);
if (socketfd == BRYNET_INVALID_SOCKET)
{
return BRYNET_INVALID_SOCKET;
}
bool ptonResult = false;
if (isIPV6)
{
ip6Addr.sin6_family = AF_INET6;
ip6Addr.sin6_port = htons(port);
ptonResult = inet_pton(AF_INET6, ip, &ip6Addr.sin6_addr) > 0;
paddr = (struct sockaddr_in*) & ip6Addr;
addrLen = sizeof(ip6Addr);
}
else
{
ip4Addr.sin_family = AF_INET;
ip4Addr.sin_port = htons(port);
ip4Addr.sin_addr.s_addr = INADDR_ANY;
ptonResult = inet_pton(AF_INET, ip, &ip4Addr.sin_addr) > 0;
}
const int reuseaddr_value = 1;
if (!ptonResult ||
::setsockopt(socketfd,
SOL_SOCKET,
SO_REUSEADDR,
(const char*)&reuseaddr_value,
sizeof(int)) < 0)
{
SocketClose(socketfd);
return BRYNET_INVALID_SOCKET;
}
const int bindRet = ::bind(socketfd, (struct sockaddr*)paddr, addrLen);
if (bindRet == BRYNET_SOCKET_ERROR ||
listen(socketfd, back_num) == BRYNET_SOCKET_ERROR)
{
SocketClose(socketfd);
return BRYNET_INVALID_SOCKET;
}
return socketfd;
}
static std::string getIPString(const struct sockaddr* sa)
{
#ifdef BRYNET_PLATFORM_WINDOWS
using PAddrType = PVOID;
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
using PAddrType = const void*;
#endif
char tmp[INET6_ADDRSTRLEN] = { 0 };
switch (sa->sa_family)
{
case AF_INET:
inet_ntop(AF_INET, (PAddrType)(&(((const struct sockaddr_in*)sa)->sin_addr)),
tmp, sizeof(tmp));
break;
case AF_INET6:
inet_ntop(AF_INET6, (PAddrType)(&(((const struct sockaddr_in6*)sa)->sin6_addr)),
tmp, sizeof(tmp));
break;
default:
return "Unknown AF";
}
return tmp;
}
static std::string GetIPOfSocket(BrynetSocketFD fd)
{
#ifdef BRYNET_PLATFORM_WINDOWS
struct sockaddr name = sockaddr();
int namelen = sizeof(name);
if (::getpeername(fd, (struct sockaddr*) & name, &namelen) == 0)
{
return getIPString(&name);
}
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
struct sockaddr_in name = sockaddr_in();
socklen_t namelen = sizeof(name);
if (::getpeername(fd, (struct sockaddr*) & name, &namelen) == 0)
{
return getIPString((const struct sockaddr*) & name);
}
#endif
return "";
}
static int SocketSend(BrynetSocketFD fd, const char* buffer, int len)
{
int transnum = ::send(fd, buffer, len, 0);
if (transnum < 0 && BRYNET_EWOULDBLOCK == BRYNET_ERRNO)
{
transnum = 0;
}
/* send error if transnum < 0 */
return transnum;
}
static BrynetSocketFD Accept(BrynetSocketFD listenSocket, struct sockaddr* addr, socklen_t* addrLen)
{
return ::accept(listenSocket, addr, addrLen);
}
static struct sockaddr_in6 getPeerAddr(BrynetSocketFD sockfd)
{
struct sockaddr_in6 peeraddr = sockaddr_in6();
auto addrlen = static_cast<socklen_t>(sizeof peeraddr);
if (::getpeername(sockfd, (struct sockaddr*)(&peeraddr), &addrlen) < 0)
{
return peeraddr;
}
return peeraddr;
}
static struct sockaddr_in6 getLocalAddr(BrynetSocketFD sockfd)
{
struct sockaddr_in6 localaddr = sockaddr_in6();
auto addrlen = static_cast<socklen_t>(sizeof localaddr);
if (::getsockname(sockfd, (struct sockaddr*)(&localaddr), &addrlen) < 0)
{
return localaddr;
}
return localaddr;
}
static bool IsSelfConnect(BrynetSocketFD fd)
{
struct sockaddr_in6 localaddr = getLocalAddr(fd);
struct sockaddr_in6 peeraddr = getPeerAddr(fd);
if (localaddr.sin6_family == AF_INET)
{
const struct sockaddr_in* laddr4 = reinterpret_cast<struct sockaddr_in*>(&localaddr);
const struct sockaddr_in* raddr4 = reinterpret_cast<struct sockaddr_in*>(&peeraddr);
return laddr4->sin_port == raddr4->sin_port
&& laddr4->sin_addr.s_addr == raddr4->sin_addr.s_addr;
}
else if (localaddr.sin6_family == AF_INET6)
{
#ifdef BRYNET_PLATFORM_WINDOWS
return localaddr.sin6_port == peeraddr.sin6_port
&& memcmp(&localaddr.sin6_addr.u.Byte,
&peeraddr.sin6_addr.u.Byte,
sizeof localaddr.sin6_addr.u.Byte) == 0;
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
return localaddr.sin6_port == peeraddr.sin6_port
&& memcmp(&localaddr.sin6_addr.s6_addr,
&peeraddr.sin6_addr.s6_addr,
sizeof localaddr.sin6_addr.s6_addr) == 0;
#endif
}
else
{
return false;
}
}
} } }

View File

@ -0,0 +1,71 @@
#pragma once
#include <brynet/base/Platform.hpp>
#ifdef BRYNET_PLATFORM_WINDOWS
#include <winsock2.h>
#include <WinError.h>
#include <winsock.h>
#include <Ws2tcpip.h>
#include <errno.h>
#elif defined BRYNET_PLATFORM_LINUX
#include <signal.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/eventfd.h>
#include <sys/uio.h>
#elif defined BRYNET_PLATFORM_DARWIN
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/event.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#else
#error "Unsupported OS, please commit an issuse."
#endif
#ifdef BRYNET_PLATFORM_WINDOWS
typedef SOCKET BrynetSocketFD;
#define BRYNET_ERRNO WSAGetLastError()
#define BRYNET_ENOTSOCK WSAENOTSOCK
#define BRYNET_EWOULDBLOCK WSAEWOULDBLOCK
#define BRYNET_EINTR WSAEINTR
#define BRYNET_ECONNABORTED WSAECONNABORTED
#define BRYNET_SOCKET_ERROR SOCKET_ERROR
#define BRYNET_INVALID_SOCKET INVALID_SOCKET
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
#define BRYNET_ERRNO errno
#define BRYNET_ENOTSOCK EBADF
#define BRYNET_EWOULDBLOCK EAGAIN
#define BRYNET_EINTR EINTR
#define BRYNET_ECONNABORTED ECONNABORTED
typedef int BrynetSocketFD;
#define BRYNET_SOCKET_ERROR (-1)
#define BRYNET_INVALID_SOCKET (-1)
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,91 @@
#pragma once
#include <brynet/net/detail/TCPServiceDetail.hpp>
namespace brynet { namespace net {
class AddSocketOption
{
private:
using AddSocketOptionFunc = detail::AddSocketOptionFunc;
using AddSocketOptionInfo = detail::AddSocketOptionInfo;
public:
static AddSocketOptionFunc AddEnterCallback(
TcpConnection::EnterCallback callback)
{
return [callback](AddSocketOptionInfo& option) {
option.enterCallback.push_back(callback);
};
}
#ifdef BRYNET_USE_OPENSSL
static AddSocketOptionFunc WithClientSideSSL()
{
return [](AddSocketOptionInfo& option) {
option.useSSL = true;
};
}
static AddSocketOptionFunc WithServerSideSSL(SSLHelper::Ptr sslHelper)
{
return [sslHelper](AddSocketOptionInfo& option) {
option.sslHelper = sslHelper;
option.useSSL = true;
};
}
#endif
static AddSocketOptionFunc WithMaxRecvBufferSize(size_t size)
{
return [size](AddSocketOptionInfo& option) {
option.maxRecvBufferSize = size;
};
}
static AddSocketOptionFunc WithForceSameThreadLoop(bool same)
{
return [same](AddSocketOptionInfo& option) {
option.forceSameThreadLoop = same;
};
}
};
class TcpService : public detail::TcpServiceDetail,
public std::enable_shared_from_this<TcpService>
{
public:
using Ptr = std::shared_ptr<TcpService>;
using FrameCallback = detail::TcpServiceDetail::FrameCallback;
public:
static Ptr Create()
{
struct make_shared_enabler : public TcpService {};
return std::make_shared<make_shared_enabler>();
}
void startWorkerThread(size_t threadNum,
FrameCallback callback = nullptr)
{
detail::TcpServiceDetail::startWorkerThread(threadNum, callback);
}
void stopWorkerThread()
{
detail::TcpServiceDetail::stopWorkerThread();
}
template<typename... Options>
bool addTcpConnection(TcpSocket::Ptr socket,
const Options& ... options)
{
return detail::TcpServiceDetail::addTcpConnection(std::move(socket), options...);
}
EventLoop::Ptr getRandomEventLoop()
{
return detail::TcpServiceDetail::getRandomEventLoop();
}
private:
TcpService() = default;
};
} }

View File

@ -0,0 +1,27 @@
#pragma once
#include <brynet/net/TcpConnection.hpp>
#include <brynet/net/SSLHelper.hpp>
namespace brynet { namespace net { namespace detail {
class AddSocketOptionInfo final
{
public:
AddSocketOptionInfo()
{
useSSL = false;
forceSameThreadLoop = false;
maxRecvBufferSize = 128;
}
std::vector<TcpConnection::EnterCallback> enterCallback;
SSLHelper::Ptr sslHelper;
bool useSSL;
bool forceSameThreadLoop;
size_t maxRecvBufferSize;
};
using AddSocketOptionFunc = std::function<void(AddSocketOptionInfo& option)>;
} } }

View File

@ -0,0 +1,161 @@
#pragma once
#include <functional>
#include <memory>
#include <cassert>
#include <set>
#include <map>
#include <thread>
#include <brynet/base/NonCopyable.hpp>
#include <brynet/base/CPP_VERSION.hpp>
#include <brynet/base/Any.hpp>
#include <brynet/base/Noexcept.hpp>
#include <brynet/net/SocketLibFunction.hpp>
#include <brynet/net/Poller.hpp>
#include <brynet/net/Exception.hpp>
#include <brynet/net/EventLoop.hpp>
#include <brynet/net/Socket.hpp>
#include <brynet/net/detail/ConnectorWorkInfo.hpp>
#ifdef BRYNET_HAVE_LANG_CXX17
#include <shared_mutex>
#else
#include <mutex>
#endif
namespace brynet { namespace net { namespace detail {
class AsyncConnectorDetail : public brynet::base::NonCopyable
{
protected:
void startWorkerThread()
{
#ifdef BRYNET_HAVE_LANG_CXX17
std::lock_guard<std::shared_mutex> lck(mThreadGuard);
#else
std::lock_guard<std::mutex> lck(mThreadGuard);
#endif
if (mThread != nullptr)
{
return;
}
mIsRun = std::make_shared<bool>(true);
mWorkInfo = std::make_shared<detail::ConnectorWorkInfo>();
mEventLoop = std::make_shared<EventLoop>();
auto eventLoop = mEventLoop;
auto workerInfo = mWorkInfo;
auto isRun = mIsRun;
mThread = std::make_shared<std::thread>([eventLoop, workerInfo, isRun]() {
while (*isRun)
{
detail::RunOnceCheckConnect(eventLoop, workerInfo);
}
workerInfo->causeAllFailed();
});
}
void stopWorkerThread()
{
#ifdef BRYNET_HAVE_LANG_CXX17
std::lock_guard<std::shared_mutex> lck(mThreadGuard);
#else
std::lock_guard<std::mutex> lck(mThreadGuard);
#endif
if (mThread == nullptr)
{
return;
}
mEventLoop->runAsyncFunctor([this]() {
*mIsRun = false;
});
try
{
if (mThread->joinable())
{
mThread->join();
}
}
catch (std::system_error & e)
{
(void)e;
}
mEventLoop = nullptr;
mWorkInfo = nullptr;
mIsRun = nullptr;
mThread = nullptr;
}
void asyncConnect(const std::vector<detail::ConnectOptionFunc>& options)
{
#ifdef BRYNET_HAVE_LANG_CXX17
std::shared_lock<std::shared_mutex> lck(mThreadGuard);
#else
std::lock_guard<std::mutex> lck(mThreadGuard);
#endif
detail::ConnectOptionsInfo option;
for (const auto& func : options)
{
func(option);
}
if (option.completedCallback == nullptr && option.faledCallback == nullptr)
{
throw ConnectException("all callback is nullptr");
}
if (option.ip.empty())
{
throw ConnectException("addr is empty");
}
if (!(*mIsRun))
{
throw ConnectException("work thread already stop");
}
auto workInfo = mWorkInfo;
auto address = detail::AsyncConnectAddr(std::move(option.ip),
option.port,
option.timeout,
std::move(option.completedCallback),
std::move(option.faledCallback),
std::move(option.processCallbacks));
mEventLoop->runAsyncFunctor([workInfo, address]() {
workInfo->processConnect(address);
});
}
protected:
AsyncConnectorDetail()
{
mIsRun = std::make_shared<bool>(false);
}
virtual ~AsyncConnectorDetail()
{
stopWorkerThread();
}
private:
std::shared_ptr<EventLoop> mEventLoop;
std::shared_ptr<detail::ConnectorWorkInfo> mWorkInfo;
std::shared_ptr<std::thread> mThread;
#ifdef BRYNET_HAVE_LANG_CXX17
std::shared_mutex mThreadGuard;
#else
std::mutex mThreadGuard;
#endif
std::shared_ptr<bool> mIsRun;
};
} } }

View File

@ -0,0 +1,368 @@
#pragma once
#include <functional>
#include <memory>
#include <brynet/base/NonCopyable.hpp>
#include <brynet/base/CPP_VERSION.hpp>
#include <brynet/net/SocketLibTypes.hpp>
#include <brynet/net/Socket.hpp>
#include <brynet/net/Poller.hpp>
#ifdef BRYNET_HAVE_LANG_CXX17
#include <shared_mutex>
#else
#include <mutex>
#endif
namespace brynet { namespace net { namespace detail {
class ConnectOptionsInfo;
using ConnectOptionFunc = std::function<void(ConnectOptionsInfo & option)>;
class AsyncConnectAddr final
{
public:
using CompletedCallback = std::function<void(TcpSocket::Ptr)>;
using ProcessTcpSocketCallback = std::function<void(TcpSocket&)>;
using FailedCallback = std::function<void()>;
public:
AsyncConnectAddr(std::string&& ip,
int port,
std::chrono::nanoseconds timeout,
CompletedCallback&& successCB,
FailedCallback&& failedCB,
std::vector<ProcessTcpSocketCallback>&& processCallbacks)
:
mIP(std::move(ip)),
mPort(port),
mTimeout(timeout),
mSuccessCB(std::move(successCB)),
mFailedCB(std::move(failedCB)),
mProcessCallbacks(std::move(processCallbacks))
{
}
const std::string& getIP() const
{
return mIP;
}
int getPort() const
{
return mPort;
}
const CompletedCallback& getSuccessCB() const
{
return mSuccessCB;
}
const FailedCallback& getFailedCB() const
{
return mFailedCB;
}
const std::vector<ProcessTcpSocketCallback>& getProcessCallbacks() const
{
return mProcessCallbacks;
}
std::chrono::nanoseconds getTimeout() const
{
return mTimeout;
}
private:
const std::string mIP;
const int mPort;
const std::chrono::nanoseconds mTimeout;
const CompletedCallback mSuccessCB;
const FailedCallback mFailedCB;
const std::vector<ProcessTcpSocketCallback> mProcessCallbacks;
};
class ConnectorWorkInfo final : public brynet::base::NonCopyable
{
public:
using Ptr = std::shared_ptr<ConnectorWorkInfo>;
ConnectorWorkInfo() BRYNET_NOEXCEPT
{
mPoller.reset(brynet::base::poller_new());
mPollResult.reset(brynet::base::stack_new(1024, sizeof(BrynetSocketFD)));
}
void checkConnectStatus(int millsecond)
{
if (poller_poll(mPoller.get(), millsecond) <= 0)
{
return;
}
std::set<BrynetSocketFD> totalFds;
std::set<BrynetSocketFD> successFds;
poller_visitor(mPoller.get(), brynet::base::WriteCheck, mPollResult.get());
while (true)
{
auto p = stack_popfront(mPollResult.get());
if (p == nullptr)
{
break;
}
const auto fd = *(BrynetSocketFD*)p;
totalFds.insert(fd);
if (isConnectSuccess(fd, false) &&
!brynet::net::base::IsSelfConnect(fd))
{
successFds.insert(fd);
}
}
for (auto fd : totalFds)
{
poller_remove(mPoller.get(), fd);
const auto it = mConnectingInfos.find(fd);
if (it == mConnectingInfos.end())
{
continue;
}
auto socket = TcpSocket::Create(fd, false);
const auto& connectingInfo = it->second;
if (successFds.find(fd) != successFds.end())
{
for (const auto& process : connectingInfo.processCallbacks)
{
process(*socket);
}
if (connectingInfo.successCB != nullptr)
{
connectingInfo.successCB(std::move(socket));
}
}
else
{
if (connectingInfo.failedCB != nullptr)
{
connectingInfo.failedCB();
}
}
mConnectingInfos.erase(it);
}
}
bool isConnectSuccess(BrynetSocketFD clientfd, bool willCheckWrite) const
{
if (willCheckWrite && !poller_check(mPoller.get(), clientfd, brynet::base::WriteCheck))
{
return false;
}
int error = BRYNET_SOCKET_ERROR;
int len = sizeof(error);
if (getsockopt(clientfd,
SOL_SOCKET,
SO_ERROR,
(char*)&error,
(socklen_t*)&len) == BRYNET_SOCKET_ERROR)
{
return false;
}
return error == 0;
}
void checkTimeout()
{
for (auto it = mConnectingInfos.begin(); it != mConnectingInfos.end();)
{
const auto now = std::chrono::steady_clock::now();
if ((now - it->second.startConnectTime) < it->second.timeout)
{
++it;
continue;
}
auto fd = it->first;
auto cb = it->second.failedCB;
poller_remove(mPoller.get(), fd);
mConnectingInfos.erase(it++);
brynet::net::base::SocketClose(fd);
if (cb != nullptr)
{
//TODO::don't modify mConnectingInfos in cb
cb();
}
}
}
void processConnect(const AsyncConnectAddr& addr)
{
struct sockaddr_in server_addr = sockaddr_in();
BrynetSocketFD clientfd = BRYNET_INVALID_SOCKET;
#ifdef BRYNET_PLATFORM_WINDOWS
const int ExpectedError = WSAEWOULDBLOCK;
#else
const int ExpectedError = EINPROGRESS;
#endif
int n = 0;
brynet::net::base::InitSocket();
clientfd = brynet::net::base::SocketCreate(AF_INET, SOCK_STREAM, 0);
if (clientfd == BRYNET_INVALID_SOCKET)
{
goto FAILED;
}
brynet::net::base::SocketNonblock(clientfd);
server_addr.sin_family = AF_INET;
inet_pton(AF_INET, addr.getIP().c_str(), &server_addr.sin_addr.s_addr);
server_addr.sin_port = static_cast<decltype(server_addr.sin_port)>(htons(addr.getPort()));
n = connect(clientfd, (struct sockaddr*) & server_addr, sizeof(struct sockaddr));
if (n == 0)
{
if (brynet::net::base::IsSelfConnect(clientfd))
{
goto FAILED;
}
}
else if (BRYNET_ERRNO != ExpectedError)
{
goto FAILED;
}
else
{
ConnectingInfo ci;
ci.startConnectTime = std::chrono::steady_clock::now();
ci.successCB = addr.getSuccessCB();
ci.failedCB = addr.getFailedCB();
ci.timeout = addr.getTimeout();
ci.processCallbacks = addr.getProcessCallbacks();
mConnectingInfos[clientfd] = ci;
poller_add(mPoller.get(), clientfd, brynet::base::WriteCheck);
return;
}
if (addr.getSuccessCB() != nullptr)
{
auto tcpSocket = TcpSocket::Create(clientfd, false);
for (const auto& process : addr.getProcessCallbacks())
{
process(*tcpSocket);
}
addr.getSuccessCB()(std::move(tcpSocket));
}
return;
FAILED:
if (clientfd != BRYNET_INVALID_SOCKET)
{
brynet::net::base::SocketClose(clientfd);
clientfd = BRYNET_INVALID_SOCKET;
(void)clientfd;
}
if (addr.getFailedCB() != nullptr)
{
addr.getFailedCB()();
}
}
void causeAllFailed()
{
auto copyMap = mConnectingInfos;
mConnectingInfos.clear();
for (const auto& v : copyMap)
{
auto fd = v.first;
auto cb = v.second.failedCB;
poller_remove(mPoller.get(), fd);
brynet::net::base::SocketClose(fd);
if (cb != nullptr)
{
cb();
}
}
}
private:
class ConnectingInfo
{
public:
ConnectingInfo()
{
timeout = std::chrono::nanoseconds::zero();
}
std::chrono::steady_clock::time_point startConnectTime;
std::chrono::nanoseconds timeout;
AsyncConnectAddr::CompletedCallback successCB;
AsyncConnectAddr::FailedCallback failedCB;
std::vector<AsyncConnectAddr::ProcessTcpSocketCallback> processCallbacks;
};
std::map<BrynetSocketFD, ConnectingInfo> mConnectingInfos;
class PollerDeleter
{
public:
void operator()(struct brynet::base::poller_s* ptr) const
{
brynet::base::poller_delete(ptr);
}
};
class StackDeleter
{
public:
void operator()(struct brynet::base::stack_s* ptr) const
{
brynet::base::stack_delete(ptr);
}
};
std::unique_ptr<struct brynet::base::poller_s, PollerDeleter> mPoller;
std::unique_ptr<struct brynet::base::stack_s, StackDeleter> mPollResult;
};
static void RunOnceCheckConnect(
const std::shared_ptr<brynet::net::EventLoop>& eventLoop,
const std::shared_ptr<ConnectorWorkInfo>& workerInfo)
{
eventLoop->loop(std::chrono::milliseconds(10).count());
workerInfo->checkConnectStatus(0);
workerInfo->checkTimeout();
}
class ConnectOptionsInfo final
{
public:
ConnectOptionsInfo()
:
port(0),
timeout(std::chrono::seconds(10))
{
}
std::string ip;
int port;
std::chrono::nanoseconds timeout;
std::vector<AsyncConnectAddr::ProcessTcpSocketCallback> processCallbacks;
AsyncConnectAddr::CompletedCallback completedCallback;
AsyncConnectAddr::FailedCallback faledCallback;
};
} } }

View File

@ -0,0 +1,67 @@
#pragma once
#include <functional>
#include <thread>
#include <cstdint>
#include <memory>
#include <brynet/base/NonCopyable.hpp>
#include <brynet/net/EventLoop.hpp>
namespace brynet { namespace net { namespace detail {
class TcpServiceDetail;
class IOLoopData : public brynet::base::NonCopyable,
public std::enable_shared_from_this<IOLoopData>
{
public:
using Ptr = std::shared_ptr<IOLoopData>;
static Ptr Create(EventLoop::Ptr eventLoop,
std::shared_ptr<std::thread> ioThread)
{
class make_shared_enabler : public IOLoopData
{
public:
make_shared_enabler(EventLoop::Ptr eventLoop,
std::shared_ptr<std::thread> ioThread)
:
IOLoopData(std::move(eventLoop), std::move(ioThread))
{}
};
return std::make_shared<make_shared_enabler>(std::move(eventLoop),
std::move(ioThread));
}
const EventLoop::Ptr& getEventLoop() const
{
return mEventLoop;
}
protected:
const std::shared_ptr<std::thread>& getIOThread() const
{
return mIOThread;
}
IOLoopData(EventLoop::Ptr eventLoop,
std::shared_ptr<std::thread> ioThread)
:
mEventLoop(std::move(eventLoop)),
mIOThread(std::move(ioThread))
{}
virtual ~IOLoopData() = default;
const EventLoop::Ptr mEventLoop;
private:
std::shared_ptr<std::thread> mIOThread;
friend class TcpServiceDetail;
};
using IOLoopDataPtr = std::shared_ptr<IOLoopData>;
} } }

View File

@ -0,0 +1,169 @@
#pragma once
#include <string>
#include <functional>
#include <thread>
#include <memory>
#include <mutex>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <brynet/base/NonCopyable.hpp>
#include <brynet/base/Noexcept.hpp>
#include <brynet/net/SocketLibFunction.hpp>
#include <brynet/net/AsyncConnector.hpp>
#include <brynet/net/wrapper/ConnectionBuilder.hpp>
#include <brynet/net/Socket.hpp>
namespace brynet { namespace net { namespace detail {
class ListenThreadDetail : public brynet::base::NonCopyable
{
protected:
using AccepCallback = std::function<void(TcpSocket::Ptr)>;
using TcpSocketProcessCallback = std::function<void(TcpSocket&)>;
void startListen()
{
std::lock_guard<std::mutex> lck(mListenThreadGuard);
if (mListenThread != nullptr)
{
return;
}
const auto fd = brynet::net::base::Listen(mIsIPV6, mIP.c_str(), mPort, 512);
if (fd == BRYNET_INVALID_SOCKET)
{
throw BrynetCommonException(
std::string("listen error of:") + std::to_string(BRYNET_ERRNO));
}
mRunListen = std::make_shared<bool>(true);
auto listenSocket = std::shared_ptr<ListenSocket>(ListenSocket::Create(fd));
auto isRunListen = mRunListen;
auto callback = mCallback;
auto processCallbacks = mProcessCallbacks;
mListenThread = std::make_shared<std::thread>(
[isRunListen, listenSocket, callback, processCallbacks]() mutable {
while (*isRunListen)
{
auto clientSocket = runOnceListen(listenSocket);
if (clientSocket == nullptr)
{
continue;
}
if (*isRunListen)
{
for (const auto& process : processCallbacks)
{
process(*clientSocket);
}
callback(std::move(clientSocket));
}
}
});
}
void stopListen()
{
std::lock_guard<std::mutex> lck(mListenThreadGuard);
if (mListenThread == nullptr)
{
return;
}
*mRunListen = false;
auto selfIP = mIP;
if (selfIP == "0.0.0.0")
{
selfIP = "127.0.0.1";
}
auto connector = AsyncConnector::Create();
connector->startWorkerThread();
wrapper::SocketConnectBuilder connectBuilder;
connectBuilder
.configureConnector(connector)
.configureConnectOptions({
ConnectOption::WithTimeout(std::chrono::seconds(2)),
ConnectOption::WithAddr(selfIP, mPort)
})
.syncConnect();
try
{
if (mListenThread->joinable())
{
mListenThread->join();
}
}
catch (std::system_error & e)
{
(void)e;
}
mListenThread = nullptr;
}
protected:
ListenThreadDetail(bool isIPV6,
const std::string& ip,
int port,
const AccepCallback& callback,
const std::vector<TcpSocketProcessCallback>& processCallbacks)
:
mIsIPV6(isIPV6),
mIP(ip),
mPort(port),
mCallback(callback),
mProcessCallbacks(processCallbacks)
{
if (mCallback == nullptr)
{
throw BrynetCommonException("accept callback is nullptr");
}
mRunListen = std::make_shared<bool>(false);
}
virtual ~ListenThreadDetail() BRYNET_NOEXCEPT
{
stopListen();
}
private:
static brynet::net::TcpSocket::Ptr runOnceListen(const std::shared_ptr<ListenSocket>& listenSocket)
{
try
{
return listenSocket->accept();
}
catch (const EintrError & e)
{
std::cerr << "accept eintr execption:" << e.what() << std::endl;
}
catch (const AcceptError & e)
{
std::cerr << "accept execption:" << e.what() << std::endl;
}
return nullptr;
}
private:
const bool mIsIPV6;
const std::string mIP;
const int mPort;
const AccepCallback mCallback;
const std::vector<TcpSocketProcessCallback> mProcessCallbacks;
std::shared_ptr<bool> mRunListen;
std::shared_ptr<std::thread> mListenThread;
std::mutex mListenThreadGuard;
};
} } }

View File

@ -0,0 +1,198 @@
#pragma once
#include <vector>
#include <string>
#include <functional>
#include <thread>
#include <cstdint>
#include <memory>
#include <random>
#include <brynet/base/NonCopyable.hpp>
#include <brynet/base/Noexcept.hpp>
#include <brynet/net/TcpConnection.hpp>
#include <brynet/net/SSLHelper.hpp>
#include <brynet/net/Socket.hpp>
#include <brynet/net/detail/IOLoopData.hpp>
#include <brynet/net/detail/AddSocketOptionInfo.hpp>
namespace brynet { namespace net { namespace detail {
class TcpServiceDetail : public brynet::base::NonCopyable
{
protected:
using FrameCallback = std::function<void(const EventLoop::Ptr&)>;
const static unsigned int sDefaultLoopTimeOutMS = 100;
void startWorkerThread(size_t threadNum,
FrameCallback callback = nullptr)
{
std::lock_guard<std::mutex> lck(mServiceGuard);
std::lock_guard<std::mutex> lock(mIOLoopGuard);
if (!mIOLoopDatas.empty())
{
return;
}
mRunIOLoop = std::make_shared<bool>(true);
mIOLoopDatas.resize(threadNum);
for (auto& v : mIOLoopDatas)
{
auto eventLoop = std::make_shared<EventLoop>();
auto runIoLoop = mRunIOLoop;
v = IOLoopData::Create(eventLoop,
std::make_shared<std::thread>(
[callback, runIoLoop, eventLoop]() {
while (*runIoLoop)
{
eventLoop->loopCompareNearTimer(sDefaultLoopTimeOutMS);
if (callback != nullptr)
{
callback(eventLoop);
}
}
}));
}
}
void stopWorkerThread()
{
std::lock_guard<std::mutex> lck(mServiceGuard);
std::lock_guard<std::mutex> lock(mIOLoopGuard);
*mRunIOLoop = false;
for (const auto& v : mIOLoopDatas)
{
v->getEventLoop()->wakeup();
try
{
if (v->getIOThread()->joinable())
{
v->getIOThread()->join();
}
}
catch (std::system_error & e)
{
(void)e;
}
}
mIOLoopDatas.clear();
}
template<typename... Options>
bool addTcpConnection(TcpSocket::Ptr socket,
const Options& ... options)
{
return _addTcpConnection(std::move(socket), { options... });
}
EventLoop::Ptr getRandomEventLoop()
{
std::lock_guard<std::mutex> lock(mIOLoopGuard);
const auto ioLoopSize = mIOLoopDatas.size();
if (ioLoopSize == 0)
{
return nullptr;
}
else if (ioLoopSize == 1)
{
return mIOLoopDatas.front()->getEventLoop();
}
else
{
return mIOLoopDatas[mRandom() % ioLoopSize]->getEventLoop();
}
}
TcpServiceDetail() BRYNET_NOEXCEPT
:
mRandom(static_cast<unsigned int>(
std::chrono::system_clock::now().time_since_epoch().count()))
{
mRunIOLoop = std::make_shared<bool>(false);
}
virtual ~TcpServiceDetail() BRYNET_NOEXCEPT
{
stopWorkerThread();
}
bool _addTcpConnection(TcpSocket::Ptr socket,
const std::vector<AddSocketOptionFunc>& optionFuncs)
{
AddSocketOptionInfo options;
for (const auto& v : optionFuncs)
{
if (v != nullptr)
{
v(options);
}
}
if (options.maxRecvBufferSize <= 0)
{
throw BrynetCommonException("buffer size is zero");
}
EventLoop::Ptr eventLoop;
if (options.forceSameThreadLoop)
{
eventLoop = getSameThreadEventLoop();
}
else
{
eventLoop = getRandomEventLoop();
}
if (eventLoop == nullptr)
{
return false;
}
auto wrapperEnterCallback = [options](const TcpConnection::Ptr& tcpConnection) {
for (const auto& callback : options.enterCallback)
{
callback(tcpConnection);
}
};
if (options.useSSL && options.sslHelper == nullptr)
{
options.sslHelper = SSLHelper::Create();
}
TcpConnection::Create(std::move(socket),
options.maxRecvBufferSize,
wrapperEnterCallback,
eventLoop,
options.sslHelper);
return true;
}
EventLoop::Ptr getSameThreadEventLoop()
{
std::lock_guard<std::mutex> lock(mIOLoopGuard);
for (const auto& v : mIOLoopDatas)
{
if (v->getEventLoop()->isInLoopThread())
{
return v->getEventLoop();
}
}
return nullptr;
}
private:
std::vector<IOLoopDataPtr> mIOLoopDatas;
mutable std::mutex mIOLoopGuard;
std::shared_ptr<bool> mRunIOLoop;
std::mutex mServiceGuard;
std::mt19937 mRandom;
};
} } }

View File

@ -0,0 +1,139 @@
#pragma once
#include <cstdint>
#include <functional>
#include <vector>
#include <mutex>
#include <memory>
#include <atomic>
#include <cassert>
#include <brynet/base/NonCopyable.hpp>
#include <brynet/net/SocketLibFunction.hpp>
#ifdef BRYNET_PLATFORM_WINDOWS
#include <brynet/net/port/Win.hpp>
#endif
#include <brynet/net/Channel.hpp>
#include <brynet/net/Socket.hpp>
namespace brynet { namespace net { namespace detail {
#ifdef BRYNET_PLATFORM_WINDOWS
class WakeupChannel final : public Channel, public brynet::base::NonCopyable
{
public:
explicit WakeupChannel(HANDLE iocp)
:
mIOCP(iocp),
mWakeupOvl(port::Win::OverlappedType::OverlappedRecv)
{
}
bool wakeup() BRYNET_NOEXCEPT
{
return PostQueuedCompletionStatus(mIOCP,
0,
reinterpret_cast<ULONG_PTR>(this),
&mWakeupOvl.base);
}
private:
void canRecv() BRYNET_NOEXCEPT override
{
;
}
void canSend() BRYNET_NOEXCEPT override
{
;
}
void onClose() BRYNET_NOEXCEPT override
{
;
}
HANDLE mIOCP;
port::Win::OverlappedExt mWakeupOvl;
};
#elif defined BRYNET_PLATFORM_LINUX
class WakeupChannel final : public Channel, public brynet::base::NonCopyable
{
public:
explicit WakeupChannel(BrynetSocketFD fd) : mUniqueFd(fd)
{
}
bool wakeup()
{
uint64_t one = 1;
return write(mUniqueFd.getFD(), &one, sizeof one) > 0;
}
private:
void canRecv() override
{
char temp[1024 * 10];
while (true)
{
auto n = read(mUniqueFd.getFD(), temp, sizeof(temp));
if (n == -1 || static_cast<size_t>(n) < sizeof(temp))
{
break;
}
}
}
void canSend() override
{
}
void onClose() override
{
}
private:
UniqueFd mUniqueFd;
};
#elif defined BRYNET_PLATFORM_DARWIN
class WakeupChannel final : public Channel, public brynet::base::NonCopyable
{
public:
explicit WakeupChannel(int kqueuefd, int ident)
:
mKqueueFd(kqueuefd),
mUserEvent(ident)
{
}
bool wakeup()
{
struct kevent ev;
EV_SET(&ev, mUserEvent, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
struct timespec timeout = { 0, 0 };
return kevent(mKqueueFd, &ev, 1, NULL, 0, &timeout) == 0;
}
private:
void canRecv() override
{
}
void canSend() override
{
}
void onClose() override
{
}
private:
int mKqueueFd;
int mUserEvent;
};
#endif
} } }

Some files were not shown because too many files have changed in this diff Show More