├── run_dogServlet_debug.sh ├── run_httpServer_debug.sh ├── run_dogServlet_release.sh ├── run_httpServer_release.sh ├── haha_web ├── haha_json │ ├── run_jsonTest_debug.sh │ ├── run_jsonTest_release.sh │ ├── bin │ │ ├── testutf.json │ │ ├── output.json │ │ └── test.json │ ├── .vscode │ │ ├── configurationCache.log │ │ ├── dryrun.log │ │ ├── c_cpp_properties.json │ │ ├── settings.json │ │ └── launch.json │ ├── build_release.sh │ ├── build_debug.sh │ ├── .gitignore │ ├── tests │ │ ├── test_type_cast.cpp │ │ ├── Makefile │ │ ├── test_lang.cpp │ │ ├── test_jsonCopy.cpp │ │ └── test_json.cpp │ ├── haha_json │ │ ├── json.h │ │ ├── stringView.cpp │ │ ├── jsonTypeCast.h │ │ ├── jsonError.h │ │ ├── jsonUtil.h │ │ ├── jsonBuffer.h │ │ └── stringView.h │ ├── README.md │ └── CMakeLists.txt ├── log │ ├── Log.h │ ├── LoggerManager.cpp │ ├── LogFile.h │ ├── LogInfo.cpp │ ├── LogUtil.h │ ├── LogInfo.h │ ├── LogStream.h │ ├── Logger.h │ ├── LogStream.cpp │ └── Logger.cpp ├── base │ ├── RetOption.h │ ├── noncopyable.h │ ├── Mutex.cpp │ ├── Singleton.h │ ├── EncodeUtil.h │ ├── util.h │ ├── ThreadPool.h │ ├── ThreadPool.cpp │ ├── Thread.h │ ├── ProcessInfo.h │ ├── uuid.h │ ├── ConditionVariable.h │ ├── ReadWriteLock.h │ ├── Thread.cpp │ ├── stringView.cpp │ ├── Buffer.h │ ├── util.cpp │ ├── Mutex.h │ ├── EncodeUtil.cpp │ └── stringView.h ├── net │ ├── Channel.cpp │ ├── TimerQueue.h │ ├── EventLoopThread.h │ ├── InetAddress.h │ ├── InetAddress.cpp │ ├── EventLoopThread.cpp │ ├── EventLoopThreadPool.h │ ├── EventLoopThreadPool.cpp │ ├── Epoller.h │ ├── EventLoop.h │ ├── Channel.h │ ├── TimerQueue.cpp │ ├── TimerHeap.h │ ├── Epoller.cpp │ ├── TcpServer.h │ ├── Socket.h │ ├── TcpConnection.cpp │ ├── TimerHeap.cpp │ └── TcpConnection.h ├── http │ ├── HttpSession.cpp │ ├── HttpResponse.cpp │ ├── HttpServer.h │ ├── HttpUtil.cpp │ ├── HttpCookie.h │ ├── HttpTest.h │ ├── Servlet.h │ ├── HttpUrl.h │ ├── HttpMultiPart.h │ ├── HttpMap.h │ ├── HttpHeader.h │ ├── HttpCookie.cpp │ ├── HttpSession.h │ ├── HttpUrl.cpp │ ├── HttpResponse.h │ ├── HttpServer.cpp │ └── HttpRequest.h └── config │ ├── config.cpp │ └── config.h ├── bin ├── resource │ ├── dog.jpg │ ├── index.html │ ├── dog.html │ ├── register.html │ └── test.json ├── configs │ └── base_config.json └── ssl │ ├── cert.crt │ └── rsa_private.key ├── webbench1.5 ├── webbench ├── Makefile └── socket.c ├── tests ├── test_timeStamp.cc ├── test_httpServer.cc ├── test_config.cc ├── test_tcpServer.cc ├── test_threadpool.cc ├── test_httpCT.cc ├── test_log.cc ├── test_thread.cc ├── test_timerheap.cc ├── test_stringview.cc ├── Makefile ├── test_servlet.cc └── test_lang.cc ├── resource ├── nginx-webbench-5000-5.png ├── haha-debug-webbench-5000-5.png ├── haha-release-webbench-5000-5.png ├── muduo-release-webbench-5000-5.png ├── haha-olpt-debug-webbench-5000-5.png ├── tiny-lt+et-debug-webbench-5000-5.png ├── haha-olpt-release-webbench-5000-5.png ├── tiny-lt+et-release-webbench-5000-5.png └── haha-olpt-asynclog-release-webbench-5000-5.png ├── .vscode ├── configurationCache.log ├── dryrun.log ├── c_cpp_properties.json └── settings.json ├── create_ssl_certificate.sh ├── .gitignore ├── third_party └── magic_enum │ └── magic_enum_fuse.hpp └── CMakeLists.txt /run_dogServlet_debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./bin/debug 4 | ./dogServletTest.out -------------------------------------------------------------------------------- /run_httpServer_debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./bin/debug 4 | ./httpServerTest.out -------------------------------------------------------------------------------- /run_dogServlet_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./bin/release 4 | ./dogServletTest.out -------------------------------------------------------------------------------- /run_httpServer_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./bin/release 4 | ./httpServerTest.out -------------------------------------------------------------------------------- /haha_web/haha_json/run_jsonTest_debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./bin/debug 4 | ./jsonParseTest.out -------------------------------------------------------------------------------- /haha_web/haha_json/run_jsonTest_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./bin/release 4 | ./jsonParseTest.out -------------------------------------------------------------------------------- /bin/resource/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatowithpotato/HAHA-WebServer/HEAD/bin/resource/dog.jpg -------------------------------------------------------------------------------- /webbench1.5/webbench: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatowithpotato/HAHA-WebServer/HEAD/webbench1.5/webbench -------------------------------------------------------------------------------- /haha_web/haha_json/bin/testutf.json: -------------------------------------------------------------------------------- 1 | {"": 0, "\u7814": 1, "\u7a76": 2, "\u53d1": 3, "\u73b0": 4, "\u7ec6": 5, "\u80de": 6} -------------------------------------------------------------------------------- /tests/test_timeStamp.cc: -------------------------------------------------------------------------------- 1 | #include "TimeStamp.h" 2 | 3 | int main(){ 4 | std::chrono::milliseconds a(1000); 5 | return 0; 6 | } -------------------------------------------------------------------------------- /resource/nginx-webbench-5000-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatowithpotato/HAHA-WebServer/HEAD/resource/nginx-webbench-5000-5.png -------------------------------------------------------------------------------- /resource/haha-debug-webbench-5000-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatowithpotato/HAHA-WebServer/HEAD/resource/haha-debug-webbench-5000-5.png -------------------------------------------------------------------------------- /resource/haha-release-webbench-5000-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatowithpotato/HAHA-WebServer/HEAD/resource/haha-release-webbench-5000-5.png -------------------------------------------------------------------------------- /resource/muduo-release-webbench-5000-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatowithpotato/HAHA-WebServer/HEAD/resource/muduo-release-webbench-5000-5.png -------------------------------------------------------------------------------- /resource/haha-olpt-debug-webbench-5000-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatowithpotato/HAHA-WebServer/HEAD/resource/haha-olpt-debug-webbench-5000-5.png -------------------------------------------------------------------------------- /resource/tiny-lt+et-debug-webbench-5000-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatowithpotato/HAHA-WebServer/HEAD/resource/tiny-lt+et-debug-webbench-5000-5.png -------------------------------------------------------------------------------- /tests/test_httpServer.cc: -------------------------------------------------------------------------------- 1 | #include "http/HttpServer.h" 2 | 3 | int main(){ 4 | haha::HttpServer server; 5 | server.start(); 6 | return 0; 7 | } -------------------------------------------------------------------------------- /resource/haha-olpt-release-webbench-5000-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatowithpotato/HAHA-WebServer/HEAD/resource/haha-olpt-release-webbench-5000-5.png -------------------------------------------------------------------------------- /resource/tiny-lt+et-release-webbench-5000-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatowithpotato/HAHA-WebServer/HEAD/resource/tiny-lt+et-release-webbench-5000-5.png -------------------------------------------------------------------------------- /haha_web/log/Log.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_LOG_LOG_H__ 2 | #define __HAHA_LOG_LOG_H__ 3 | 4 | #include "log/Logger.h" 5 | #include "log/LoggerManager.h" 6 | 7 | 8 | #endif -------------------------------------------------------------------------------- /.vscode/configurationCache.log: -------------------------------------------------------------------------------- 1 | {"buildTargets":[],"launchTargets":[],"customConfigurationProvider":{"workspaceBrowse":{"browsePath":[],"compilerArgs":[]},"fileIndex":[]}} -------------------------------------------------------------------------------- /tests/test_config.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "config/config.h" 3 | 4 | int main(){ 5 | haha::Config &config = haha::Config::getInstance(); 6 | return 0; 7 | } -------------------------------------------------------------------------------- /resource/haha-olpt-asynclog-release-webbench-5000-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatowithpotato/HAHA-WebServer/HEAD/resource/haha-olpt-asynclog-release-webbench-5000-5.png -------------------------------------------------------------------------------- /haha_web/haha_json/.vscode/configurationCache.log: -------------------------------------------------------------------------------- 1 | {"buildTargets":[],"launchTargets":[],"customConfigurationProvider":{"workspaceBrowse":{"browsePath":[],"compilerArgs":[]},"fileIndex":[]}} -------------------------------------------------------------------------------- /tests/test_tcpServer.cc: -------------------------------------------------------------------------------- 1 | #include "TcpServer.h" 2 | 3 | int main(){ 4 | haha::TcpServer server; 5 | haha::InetAddress address(9999); 6 | server.start(address); 7 | return 0; 8 | } -------------------------------------------------------------------------------- /haha_web/haha_json/build_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # cd ./tests 4 | # make httpServer 5 | 6 | if [ ! -d "build" ]; then 7 | mkdir -p build 8 | fi 9 | 10 | cd ./build 11 | cmake .. 12 | make -------------------------------------------------------------------------------- /haha_web/haha_json/build_debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # cd ./tests 4 | # make httpServer 5 | 6 | if [ ! -d "build" ]; then 7 | mkdir -p build 8 | fi 9 | 10 | cd ./build 11 | cmake -DCMAKE_BUILD_TYPE=Debug .. 12 | make -------------------------------------------------------------------------------- /create_ssl_certificate.sh: -------------------------------------------------------------------------------- 1 | cd ./bin 2 | if [ ! -d "ssl" ]; then 3 | mkdir -p ssl 4 | fi 5 | cd ./ssl 6 | openssl req -newkey rsa:2048 -nodes -keyout rsa_private.key -x509 -days 365 -out cert.crt -subj "/C=CN/ST=GD/L=SZ/O=LFR/OU=dev/CN=haha_web.com/emailAddress=1107682452@qq.com" -------------------------------------------------------------------------------- /haha_web/haha_json/.vscode/dryrun.log: -------------------------------------------------------------------------------- 1 | make --dry-run --always-make --keep-going --print-directory 2 | make: Entering directory '/home/lighthouse/MyCode/haha_json' 3 | make: Leaving directory '/home/lighthouse/MyCode/haha_json' 4 | 5 | make: *** No targets specified and no makefile found. Stop. 6 | 7 | -------------------------------------------------------------------------------- /.vscode/dryrun.log: -------------------------------------------------------------------------------- 1 | make --dry-run --always-make --keep-going --print-directory 2 | make: Entering directory '/home/lighthouse/MyCode/linux/linux_network/HAHAWebServer' 3 | make: Leaving directory '/home/lighthouse/MyCode/linux/linux_network/HAHAWebServer' 4 | 5 | make: *** No targets specified and no makefile found. Stop. 6 | 7 | -------------------------------------------------------------------------------- /bin/resource/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | HAHAHAHAHAHAHAHAHAHA 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /haha_web/base/RetOption.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_RETOPTION_H__ 2 | #define __HAHA_RETOPTION_H__ 3 | 4 | 5 | namespace haha{ 6 | 7 | template 8 | class RetOption{ 9 | public: 10 | RetOption(const T& val, bool exist):val_(val),exist_(exist){} 11 | T& value() { return val_; } 12 | bool exist() { return exist_; } 13 | private: 14 | T val_; 15 | bool exist_; 16 | }; 17 | 18 | } 19 | 20 | 21 | #endif -------------------------------------------------------------------------------- /bin/resource/dog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | HAHA-DOG 8 | 9 | 10 |
you are dog
11 |
12 | 13 | -------------------------------------------------------------------------------- /haha_web/base/noncopyable.h: -------------------------------------------------------------------------------- 1 | #ifndef HAHA_BASE_NONCOPYABLE_H 2 | #define HAHA_BASE_NONCOPYABLE_H 3 | 4 | namespace haha 5 | { 6 | 7 | /*不可拷贝*/ 8 | class noncopyable 9 | { 10 | public: 11 | noncopyable(const noncopyable&) = delete; 12 | void operator=(const noncopyable&) = delete; 13 | 14 | protected: 15 | noncopyable() = default; 16 | ~noncopyable() = default; 17 | }; 18 | 19 | } // namespace haha 20 | 21 | #endif // HAHA_BASE_NONCOPYABLE_H 22 | -------------------------------------------------------------------------------- /haha_web/haha_json/.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # cmake 35 | build/* 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # cmake 35 | build/* 36 | 37 | # log 38 | *.log -------------------------------------------------------------------------------- /haha_web/haha_json/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [], 9 | "compilerPath": "/usr/bin/gcc", 10 | "cStandard": "gnu17", 11 | "cppStandard": "gnu++20", 12 | "intelliSenseMode": "linux-gcc-x64", 13 | "configurationProvider": "ms-vscode.makefile-tools" 14 | } 15 | ], 16 | "version": 4 17 | } -------------------------------------------------------------------------------- /haha_web/net/Channel.cpp: -------------------------------------------------------------------------------- 1 | #include "Channel.h" 2 | 3 | namespace haha{ 4 | 5 | Channel::Channel(EventLoop* loop, int fd, bool BlockFd) 6 | :fd_(fd) 7 | ,isBlockFd_(BlockFd) 8 | ,eventloop_(loop){ 9 | 10 | } 11 | 12 | void Channel::handleEvents(uint32_t revents){ 13 | if (revents & (EPOLLERR | EPOLLRDHUP | EPOLLHUP)) { 14 | if (closeCb_) 15 | closeCb_(); 16 | } else if (revents & EPOLLIN) { 17 | if (readCb_) 18 | readCb_(); 19 | } else if (revents & EPOLLOUT) { 20 | if (writeCb_) 21 | writeCb_(); 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**", 7 | "${workspaceFolder}/haha/include" 8 | ], 9 | "defines": [], 10 | "compilerPath": "/usr/bin/gcc", 11 | "cStandard": "gnu17", 12 | "cppStandard": "gnu++17", 13 | "intelliSenseMode": "linux-gcc-x64", 14 | "configurationProvider": "ms-vscode.makefile-tools" 15 | } 16 | ], 17 | "version": 4 18 | } -------------------------------------------------------------------------------- /bin/configs/base_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "https": { 4 | "enable": false, 5 | "cert_file": "../ssl/cert.crt", 6 | "private_key_file": "../ssl/rsa_private.key", 7 | "password": "" 8 | }, 9 | "port": 9999, 10 | "timeout": 120, 11 | "timerInterval": 5 12 | }, 13 | 14 | "EventLoopThreadPool":{ 15 | "threadNum": 4 16 | }, 17 | 18 | "log": { 19 | "open": true, 20 | "default_format": "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%T[%p]%T[%c]%T%f:%l%T%m%n" 21 | } 22 | } -------------------------------------------------------------------------------- /tests/test_threadpool.cc: -------------------------------------------------------------------------------- 1 | #include "base/ThreadPool.h" 2 | #include "log/Log.h" 3 | #include 4 | #include 5 | #include 6 | 7 | haha::ThreadPool &pool = haha::ThreadPool::getInstance(); 8 | 9 | void work(int i){ 10 | // sleep(1); 11 | usleep(100 * 1000); 12 | HAHA_LOG_INFO(HAHA_LOG_ASYNC_FILE_ROOT()); 13 | } 14 | 15 | int main(){ 16 | pool.start(); 17 | for(int i = 0; i < 10; ++i){ 18 | for(int j = 0; j < 1000; ++j){ 19 | pool.addTask(std::bind(work, j)); 20 | } 21 | sleep(1); 22 | } 23 | 24 | return 0; 25 | } -------------------------------------------------------------------------------- /haha_web/haha_json/tests/test_type_cast.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "json.h" 4 | 5 | 6 | namespace JSON = haha::json; 7 | 8 | int main(){ 9 | std::string str = "{\"check\": 123.5e10, \"2893h\":\"ok\", \"arr\": [\"sd\", null]}"; 10 | JSON::Json json; 11 | 12 | bool ok = true; 13 | ok = json.fromString(str); 14 | 15 | std::cout << json.toString() << std::endl; 16 | 17 | auto p = json.getValuePtr(); 18 | 19 | auto obj = JSON::pointer_cast(p); 20 | 21 | std::cout << obj->toString() << std::endl; 22 | 23 | return 0; 24 | } -------------------------------------------------------------------------------- /haha_web/base/Mutex.cpp: -------------------------------------------------------------------------------- 1 | #include "base/Mutex.h" 2 | #include 3 | 4 | namespace haha{ 5 | 6 | Semaphore::Semaphore(uint32_t count){ 7 | if(sem_init(&m_semaphore, 0, count)){ 8 | throw std::logic_error("sem_init error"); 9 | } 10 | } 11 | 12 | Semaphore::~Semaphore(){ 13 | sem_destroy(&m_semaphore); 14 | } 15 | 16 | void Semaphore::wait(){ 17 | if(sem_wait(&m_semaphore)){ 18 | throw std::logic_error("sem_wait error"); 19 | } 20 | } 21 | 22 | void Semaphore::notify(){ 23 | if(sem_post(&m_semaphore)){ 24 | throw std::logic_error("sem_post error"); 25 | } 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /tests/test_httpCT.cc: -------------------------------------------------------------------------------- 1 | #include "http/HttpUtil.h" 2 | #include "magic_enum/magic_enum.hpp" 3 | #include 4 | 5 | int main(){ 6 | for(auto &[k, v] : haha::Ext2HttpContentType){ 7 | std::cout << k << ": "; 8 | std::cout << magic_enum::enum_name(v) << std::endl; 9 | } 10 | std::string html("html"); 11 | haha::Ext2HttpContentType.at(html); 12 | std::cout << "********************************************************************" << std::endl; 13 | for(auto &[k, v] : haha::HttpContentType2Ext){ 14 | std::cout << magic_enum::enum_name(k) << ": "; 15 | std::cout << v << std::endl; 16 | } 17 | return 0; 18 | } -------------------------------------------------------------------------------- /haha_web/base/Singleton.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_SINGLETON_H__ 2 | #define __HAHA_SINGLETON_H__ 3 | 4 | #include "base/noncopyable.h" 5 | #include 6 | 7 | namespace haha{ 8 | 9 | template 10 | class Singleton : noncopyable{ 11 | 12 | public: 13 | static T& getInstance(){ 14 | static T instance_; 15 | return instance_; 16 | } 17 | }; 18 | 19 | template 20 | class SingletonPtr : noncopyable{ 21 | public: 22 | /** 23 | * @brief 返回单例智能指针 24 | */ 25 | static std::shared_ptr GetInstance() { 26 | static std::shared_ptr v(new T); 27 | return v; 28 | } 29 | }; 30 | 31 | } 32 | 33 | #endif -------------------------------------------------------------------------------- /tests/test_log.cc: -------------------------------------------------------------------------------- 1 | #include "../haha/include/base/Log.h" 2 | #include 3 | #include 4 | #include 5 | 6 | void log_info(haha::Logger::ptr logger){ 7 | auto level = haha::LogLevel::Level::INFO; 8 | if(logger->getLevel() <= level){ 9 | haha::LogEventWrap(logger, haha::LogEvent::ptr(new haha::LogEvent(logger, level, 10 | __FILE__, __LINE__, 0, haha::GetThreadId(), 11 | time(0), haha::Thread::CurrentThreadName()))).getSS(); 12 | } 13 | } 14 | 15 | int main(){ 16 | // HAHA_LOG_INFO(HAHA_LOG_ASYNC_FILE_ROOT()) << "hello"; 17 | log_info(HAHA_LOG_ASYNC_FILE_ROOT()); 18 | return 0; 19 | } -------------------------------------------------------------------------------- /haha_web/net/TimerQueue.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_TIMERQUEUE_H__ 2 | #define __HAHA_TIMERQUEUE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "log/Log.h" 8 | #include "TimerHeap.h" 9 | 10 | namespace haha{ 11 | 12 | class TimerQueue{ 13 | public: 14 | TimerQueue(int timeFd); 15 | void push(const Timer &timer); 16 | void adjust(const Timer &timer); 17 | void remove(const Timer &timer); 18 | int getTimeFd() const { return timeFd_; } 19 | 20 | void runOnce(const TimeStamp &t); 21 | void runForever(const TimeStamp &t); 22 | void handleTimeout(); 23 | private: 24 | int timeFd_; 25 | std::unique_ptr timerHeap_; 26 | }; 27 | 28 | } 29 | 30 | #endif -------------------------------------------------------------------------------- /haha_web/net/EventLoopThread.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_EVENTLOOPTHREAD_H__ 2 | #define __HAHA_EVENTLOOPTHREAD_H__ 3 | 4 | #include 5 | #include "base/Thread.h" 6 | #include "EventLoop.h" 7 | 8 | namespace haha{ 9 | 10 | class EventLoopThread{ 11 | public: 12 | typedef std::shared_ptr ptr; 13 | EventLoopThread(const std::string& name = ""); 14 | ~EventLoopThread(); 15 | EventLoop::ptr getLoop() const { return loop_; } 16 | const std::string& getName() const { return thread_->name(); } 17 | 18 | private: 19 | void work(); 20 | 21 | private: 22 | bool exiting_; 23 | volatile bool loop_ok_; 24 | EventLoop::ptr loop_; 25 | std::unique_ptr thread_; 26 | }; 27 | 28 | } 29 | 30 | #endif -------------------------------------------------------------------------------- /haha_web/haha_json/bin/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "MIMode": "gdb", 5 | "args": [], 6 | "cwd": "${workspaceFolder}/bin", 7 | "environment": [], 8 | "externalConsole": false, 9 | "name": "(gdb) 启\t动", 10 | "program": "${workspaceFolder}/bin/parseTest.out", 11 | "request": "launch", 12 | "setupCommands": [ 13 | { 14 | "description": "为 gdb 启用整齐打印", 15 | "ignoreFailures": true, 16 | "text": "-enable-pretty-printing" 17 | }, 18 | { 19 | "description": "将反汇编风格设置为 Intel", 20 | "ignoreFailures": true, 21 | "text": "-gdb-set disassembly-flavor intel" 22 | } 23 | ], 24 | "stopAtEntry": false, 25 | "type": "cppdbg" 26 | } 27 | ], 28 | "version": "0.2.0" 29 | } -------------------------------------------------------------------------------- /haha_web/haha_json/tests/Makefile: -------------------------------------------------------------------------------- 1 | SOURCE_DIR = ../haha_json 2 | 3 | SOURCE_FILES = $(wildcard $(SOURCE_DIR)/*.cpp) 4 | 5 | INCLUDE_DIR = -I ../haha_json 6 | 7 | BINARY_DIR = ../bin 8 | 9 | parse_test: test_json.cpp ${SOURCE_FILES} 10 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_json.cpp $(SOURCE_FILES) -o $(BINARY_DIR)/parseTest.out 11 | 12 | cast_test: test_type_cast.cpp ${SOURCE_FILES} 13 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_type_cast.cpp $(SOURCE_FILES) -o $(BINARY_DIR)/typeCastTest.out 14 | 15 | copy_test: test_jsonCopy.cpp 16 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_jsonCopy.cpp $(SOURCE_FILES) -o $(BINARY_DIR)/jsonCopyTest.out 17 | 18 | lang_test: test_lang.cpp 19 | g++ -std=c++2a -ggdb -o0 test_lang.cpp -o $(BINARY_DIR)/langTest.out -------------------------------------------------------------------------------- /bin/resource/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sign up 6 | 7 | 8 |
9 |
10 |
注册
11 |
12 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /haha_web/base/EncodeUtil.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_ENCODEUTIL_H__ 2 | #define __HAHA_ENCODEUTIL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "base/util.h" 10 | 11 | namespace haha{ 12 | 13 | namespace EncodeUtil{ 14 | 15 | unsigned int murmurHash2(const std::string& s); 16 | 17 | /* 18 | 字符'a'-'z','A'-'Z','0'-'9','.','-','*'和'_' 都不被编码,维持原值; 19 | 空格' '被转换为加号'+'。 20 | 其他每个字节都被表示成"%XY"的格式,X和Y分别代表一个十六进制位。编码为UTF-8。 21 | */ 22 | 23 | std::string urlDecode(const char *, size_t); 24 | std::string urlDecode(std::string_view); 25 | std::string urlDecode(const char *); 26 | 27 | std::string urlEncode(const char *, size_t); 28 | std::string urlEncode(std::string_view); 29 | std::string urlEncode(const char *); 30 | 31 | } 32 | 33 | } 34 | 35 | #endif -------------------------------------------------------------------------------- /haha_web/net/InetAddress.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_INETADDRESS_H__ 2 | #define __HAHA_INETADDRESS_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace haha{ 9 | 10 | class InetAddress{ 11 | public: 12 | explicit InetAddress(uint16_t port = 0); 13 | explicit InetAddress(sockaddr_in si) : sockin_(si) {} 14 | explicit InetAddress(const std::string &ip, uint16_t port = 0); 15 | 16 | std::string getIp() const noexcept; 17 | uint16_t getPort() const noexcept { return ::htons(sockin_.sin_port); } 18 | sockaddr *getSockAddr() const noexcept { return (struct sockaddr *)&sockin_; } 19 | std::string toString() const { 20 | return getIp() + ":" + std::to_string(getPort()); 21 | } 22 | 23 | private: 24 | sockaddr_in sockin_; 25 | }; 26 | 27 | } 28 | 29 | #endif -------------------------------------------------------------------------------- /haha_web/net/InetAddress.cpp: -------------------------------------------------------------------------------- 1 | #include "InetAddress.h" 2 | #include 3 | #include "string.h" 4 | 5 | namespace haha{ 6 | 7 | InetAddress::InetAddress(uint16_t port) { 8 | ::memset(&sockin_, 0, sizeof(sockin_)); 9 | sockin_.sin_port = ::htons(port); 10 | sockin_.sin_family = AF_INET; 11 | sockin_.sin_addr.s_addr = htonl(INADDR_ANY); 12 | } 13 | 14 | InetAddress::InetAddress(const std::string &ip, uint16_t port) { 15 | ::memset(&sockin_, 0, sizeof(sockin_)); 16 | sockin_.sin_port = ::htons(port); 17 | sockin_.sin_family = AF_INET; 18 | ::inet_pton(AF_INET, ip.data(), &sockin_.sin_addr); 19 | } 20 | 21 | std::string InetAddress::getIp() const noexcept { 22 | char buf[64]; 23 | ::inet_ntop(AF_INET, &sockin_.sin_addr, buf, sizeof(sockin_)); 24 | return std::string(buf); 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /haha_web/net/EventLoopThread.cpp: -------------------------------------------------------------------------------- 1 | #include "EventLoopThread.h" 2 | #include "log/Log.h" 3 | 4 | namespace haha{ 5 | 6 | EventLoopThread::EventLoopThread(const std::string& name) 7 | :exiting_(false) 8 | ,loop_ok_(false) 9 | ,loop_(nullptr) 10 | ,thread_(std::make_unique(std::bind(&EventLoopThread::work, this), name)) 11 | { 12 | while(!loop_ok_); 13 | } 14 | 15 | EventLoopThread::~EventLoopThread(){ 16 | exiting_ = true; 17 | loop_->quit(); 18 | if(thread_->joinable()){ 19 | thread_->join(); 20 | } 21 | HAHA_LOG_DEBUG(HAHA_LOG_ASYNC_FILE_ROOT()) << "~EventLoopThread"; 22 | } 23 | 24 | void EventLoopThread::work(){ 25 | HAHA_LOG_DEBUG(HAHA_LOG_ASYNC_FILE_ROOT()) << "start work"; 26 | loop_ = std::make_shared(); 27 | loop_ok_ = true; 28 | loop_->loop(5); 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /haha_web/base/util.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_UTIL_H__ 2 | #define __HAHA_UTIL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace haha{ 14 | 15 | // 十六进制数转十进制数 16 | unsigned char hex2dec(char); 17 | 18 | // 十进制数转十六进制数 19 | char dec2hex(char); 20 | 21 | // 十六进制字符串转为int,如果转换失败返回-1 22 | int hexString2Int(const std::string& hexStr); 23 | int hexString2Int(std::string_view hexStr); 24 | 25 | // int转十六进制字符串 26 | std::string int2HexString(int dec); 27 | 28 | void toLowers(char *, size_t); 29 | void toUppers(char *, size_t); 30 | std::string toLowers(const std::string &); 31 | std::string toUppers(const std::string &); 32 | 33 | bool stringCaseCmp(const std::string &, const std::string &); 34 | 35 | pid_t GetThreadId(); 36 | 37 | } 38 | 39 | #endif -------------------------------------------------------------------------------- /tests/test_thread.cc: -------------------------------------------------------------------------------- 1 | #include "../haha/include/base/Thread.h" 2 | #include "../haha/include/base/Mutex.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int count = 0; 9 | haha::CASLock mutex_; 10 | 11 | void doWork(){ 12 | haha::CASLock::RAIILock lock(mutex_); 13 | usleep(5000); 14 | ++count; 15 | std::cout << haha::Thread::getCurrentThreadName() << std::endl; 16 | } 17 | 18 | int main(){ 19 | std::vector> threads; 20 | for(int i = 0; i < 100; ++i){ 21 | std::shared_ptr t = std::make_shared(doWork); 22 | threads.push_back(t); 23 | } 24 | 25 | std::cout << haha::Thread::getThreadCount() << std::endl; 26 | 27 | for(int i = 0; i < threads.size(); ++i){ 28 | threads[i]->join(); 29 | } 30 | 31 | std::cout << count << std::endl; 32 | 33 | return 0; 34 | } -------------------------------------------------------------------------------- /bin/resource/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "(gdb) 启\t动", 6 | "type": "cppdbg", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/bin/parseTest.out", 9 | "args": [], 10 | "stopAtEntry": false, 11 | "cwd": "${workspaceFolder}/bin", 12 | "environment": [], 13 | "externalConsole": false, 14 | "MIMode": "gdb", 15 | "setupCommands": [ 16 | { 17 | "description": "为 gdb 启用整齐打印", 18 | "text": "-enable-pretty-printing", 19 | "ignoreFailures": true 20 | }, 21 | { 22 | "description": "将反汇编风格设置为 Intel", 23 | "text": "-gdb-set disassembly-flavor intel", 24 | "ignoreFailures": true 25 | } 26 | ] 27 | } 28 | 29 | ] 30 | } -------------------------------------------------------------------------------- /haha_web/haha_json/bin/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "(gdb) 启\t动", 6 | "type": "cppdbg", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/bin/parseTest.out", 9 | "args": [], 10 | "stopAtEntry": false, 11 | "cwd": "${workspaceFolder}/bin", 12 | "environment": [], 13 | "externalConsole": false, 14 | "MIMode": "gdb", 15 | "setupCommands": [ 16 | { 17 | "description": "为 gdb 启用整齐打印", 18 | "text": "-enable-pretty-printing", 19 | "ignoreFailures": true 20 | }, 21 | { 22 | "description": "将反汇编风格设置为 Intel", 23 | "text": "-gdb-set disassembly-flavor intel", 24 | "ignoreFailures": true 25 | } 26 | ] 27 | } 28 | 29 | ] 30 | } -------------------------------------------------------------------------------- /tests/test_timerheap.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "TimerHeap.h" 7 | 8 | int main(){ 9 | haha::TimerHeap heap; 10 | std::vector nums; 11 | for(int i = 0; i < 1000; ++i){ 12 | nums.push_back(i); 13 | } 14 | 15 | int cnt = 1000; 16 | while(cnt--){ 17 | std::random_shuffle(nums.begin(), nums.end()); 18 | for(int i = 0; i < nums.size(); ++i){ 19 | heap.push(haha::Timer( 20 | i, 21 | haha::TimeStamp(nums[i]), 22 | nullptr 23 | )); 24 | } 25 | while(!heap.empty()){ 26 | auto t = heap.top(); 27 | // std::cout << t.expire.microsecond() << std::endl; 28 | heap.pop(); 29 | if(!heap.empty()){ 30 | assert(t < heap.top()); 31 | } 32 | } 33 | std::cout << "ok" << std::endl; 34 | } 35 | 36 | return 0; 37 | } -------------------------------------------------------------------------------- /haha_web/net/EventLoopThreadPool.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_EVENTLOOPTHREADPOOL_H__ 2 | #define __HAHA_EVENTLOOPTHREADPOOL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "config/config.h" 8 | #include "base/noncopyable.h" 9 | #include "EventLoop.h" 10 | #include "EventLoopThread.h" 11 | 12 | namespace haha{ 13 | 14 | class EventLoopThreadPool : noncopyable{ 15 | public: 16 | static EventLoopThreadPool& getInstance(){ 17 | static EventLoopThreadPool pool( 18 | config::GET_CONFIG("EventLoopThreadPool.threadNum", std::thread::hardware_concurrency())); 19 | return pool; 20 | } 21 | void start(); 22 | bool isStarted() const { return started_; } 23 | EventLoop::ptr getBaseLoop() const { return baseLoop_; } 24 | EventLoop::ptr getNextLoop(); 25 | 26 | private: 27 | EventLoopThreadPool(size_t ThreadNum); 28 | 29 | private: 30 | bool started_; 31 | size_t num_thread_; 32 | EventLoop::ptr baseLoop_; 33 | std::vector loops_; 34 | std::vector> threads_; 35 | int next_; 36 | }; 37 | 38 | } 39 | 40 | #endif -------------------------------------------------------------------------------- /haha_web/base/ThreadPool.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_THREADPOOL_H__ 2 | #define __HAHA_THREADPOOL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "base/noncopyable.h" 10 | #include "base/Thread.h" 11 | #include "base/Mutex.h" 12 | #include "base/ConditionVariable.h" 13 | 14 | namespace haha{ 15 | 16 | using Task = std::function; 17 | 18 | class ThreadPool : noncopyable{ 19 | public: 20 | static ThreadPool& getInstance(){ 21 | static ThreadPool pool; 22 | return pool; 23 | } 24 | ~ThreadPool(); 25 | void addTask(const Task &task); 26 | void start(); 27 | bool isRunning() const {return isRunning_;} 28 | size_t getThreadNum() const {return num_thread_;} 29 | 30 | private: 31 | ThreadPool(size_t ThreadNum = 4): num_thread_(ThreadNum){} 32 | void run(); 33 | 34 | private: 35 | std::queue taskPool_; 36 | std::vector threads_; 37 | size_t num_thread_; 38 | std::atomic isRunning_; 39 | MutexLock mtx_; 40 | ConditionVariable cond_; 41 | }; 42 | 43 | } 44 | 45 | #endif -------------------------------------------------------------------------------- /haha_web/net/EventLoopThreadPool.cpp: -------------------------------------------------------------------------------- 1 | #include "EventLoopThreadPool.h" 2 | 3 | namespace haha{ 4 | 5 | EventLoopThreadPool::EventLoopThreadPool(size_t ThreadNum) 6 | :started_(false) 7 | ,num_thread_(ThreadNum) 8 | ,baseLoop_(std::make_shared()) 9 | ,next_(0) 10 | { 11 | 12 | } 13 | 14 | void EventLoopThreadPool::start(){ 15 | assert(!started_); 16 | baseLoop_->assertInLoopThread(); 17 | 18 | started_ = true; 19 | 20 | for(size_t i = 0; i < num_thread_; ++i){ 21 | auto t = std::make_unique(); 22 | HAHA_LOG_INFO(HAHA_LOG_ASYNC_FILE_ROOT()) << "create thread: " << t->getName(); 23 | loops_.push_back(t->getLoop()); 24 | threads_.push_back(std::move(t)); 25 | } 26 | } 27 | 28 | EventLoop::ptr EventLoopThreadPool::getNextLoop(){ 29 | baseLoop_->assertInLoopThread(); 30 | EventLoop::ptr loop = baseLoop_; 31 | 32 | if(!loops_.empty()){ 33 | loop = loops_[next_]; 34 | ++next_; 35 | if (static_cast(next_) >= loops_.size()) 36 | { 37 | next_ = 0; 38 | } 39 | } 40 | 41 | return loop; 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /tests/test_stringview.cc: -------------------------------------------------------------------------------- 1 | #include "base/stringView.h" 2 | 3 | int main(){ 4 | const char* str = "hello world!"; 5 | haha::StringView view(str, 5); 6 | haha::StringView view1(str, strlen(str)); 7 | std::cout << view << std::endl; 8 | std::cout << view1 << std::endl; 9 | std::cout << view.starts_with("hello") << std::endl; 10 | std::cout << view.starts_with("worldd") << std::endl; 11 | std::cout << view1.ends_with("world!") << std::endl; 12 | haha::StringView view2(view1); 13 | view2.remove_prefix(4); 14 | std::cout << view1 << std::endl; 15 | std::cout << view2 << std::endl; 16 | haha::StringView view3 = view1.substr(2); 17 | std::cout << view3 << std::endl; 18 | std::cout << view3.find("wor") << std::endl; 19 | std::cout << view3.find("wor", 4) << std::endl; 20 | std::cout << view3.find("wor", 5) << std::endl; 21 | 22 | char buffer[100] = "you are very \r\n good haha \r \n xixi\r\n\r\n"; 23 | haha::StringView view4(buffer, strlen(buffer)); 24 | haha::StringView view5(buffer, buffer+10); 25 | std::cout << view4.find("\r\n") << std::endl; 26 | std::cout << view5.find("\r\n") << std::endl; 27 | return 0; 28 | } -------------------------------------------------------------------------------- /haha_web/http/HttpSession.cpp: -------------------------------------------------------------------------------- 1 | #include "http/HttpSession.h" 2 | 3 | namespace haha{ 4 | 5 | static const int INTERVAL = 180; // 三分钟会话过期 6 | 7 | HttpSession::HttpSession() 8 | :id_(uuid::generate_threadSafe()) 9 | ,status_(Status::New) 10 | ,interval_(INTERVAL){ 11 | } 12 | 13 | RetOption HttpSessionManager::getSession(const std::string &id) const{ 14 | ReadWriteLock::RAIIReadLock lock(mutex_); 15 | auto it = sessions_.find(id); 16 | if(it == sessions_.end()){ 17 | return {nullptr, false}; 18 | } 19 | return {it->second, true}; 20 | } 21 | 22 | void HttpSessionManager::addSession(const std::string &id, HttpSession::ptr session){ 23 | ReadWriteLock::RAIIWriteLock lock(mutex_); 24 | sessions_[id] = session; 25 | } 26 | 27 | HttpSession::ptr HttpSessionManager::newSession(){ 28 | HttpSession::ptr session = std::make_shared(); 29 | ReadWriteLock::RAIIWriteLock lock(mutex_); 30 | sessions_[session->getId()] = session; 31 | return session; 32 | } 33 | 34 | void HttpSessionManager::delSession(const std::string &id){ 35 | ReadWriteLock::RAIIWriteLock lock(mutex_); 36 | sessions_.erase(id); 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /haha_web/haha_json/tests/test_lang.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class A{ 9 | public: 10 | A(const std::string &name = ""):name_(name){ std::cout << "construct A:" << name_ << std::endl; } 11 | A(const A& a) { 12 | name_ = a.name_; 13 | std::cout << "copy construct A:" << name_ << std::endl; 14 | } 15 | ~A(){ std::cout << "deconstruct A:" << name_ << std::endl; } 16 | private: 17 | std::string name_; 18 | }; 19 | 20 | void test_utf(){ 21 | char utf_str[6] = {0}; 22 | std::string_view view("\u7814ss"); 23 | 24 | sprintf(utf_str, "u%04x", *(unsigned char *)view.data()); 25 | 26 | printf("%s\n", view.data()); 27 | printf("%s\n", utf_str); 28 | } 29 | 30 | void test_shared_ptr(){ 31 | A a1("a1"); 32 | std::shared_ptr p1 = std::make_shared(a1); 33 | std::shared_ptr p2(new A("a2")); 34 | auto a2 = *p2; 35 | } 36 | 37 | void test_fstream(){ 38 | std::ofstream ofs("./test_fstream.txt"); 39 | ofs << "hehe\n"; 40 | } 41 | 42 | int main(){ 43 | // test_utf(); 44 | // test_shared_ptr(); 45 | test_fstream(); 46 | return 0; 47 | } -------------------------------------------------------------------------------- /webbench1.5/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS?= -Wall -ggdb -W -O0 2 | CC?= gcc 3 | LIBS?= 4 | LDFLAGS?= 5 | PREFIX?= /usr/local 6 | VERSION=1.5 7 | TMPDIR=/tmp/webbench-$(VERSION) 8 | 9 | all: webbench tags 10 | 11 | tags: *.c 12 | -ctags *.c 13 | 14 | install: webbench 15 | install -s webbench $(DESTDIR)$(PREFIX)/bin 16 | install -m 644 webbench.1 $(DESTDIR)$(PREFIX)/man/man1 17 | install -d $(DESTDIR)$(PREFIX)/share/doc/webbench 18 | install -m 644 debian/copyright $(DESTDIR)$(PREFIX)/share/doc/webbench 19 | install -m 644 debian/changelog $(DESTDIR)$(PREFIX)/share/doc/webbench 20 | 21 | webbench: webbench.c webbench.o Makefile 22 | $(CC) $(CFLAGS) $(LDFLAGS) -o webbench webbench.o $(LIBS) 23 | 24 | clean: 25 | -rm -f *.o webbench *~ core *.core tags 26 | 27 | tar: clean 28 | -debian/rules clean 29 | rm -rf $(TMPDIR) 30 | install -d $(TMPDIR) 31 | cp -p Makefile webbench.c socket.c webbench.1 $(TMPDIR) 32 | install -d $(TMPDIR)/debian 33 | -cp -p debian/* $(TMPDIR)/debian 34 | ln -sf debian/copyright $(TMPDIR)/COPYRIGHT 35 | ln -sf debian/changelog $(TMPDIR)/ChangeLog 36 | -cd $(TMPDIR) && cd .. && tar cozf webbench-$(VERSION).tar.gz webbench-$(VERSION) 37 | 38 | webbench.o: webbench.c socket.c Makefile 39 | 40 | .PHONY: clean install all tar 41 | -------------------------------------------------------------------------------- /haha_web/base/ThreadPool.cpp: -------------------------------------------------------------------------------- 1 | #include "base/ThreadPool.h" 2 | 3 | 4 | namespace haha{ 5 | 6 | ThreadPool::~ThreadPool(){ 7 | isRunning_ = false; 8 | cond_.notify_all(); 9 | for(auto &t : threads_){ 10 | if(t->joinable()){ 11 | t->join(); 12 | } 13 | } 14 | } 15 | 16 | void ThreadPool::addTask(const Task &task){ 17 | if(!isRunning_)return; 18 | 19 | { 20 | LockGuard lock(mtx_); 21 | taskPool_.emplace(task); 22 | } 23 | cond_.notify_one(); 24 | } 25 | 26 | void ThreadPool::start(){ 27 | isRunning_ = true; 28 | for(size_t i = 0; i < num_thread_; ++i){ 29 | Thread::ptr p = std::make_shared(std::bind(&ThreadPool::run, this)); 30 | threads_.emplace_back(p); 31 | } 32 | } 33 | 34 | void ThreadPool::run(){ 35 | Task task; 36 | while(isRunning_){ 37 | { 38 | // LockGuard lock_gaurd(mtx_); 39 | MutexLock::RAIILock lock_gaurd(mtx_); 40 | while(isRunning_ && taskPool_.empty()){ 41 | cond_.wait(mtx_); 42 | } 43 | if(!isRunning_)break; 44 | task = taskPool_.front(); 45 | taskPool_.pop(); 46 | } 47 | task(); 48 | } 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /haha_web/haha_json/tests/test_jsonCopy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "json.h" 7 | 8 | namespace JSON = haha::json; 9 | 10 | int main(){ 11 | std::string str = "{\"check\": 123.5e10, \"2893h\":\"ok\", \"arr\": [\"sd\", null]}"; 12 | 13 | JSON::Json json; 14 | 15 | bool ok = true; 16 | ok = json.fromString(str); 17 | 18 | auto obj1 = json.getValuePtr(); 19 | 20 | std::cout << obj1->toString() << std::endl; 21 | 22 | auto fuck = JSON::pointer_cast(obj1); 23 | JSON::JsonObject::ptr obj2(new JSON::JsonObject(*fuck)); 24 | 25 | std::cout << JSON::getJsonTypeName(obj2->getType()) << std::endl; 26 | std::cout << obj2->toString() << std::endl; 27 | 28 | JSON::JsonObject obj3; 29 | obj3 = *obj2; 30 | std::cout << obj3.toString() << std::endl; 31 | 32 | obj2->add("nothing"); 33 | auto cnt = obj2->del("arr"); 34 | std::cout << cnt << std::endl; 35 | std::cout << obj2->toString() << std::endl; 36 | std::cout << obj3.toString() << std::endl; 37 | 38 | json.fromString("\"123\""); 39 | auto p = json.getValuePtr(); 40 | 41 | JSON::JsonString hehe(*p); 42 | 43 | std::cout << hehe.toString() << std::endl; 44 | 45 | return 0; 46 | } -------------------------------------------------------------------------------- /haha_web/log/LoggerManager.cpp: -------------------------------------------------------------------------------- 1 | #include "LoggerManager.h" 2 | 3 | namespace haha 4 | { 5 | 6 | namespace log 7 | { 8 | 9 | // LoggerManager::LoggerManager() 10 | // { 11 | // sync_root_ = std::make_shared("sync_root"); 12 | // sync_root_->addAppender(LogAppender::ptr(new FileSyncLogAppender(default_sync_log_file, default_roll_size))); 13 | 14 | // sync_stdout_ = std::make_shared("sync_stdout"); 15 | // sync_stdout_->addAppender(LogAppender::ptr(new StdoutSyncLogAppender)); 16 | 17 | // async_root_ = std::make_shared("async_root", default_flush_interval); 18 | // async_root_->addAppender(LogAppender::ptr(new FileAsyncLogAppender(default_async_log_file, default_roll_size))); 19 | 20 | // loggers_[sync_root_->getName()] = sync_root_; 21 | // loggers_[async_root_->getName()] = async_root_; 22 | // loggers_[sync_stdout_->getName()] = sync_stdout_; 23 | // } 24 | 25 | Logger::ptr LoggerManager::getLogger(const std::string& name) { 26 | static const bool log_open = haha::config::GET_CONFIG("log.open",true); 27 | if(log_open == false){ 28 | return nullptr; 29 | } 30 | 31 | MutexType::RAIILock lock(mutex_); 32 | auto it = loggers_.find(name); 33 | if(it != loggers_.end()) { 34 | return it->second; 35 | } 36 | return nullptr; 37 | } 38 | 39 | } // namespace log 40 | 41 | } // namespace haha 42 | -------------------------------------------------------------------------------- /haha_web/http/HttpResponse.cpp: -------------------------------------------------------------------------------- 1 | #include "http/HttpResponse.h" 2 | 3 | namespace haha{ 4 | 5 | HttpResponse::HttpResponse(HttpVersion version, TcpConnection::ptr conn) 6 | :conn_(conn) 7 | ,version_(version) 8 | ,statusCode_(HttpStatus::OK) 9 | ,contentLength_(-1) 10 | ,keepAlive_(false) 11 | ,hasSession_(false) 12 | ,isFileBody_(false) 13 | { 14 | buffer_ = conn_->getSender(); 15 | header_.add("Server", "HAHA_WEBSERVER"); 16 | header_.add("Date", TimeStamp::getDate()); 17 | header_.setContentType("application/octet-stream"); 18 | } 19 | 20 | HttpResponse::~HttpResponse(){ 21 | // 响应首行 22 | buffer_->Append(std::string(HttpVersion2Str.at(version_)) + 23 | " " + std::to_string(statusCode_) + " " 24 | + HttpStatus2Str.at(statusCode_) + "\r\n"); 25 | if(contentLength_ == -1){ 26 | if(isFileBody_){ 27 | contentLength_ = std::filesystem::file_size(file_path_); 28 | } 29 | else{ 30 | contentLength_ = body_.size(); 31 | } 32 | header_.setContentLength(std::to_string(contentLength_)); 33 | } 34 | 35 | // 响应头 36 | for(const auto &[k, v] : header_){ 37 | buffer_->Append(k + ": " + v + "\r\n"); 38 | } 39 | buffer_->Append("\r\n"); 40 | 41 | if(!isFileBody_){ 42 | // 普通内容,填入buffer中 43 | buffer_->Append(body_); 44 | } 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /haha_web/http/HttpServer.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_HTTPSERVER_H__ 2 | #define __HAHA_HTTPSERVER_H__ 3 | 4 | #include 5 | #include "net/TcpServer.h" 6 | #include "http/HttpRequest.h" 7 | #include "http/HttpResponse.h" 8 | #include "http/HttpSession.h" 9 | #include "http/Servlet.h" 10 | #include "base/EncodeUtil.h" 11 | #include "base/TimeStamp.h" 12 | 13 | namespace haha{ 14 | 15 | class HttpServer : public TcpServer{ 16 | public: 17 | HttpServer(); 18 | void setServletDispatcher(ServletDispatcher::ptr servletDispatcher){ 19 | servletDispatcher_ = servletDispatcher; 20 | } 21 | void addServlet(const std::string &uri, Servlet::servletFunc func){ 22 | servletDispatcher_->addServlet(uri, std::move(func)); 23 | } 24 | void addServlet(const std::string &uri, Servlet::ptr servlet){ 25 | servletDispatcher_->addServlet(uri, servlet); 26 | } 27 | 28 | protected: 29 | MESSAGE_STATUS onMessage(TcpConnection::ptr) override; 30 | bool onCreateConnection(TcpConnection::ptr) override; 31 | bool onCloseConntection(TcpConnection::ptr) override; 32 | 33 | void beforeServlet(HttpRequest::ptr, HttpResponse::ptr); 34 | void afterServlet(HttpRequest::ptr, HttpResponse::ptr); 35 | 36 | void handleSessionTimeout(HttpSession::ptr session); 37 | 38 | private: 39 | HttpSessionManager *sessionManger_; 40 | ServletDispatcher::ptr servletDispatcher_; 41 | }; 42 | 43 | } 44 | 45 | #endif -------------------------------------------------------------------------------- /haha_web/http/HttpUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "http/HttpUtil.h" 2 | #include 3 | 4 | namespace haha{ 5 | /* ************************************************解析键值对************************************************ */ 6 | 7 | void parseKeyValue(const std::string& src, const std::string& join, const std::string& split, 8 | HttpMap &map, bool urldecode){ 9 | // size_t i; 10 | // size_t j = 0; 11 | std::string_view view; 12 | std::string decoded_src; 13 | if(urldecode){ 14 | decoded_src = EncodeUtil::urlDecode(src); 15 | view = decoded_src; 16 | } 17 | else{ 18 | view = src; 19 | } 20 | 21 | while(!view.empty()){ 22 | size_t pos = view.find(split); 23 | std::string_view kv = view.substr(0,pos); 24 | size_t pos1 = kv.find(join); 25 | if(pos1 == kv.npos){ 26 | // 缺少分隔符,解析失败,停止解析后续内容 27 | break; 28 | } 29 | std::string_view k = kv.substr(0, pos1); 30 | std::string_view v = kv.substr(pos1+join.size()); 31 | map.add(std::string(k), std::string(v)); 32 | if(pos == view.npos){ 33 | break; 34 | } 35 | else{ 36 | pos += split.size(); 37 | while(pos < view.size() && view[pos] == ' '){ 38 | ++pos; 39 | } 40 | } 41 | view.remove_prefix(pos); 42 | } 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /bin/ssl/cert.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID3TCCAsWgAwIBAgIURYwRznUKl4I2ZkhbPI4YeeqH4eEwDQYJKoZIhvcNAQEL 3 | BQAwfjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJTWjEOMAwG 4 | A1UECgwFdmlob28xDDAKBgNVBAsMA2RldjEVMBMGA1UEAwwMaGFoYV93ZWIuY29t 5 | MSAwHgYJKoZIhvcNAQkBFhExMTA3NjgyNDUyQHFxLmNvbTAeFw0yMjA4MTkxMzIx 6 | MTFaFw0yMzA4MTkxMzIxMTFaMH4xCzAJBgNVBAYTAkNOMQswCQYDVQQIDAJHRDEL 7 | MAkGA1UEBwwCU1oxDjAMBgNVBAoMBXZpaG9vMQwwCgYDVQQLDANkZXYxFTATBgNV 8 | BAMMDGhhaGFfd2ViLmNvbTEgMB4GCSqGSIb3DQEJARYRMTEwNzY4MjQ1MkBxcS5j 9 | b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCqSGurhAWgmV1Z7s8 10 | f/J8X12aGPtcbEAvFF8XxLRt7iqzki6AoD/g/anvcMEEgGck55d9JQi3sYAKRN1h 11 | LJdk1zi1RYfUlBhrl0b0SYF94YQ1NIMtawXYRehzMlY4z39GcY1RdNdBG9QHePer 12 | Gmp4e7AZMusx54gqmJB5tSd5I//3ak/CW3AwlzFAbWOBZUMqm3TIOFPfnyeicWRv 13 | y0yRrTE8FCzcxqGaf+DEjmEfjDUyKTY6+FIWezPzGAX2ZpgfkGXLrtdOSJBOb/uw 14 | JKifajo8yfUYAk0Zndyt572qAjgYyUU9TiS6T6/wXBQBMAov5XG8MoOVZmqyiVQN 15 | ivCDAgMBAAGjUzBRMB0GA1UdDgQWBBQDc7LQEoACQeG/lfrnkyuJes71xDAfBgNV 16 | HSMEGDAWgBQDc7LQEoACQeG/lfrnkyuJes71xDAPBgNVHRMBAf8EBTADAQH/MA0G 17 | CSqGSIb3DQEBCwUAA4IBAQAclZjop+tJWxWQCLeUO+NElLzNLRXwRNBycp4szkzN 18 | cTQpRFtSOLXpNzhExnIzuZV7SuhiNWGbAc9dHds9v+FmYNLOvnjezBs81e1JXhmx 19 | A8cxtvIENKy7uvoKFY9TsfLDSocnqvAzR4a4qfkolHz0fYmawfnjqktZU7GfrAH7 20 | SKrRthAViJUgvAsyqBJJVAhwecy8AA11tQahHBnaPjjfrZ9L1iScs1V5VO5Mhzzr 21 | Y95ravXqh8nz5Rde4P3EocymoE2rlmXZDsFluAi6xL+mjOekAePAVLgRLrzpnAO0 22 | n4/rTFchbdRMqWgek6lxkmacHj8xjK1HScsMX/jEfdtQ 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /haha_web/haha_json/haha_json/json.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_JSON_JSON_H__ 2 | #define __HAHA_JSON_JSON_H__ 3 | 4 | #include "jsonValue.h" 5 | #include "jsonTypeCast.h" 6 | #include "jsonUtil.h" 7 | #include "jsonBuffer.h" 8 | #include 9 | #include 10 | #include 11 | 12 | namespace haha 13 | { 14 | 15 | namespace json 16 | { 17 | 18 | JsonNode::ptr parse(const char *str); 19 | JsonNode::ptr parse(const std::string &str); 20 | 21 | JsonNode::ptr parse(std::string_view str); 22 | JsonNode::ptr parse_value(std::string_view &str); 23 | JsonNode::ptr parse_string(std::string_view &str); 24 | JsonNode::ptr parse_number(std::string_view &str); 25 | JsonNode::ptr parse_array(std::string_view &str); 26 | JsonNode::ptr parse_object(std::string_view &str); 27 | 28 | JsonNode::ptr fromString(const char* str); 29 | JsonNode::ptr fromString(const std::string &str); 30 | 31 | JsonNode::ptr fromFile(const char* filePath); 32 | JsonNode::ptr fromFile(const std::string &filePath); 33 | 34 | void toFile(JsonNode::ptr, const char* filePath, 35 | const PrintFormatter &fmter = {}); 36 | void toFile(JsonNode::ptr, const std::string &filePath, 37 | const PrintFormatter &fmter = {}); 38 | 39 | void toFile(JsonNode::ptr, const char* filePath, 40 | JsonFormatType t, int indent); 41 | void toFile(JsonNode::ptr, const std::string &filePath, 42 | JsonFormatType t, int indent); 43 | 44 | } // namespace json 45 | 46 | 47 | } // namespace haha 48 | 49 | 50 | #endif -------------------------------------------------------------------------------- /haha_web/base/Thread.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_THREAD_H__ 2 | #define __HAHA_THREAD_H__ 3 | 4 | #include "noncopyable.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace haha{ 13 | 14 | class Thread : noncopyable{ 15 | public: 16 | typedef std::shared_ptr ptr; 17 | typedef std::function Task; 18 | typedef pthread_t ID; 19 | 20 | explicit Thread(Task task, const std::string& name = ""); 21 | ~Thread(); 22 | 23 | void start(); 24 | void join(); // return pthread_join() 25 | bool joinable() { return !joined_ && thread_; } 26 | const std::string& name() const { return name_; } 27 | 28 | static const Thread* getCurrentThread(); 29 | static const ID getCurrentThreadId(); 30 | static const std::string& getCurrentThreadName(); 31 | 32 | bool isStarted() const { return started_; } 33 | static const int getThreadCount() { return numThread_; } 34 | 35 | private: 36 | void setDefaultName(); 37 | static void* run(void*); 38 | 39 | private: 40 | ID thread_; // 线程号 41 | bool joined_; // 是否要join 42 | Task task_; // 任务 43 | std::string name_; // 名字 44 | volatile bool started_; // 已启动 45 | static std::atomic numThread_; // 线程数量 46 | }; 47 | 48 | } 49 | 50 | #endif -------------------------------------------------------------------------------- /haha_web/http/HttpCookie.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_HTTPCOOKIE_H__ 2 | #define __HAHA_HTTPCOOKIE_H__ 3 | 4 | #include 5 | #include "HttpUtil.h" 6 | 7 | namespace haha{ 8 | 9 | class HttpCookie : public HttpMap{ 10 | public: 11 | HttpCookie(const std::string &str = ""); 12 | std::string toString() const; 13 | 14 | void setExpires(const std::string &expires) { expires_ = expires; } 15 | const std::string &getExpires() const noexcept { return expires_; } 16 | void setDomain(const std::string &domain) { domain_ = domain; } 17 | const std::string &getDomain() const noexcept { return domain_; } 18 | void setPath(const std::string &path) { path_ = path; } 19 | const std::string &getPath() const noexcept { return path_; } 20 | void setMaxAge(int second) { maxAge_ = second; } 21 | int getMaxAge() const noexcept { return maxAge_; } 22 | void setSessionId(const std::string &ssid) { sessionId_ = ssid; } 23 | const std::string &getSessionId() const { return sessionId_; } 24 | 25 | void setSecure(bool secure) { secure_ = secure; } 26 | bool isSecure() const noexcept { return secure_; } 27 | void setHttpOnly(bool httponly) { httpOnly_ = httponly; } 28 | bool isHttpOnly() const noexcept { return httpOnly_; } 29 | private: 30 | std::string expires_; 31 | std::string domain_; 32 | std::string path_; 33 | std::string sessionId_; 34 | int maxAge_; 35 | bool secure_; 36 | bool httpOnly_; 37 | }; 38 | 39 | } 40 | 41 | #endif -------------------------------------------------------------------------------- /haha_web/base/ProcessInfo.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_PROCESSINFO_H__ 2 | #define __HAHA_PROCESSINFO_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "base/FileUtil.h" 11 | #include "base/Thread.h" 12 | #include "base/TimeStamp.h" 13 | 14 | namespace haha{ 15 | 16 | namespace ProcessInfo 17 | { 18 | pid_t pid(); 19 | std::string pidString(); 20 | uid_t uid(); 21 | std::string username(); 22 | uid_t euid(); 23 | TimeStamp startTime(); 24 | int clockTicksPerSecond(); 25 | int pageSize(); 26 | bool isDebugBuild(); // constexpr 27 | 28 | std::string hostname(); 29 | std::string procname(); 30 | std::string_view procname(const std::string& stat); 31 | 32 | /// read /proc/self/status 33 | std::string procStatus(); 34 | 35 | /// read /proc/self/stat 36 | std::string procStat(); 37 | 38 | /// read /proc/self/task/tid/stat 39 | std::string threadStat(); 40 | 41 | /// readlink /proc/self/exe 42 | std::string exePath(); 43 | 44 | int openedFiles(); 45 | int maxOpenFiles(); 46 | 47 | struct CpuTime 48 | { 49 | double userSeconds; 50 | double systemSeconds; 51 | 52 | CpuTime() : userSeconds(0.0), systemSeconds(0.0) { } 53 | 54 | double total() const { return userSeconds + systemSeconds; } 55 | }; 56 | CpuTime cpuTime(); 57 | 58 | int numThreads(); 59 | std::vector threads(); 60 | } // namespace ProcessInfo 61 | 62 | } 63 | 64 | #endif -------------------------------------------------------------------------------- /haha_web/http/HttpTest.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_HTTPTEST_H__ 2 | #define __HAHA_HTTPTEST_H__ 3 | 4 | #include 5 | #include 6 | #include "http/HttpUtil.h" 7 | #include "base/util.h" 8 | #include "base/EncodeUtil.h" 9 | 10 | namespace haha{ 11 | 12 | // 测试专用 13 | namespace test{ 14 | 15 | std::string genRandomStr(size_t n); 16 | 17 | std::string genRandomKVStr(size_t n, const std::string &join, const std::string &split, bool urlencode=false); 18 | 19 | std::string genRandomKVStrs(size_t n, const std::string &join, const std::string &split, bool urlencode=false); 20 | 21 | std::vector randomSplitStr(const std::string &str, size_t limit = -1); 22 | 23 | class RandomHttpRequestString{ 24 | public: 25 | RandomHttpRequestString(); 26 | 27 | std::string create_random_RequestLine(); 28 | 29 | std::string create_random_RequestHeader(const std::string &requestBody, const std::string &contentType, bool chunked); 30 | 31 | std::tuple create_random_RequestBody(); 32 | 33 | std::string getRequestLine() const { return requestLine_; } 34 | std::string getRequestHeader() const { return requestHeader_; } 35 | std::string getRequestBody() const { return requestBody_; } 36 | std::string getRequest() const { return request_;} 37 | 38 | std::string getContentType() const { return contentType_; } 39 | 40 | private: 41 | std::string requestLine_; 42 | std::string requestHeader_; 43 | std::string requestBody_; 44 | std::string request_; 45 | std::string contentType_; 46 | bool chunked_; 47 | }; 48 | 49 | } 50 | 51 | } 52 | 53 | #endif -------------------------------------------------------------------------------- /haha_web/net/Epoller.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_EPOLLER_H__ 2 | #define __HAHA_EPOLLER_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | // #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "base/noncopyable.h" 15 | #include "Channel.h" 16 | 17 | namespace haha { 18 | 19 | class Channel; 20 | 21 | static constexpr auto kServerEvent = EPOLLRDHUP; 22 | static constexpr auto kConnectionEvent = EPOLLET | EPOLLONESHOT | EPOLLRDHUP; 23 | 24 | class Epoller : noncopyable { 25 | 26 | typedef std::vector EventList; 27 | typedef std::vector ChannelList; 28 | 29 | public: 30 | enum TrigerMod {LT, ET}; 31 | 32 | Epoller(); 33 | ~Epoller(); 34 | 35 | void addEvent(int fd, uint32_t event); 36 | void modEvent(int fd, uint32_t event); 37 | void delEvent(int fd); 38 | void removeAndCloseEvent(int fd); 39 | 40 | void addChannel(Channel* channel); 41 | void modChannel(Channel* channel); 42 | void delChannel(Channel* channel); 43 | 44 | void setTrigerMod(TrigerMod mod) {trigerMod_ = mod;} 45 | TrigerMod getTrigerMod() {return trigerMod_;} 46 | 47 | Channel* getActiveChannel(size_t i); 48 | 49 | int wait(int timeoutMs = -1); 50 | int getEventFd(size_t i); 51 | uint32_t getReturnEvents(size_t i); 52 | 53 | private: 54 | static const int kInitEventListSize = 16; 55 | int epfd_; 56 | EventList events_; 57 | ChannelList activeChannels_; 58 | TrigerMod trigerMod_; 59 | }; 60 | 61 | } 62 | 63 | #endif -------------------------------------------------------------------------------- /haha_web/http/Servlet.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_SERVLET_H__ 2 | #define __HAHA_SERVLET_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "base/noncopyable.h" 10 | #include "base/ReadWriteLock.h" 11 | #include "http/HttpRequest.h" 12 | #include "http/HttpResponse.h" 13 | 14 | namespace haha{ 15 | 16 | class Servlet{ 17 | public: 18 | typedef std::shared_ptr ptr; 19 | typedef std::function servletFunc; 20 | Servlet(servletFunc func):func_(func){} 21 | void handle(HttpRequest::ptr request, HttpResponse::ptr response) { 22 | func_(request, response); 23 | } 24 | static std::string basePage(int code, const std::string &content = "HAHA"); 25 | private: 26 | servletFunc func_; 27 | }; 28 | 29 | class ServletDispatcher{ 30 | public: 31 | ServletDispatcher(); 32 | typedef std::shared_ptr ptr; 33 | typedef std::unordered_map Dispatcher; 34 | void addServlet(const std::string &uri, Servlet::servletFunc); 35 | void addServlet(const std::string &uri, Servlet::ptr); 36 | Servlet::ptr getServlet(const std::string &uri); 37 | // 将请求分发到对应的servlet 38 | void dispatch(HttpRequest::ptr, HttpResponse::ptr, Servlet::servletFunc preprocess, Servlet::servletFunc postprocess); 39 | 40 | private: 41 | // 找到匹配的servlet 42 | Dispatcher::iterator find_match(const std::string& uri); 43 | 44 | private: 45 | Dispatcher dispatcher_; 46 | Servlet::ptr defaultServlet_; 47 | ReadWriteLock mutex_; 48 | }; 49 | 50 | } 51 | 52 | #endif -------------------------------------------------------------------------------- /haha_web/net/EventLoop.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_EVENTLOOP_H__ 2 | #define __HAHA_EVENTLOOP_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "base/Mutex.h" 8 | #include "base/Thread.h" 9 | #include "Channel.h" 10 | #include "Epoller.h" 11 | #include "TimerQueue.h" 12 | 13 | namespace haha{ 14 | 15 | class EventLoop{ 16 | 17 | public: 18 | typedef std::shared_ptr ptr; 19 | typedef std::weak_ptr weak_ptr; 20 | typedef std::function Task; 21 | 22 | EventLoop(); 23 | void loop(int idle_timeout_ = -1); 24 | 25 | void addChannel(Channel* channel); 26 | void modChannel(Channel* channel); 27 | void delChannel(Channel* channel); 28 | 29 | void addTimer(const Timer& timer); 30 | void adjustTimer(const Timer& timer); 31 | void delTimer(const Timer& timer); 32 | 33 | void wakeup(); 34 | void quit(); 35 | 36 | bool isInLoopThread() const { return threadId_ == Thread::getCurrentThreadId(); } 37 | void runInLoop(Task task); 38 | void assertInLoopThread(); 39 | 40 | private: 41 | void handleWakeup(); 42 | void doPendingTasks(); 43 | void queueInLoop(Task task); 44 | 45 | private: 46 | int timeFd_; 47 | int wakeupFd_; 48 | bool quit_; 49 | Epoller epoller_; 50 | std::unique_ptr timeoutChannel_; 51 | std::unique_ptr wakeupChannel_; 52 | std::unique_ptr timerQueue_; 53 | 54 | // MutexLock mutex_; 55 | SpinLock mutex_; 56 | Thread::ID threadId_; 57 | std::vector pendingTasks_; 58 | std::atomic_bool callingPendingTasks_; 59 | }; 60 | 61 | } 62 | 63 | 64 | #endif -------------------------------------------------------------------------------- /haha_web/net/Channel.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_CHANNEL_H__ 2 | #define __HAHA_CHANNEL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "base/Buffer.h" 11 | #include "base/noncopyable.h" 12 | #include "log/Log.h" 13 | 14 | namespace haha{ 15 | 16 | class EventLoop; 17 | 18 | class Channel : noncopyable { 19 | 20 | using ReadCallback = std::function; 21 | using WriteCallback = std::function; 22 | using CloseCallback = std::function; 23 | using ErrorCallback = std::function; 24 | 25 | public: 26 | typedef std::shared_ptr ptr; 27 | 28 | Channel(EventLoop* loop, int fd, bool BlockFd = true); 29 | ~Channel() { 30 | // HAHA_LOG_DEBUG(HAHA_LOG_ASYNC_FILE_ROOT()) << "~Channel close fd: " << fd_; 31 | // ::close(fd_); 32 | } 33 | 34 | void handleEvents(uint32_t revents); 35 | int getFd() const {return fd_;} 36 | uint32_t getEvents() const {return events_;} 37 | void setEvents(uint32_t events) {events_ = events;} 38 | EventLoop* getEventLoop() {return eventloop_;} 39 | bool isBlockFd() {return isBlockFd_;} 40 | 41 | void setReadCallback(ReadCallback readcb) {readCb_ = readcb;} 42 | void setWriteCallback(WriteCallback writecb) {writeCb_ = writecb;} 43 | void setCloseCallback(CloseCallback closecb) {closeCb_ = closecb;} 44 | 45 | private: 46 | int fd_; 47 | uint32_t events_; 48 | bool isBlockFd_; 49 | 50 | EventLoop *eventloop_; 51 | 52 | ReadCallback readCb_; 53 | WriteCallback writeCb_; 54 | CloseCallback closeCb_; 55 | ErrorCallback errorCb_; 56 | }; 57 | 58 | } 59 | 60 | #endif -------------------------------------------------------------------------------- /haha_web/haha_json/tests/test_json.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "json.h" 4 | 5 | namespace JSON = haha::json; 6 | 7 | int main(){ 8 | JSON::JsonNode::ptr js; 9 | 10 | std::string filePath = "../test.json"; // 文件位置自己定 11 | 12 | // 从文件读取 13 | js = JSON::fromFile(filePath); 14 | 15 | // 序列化(转为字符串) 16 | std::cout << js->toString() << std::endl; 17 | 18 | // 类型转换 19 | JSON::JsonObject::ptr obj; 20 | if(js->getType() == JSON::JsonType::Object){ 21 | obj = JSON::pointer_cast(js); 22 | } 23 | 24 | // 输出格式 25 | JSON::PrintFormatter fmt{ 26 | JSON::JsonFormatType::NEWLINE, 27 | 1, 28 | }; 29 | 30 | /* 序列化(转为字符串) */ 31 | std::string output = obj->toString(fmt); 32 | std::cout << output << std::endl; 33 | 34 | std::cout << std::string(60, '*') << std::endl; 35 | 36 | /* 反序列化(从字符串读取) */ 37 | JSON::JsonNode::ptr js1; 38 | js1 = JSON::fromString(output); 39 | 40 | JSON::PrintFormatter fmt1{ 41 | JSON::JsonFormatType::NEWLINE, 42 | 1, 43 | }; 44 | 45 | std::string output1 = js1->toString(fmt1); 46 | std::cout << output1 << std::endl; 47 | 48 | std::cout << std::string(60, '*') << std::endl; 49 | 50 | // 直接通过基类[]方法访问 51 | JsonNode& conf = (*js1)["configurations"]; 52 | std::cout << conf.toString() << std::endl; 53 | 54 | std::cout << std::string(60, '*') << std::endl; 55 | 56 | JsonNode& name = conf[0]["name"]; 57 | std::cout << name.toString() << std::endl; 58 | 59 | // 输出到文件 60 | JSON::toFile(js1, "../output.json", fmt1); 61 | 62 | // 异常测试 63 | conf["yyk"]; 64 | 65 | return 0; 66 | } -------------------------------------------------------------------------------- /haha_web/base/uuid.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * uuid.hpp 4 | * uuid generator 5 | * 6 | * @author : yandaren1220@126.com 7 | * @date : 2017-06-25 8 | */ 9 | 10 | #ifndef __HAHA_UUID_H__ 11 | #define __HAHA_UUID_H__ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include "base/Mutex.h" 20 | 21 | #define GUID_LEN 64 22 | 23 | namespace haha 24 | { 25 | namespace uuid 26 | { 27 | 28 | static SpinLock uuid_mutex_; 29 | 30 | // 生成uuid的线程不安全版本 31 | static std::string generate() 32 | { 33 | char buf[GUID_LEN] = { 0 }; 34 | 35 | uuid_t uu; 36 | ::uuid_generate( uu ); 37 | 38 | int32_t index = 0; 39 | for (int32_t i = 0; i < 16; i++) 40 | { 41 | int32_t len = i < 15 ? 42 | sprintf(buf + index, "%02X-", uu[i]) : 43 | sprintf(buf + index, "%02X", uu[i]); 44 | if(len < 0 ) 45 | return std::move(std::string("")); 46 | index += len; 47 | } 48 | 49 | return std::move(std::string(buf)); 50 | } 51 | 52 | // 生成uuid的线程安全版本 53 | static std::string generate_threadSafe(){ 54 | char buf[GUID_LEN] = { 0 }; 55 | 56 | uuid_t uu; 57 | { 58 | SpinLock::RAIILock lock(uuid_mutex_); 59 | ::uuid_generate( uu ); 60 | } 61 | 62 | int32_t index = 0; 63 | for (int32_t i = 0; i < 16; i++) 64 | { 65 | int32_t len = i < 15 ? 66 | sprintf(buf + index, "%02X-", uu[i]) : 67 | sprintf(buf + index, "%02X", uu[i]); 68 | if(len < 0 ) 69 | return std::move(std::string("")); 70 | index += len; 71 | } 72 | 73 | return std::move(std::string(buf)); 74 | } 75 | 76 | } 77 | } 78 | 79 | #endif -------------------------------------------------------------------------------- /haha_web/http/HttpUrl.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_HTTPURL_H__ 2 | #define __HAHA_HTTPURL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "http/HttpUtil.h" 8 | 9 | namespace haha{ 10 | 11 | class HttpUrl{ 12 | public: 13 | typedef std::unordered_map Query; 14 | enum STATE{ 15 | PARSE_SUCCESS, 16 | PARSE_FAIL, 17 | }; 18 | enum SCHEME{ 19 | HTTP, 20 | HTTPS, 21 | }; 22 | HttpUrl(const std::string& url = "", bool decode = true); 23 | 24 | bool empty() const {return url_.empty();} 25 | size_t size() const { return url_.size(); } 26 | 27 | STATE getState() const { return state_; } 28 | 29 | const std::string& getUrl() const { return url_; } 30 | 31 | std::string_view getScheme() const { 32 | switch (scheme_) 33 | { 34 | case HTTP: 35 | return "HTTP"; 36 | break; 37 | case HTTPS: 38 | return "HTTPS"; 39 | break; 40 | default: 41 | return ""; 42 | break; 43 | } 44 | } 45 | std::string_view getHost() const { return host_; } 46 | std::string_view getHostName() const { return hostName_; } 47 | std::string_view getPort() const { return port_; } 48 | std::string_view getPath() const { return path_; } 49 | const Query& getQuery() const { return query_; } 50 | 51 | private: 52 | STATE parseFromUrl(); 53 | 54 | private: 55 | STATE state_; 56 | std::string url_; 57 | SCHEME scheme_; 58 | std::string host_; 59 | std::string hostName_; 60 | std::string port_; 61 | std::string path_; 62 | Query query_; 63 | }; 64 | 65 | } 66 | 67 | 68 | #endif -------------------------------------------------------------------------------- /haha_web/base/ConditionVariable.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_CONDITIONVARIABLE_H__ 2 | #define __HAHA_CONDITIONVARIABLE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Mutex.h" 10 | #include "noncopyable.h" 11 | 12 | namespace haha 13 | { 14 | 15 | template 16 | class ConditionVariable : public noncopyable{ 17 | public: 18 | typedef std::shared_ptr> ptr; 19 | 20 | ConditionVariable(){ 21 | pthread_cond_init(&cond_, nullptr); 22 | } 23 | 24 | ~ConditionVariable(){ 25 | pthread_cond_destroy(&cond_); 26 | } 27 | 28 | void wait(MutexType &mutex){ 29 | pthread_cond_wait(&cond_, &(mutex.getMutex())); 30 | } 31 | 32 | bool waitForSeconds(MutexType &mutex, double seconds){ 33 | struct timespec abstime; 34 | // FIXME: use CLOCK_MONOTONIC or CLOCK_MONOTONIC_RAW to prevent time rewind. 35 | clock_gettime(CLOCK_REALTIME, &abstime); 36 | 37 | const int64_t kNanoSecondsPerSecond = 1000000000; 38 | int64_t nanoseconds = static_cast(seconds * kNanoSecondsPerSecond); 39 | 40 | abstime.tv_sec += static_cast((abstime.tv_nsec + nanoseconds) / kNanoSecondsPerSecond); 41 | abstime.tv_nsec = static_cast((abstime.tv_nsec + nanoseconds) % kNanoSecondsPerSecond); 42 | 43 | return ETIMEDOUT == pthread_cond_timedwait(&cond_, &(mutex.getMutex()), &abstime); 44 | } 45 | 46 | void notify_one(){ 47 | pthread_cond_signal(&cond_); 48 | } 49 | 50 | void notify_all(){ 51 | pthread_cond_broadcast(&cond_); 52 | } 53 | private: 54 | pthread_cond_t cond_; 55 | }; 56 | 57 | } // namespace haha 58 | 59 | #endif -------------------------------------------------------------------------------- /haha_web/http/HttpMultiPart.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_HTTPMULTIPART_H__ 2 | #define __HAHA_HTTPMULTIPART_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "http/HttpMap.h" 8 | #include "base/RetOption.h" 9 | 10 | namespace haha{ 11 | 12 | class HttpMultiPart { 13 | public: 14 | struct Part { 15 | std::string name; 16 | std::string file_name; 17 | std::string file_type; 18 | std::string data; 19 | 20 | Part() {} 21 | Part(std::string name, std::string fn, std::string ft, std::string data) 22 | : name(std::move(name)), file_name(std::move(fn)), 23 | file_type(std::move(ft)), data(std::move(data)) {} 24 | }; 25 | 26 | typedef HttpBaseMap> Files; 27 | typedef HttpBaseMap Form; 28 | 29 | void setBoundary(const std::string &boundary) noexcept { bd_ = boundary; } 30 | const std::string &getBoundary() const noexcept { return bd_; } 31 | RetOption getValue(const std::string &name) const; 32 | Part *getFile(const std::string &name, size_t index = 0) const; 33 | 34 | void parse(std::string_view body); 35 | 36 | private: 37 | enum State { 38 | start_body, 39 | start_boundary, 40 | end_boundary, 41 | start_content_disposition, 42 | end_content_disposition, 43 | start_content_type, 44 | end_content_type, 45 | start_content_data, 46 | end_content_data, 47 | end_body 48 | }; 49 | 50 | std::string bd_; 51 | Form form_; 52 | mutable Files files_; 53 | 54 | inline constexpr static char CR = '\r'; 55 | inline constexpr static char LF = '\n'; 56 | }; 57 | 58 | } 59 | 60 | 61 | #endif -------------------------------------------------------------------------------- /haha_web/log/LogFile.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_LOG_LOGFILE_H__ 2 | #define __HAHA_LOG_LOGFILE_H__ 3 | 4 | #include 5 | #include 6 | #include "base/noncopyable.h" 7 | #include "base/Mutex.h" 8 | 9 | namespace haha{ 10 | 11 | namespace log{ 12 | 13 | class AppendFile : noncopyable 14 | { 15 | public: 16 | AppendFile(const char* filename); 17 | 18 | ~AppendFile(); 19 | 20 | void append(const char* logline, size_t len); 21 | 22 | void flush(); 23 | 24 | off_t writtenBytes() const { return writtenBytes_; } 25 | 26 | private: 27 | 28 | size_t write(const char* logline, size_t len); 29 | 30 | FILE* fp_; 31 | char buffer_[64*1024]; 32 | off_t writtenBytes_; 33 | }; 34 | 35 | 36 | class LogFile : noncopyable 37 | { 38 | public: 39 | typedef std::shared_ptr ptr; 40 | typedef MutexLock MutexType; 41 | LogFile(const std::string& basename, 42 | off_t rollSize, 43 | bool threadSafe = true, 44 | int flushInterval = 3, 45 | int checkEveryN = 1024); 46 | ~LogFile(); 47 | 48 | void append(const char* logline, int len); 49 | void flush(); 50 | bool rollFile(); 51 | 52 | private: 53 | void append_unlocked(const char* logline, int len); 54 | 55 | static std::string getLogFileName(const std::string& basename, time_t* now); 56 | 57 | const std::string basename_; 58 | const off_t rollSize_; 59 | const int flushInterval_; 60 | const int checkEveryN_; 61 | 62 | int count_; 63 | 64 | MutexType mutex_; 65 | time_t startOfPeriod_; 66 | time_t lastRoll_; 67 | time_t lastFlush_; 68 | std::unique_ptr file_; 69 | 70 | const static int kRollPerSeconds_ = 60*60*24; 71 | }; 72 | 73 | } 74 | 75 | } 76 | 77 | #endif -------------------------------------------------------------------------------- /webbench1.5/socket.c: -------------------------------------------------------------------------------- 1 | /* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $ 2 | * 3 | * This module has been modified by Radim Kolar for OS/2 emx 4 | */ 5 | 6 | /*********************************************************************** 7 | module: socket.c 8 | program: popclient 9 | SCCS ID: @(#)socket.c 1.5 4/1/94 10 | programmer: Virginia Tech Computing Center 11 | compiler: DEC RISC C compiler (Ultrix 4.1) 12 | environment: DEC Ultrix 4.3 13 | description: UNIX sockets code. 14 | ***********************************************************************/ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | int Socket(const char *host, int clientPort) 30 | { 31 | int sock; 32 | unsigned long inaddr; 33 | struct sockaddr_in ad; 34 | struct hostent *hp; 35 | 36 | memset(&ad, 0, sizeof(ad)); 37 | ad.sin_family = AF_INET; 38 | 39 | inaddr = inet_addr(host); 40 | if (inaddr != INADDR_NONE) 41 | memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr)); 42 | else 43 | { 44 | hp = gethostbyname(host); 45 | if (hp == NULL) 46 | return -1; 47 | memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); 48 | } 49 | ad.sin_port = htons(clientPort); 50 | 51 | sock = socket(AF_INET, SOCK_STREAM, 0); 52 | if (sock < 0) 53 | return sock; 54 | if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0) 55 | { 56 | close(sock); 57 | return -1; 58 | } 59 | return sock; 60 | } 61 | 62 | -------------------------------------------------------------------------------- /bin/ssl/rsa_private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDCqSGurhAWgmV1 3 | Z7s8f/J8X12aGPtcbEAvFF8XxLRt7iqzki6AoD/g/anvcMEEgGck55d9JQi3sYAK 4 | RN1hLJdk1zi1RYfUlBhrl0b0SYF94YQ1NIMtawXYRehzMlY4z39GcY1RdNdBG9QH 5 | ePerGmp4e7AZMusx54gqmJB5tSd5I//3ak/CW3AwlzFAbWOBZUMqm3TIOFPfnyei 6 | cWRvy0yRrTE8FCzcxqGaf+DEjmEfjDUyKTY6+FIWezPzGAX2ZpgfkGXLrtdOSJBO 7 | b/uwJKifajo8yfUYAk0Zndyt572qAjgYyUU9TiS6T6/wXBQBMAov5XG8MoOVZmqy 8 | iVQNivCDAgMBAAECggEAVRRfKF3jojLz7VCdQWAat8lmu8dXdZnKo8kflt/ejH2g 9 | JEQsFwPzZ373GzQI6NLGQhcG2yeom8D02YgnyiUXNnfqzS/KolroIX5JANRvyHkc 10 | s7Rz8RHpyBEl+9NwY/wtALj4rzWGL2reTTXN6fbtuiM1YI4kxfcQSK3N16QZ0JPs 11 | G5oM283m79EqM0E/VGDDPNNV4TMKRfqm37+914H+wZFgOW7eciDZEQqeWjwY3Yox 12 | iPGdrhvG1B+S47KhIk3Q2336ivRPBaPwHi5+Rw9Dn5m2twBADSkAil1ih1UHnDi4 13 | SODkpZ7sAUJDCv4G5Gb1bCHmT2sKikXX9/nNLrAcAQKBgQDqLtYQSn6JqohruE4t 14 | C3pE00zKbk7zy7ol28iK+HTWj4Ks3LQ7WC0FzKXzvp5i04AL9IzJzgQ3LITxb66r 15 | /d4jIFJdJxnq3+QgB3N2LkrLxWov1b/UoHorK4CkRrpq2pRw7/VgZR4sBFE+mt4Q 16 | fuuQsXtHHaRbUwSUfvyhzXGfgQKBgQDUy7SrF6xBz86w5pOEfJEEJ2qGj73fjNyQ 17 | ACgaNvRzwppTFcZ5JDn8mJZ4+UKnTfyC3yIsb984HzTv3iEhcWkTo+ncqb1o5Hsx 18 | 8o8pHg+tULYXMb8rUnNPsdwKoz1ZO+ORKfYOeEaxiFuREWqMnv5IWMbgyRWzYijn 19 | ao1CB38SAwKBgQDMquRhVonizUzvQCPq0FYq4rUBUojkH5Bv1/ccJesPPluJslY+ 20 | a2Dl6aa8MUOBwbDuQevtz4T1p44E6IOzQ2uqLWxuh2bqVISuzXRGvEyYbM/SQLCo 21 | Jxy+sAV9DWeNTb1gOwesGJPXCmrrGTStHA/K5KKGU8y6GAMvuRAYZVJBgQKBgB2W 22 | FRCUwgaEdiBybqBFEYSaVl1PLv8d/qym2OyMJ4DQ1dQneujs+IL+VC2Nuo64cRth 23 | GsdC5q5O6HVRVRnpQsrQR3u+ve8PlXFkYDM/Ur6uMiMXb5T+abny6L0x5R27DfDo 24 | /KUWY/3bD6Aj48SZdpFohn5KdxHx2+4RCoLbt/cxAoGBANfWAF8GKcvwFXIxxhoG 25 | LxjLc9OUL62SvrbTUJY4PeDEk40vxwWIj46OaHmoM8HgYBJFPHlBCklB6Tz9Ocun 26 | u8rHCvoa9fMkWMrpHFdz9ljtoQd6C3/2Fh/EK+ZOcnIu1nT3LfK+JHaoSJCIquUI 27 | YSyof3Gz3C6dNOAds8EelA3y 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /haha_web/haha_json/README.md: -------------------------------------------------------------------------------- 1 | # HAHA_JSON 2 | 3 | 一个用c++17实现的json解析库,还在更新中 4 | 5 | ## 技术特点 6 | 7 | * 使用了c++17中的variant和string_view 8 | 9 | * 多态、泛型编程 10 | 11 | * 使用智能指针管理内存 12 | 13 | * 支持读取ANSI、utf8、utf16编码,统一解析为utf8格式 14 | 15 | ## 未来的计划: 16 | 17 | * 提供更易用的API 18 | 19 | * 性能优化 20 | 21 | 22 | ## 快速使用: 23 | 24 | 编译 25 | ```shell 26 | cd 当前目录 27 | ./build_release.sh 28 | ``` 29 | 运行 30 | ```shell 31 | cd 当前目录 32 | ./run_jsonTest_release.sh 33 | ``` 34 | 35 | 36 | ## 代码示例 37 | 38 | 代码在tests文件夹下的test_json.cc 39 | ```c++ 40 | #include 41 | #include 42 | #include "json.h" 43 | 44 | namespace JSON = haha::json; 45 | 46 | int main(){ 47 | JSON::JsonNode::ptr js; 48 | 49 | std::string filePath = "../test.json"; // 文件位置自己定 50 | 51 | // 从文件读取 52 | js = JSON::fromFile(filePath); 53 | 54 | // 序列化(转为字符串) 55 | std::cout << js->toString() << std::endl; 56 | 57 | // 类型转换 58 | JSON::JsonObject::ptr obj; 59 | if(js->getType() == JSON::JsonType::Object){ 60 | obj = JSON::pointer_cast(js); 61 | } 62 | 63 | // 输出格式 64 | JSON::PrintFormatter fmt{ 65 | JSON::JsonFormatType::NEWLINE, 66 | 1, 67 | }; 68 | 69 | /* 序列化(转为字符串) */ 70 | std::string output = obj->toString(fmt); 71 | std::cout << output << std::endl; 72 | 73 | std::cout << std::string(60, '*') << std::endl; 74 | 75 | /* 反序列化(从字符串读取) */ 76 | JSON::JsonNode::ptr js1; 77 | js1 = JSON::fromString(output); 78 | 79 | JSON::PrintFormatter fmt1{ 80 | JSON::JsonFormatType::NEWLINE, 81 | 1, 82 | }; 83 | 84 | std::string output1 = js1->toString(fmt1); 85 | std::cout << output1 << std::endl; 86 | 87 | // 输出到文件 88 | JSON::toFile(js1, "../output.json", fmt1); 89 | 90 | return 0; 91 | } 92 | ``` 93 | -------------------------------------------------------------------------------- /haha_web/base/ReadWriteLock.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_READWRITELOCK_H__ 2 | #define __HAHA_READWRITELOCK_H__ 3 | 4 | #include 5 | 6 | namespace haha{ 7 | 8 | /* *********************************************读写锁********************************************* */ 9 | 10 | // 读锁的RAII机制 11 | template 12 | class ReadLockGuard{ 13 | public: 14 | ReadLockGuard(T& mutex) 15 | :m_mutex(mutex){ 16 | m_mutex.rdlock(); 17 | m_locked = true; 18 | } 19 | ~ReadLockGuard(){ 20 | if(m_locked){ 21 | m_mutex.unlock(); 22 | m_locked = false; 23 | } 24 | } 25 | T& getLock(){ 26 | return m_mutex; 27 | } 28 | private: 29 | T& m_mutex; 30 | bool m_locked; 31 | }; 32 | 33 | // 写锁的RAII机制 34 | template 35 | class WriteLockGuard{ 36 | public: 37 | WriteLockGuard(T& mutex) 38 | :m_mutex(mutex){ 39 | m_mutex.wrlock(); 40 | m_locked = true; 41 | } 42 | ~WriteLockGuard(){ 43 | if(m_locked){ 44 | m_mutex.unlock(); 45 | m_locked = false; 46 | } 47 | } 48 | T& getLock(){ 49 | return m_mutex; 50 | } 51 | private: 52 | T& m_mutex; 53 | bool m_locked; 54 | }; 55 | 56 | /* 读写锁 */ 57 | class ReadWriteLock{ 58 | public: 59 | typedef ReadLockGuard RAIIReadLock; 60 | typedef WriteLockGuard RAIIWriteLock; 61 | ReadWriteLock(){ 62 | pthread_rwlock_init(&m_mutex, nullptr); 63 | } 64 | void rdlock(){ 65 | pthread_rwlock_rdlock(&m_mutex); 66 | } 67 | void wrlock(){ 68 | pthread_rwlock_wrlock(&m_mutex); 69 | } 70 | void unlock(){ 71 | pthread_rwlock_unlock(&m_mutex); 72 | } 73 | private: 74 | pthread_rwlock_t m_mutex; 75 | }; 76 | 77 | } 78 | 79 | #endif -------------------------------------------------------------------------------- /haha_web/base/Thread.cpp: -------------------------------------------------------------------------------- 1 | #include "base/Thread.h" 2 | #include 3 | 4 | namespace haha{ 5 | 6 | std::atomic Thread::numThread_{0}; 7 | 8 | static thread_local Thread* cur_thread = nullptr; 9 | static thread_local std::string cur_thread_name = "UNKNOW"; 10 | 11 | const Thread* Thread::getCurrentThread(){ 12 | return cur_thread; 13 | } 14 | 15 | const Thread::ID Thread::getCurrentThreadId(){ 16 | return pthread_self(); 17 | } 18 | 19 | const std::string& Thread::getCurrentThreadName(){ 20 | return cur_thread_name; 21 | } 22 | 23 | Thread::Thread(Task task, const std::string& name) 24 | :thread_(0) 25 | ,joined_(false) 26 | ,task_(std::move(task)) 27 | ,name_(name) 28 | ,started_(false){ 29 | ++numThread_; 30 | setDefaultName(); 31 | int ret = pthread_create(&thread_, nullptr, &Thread::run, this); 32 | if(ret){ 33 | throw std::logic_error("pthread_create error"); 34 | } 35 | while(!started_); // 等待线程成功运行 36 | } 37 | 38 | Thread::~Thread(){ 39 | if(thread_){ 40 | pthread_detach(thread_); 41 | } 42 | --numThread_; 43 | } 44 | 45 | void Thread::setDefaultName() 46 | { 47 | if (name_.empty()) 48 | { 49 | int curThreadCnt = numThread_; 50 | // std::cout << curThreadCnt << std::endl; 51 | char buf[32]; 52 | snprintf(buf, sizeof(buf), "Thread_%d", curThreadCnt); 53 | name_ = buf; 54 | } 55 | } 56 | 57 | void* Thread::run(void* args){ 58 | Thread* t = static_cast(args); 59 | cur_thread = t; 60 | cur_thread_name = t->name_; 61 | t->started_ = true; 62 | t->task_(); 63 | return nullptr; 64 | } 65 | 66 | void Thread::join(){ 67 | if(thread_){ 68 | int rt = pthread_join(thread_, nullptr); 69 | if(rt){ 70 | throw std::logic_error("pthread_join error"); 71 | } 72 | thread_ = 0; 73 | joined_ = true; 74 | } 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /haha_web/haha_json/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "cctype": "cpp", 4 | "clocale": "cpp", 5 | "cmath": "cpp", 6 | "cstdarg": "cpp", 7 | "cstddef": "cpp", 8 | "cstdio": "cpp", 9 | "cstdlib": "cpp", 10 | "cstring": "cpp", 11 | "ctime": "cpp", 12 | "cwchar": "cpp", 13 | "cwctype": "cpp", 14 | "array": "cpp", 15 | "atomic": "cpp", 16 | "strstream": "cpp", 17 | "bit": "cpp", 18 | "*.tcc": "cpp", 19 | "chrono": "cpp", 20 | "condition_variable": "cpp", 21 | "cstdint": "cpp", 22 | "deque": "cpp", 23 | "map": "cpp", 24 | "set": "cpp", 25 | "unordered_map": "cpp", 26 | "vector": "cpp", 27 | "exception": "cpp", 28 | "algorithm": "cpp", 29 | "functional": "cpp", 30 | "iterator": "cpp", 31 | "memory": "cpp", 32 | "memory_resource": "cpp", 33 | "numeric": "cpp", 34 | "optional": "cpp", 35 | "random": "cpp", 36 | "ratio": "cpp", 37 | "string": "cpp", 38 | "string_view": "cpp", 39 | "system_error": "cpp", 40 | "tuple": "cpp", 41 | "type_traits": "cpp", 42 | "utility": "cpp", 43 | "fstream": "cpp", 44 | "initializer_list": "cpp", 45 | "iosfwd": "cpp", 46 | "iostream": "cpp", 47 | "istream": "cpp", 48 | "limits": "cpp", 49 | "mutex": "cpp", 50 | "new": "cpp", 51 | "ostream": "cpp", 52 | "sstream": "cpp", 53 | "stdexcept": "cpp", 54 | "streambuf": "cpp", 55 | "thread": "cpp", 56 | "cinttypes": "cpp", 57 | "typeinfo": "cpp", 58 | "hash_map": "cpp", 59 | "hash_set": "cpp", 60 | "list": "cpp", 61 | "unordered_set": "cpp", 62 | "bitset": "cpp", 63 | "codecvt": "cpp", 64 | "iomanip": "cpp", 65 | "variant": "cpp" 66 | } 67 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "cctype": "cpp", 4 | "clocale": "cpp", 5 | "cmath": "cpp", 6 | "cstdarg": "cpp", 7 | "cstddef": "cpp", 8 | "cstdio": "cpp", 9 | "cstdlib": "cpp", 10 | "cstring": "cpp", 11 | "ctime": "cpp", 12 | "cwchar": "cpp", 13 | "cwctype": "cpp", 14 | "array": "cpp", 15 | "atomic": "cpp", 16 | "strstream": "cpp", 17 | "bit": "cpp", 18 | "*.tcc": "cpp", 19 | "chrono": "cpp", 20 | "condition_variable": "cpp", 21 | "cstdint": "cpp", 22 | "deque": "cpp", 23 | "map": "cpp", 24 | "set": "cpp", 25 | "unordered_map": "cpp", 26 | "vector": "cpp", 27 | "exception": "cpp", 28 | "algorithm": "cpp", 29 | "functional": "cpp", 30 | "iterator": "cpp", 31 | "memory": "cpp", 32 | "memory_resource": "cpp", 33 | "numeric": "cpp", 34 | "optional": "cpp", 35 | "random": "cpp", 36 | "ratio": "cpp", 37 | "string": "cpp", 38 | "string_view": "cpp", 39 | "system_error": "cpp", 40 | "tuple": "cpp", 41 | "type_traits": "cpp", 42 | "utility": "cpp", 43 | "fstream": "cpp", 44 | "initializer_list": "cpp", 45 | "iosfwd": "cpp", 46 | "iostream": "cpp", 47 | "istream": "cpp", 48 | "limits": "cpp", 49 | "mutex": "cpp", 50 | "new": "cpp", 51 | "ostream": "cpp", 52 | "sstream": "cpp", 53 | "stdexcept": "cpp", 54 | "streambuf": "cpp", 55 | "thread": "cpp", 56 | "cinttypes": "cpp", 57 | "typeinfo": "cpp", 58 | "list": "cpp", 59 | "unordered_set": "cpp", 60 | "any": "cpp", 61 | "codecvt": "cpp", 62 | "iomanip": "cpp", 63 | "variant": "cpp", 64 | "bitset": "cpp", 65 | "regex": "cpp", 66 | "shared_mutex": "cpp" 67 | } 68 | } -------------------------------------------------------------------------------- /haha_web/config/config.cpp: -------------------------------------------------------------------------------- 1 | #include "config/config.h" 2 | 3 | namespace haha 4 | { 5 | 6 | namespace config{ 7 | 8 | template<> 9 | const std::string& Config::query(const char* query_str, const std::string& default_value){ 10 | auto it = map_.find(query_str); 11 | if(it == map_.end()){ 12 | return default_value; 13 | } 14 | auto node = it->second; 15 | auto t = node->getType(); 16 | if(t == JsonType::String){ 17 | const auto p = json::pointer_cast(node); 18 | return p->getValue(); 19 | } 20 | return default_value; 21 | } 22 | 23 | template<> 24 | const int& Config::query(const char* query_str, const int &default_value){ 25 | auto it = map_.find(query_str); 26 | if(it == map_.end()){ 27 | return default_value; 28 | } 29 | auto node = it->second; 30 | auto t = node->getType(); 31 | if(t == JsonType::Integer){ 32 | const auto p = json::pointer_cast(node); 33 | return p->getValue(); 34 | } 35 | return default_value; 36 | } 37 | 38 | template<> 39 | const double& Config::query(const char* query_str, const double& default_value){ 40 | auto it = map_.find(query_str); 41 | if(it == map_.end()){ 42 | return default_value; 43 | } 44 | auto node = it->second; 45 | auto t = node->getType(); 46 | if(t == JsonType::Double){ 47 | const auto p = json::pointer_cast(node); 48 | return p->getValue(); 49 | } 50 | return default_value; 51 | } 52 | 53 | template<> 54 | const bool& Config::query(const char* query_str, const bool& default_value){ 55 | auto it = map_.find(query_str); 56 | if(it == map_.end()){ 57 | return default_value; 58 | } 59 | auto node = it->second; 60 | auto t = node->getType(); 61 | if(t == JsonType::Boolean){ 62 | const auto p = json::pointer_cast(node); 63 | return p->getValue(); 64 | } 65 | return default_value; 66 | } 67 | 68 | } 69 | 70 | } // namespace haha 71 | -------------------------------------------------------------------------------- /haha_web/base/stringView.cpp: -------------------------------------------------------------------------------- 1 | #include "stringView.h" 2 | #include 3 | 4 | namespace haha 5 | { 6 | 7 | void get_next(StringView t, std::vector &next) 8 | { 9 | int i = 0, j = -1; 10 | next[0] = -1; 11 | while (i < (int)t.size() - 1) 12 | { 13 | if (j == -1 || t[i] == t[j]) 14 | { 15 | next[++i] = ++j; 16 | } 17 | else 18 | { 19 | j = next[j]; 20 | } 21 | } 22 | } 23 | 24 | int KMP_search(const StringView &s, const StringView &t) 25 | { 26 | int i = 0, j = 0; 27 | std::vector next(t.size()); 28 | get_next(t, next); 29 | // 此处涉及-1和unsigned比较 30 | // 由于signed int的负数最高位是1,转换成unsigned int之后,就会变成一个很大的unsigned int型正数 31 | while (j == -1 || (i < (int)s.size() && j < (int)t.size())) 32 | { 33 | if (j == -1 || s[i] == t[j]) 34 | { 35 | i++; 36 | j++; 37 | } 38 | else 39 | { 40 | j = next[j]; 41 | } 42 | } 43 | if (j == (int)t.size()) 44 | return i - j; 45 | else 46 | return -1; 47 | } 48 | 49 | 50 | size_t StringView::find(const char *begin, const char* end, size_t pos){ 51 | if(pos >= size()){ 52 | throw std::out_of_range("StringView find out of range"); 53 | } 54 | // size_t len = end - begin; 55 | // if(len * size() < 100){ 56 | // return std::search(begin_+pos, end_, begin, end) - begin_; 57 | // } 58 | // return KMP_search(*this, StringView(begin, end)); 59 | 60 | return std::search(begin_+pos, end_, begin, end) - begin_; 61 | } 62 | 63 | size_t StringView::find(const char *str, size_t pos){ 64 | if(pos >= size()){ 65 | throw std::out_of_range("StringView find out of range"); 66 | } 67 | size_t len = strlen(str); 68 | // if(len * size() < 100){ 69 | // return std::search(begin_+pos, end_, str, str+len) - begin_; 70 | // } 71 | // return KMP_search(*this, StringView(str, len)); 72 | 73 | return std::search(begin_+pos, end_, str, str+len) - begin_; 74 | } 75 | 76 | } // namespace haha 77 | -------------------------------------------------------------------------------- /haha_web/haha_json/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) 启动", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/bin/debug/jsonParseTest.out", 12 | "args": [], 13 | "stopAtEntry": false, 14 | // "cwd": "${fileDirname}", 15 | "cwd": "${workspaceFolder}/bin/debug", 16 | "environment": [], 17 | "externalConsole": false, 18 | "MIMode": "gdb", 19 | "setupCommands": [ 20 | { 21 | "description": "为 gdb 启用整齐打印", 22 | "text": "-enable-pretty-printing", 23 | "ignoreFailures": true 24 | }, 25 | { 26 | "description": "将反汇编风格设置为 Intel", 27 | "text": "-gdb-set disassembly-flavor intel", 28 | "ignoreFailures": true 29 | } 30 | ] 31 | }, 32 | { 33 | "name": "(gdb) jsonCopy Test", 34 | "type": "cppdbg", 35 | "request": "launch", 36 | "program": "${workspaceFolder}/bin/jsonCopyTest.out", 37 | "args": [], 38 | "stopAtEntry": false, 39 | "cwd": "${workspaceFolder}/bin", 40 | "environment": [], 41 | "externalConsole": false, 42 | "MIMode": "gdb", 43 | "setupCommands": [ 44 | { 45 | "description": "为 gdb 启用整齐打印", 46 | "text": "-enable-pretty-printing", 47 | "ignoreFailures": true 48 | }, 49 | { 50 | "description": "将反汇编风格设置为 Intel", 51 | "text": "-gdb-set disassembly-flavor intel", 52 | "ignoreFailures": true 53 | } 54 | ] 55 | }, 56 | ] 57 | } -------------------------------------------------------------------------------- /haha_web/haha_json/haha_json/stringView.cpp: -------------------------------------------------------------------------------- 1 | #include "stringView.h" 2 | #include 3 | 4 | namespace haha 5 | { 6 | 7 | namespace json{ 8 | 9 | void get_next(StringView t, std::vector &next) 10 | { 11 | int i = 0, j = -1; 12 | next[0] = -1; 13 | while (i < (int)t.size() - 1) 14 | { 15 | if (j == -1 || t[i] == t[j]) 16 | { 17 | next[++i] = ++j; 18 | } 19 | else 20 | { 21 | j = next[j]; 22 | } 23 | } 24 | } 25 | 26 | int KMP_search(const StringView &s, const StringView &t) 27 | { 28 | int i = 0, j = 0; 29 | std::vector next(t.size()); 30 | get_next(t, next); 31 | // 此处涉及-1和unsigned比较 32 | // 由于signed int的负数最高位是1,转换成unsigned int之后,就会变成一个很大的unsigned int型正数 33 | while (j == -1 || (i < (int)s.size() && j < (int)t.size())) 34 | { 35 | if (j == -1 || s[i] == t[j]) 36 | { 37 | i++; 38 | j++; 39 | } 40 | else 41 | { 42 | j = next[j]; 43 | } 44 | } 45 | if (j == (int)t.size()) 46 | return i - j; 47 | else 48 | return -1; 49 | } 50 | 51 | 52 | size_t StringView::find(const char *begin, const char* end, size_t pos){ 53 | if(pos >= size()){ 54 | throw std::out_of_range("StringView find out of range"); 55 | } 56 | // size_t len = end - begin; 57 | // if(len * size() < 100){ 58 | // return std::search(begin_+pos, end_, begin, end) - begin_; 59 | // } 60 | // return KMP_search(*this, StringView(begin, end)); 61 | 62 | return std::search(begin_+pos, end_, begin, end) - begin_; 63 | } 64 | 65 | size_t StringView::find(const char *str, size_t pos){ 66 | if(pos >= size()){ 67 | throw std::out_of_range("StringView find out of range"); 68 | } 69 | size_t len = strlen(str); 70 | // if(len * size() < 100){ 71 | // return std::search(begin_+pos, end_, str, str+len) - begin_; 72 | // } 73 | // return KMP_search(*this, StringView(str, len)); 74 | 75 | return std::search(begin_+pos, end_, str, str+len) - begin_; 76 | } 77 | 78 | } 79 | 80 | } // namespace haha 81 | -------------------------------------------------------------------------------- /haha_web/log/LogInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "log/LogInfo.h" 2 | #include 3 | 4 | namespace haha{ 5 | 6 | namespace log{ 7 | 8 | const char* LogLevel::toString(LogLevel::Level level){ 9 | // 骚操作时间 10 | switch (level) 11 | { 12 | #define XX(name) \ 13 | case LogLevel::name: \ 14 | return #name; \ 15 | break; 16 | XX(DEBUG); 17 | XX(INFO); 18 | XX(WARN); 19 | XX(ERROR); 20 | XX(FATAL); 21 | #undef XX 22 | default: 23 | return "UNKNOW"; 24 | break; 25 | } 26 | return "UNKNOW"; 27 | } 28 | 29 | LogLevel::Level LogLevel::FromString(const std::string &str){ 30 | #define XX(level, v) \ 31 | if(str == #v){ \ 32 | return LogLevel::level; \ 33 | } 34 | XX(DEBUG, debug); 35 | XX(INFO, info); 36 | XX(WARN, warn); 37 | XX(ERROR, error); 38 | XX(FATAL, fatal); 39 | 40 | XX(DEBUG, DEBUG); 41 | XX(INFO, INFO); 42 | XX(WARN, WARN); 43 | XX(ERROR, ERROR); 44 | XX(FATAL, FATAL); 45 | #undef XX 46 | return LogLevel::UNKNOW; 47 | } 48 | 49 | 50 | LogInfo::LogInfo(const std::string &loggerName, 51 | LogLevel::Level level, 52 | const char *file, 53 | int32_t line, 54 | uint32_t elapse, 55 | uint32_t thread_id, 56 | // uint32_t fiber_id, 57 | const TimePoint& time) 58 | // const std::string &thread_name) 59 | :loggerName_(loggerName), 60 | file_(file), 61 | line_(line), 62 | elapse_(elapse), 63 | threadId_(thread_id), 64 | // m_fiberId(fiber_id), 65 | time_(time), 66 | // threadName_(thread_name), 67 | level_(level) 68 | { 69 | } 70 | 71 | void LogInfo::format(const char* fmt, ...){ 72 | va_list al; 73 | va_start(al, fmt); 74 | format(fmt, al); 75 | va_end(al); 76 | } 77 | 78 | void LogInfo::format(const char* fmt, va_list al){ 79 | char *buf = nullptr; 80 | int len = vasprintf(&buf, fmt, al); 81 | if(len != -1){ 82 | ss_.append(buf, len); 83 | free(buf); 84 | } 85 | } 86 | 87 | 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /haha_web/net/TimerQueue.cpp: -------------------------------------------------------------------------------- 1 | #include "TimerQueue.h" 2 | #include 3 | 4 | namespace haha{ 5 | 6 | TimerQueue::TimerQueue(int timeFd) 7 | :timeFd_(timeFd) 8 | ,timerHeap_(std::make_unique()){ 9 | 10 | } 11 | 12 | void TimerQueue::push(const Timer& timer) { 13 | timerHeap_->push(timer); 14 | } 15 | 16 | void TimerQueue::adjust(const Timer& timer) { 17 | timerHeap_->adjust(timer); 18 | } 19 | 20 | void TimerQueue::remove(const Timer &timer){ 21 | timerHeap_->remove(timer); 22 | } 23 | 24 | // run once 25 | void TimerQueue::runOnce(const TimeStamp &t) { 26 | uint64_t second = t.second(); 27 | uint64_t nanosecond = (t.microsecond() - t.second() * 1000000) * 100; 28 | timespec now; 29 | ::clock_gettime(CLOCK_REALTIME, &now); 30 | itimerspec itsp; 31 | itsp.it_value.tv_sec = now.tv_sec + second; 32 | itsp.it_value.tv_nsec = now.tv_nsec + nanosecond; 33 | itsp.it_interval.tv_sec = 0; 34 | itsp.it_interval.tv_nsec = 0; 35 | 36 | ::timerfd_settime(timeFd_, TFD_TIMER_ABSTIME, &itsp, nullptr); 37 | } 38 | 39 | // run forever 40 | void TimerQueue::runForever(const TimeStamp &t) { 41 | uint64_t second = t.second(); 42 | uint64_t nanosecond = (t.microsecond() - t.second() * 1000000) * 100; 43 | timespec now; 44 | ::clock_gettime(CLOCK_REALTIME, &now); 45 | itimerspec itsp; 46 | itsp.it_value.tv_sec = now.tv_sec + second; 47 | itsp.it_value.tv_nsec = now.tv_nsec + nanosecond; 48 | itsp.it_interval.tv_sec = second; 49 | itsp.it_interval.tv_nsec = nanosecond; 50 | 51 | ::timerfd_settime(timeFd_, TFD_TIMER_ABSTIME, &itsp, nullptr); 52 | } 53 | 54 | void TimerQueue::handleTimeout() { 55 | HAHA_LOG_DEBUG(HAHA_LOG_ASYNC_FILE_ROOT()) << "timeout"; 56 | 57 | uint64_t one; 58 | int n = ::read(timeFd_, &one, sizeof(one)); 59 | if(n != sizeof(one)){ 60 | HAHA_LOG_DEBUG(HAHA_LOG_ASYNC_FILE_ROOT()) << "read time fd error?"; 61 | } 62 | 63 | TimeStamp now = TimeStamp::now(); 64 | while (!timerHeap_->empty()) { 65 | auto timer = timerHeap_->top(); 66 | if (timer.expire <= now) { 67 | timer.cb(); 68 | timerHeap_->pop(); 69 | } 70 | else { 71 | break; 72 | } 73 | } 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | SOURCE_DIR = ../haha_web 2 | 3 | SOURCE_FILES = $(wildcard $(SOURCE_DIR)/*.cpp) 4 | SOURCE_FILES += $(wildcard $(SOURCE_DIR)/*/*.cpp) 5 | 6 | INCLUDE_DIR = -I ../haha_web 7 | 8 | BINARY_DIR = ../bin 9 | 10 | lang: test_lang.cc 11 | g++ -std=c++2a -ggdb test_lang.cc -o $(BINARY_DIR)/langTest.out 12 | 13 | config: test_config.cc $(SOURCE_FILES) 14 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_config.cc $(SOURCE_FILES) -lpthread -luuid -o $(BINARY_DIR)/configTest.out 15 | 16 | timestamp: test_timeStamp.cc 17 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_timeStamp.cc $(SOURCE_FILES) -lpthread -luuid -o $(BINARY_DIR)/timeStampTest.out 18 | 19 | timerheap: test_timerheap.cc 20 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_timerheap.cc $(SOURCE_FILES) -lpthread -luuid -o $(BINARY_DIR)/timerheapTest.out 21 | 22 | tcpserver: test_tcpServer.cc 23 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_tcpServer.cc $(SOURCE_FILES) -lpthread -luuid -o $(BINARY_DIR)/tcpserverTest.out 24 | 25 | thread: test_thread.cc 26 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_thread.cc $(SOURCE_FILES) -lpthread -luuid -o $(BINARY_DIR)/threadTest.out 27 | 28 | 29 | threadpool: test_threadpool.cc 30 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_threadpool.cc $(SOURCE_FILES) -lpthread -luuid -o $(BINARY_DIR)/threadpoolTest.out 31 | 32 | log: test_log.cc 33 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_log.cc $(SOURCE_FILES) -lpthread -luuid -o $(BINARY_DIR)/loglTest.out 34 | 35 | stringview: test_stringview.cc 36 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_stringview.cc -o $(BINARY_DIR)/stringviewlTest.out 37 | 38 | 39 | httpRequest: test_httpRequest.cc $(SOURCE_FILES) 40 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_httpRequest.cc $(SOURCE_FILES) -lpthread -luuid -o $(BINARY_DIR)/httpRequestTest.out 41 | 42 | httpCT: test_httpCT.cc $(SOURCE_FILES) 43 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_httpCT.cc $(SOURCE_FILES) -lpthread -luuid -o $(BINARY_DIR)/httpCTTest.out 44 | 45 | httpServer: test_httpServer.cc $(SOURCE_FILES) 46 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_httpServer.cc $(SOURCE_FILES) -lpthread -luuid -o $(BINARY_DIR)/httpServerTest.out 47 | 48 | dogServlet: test_servlet.cc $(SOURCE_FILES) 49 | g++ -std=c++2a -ggdb $(INCLUDE_DIR) test_servlet.cc $(SOURCE_FILES) -lpthread -luuid -o $(BINARY_DIR)/dogServletTest.out -------------------------------------------------------------------------------- /haha_web/net/TimerHeap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author : mark 3 | * @Date : 2020-06-17 4 | * @copyleft Apache 2.0 5 | */ 6 | #ifndef __HAHA_TIMERHEAP_H__ 7 | #define __HAHA_TIMERHEAP_H__ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "base/TimeStamp.h" 16 | #include "base/Thread.h" 17 | #include "base/Mutex.h" 18 | #include "base/ReadWriteLock.h" 19 | 20 | namespace haha{ 21 | 22 | typedef std::function TimeoutCallBack; 23 | 24 | struct Timer { 25 | int fd; 26 | TimeStamp expire; 27 | TimeoutCallBack cb; 28 | Timer(int fd, const TimeStamp &expire, const TimeoutCallBack cb){ 29 | this->fd = fd; 30 | this->expire = expire; 31 | this->cb = cb; 32 | } 33 | bool operator<(const Timer& t) { 34 | return expire < t.expire; 35 | } 36 | }; 37 | 38 | 39 | class TimerHeap { 40 | public: 41 | TimerHeap():threadId_(Thread::getCurrentThreadId()) { heap_.reserve(64); } 42 | 43 | ~TimerHeap() { clear(); } 44 | 45 | void adjust(const Timer &timer); 46 | 47 | void push(const Timer &timer); 48 | 49 | void remove(const Timer &timer); 50 | 51 | void clear(); 52 | 53 | void pop(); 54 | 55 | bool isInLoopThread() const { return threadId_ == Thread::getCurrentThreadId(); } 56 | void assertInLoopThread() const; 57 | 58 | Timer& top() { 59 | assertInLoopThread(); 60 | // ReadWriteLock::RAIIReadLock lock(mtx_); 61 | return heap_.front(); 62 | } 63 | 64 | bool empty() const { 65 | assertInLoopThread(); 66 | // ReadWriteLock::RAIIReadLock lock(mtx_); 67 | return heap_.empty(); 68 | } 69 | 70 | size_t size() const { 71 | assertInLoopThread(); 72 | // ReadWriteLock::RAIIReadLock lock(mtx_); 73 | return heap_.size(); 74 | } 75 | 76 | private: 77 | void del_(size_t i); 78 | 79 | void siftup_(size_t i); 80 | 81 | bool siftdown_(size_t index, size_t n); 82 | 83 | void SwapNode_(size_t i, size_t j); 84 | 85 | std::vector heap_; 86 | 87 | // 记录定时器在堆中的位置 88 | std::unordered_map ref_; 89 | 90 | // mutable ReadWriteLock mtx_; 91 | 92 | Thread::ID threadId_; 93 | }; 94 | 95 | } 96 | 97 | #endif //HEAP_TIMER_H -------------------------------------------------------------------------------- /haha_web/http/HttpMap.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_HTTPMAP_H__ 2 | #define __HAHA_HTTPMAP_H__ 3 | 4 | #include 5 | #include "base/util.h" 6 | #include "base/RetOption.h" 7 | 8 | namespace haha{ 9 | 10 | enum class CASE_SENSITIVE{YES, NO}; 11 | 12 | /* 13 | CASE_SENSITIVE::YES 键区分大小写 14 | CASE_SENSITIVE::NO 键不区分大小写 15 | */ 16 | template 17 | class HttpBaseMap{ 18 | public: 19 | typedef typename std::unordered_map::iterator Iterator; 20 | typedef typename std::unordered_map::const_iterator ConstIterator; 21 | 22 | virtual void add(const K& key, const V& value){ 23 | if(C == CASE_SENSITIVE::YES){ 24 | map_[key] = value; 25 | } 26 | else{ 27 | map_[toLowers(key)] = value; 28 | } 29 | } 30 | 31 | virtual void del(const K& key){ 32 | ConstIterator it; 33 | if(C == CASE_SENSITIVE::YES){ 34 | it = map_.find(key); 35 | } 36 | else{ 37 | it = map_.find(toLowers(key)); 38 | } 39 | 40 | if(it != map_.end()){ 41 | map_.erase(it); 42 | } 43 | } 44 | 45 | virtual bool contains(const K &key){ 46 | if(C == CASE_SENSITIVE::YES){ 47 | return map_.contains(key); 48 | } 49 | return map_.contains(toLowers(key)); 50 | } 51 | 52 | virtual RetOption get(const K &key) const { 53 | ConstIterator it; 54 | if(C == CASE_SENSITIVE::YES){ 55 | it = map_.find(key); 56 | } 57 | else{ 58 | it = map_.find(toLowers(key)); 59 | } 60 | 61 | if(it == map_.end()){ 62 | return {V(), false}; 63 | } 64 | return {it->second, true}; 65 | } 66 | 67 | ConstIterator begin() const { return map_.begin(); } 68 | ConstIterator end() const { return map_.end(); } 69 | Iterator begin() { return map_.begin(); } 70 | Iterator end() { return map_.end(); } 71 | 72 | virtual size_t size() const { return map_.size(); } 73 | 74 | virtual bool empty() const { return map_.empty(); } 75 | 76 | protected: 77 | std::unordered_map map_; 78 | }; 79 | 80 | template 81 | using HttpMap = HttpBaseMap; 82 | 83 | } 84 | 85 | #endif -------------------------------------------------------------------------------- /haha_web/haha_json/haha_json/jsonTypeCast.h: -------------------------------------------------------------------------------- 1 | #include "jsonValue.h" 2 | 3 | using namespace haha::json; 4 | 5 | namespace haha{ 6 | 7 | namespace json 8 | { 9 | 10 | /* 实际值类型V映射到json数据类型T */ 11 | template struct Value2JClassMap; 12 | template<> struct Value2JClassMap { using T = JsonString; }; 13 | template<> struct Value2JClassMap { using T = JsonInteger; }; 14 | template<> struct Value2JClassMap { using T = JsonDouble; }; 15 | template<> struct Value2JClassMap { using T = JsonBoolean; }; 16 | template<> struct Value2JClassMap { using T = JsonNull; }; 17 | template<> struct Value2JClassMap { using T = JsonArray; }; 18 | template<> struct Value2JClassMap { using T = JsonObject; }; 19 | 20 | /* json枚举类型jtype映射json数据类型T */ 21 | template struct JType2JClassMap; 22 | template<> struct JType2JClassMap { using T = JsonString; }; 23 | template<> struct JType2JClassMap { using T = JsonInteger; }; 24 | template<> struct JType2JClassMap { using T = JsonDouble; }; 25 | template<> struct JType2JClassMap { using T = JsonBoolean; }; 26 | template<> struct JType2JClassMap { using T = JsonNull; }; 27 | template<> struct JType2JClassMap { using T = JsonArray; }; 28 | template<> struct JType2JClassMap { using T = JsonObject; }; 29 | 30 | /* json枚举类型jtype映射实际值类型T */ 31 | template struct JType2ValueMap; 32 | template<> struct JType2ValueMap { using T = std::string; }; 33 | template<> struct JType2ValueMap { using T = int; }; 34 | template<> struct JType2ValueMap { using T = double; }; 35 | template<> struct JType2ValueMap { using T = bool; }; 36 | template<> struct JType2ValueMap { using T = decltype(nullptr); }; 37 | template<> struct JType2ValueMap { using T = JsonArray::ValueType; }; 38 | template<> struct JType2ValueMap { using T = JsonObject::ValueType; }; 39 | 40 | 41 | /* 判断json数据类型J是否匹配值类型V */ 42 | template 43 | struct is_Jclass_Match_Value{ 44 | bool operator() (){ 45 | return std::is_same::value; 46 | } 47 | }; 48 | 49 | } // namespace json 50 | 51 | 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /haha_web/net/Epoller.cpp: -------------------------------------------------------------------------------- 1 | #include "./Epoller.h" 2 | 3 | namespace haha { 4 | 5 | Epoller::Epoller() 6 | :epfd_(::epoll_create1(EPOLL_CLOEXEC)) 7 | ,events_(kInitEventListSize) 8 | { 9 | } 10 | 11 | Epoller::~Epoller() {} 12 | 13 | void Epoller::addEvent(int fd, uint32_t event) { 14 | struct epoll_event ee; 15 | ::memset(&ee, 0, sizeof(ee)); 16 | ee.data.fd = fd; 17 | ee.events = event; 18 | ::epoll_ctl(epfd_, EPOLL_CTL_ADD, fd, &ee); 19 | } 20 | 21 | void Epoller::delEvent(int fd){ 22 | ::epoll_ctl(epfd_, EPOLL_CTL_DEL, fd, nullptr); 23 | } 24 | 25 | void Epoller::modEvent(int fd, uint32_t event){ 26 | struct epoll_event ee; 27 | ::memset(&ee, 0, sizeof(ee)); 28 | ee.data.fd = fd; 29 | ee.events = event; 30 | ::epoll_ctl(epfd_, EPOLL_CTL_MOD, fd, &ee); 31 | } 32 | 33 | void Epoller::addChannel(Channel* channel){ 34 | /* epoll_event.data是联合体,不能同时对fd和ptr赋值!!!! */ 35 | struct epoll_event ee; 36 | ::memset(&ee, 0, sizeof(ee)); 37 | ee.data.ptr = static_cast(channel); 38 | // ee.data.fd = fd; 39 | ee.events = channel->getEvents(); 40 | ::epoll_ctl(epfd_, EPOLL_CTL_ADD, channel->getFd(), &ee); 41 | } 42 | 43 | void Epoller::modChannel(Channel* channel){ 44 | /* epoll_event.data是联合体,不能同时对fd和ptr赋值!!!! */ 45 | struct epoll_event ee; 46 | ::memset(&ee, 0, sizeof(ee)); 47 | ee.data.ptr = static_cast(channel); 48 | // ee.data.fd = fd; 49 | ee.events = channel->getEvents(); 50 | ::epoll_ctl(epfd_, EPOLL_CTL_MOD, channel->getFd(), &ee); 51 | } 52 | 53 | void Epoller::delChannel(Channel* channel){ 54 | ::epoll_ctl(epfd_, EPOLL_CTL_DEL, channel->getFd(), nullptr); 55 | } 56 | 57 | 58 | int Epoller::wait(int timeoutMs) { 59 | int num_events = ::epoll_wait(epfd_, events_.data(), static_cast(events_.size()), timeoutMs); 60 | if (num_events < 0){ 61 | if(errno != EINTR){ 62 | throw "epoll wait error"; 63 | } 64 | } 65 | 66 | if (num_events > (int)(events_.size() * 0.75)) 67 | events_.resize(num_events * 2); 68 | 69 | return num_events; 70 | } 71 | 72 | int Epoller::getEventFd(size_t i){ 73 | return events_[i].data.fd; 74 | } 75 | 76 | uint32_t Epoller::getReturnEvents(size_t i){ 77 | return events_[i].events; 78 | } 79 | 80 | Channel* Epoller::getActiveChannel(size_t i){ 81 | return static_cast(events_[i].data.ptr); 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /haha_web/base/Buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_BUFFER_H__ 2 | #define __HAHA_BUFFER_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace haha{ 20 | 21 | class Buffer{ 22 | /// A buffer class modeled after org.jboss.netty.buffer.ChannelBuffer 23 | /// 24 | /// @code 25 | /// +-------------------+------------------+------------------+ 26 | /// | prependable bytes | readable bytes | writable bytes | 27 | /// | | (CONTENT) | | 28 | /// +-------------------+------------------+------------------+ 29 | /// | | | | 30 | /// 0 <= readerIndex <= writerIndex <= size 31 | /// @endcode 32 | 33 | public: 34 | typedef std::shared_ptr ptr; 35 | // static const size_t kCheapPrepend = 8; 36 | static const size_t kInitialSize = 1024; 37 | 38 | Buffer(size_t initialSize = kInitialSize); 39 | ~Buffer() = default; 40 | // 可写字节数 41 | size_t WritableBytes() const; 42 | // 可读字节数 43 | size_t ReadableBytes() const ; 44 | // 头部预留字节数 45 | size_t PrependableBytes() const; 46 | // 数据可读处 47 | const char* Peek() const; 48 | 49 | void EnsureWriteable(size_t len); 50 | void HasWritten(size_t len); 51 | 52 | void Retrieve(size_t len); 53 | void RetrieveUntil(const char* end); 54 | 55 | void RetrieveAll() ; 56 | std::string RetrieveAllToStr(); 57 | 58 | const char* BeginWriteConst() const; 59 | char* BeginWrite(); 60 | 61 | void Append(const std::string& str); 62 | void Append(const char* str, size_t len); 63 | void Append(const void* data, size_t len); 64 | void Append(const Buffer& buff); 65 | 66 | ssize_t ReadFd(int fd, int* Errno); 67 | ssize_t WriteFd(int fd, int* Errno); 68 | 69 | ssize_t ReadSsl(SSL *ssl, int* Errno); 70 | ssize_t WriteSsl(SSL *ssl, int* Errno); 71 | 72 | private: 73 | char* BeginPtr_(); 74 | const char* BeginPtr_() const; 75 | void MakeSpace_(size_t len); 76 | 77 | std::vector buffer_; 78 | std::atomic readPos_; 79 | std::atomic writePos_; 80 | }; 81 | 82 | } 83 | 84 | #endif -------------------------------------------------------------------------------- /haha_web/log/LogUtil.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_LOG_LOGUTIL_H__ 2 | #define __HAHA_LOG_LOGUTIL_H__ 3 | 4 | #include 5 | #include 6 | #include "base/noncopyable.h" 7 | #include "base/ConditionVariable.h" 8 | 9 | namespace haha{ 10 | 11 | namespace log{ 12 | 13 | const int kSmallBuffer = 4000; 14 | const int kLargeBuffer = 4000*1000; 15 | const int kbuffersMaxSize = 25; 16 | 17 | template 18 | class FixedBuffer : noncopyable 19 | { 20 | public: 21 | FixedBuffer() 22 | : cur_(data_) 23 | { 24 | } 25 | 26 | void append(const char* /*restrict*/ buf, size_t len) 27 | { 28 | // FIXME: append partially 29 | if (static_cast(avail()) > len) 30 | { 31 | memcpy(cur_, buf, len); 32 | cur_ += len; 33 | } 34 | } 35 | 36 | const char* data() const { return data_; } 37 | int length() const { return static_cast(cur_ - data_); } 38 | 39 | // write to data_ directly 40 | char* current() { return cur_; } 41 | int avail() const { return static_cast(end() - cur_); } 42 | void add(size_t len) { cur_ += len; } 43 | 44 | void reset() { cur_ = data_; } 45 | void bzero() { ::bzero(data_, sizeof(data_)); } 46 | 47 | // for used by unit test 48 | std::string toString() const { return string(data_, length()); } 49 | std::string_view toStringView() const { return std::string_view(data_, length()); } 50 | 51 | private: 52 | const char* end() const { return data_ + sizeof(data_); } 53 | 54 | void (*cookie_)(); 55 | char data_[SIZE]; 56 | char* cur_; 57 | }; 58 | 59 | 60 | // compile time calculation of basename of source file 61 | class SourceFile 62 | { 63 | public: 64 | template 65 | SourceFile(const char (&arr)[N]) 66 | : data_(arr), 67 | size_(N-1) 68 | { 69 | const char* slash = strrchr(data_, '/'); // builtin function 70 | if (slash) 71 | { 72 | data_ = slash + 1; 73 | size_ -= static_cast(data_ - arr); 74 | } 75 | } 76 | 77 | explicit SourceFile(const char* filename) 78 | : data_(filename) 79 | { 80 | const char* slash = strrchr(filename, '/'); 81 | if (slash) 82 | { 83 | data_ = slash + 1; 84 | } 85 | size_ = static_cast(strlen(data_)); 86 | } 87 | 88 | const char* data_; 89 | int size_; 90 | }; 91 | 92 | } 93 | 94 | } 95 | 96 | #endif -------------------------------------------------------------------------------- /haha_web/http/HttpHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_HTTPHEADER_H__ 2 | #define __HAHA_HTTPHEADER_H__ 3 | 4 | #include 5 | #include 6 | #include "http/HttpMap.h" 7 | 8 | namespace haha{ 9 | 10 | // class HttpField{ 11 | // public: 12 | // HttpField(const std::string& key, const std::string& val, 13 | // const std::string &split, const std::string &stop) 14 | // :key_(key){ 15 | 16 | // } 17 | // private: 18 | // std::string key_; 19 | // std::vector vals_; 20 | // std::string split_; 21 | // std::string stop_; 22 | // }; 23 | 24 | class HttpHeader : public HttpMap{ 25 | public: 26 | HttpHeader():length_(0){} 27 | 28 | void add(const std::string& key, const std::string& value) override{ 29 | map_[toLowers(key)] = value; 30 | length_ += key.size() + value.size(); 31 | } 32 | 33 | void del(const std::string& key) override { 34 | ConstIterator it; 35 | it = map_.find(toLowers(key)); 36 | 37 | if(it != map_.end()){ 38 | length_ -= key.size() + it->second.size(); 39 | map_.erase(it); 40 | } 41 | } 42 | 43 | // 返回键值对数量 44 | size_t keyCount() const { return map_.size(); } 45 | 46 | // 返回总大小 47 | size_t size() const override { return length_; } 48 | 49 | bool empty() const override { return length_ > 0; } 50 | 51 | RetOption getCookie() const { return get("Cookie"); } 52 | 53 | RetOption getContentLength() const { return get("Content-Length"); } 54 | 55 | RetOption getTransferEncoding() const { return get("Transfer-Encoding"); } 56 | 57 | RetOption getContentType() const { return get("Content-Type"); } 58 | 59 | RetOption getConnection() const { return get("Connection"); } 60 | 61 | RetOption getAcceptEncoding() const { return get("Accept-Encoding"); } 62 | 63 | void setCookie(const std::string &val) { add("Cookie", val); } 64 | 65 | void setContentLength(const std::string &val) { add("Content-Length", val); } 66 | 67 | void setTransferEncoding(const std::string &val) { add("Transfer-Encoding", val); } 68 | 69 | void setContentType(const std::string &val) { add("Content-Type", val); } 70 | 71 | void setConnection(const std::string &val) { add("Connection", val); } 72 | 73 | void setAcceptEncoding(const std::string &val) { add("Accept-Encoding", val); } 74 | 75 | private: 76 | size_t length_; 77 | 78 | }; 79 | 80 | } 81 | 82 | 83 | #endif -------------------------------------------------------------------------------- /tests/test_servlet.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "http/Servlet.h" 4 | #include "http/HttpServer.h" 5 | 6 | // 工作目录为可执行文件所在目录,即bin目录 7 | // bin目录下有个resource目录,建议把你要加的html、图片之类的放那里边 8 | static const std::string BASE_ROOT = "../resource"; 9 | 10 | // 这里只是个示例,与默认处理近似,可自行修改 11 | // 但大体流程不建议变动 12 | void watch_dog(haha::HttpRequest::ptr req, haha::HttpResponse::ptr resp){ 13 | // auto &reqBody = req->getBody(); 14 | // // 如果请求中有session,就会返回一个已有的 15 | // // 如果没有或者是过期的,那就会创建一个新的session 16 | // haha::HttpSession::ptr session = req->getSession(); 17 | // // 我还没怎么测过session,可能有bug 18 | 19 | auto code = resp->getStatusCode(); 20 | // 出错,返回错误页面 21 | if(code >= 400){ 22 | resp->setContentType(haha::HttpContentType::HTML); 23 | resp->appendBody(haha::Servlet::basePage(code)); 24 | return; 25 | } 26 | 27 | std::string path = "/dog.html"; 28 | 29 | std::filesystem::path p(BASE_ROOT + std::string(path)); 30 | bool exists = std::filesystem::exists(p); 31 | 32 | if(exists){ 33 | std::string ext; 34 | if(p.has_extension()){ 35 | ext = std::string(p.extension().c_str()); 36 | } 37 | std::unordered_set accpetable_exts = { 38 | ".html", ".htm", ".txt", ".jpg", ".png" 39 | }; 40 | if(accpetable_exts.find(ext) != accpetable_exts.end()){ 41 | haha::HttpCookie cookie; 42 | cookie.add("liming", "hi"); 43 | resp->setCookie(cookie); 44 | resp->setContentType(haha::Ext2HttpContentType.at(ext)); 45 | resp->setFileBody(p.c_str()); 46 | } 47 | else{ 48 | resp->setContentType(haha::HttpContentType::HTML); 49 | resp->appendBody(haha::Servlet::basePage(code, "I don't know what do you really want")); 50 | } 51 | } 52 | else{ 53 | resp->setStatusCode(haha::HttpStatus::NOT_FOUND); 54 | resp->setContentType(haha::HttpContentType::HTML); 55 | resp->appendBody(haha::Servlet::basePage(haha::HttpStatus::NOT_FOUND)); 56 | return; 57 | } 58 | } 59 | 60 | 61 | int main(){ 62 | haha::HttpServer server; 63 | // 添加servlet到服务器,这边我把两种请求都映射到了watch_dog上 64 | server.addServlet("/dog", [](haha::HttpRequest::ptr req, haha::HttpResponse::ptr resp){ 65 | watch_dog(req, resp); 66 | }); 67 | server.addServlet("/watch_dog", [](haha::HttpRequest::ptr req, haha::HttpResponse::ptr resp){ 68 | watch_dog(req, resp); 69 | }); 70 | 71 | server.start(); 72 | return 0; 73 | } -------------------------------------------------------------------------------- /haha_web/base/util.cpp: -------------------------------------------------------------------------------- 1 | #include "base/util.h" 2 | #include 3 | #include 4 | 5 | namespace haha{ 6 | 7 | thread_local static const pid_t PID = gettid(); 8 | 9 | pid_t GetThreadId(){ 10 | return PID; 11 | } 12 | 13 | // 十六进制数转十进制数 14 | unsigned char hex2dec(char c) { 15 | if (c >= '0' && c <= '9') 16 | return c - '0'; // [0-9] 17 | else if (c >= 'a' && c <= 'z') 18 | return c - 'a' + 10; // a-f [10-15] 19 | else if (c >= 'A' && c <= 'Z') 20 | return c - 'A' + 10; // A-F [10-15] 21 | return c; 22 | } 23 | 24 | // 十进制数转十六进制数 25 | char dec2hex(char c) { 26 | if (c >= 0 && c <= 9) 27 | return c + '0'; // ['0'-'9'] 28 | else if (c >= 10 && c <= 15) 29 | return c - 10 + 'A'; // ['A'-'Z'] 30 | return c; 31 | } 32 | 33 | // 十六进制字符串转为int,如果转换失败返回-1 34 | int hexString2Int(const std::string& hexStr){ 35 | int res = 0; 36 | for(const auto &c : hexStr){ 37 | if(!isxdigit(c)){ 38 | return - 1; 39 | } 40 | res = res*10 + hex2dec(c); 41 | if(res < 0){ 42 | return -1; 43 | } 44 | } 45 | return res; 46 | } 47 | 48 | // 十六进制字符串转为int,如果转换失败返回-1 49 | int hexString2Int(std::string_view hexStr){ 50 | int res = 0; 51 | for(const auto &c : hexStr){ 52 | if(!isxdigit(c)){ 53 | return - 1; 54 | } 55 | res = res*10 + hex2dec(c); 56 | if(res < 0){ 57 | return -1; 58 | } 59 | } 60 | return res; 61 | } 62 | 63 | 64 | std::string int2HexString(int dec){ 65 | std::string hex; 66 | while(dec){ 67 | hex += dec2hex(dec % 10); 68 | dec /= 10; 69 | } 70 | std::reverse(hex.begin(), hex.end()); 71 | return hex; 72 | } 73 | 74 | 75 | void toLowers(char *s, size_t size) { 76 | for (size_t i = 0; i < size; i++) 77 | if (isupper(s[i])) 78 | s[i] = tolower(s[i]); 79 | } 80 | 81 | void toUppers(char *s, size_t size) { 82 | for (size_t i = 0; i < size; i++) 83 | if (islower(s[i])) 84 | s[i] = toupper(s[i]); 85 | } 86 | 87 | std::string toLowers(const std::string &s) { 88 | std::string ls(s); 89 | toLowers(ls.data(), ls.size()); 90 | return ls; 91 | } 92 | 93 | std::string toUppers(const std::string &s) { 94 | std::string us(s); 95 | toUppers(us.data(), us.size()); 96 | return us; 97 | } 98 | 99 | bool stringCaseCmp(const std::string &a, const std::string &b){ 100 | return toLowers(a) == toLowers(b); 101 | } 102 | 103 | } -------------------------------------------------------------------------------- /haha_web/log/LogInfo.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_LOG_LOGINFO_H__ 2 | #define __HAHA_LOG_LOGINFO_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "base/Thread.h" 9 | #include "LogStream.h" 10 | 11 | namespace haha{ 12 | 13 | namespace log{ 14 | 15 | // 日志级别 16 | class LogLevel{ 17 | public: 18 | enum Level{ 19 | UNKNOW = 0, 20 | DEBUG = 1, 21 | INFO = 2, 22 | WARN = 3, 23 | ERROR = 4, 24 | FATAL = 5 25 | }; 26 | 27 | static const char* toString(LogLevel::Level level); 28 | static LogLevel::Level FromString(const std::string &str); 29 | }; 30 | 31 | // 日志事件 32 | class LogInfo{ 33 | public: 34 | using OutStream = LogStream; 35 | using TimePoint = std::chrono::_V2::system_clock::time_point; 36 | typedef std::shared_ptr ptr; 37 | LogInfo(const std::string &loggerName, 38 | LogLevel::Level level, 39 | const char *file, 40 | int32_t line, 41 | uint32_t elapse, 42 | uint32_t thread_id, 43 | // uint32_t fiber_id, 44 | const TimePoint& time); 45 | 46 | const std::string& getLoggerName() const {return loggerName_;} 47 | const SourceFile& getFile() const {return file_;} 48 | int32_t getLine() const {return line_;} 49 | uint32_t getElapse() const {return elapse_;} 50 | uint32_t getThreadId() const {return threadId_;} 51 | 52 | // uint32_t getFiberId() const {return m_fiberId;} 53 | 54 | const TimePoint& getTime() const {return time_;} 55 | const std::string& getThreadName() const { return haha::Thread::getCurrentThreadName();} 56 | const OutStream::Buffer& getContent() const {return ss_.buffer();} 57 | LogLevel::Level getLevel() const {return level_;} 58 | 59 | OutStream& getSS() {return ss_;} 60 | void format(const char* fmt, ...); 61 | void format(const char* fmt, va_list al); 62 | 63 | private: 64 | std::string loggerName_; // 日志器名称 65 | SourceFile file_; // 文件名 66 | int32_t line_ = 0; // 行号 67 | uint32_t elapse_ = 0; // 启动到现在的毫秒数 68 | uint32_t threadId_ = 0; // 线程ID 69 | // uint32_t m_fiberId = 0; // 协程ID 70 | // uint64_t time_; // 时间戳 71 | TimePoint time_; // 时间戳 72 | // std::string threadName_; // 线程名 73 | OutStream ss_; // 事件内容 74 | 75 | LogLevel::Level level_; 76 | }; 77 | 78 | } 79 | 80 | } 81 | 82 | #endif -------------------------------------------------------------------------------- /haha_web/haha_json/haha_json/jsonError.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_JSON_JSONERROR_H__ 2 | #define __HAHA_JSON_JSONERROR_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define HAHA_JSON_ERROR(msg) JsonError(__FILE__, __LINE__, msg) 9 | 10 | namespace haha { 11 | 12 | namespace json 13 | { 14 | 15 | class JsonError : public std::exception { 16 | public: 17 | JsonError() {} 18 | explicit JsonError(const char* file, int line, const std::string &msg) 19 | { 20 | std::stringstream ss; 21 | ss << "SyntaxError: [" << file << ":" 22 | << std::to_string(line) << "]: [" << msg << "]"; 23 | __error_msg = ss.str(); 24 | } 25 | virtual ~JsonError() {} 26 | virtual const char *what() const noexcept { return __error_msg.c_str(); } 27 | 28 | protected: 29 | std::string __error_msg; 30 | }; 31 | 32 | // class JsonNumberError : public JsonError { 33 | // public: 34 | // JsonNumberError(int line, int col, const std::string &msg) 35 | // : __line(line), __col(col) { 36 | // std::stringstream ss; 37 | // ss << "SyntaxError: [" << std::to_string(__line) << "," 38 | // << std::to_string(__col) << "] => [" << msg << "]"; 39 | // __error_msg = ss.str(); 40 | // } 41 | // int line() const { return __line; } 42 | // int col() const { return __col; } 43 | // protected: 44 | // int __line, __col; 45 | // }; 46 | 47 | 48 | // class JsonTypeCastError : public JsonError { 49 | // public: 50 | // JsonTypeCastError(int line, int col, const std::string &msg) 51 | // : __line(line), __col(col) { 52 | // std::stringstream ss; 53 | // ss << "SyntaxError: [" << std::to_string(__line) << "," 54 | // << std::to_string(__col) << "] => [" << msg << "]"; 55 | // __error_msg = ss.str(); 56 | // } 57 | // int line() const { return __line; } 58 | // int col() const { return __col; } 59 | // protected: 60 | // int __line, __col; 61 | // }; 62 | 63 | 64 | // class JsonSyntaxError : public JsonError { 65 | // public: 66 | // JsonSyntaxError(int line, int col, const std::string &msg) 67 | // : __line(line), __col(col) { 68 | // std::stringstream ss; 69 | // ss << "SyntaxError: [" << std::to_string(__line) << "," 70 | // << std::to_string(__col) << "] => [" << msg << "]"; 71 | // __error_msg = ss.str(); 72 | // } 73 | // int line() const { return __line; } 74 | // int col() const { return __col; } 75 | 76 | // protected: 77 | // int __line, __col; 78 | // }; 79 | 80 | } // namespace json 81 | 82 | } // namespace haha 83 | #endif 84 | -------------------------------------------------------------------------------- /haha_web/net/TcpServer.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_TCPSERVER_H__ 2 | #define __HAHA_TCPSERVER_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "log/Log.h" 10 | #include "config/config.h" 11 | #include "base/Mutex.h" 12 | #include "base/ReadWriteLock.h" 13 | #include "InetAddress.h" 14 | #include "Socket.h" 15 | #include "EventLoopThreadPool.h" 16 | #include "Channel.h" 17 | #include "EventLoop.h" 18 | #include "TcpConnection.h" 19 | 20 | namespace haha{ 21 | 22 | class TcpServer{ 23 | 24 | public: 25 | typedef std::unordered_map> ConnectMap; 26 | // typedef CASLock MutexType; 27 | typedef SpinLock MutexType; 28 | 29 | enum MESSAGE_STATUS{ 30 | OK, 31 | AGAIN, 32 | }; 33 | 34 | TcpServer(); 35 | void start(const InetAddress &address); 36 | void start(); 37 | 38 | EventLoop::ptr getMainLoop() { return mainLoop_; } 39 | 40 | bool isHttps() const { return enable_https; } 41 | 42 | private: 43 | void handleServerAccept(); 44 | void handleRead(int fd); 45 | void handleWrite(int fd); 46 | void handleClose(int fd); 47 | void handleTimeout(int fd); 48 | void handleWakeup(); 49 | void handleRead(); 50 | void handleWrite(); 51 | void handleClose(); 52 | 53 | /* 因为函数都会被注册为回调函数,因此就不能持有shared_ptr,不然无法释放 */ 54 | 55 | void handleConnectionRead(TcpConnection::weak_ptr); 56 | void handleConnectionWrite(TcpConnection::weak_ptr); 57 | void handleConnectionClose(TcpConnection::weak_ptr); 58 | 59 | void onSend(TcpConnection::weak_ptr); 60 | void onRecv(TcpConnection::weak_ptr); 61 | void onConnect(TcpConnection::weak_ptr); 62 | 63 | protected: 64 | virtual MESSAGE_STATUS onMessage(TcpConnection::ptr); 65 | virtual bool onCreateConnection(TcpConnection::ptr); 66 | virtual bool onCloseConntection(TcpConnection::ptr); 67 | 68 | private: 69 | // 定时器轮询间隔 70 | int timerInterval_; 71 | 72 | // 连接超时时间 73 | int connTimeOut_; 74 | 75 | // 事件循环线程池 76 | EventLoopThreadPool *threadPool_; 77 | 78 | // 主事件循环,用于接收连接 79 | EventLoop::ptr mainLoop_; 80 | 81 | // 是否开启https 82 | bool enable_https; 83 | 84 | // ssl连接上下文 85 | std::shared_ptr sslCtx_; 86 | 87 | // listen sock 88 | std::unique_ptr servSock_; 89 | 90 | // listen channel 91 | Channel::ptr listenChannel_; 92 | 93 | MutexType connMtx_; 94 | ConnectMap connects_; 95 | 96 | int evfd_; 97 | }; 98 | 99 | } 100 | 101 | #endif -------------------------------------------------------------------------------- /haha_web/log/LogStream.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_LOG_LOGSTREAM_H__ 2 | #define __HAHA_LOG_LOGSTREAM_H__ 3 | 4 | #include 5 | #include 6 | #include "base/noncopyable.h" 7 | #include "log/LogUtil.h" 8 | 9 | namespace haha{ 10 | 11 | namespace log{ 12 | 13 | class LogStream{ 14 | typedef LogStream self; 15 | public: 16 | typedef FixedBuffer Buffer; 17 | 18 | self& operator<<(bool v) 19 | { 20 | buffer_.append(v ? "1" : "0", 1); 21 | return *this; 22 | } 23 | 24 | self& operator<<(short); 25 | self& operator<<(unsigned short); 26 | self& operator<<(int); 27 | self& operator<<(unsigned int); 28 | self& operator<<(long); 29 | self& operator<<(unsigned long); 30 | self& operator<<(long long); 31 | self& operator<<(unsigned long long); 32 | 33 | self& operator<<(const void*); 34 | 35 | self& operator<<(float v) 36 | { 37 | *this << static_cast(v); 38 | return *this; 39 | } 40 | self& operator<<(double); 41 | // self& operator<<(long double); 42 | 43 | self& operator<<(char v) 44 | { 45 | buffer_.append(&v, 1); 46 | return *this; 47 | } 48 | 49 | // self& operator<<(signed char); 50 | // self& operator<<(unsigned char); 51 | 52 | self& operator<<(const char* str) 53 | { 54 | if (str) 55 | { 56 | buffer_.append(str, strlen(str)); 57 | } 58 | else 59 | { 60 | buffer_.append("(null)", 6); 61 | } 62 | return *this; 63 | } 64 | 65 | self& operator<<(const unsigned char* str) 66 | { 67 | return operator<<(reinterpret_cast(str)); 68 | } 69 | 70 | self& operator<<(const std::string& v) 71 | { 72 | buffer_.append(v.c_str(), v.size()); 73 | return *this; 74 | } 75 | 76 | self& operator<<(const std::string_view& v) 77 | { 78 | buffer_.append(v.data(), v.size()); 79 | return *this; 80 | } 81 | 82 | self& operator<<(const Buffer& v) 83 | { 84 | *this << v.toStringView(); 85 | return *this; 86 | } 87 | 88 | void append(const char* data, int len) { buffer_.append(data, len); } 89 | const Buffer& buffer() const { return buffer_; } 90 | void resetBuffer() { buffer_.reset(); } 91 | 92 | private: 93 | void staticCheck(); 94 | 95 | template 96 | void formatInteger(T); 97 | 98 | Buffer buffer_; 99 | 100 | static const int kMaxNumericSize = 48; 101 | }; 102 | 103 | } 104 | 105 | } 106 | 107 | #endif -------------------------------------------------------------------------------- /haha_web/http/HttpCookie.cpp: -------------------------------------------------------------------------------- 1 | #include "http/HttpCookie.h" 2 | 3 | namespace haha{ 4 | 5 | HttpCookie::HttpCookie(const std::string &str) 6 | :secure_(false) 7 | ,httpOnly_(false){ 8 | std::string decoded_str = EncodeUtil::urlDecode(str); 9 | std::string key; 10 | std::string val; 11 | size_t j = 0; 12 | size_t i = 0; 13 | while(i < decoded_str.size()){ 14 | if(i == decoded_str.size()-1 || decoded_str[i] == ';'){ 15 | i = decoded_str[i] == ';' ? i : i+1; 16 | if(key.empty()){ 17 | std::string attr = toLowers(decoded_str.substr(j, i-j)); 18 | if(attr == "secure"){ 19 | secure_ = true; 20 | } 21 | else if(attr == "httponly"){ 22 | httpOnly_ = true; 23 | } 24 | } 25 | else{ 26 | std::string attr = toLowers(key); 27 | val = decoded_str.substr(j, i-j); 28 | if(attr == "domain"){ 29 | domain_ = val; 30 | } 31 | else if(attr == "path"){ 32 | path_ = val; 33 | } 34 | else if(attr == "expires"){ 35 | expires_ = val; 36 | } 37 | else if(attr == "max-age"){ 38 | maxAge_ = std::stoi(val); 39 | } 40 | else if(attr == "sessionid"){ 41 | sessionId_ = val; 42 | } 43 | else{ 44 | add(key, val); 45 | } 46 | } 47 | ++i; 48 | while(i < decoded_str.size() && decoded_str[i] == ' ')++i; 49 | j = i; 50 | key.clear(); 51 | val.clear(); 52 | } 53 | else if(decoded_str[i] == '='){ 54 | key = decoded_str.substr(j, i-j); 55 | j = i + 1; 56 | ++i; 57 | } 58 | else{ 59 | ++i; 60 | } 61 | } 62 | } 63 | 64 | std::string HttpCookie::toString() const{ 65 | std::string str; 66 | int i = 0; 67 | int n = size(); 68 | for (const auto &[key, value] : map_) { 69 | str += key + "=" + value; 70 | if (i++ < n-1){ 71 | str += "; "; 72 | } 73 | } 74 | if (!expires_.empty()) 75 | str += "; Expires=" + expires_; 76 | if (!domain_.empty()) 77 | str += "; Domain=" + domain_; 78 | if (!path_.empty()) 79 | str += "; Path=" + path_; 80 | if (maxAge_ > 0) 81 | str += "; Max-Age=" + std::to_string(maxAge_); 82 | if (secure_) 83 | str += "; Secure"; 84 | if (httpOnly_) 85 | str += "; HttpOnly"; 86 | return str; 87 | } 88 | 89 | 90 | } -------------------------------------------------------------------------------- /haha_web/http/HttpSession.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_HTTPSESSION_H__ 2 | #define __HAHA_HTTPSESSION_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "base/noncopyable.h" 9 | #include "base/ReadWriteLock.h" 10 | #include "http/HttpMap.h" 11 | #include "base/RetOption.h" 12 | #include "base/uuid.h" 13 | 14 | namespace haha{ 15 | 16 | class HttpSession : public HttpBaseMap{ 17 | public: 18 | typedef std::shared_ptr ptr; 19 | HttpSession(); 20 | enum Status { New, Accessed, Destroy }; 21 | const std::string& getId() const { 22 | ReadWriteLock::RAIIReadLock lock(mutex_); 23 | return id_; 24 | } 25 | bool isNew() const { 26 | ReadWriteLock::RAIIReadLock lock(mutex_); 27 | return status_ == Status::New; 28 | } 29 | bool isAccessed() const { 30 | ReadWriteLock::RAIIReadLock lock(mutex_); 31 | return status_ == Status::Accessed; 32 | } 33 | bool isDestroy() const { 34 | ReadWriteLock::RAIIReadLock lock(mutex_); 35 | return status_ == Status::Destroy; 36 | } 37 | int getMaxInactiveInterval() const { 38 | ReadWriteLock::RAIIReadLock lock(mutex_); 39 | return interval_; 40 | } 41 | 42 | void setStatus(Status status) { 43 | ReadWriteLock::RAIIWriteLock lock(mutex_); 44 | status_ = status; 45 | } 46 | 47 | template 48 | void setValue(const std::string& key, const V& val){ 49 | ReadWriteLock::RAIIWriteLock lock(mutex_); 50 | add(key, std::forward(val)); 51 | } 52 | 53 | template 54 | const V* getValue(const std::string& key) const { 55 | ReadWriteLock::RAIIReadLock lock(mutex_); 56 | auto ret = get(key); 57 | if(!ret.exist()){ 58 | return nullptr; 59 | } 60 | try{ 61 | return &(std::any_cast(ret.value())); 62 | } 63 | catch(std::bad_any_cast &){ 64 | 65 | } 66 | return nullptr; 67 | } 68 | 69 | private: 70 | std::string id_; 71 | Status status_; 72 | int interval_; 73 | 74 | mutable ReadWriteLock mutex_; 75 | }; 76 | 77 | 78 | class HttpSessionManager : public noncopyable{ 79 | public: 80 | static HttpSessionManager& getInstance(){ 81 | static HttpSessionManager manger; 82 | return manger; 83 | } 84 | RetOption getSession(const std::string &id) const; 85 | void addSession(const std::string &id, HttpSession::ptr session); 86 | HttpSession::ptr newSession(); 87 | void delSession(const std::string &id); 88 | private: 89 | mutable ReadWriteLock mutex_; 90 | std::unordered_map sessions_; 91 | }; 92 | 93 | } 94 | 95 | #endif -------------------------------------------------------------------------------- /haha_web/net/Socket.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_SOCKET_H__ 2 | #define __HAHA_SOCKET_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include "log/Log.h" 16 | #include "InetAddress.h" 17 | #include "base/Buffer.h" 18 | 19 | namespace haha{ 20 | 21 | namespace sockops{ 22 | 23 | /*创建非阻塞socket*/ 24 | int createNBSocket(); 25 | /*创建默认(阻塞)socket*/ 26 | int createSocket(); 27 | void setNonBlocking(int fd); 28 | /*禁用Nagle算法*/ 29 | void setNoDelay(int fd); 30 | /*获取socket对端地址*/ 31 | InetAddress getPeerName(int fd); 32 | /*获取socket本端地址*/ 33 | InetAddress getSockName(int fd); 34 | 35 | /*创建SSL_CTX*/ 36 | std::shared_ptr createSslCtx(const std::string &certfile, 37 | const std::string &pkfile, 38 | const std::string &password); 39 | } 40 | 41 | class Socket{ 42 | public: 43 | typedef std::shared_ptr ptr; 44 | 45 | enum FDTYPE{ 46 | BLOCK, 47 | NONBLOCK, 48 | }; 49 | 50 | // 根据已有描述符创建一个socket 51 | explicit Socket(int fd, FDTYPE fdtype); 52 | // 新生成一个socket,默认读写阻塞 53 | explicit Socket(FDTYPE fdtype = FDTYPE::BLOCK); 54 | 55 | virtual ~Socket(); 56 | 57 | int getFd() const noexcept { return fd_; } 58 | bool isBlocked() { return isBlocked_; } 59 | 60 | // 获取本地地址 61 | InetAddress getLocalAddress(); 62 | // 获取远端地址 63 | InetAddress getRemoteAddress(); 64 | 65 | // 设置非阻塞 66 | void setNonBlocking(); 67 | 68 | // 绑定地址 69 | void bind(const InetAddress &address); 70 | // 创建缓冲区 71 | void listen(); 72 | 73 | // 接受连接, 默认返回读写阻塞的socket 74 | virtual Socket::ptr accept(); 75 | 76 | // 开启地址复用 77 | void enableReuseAddr(bool on); 78 | // 开启端口复用 79 | void enableReusePort(bool on); 80 | // 开启保活 81 | void enableKeepAlive(bool on); 82 | // 忽略SIGPIPE信号 83 | void ignoreSIGPIPE(bool on); 84 | 85 | // 把buff中的数据发到对端 86 | virtual int send(Buffer::ptr buff, int *lastLen = nullptr); 87 | // 从对端读取数据存到buff中 88 | virtual int recv(Buffer::ptr buff, int *lastLen = nullptr); 89 | 90 | protected: 91 | int fd_; 92 | bool isBlocked_; 93 | }; 94 | 95 | 96 | class SslSocket : public Socket{ 97 | public: 98 | explicit SslSocket(int fd, FDTYPE fdtype, std::shared_ptr ctx); 99 | 100 | ~SslSocket(); 101 | 102 | // 接受连接, 默认返回读写阻塞的socket 103 | Socket::ptr accept() override; 104 | 105 | // 把buff中的数据发到对端 106 | int send(Buffer::ptr buff, int *lastLen = nullptr) override; 107 | // 从对端读取数据存到buff中 108 | int recv(Buffer::ptr buff, int *lastLen = nullptr) override; 109 | 110 | private: 111 | std::shared_ptr ctx_ = nullptr; 112 | SSL *ssl_ = nullptr; 113 | }; 114 | 115 | } 116 | 117 | #endif -------------------------------------------------------------------------------- /haha_web/base/Mutex.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_LOCK_H__ 2 | #define __HAHA_LOCK_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "base/noncopyable.h" 9 | 10 | namespace haha{ 11 | 12 | /* 信号量 */ 13 | class Semaphore{ 14 | public: 15 | Semaphore(uint32_t count = 0); 16 | ~Semaphore(); 17 | 18 | void wait(); 19 | void notify(); 20 | private: 21 | Semaphore(const Semaphore&) = delete; 22 | Semaphore(const Semaphore&&) = delete; 23 | Semaphore& operator=(const Semaphore&) = delete; 24 | 25 | private: 26 | sem_t m_semaphore; 27 | }; 28 | 29 | /* 实现普通锁(不区分读写)的RAII机制 */ 30 | template 31 | struct LockGuard : public noncopyable{ 32 | public: 33 | LockGuard(T& mutex) 34 | :m_mutex(mutex){ 35 | m_mutex.lock(); 36 | m_locked = true; 37 | } 38 | 39 | ~LockGuard(){ 40 | if(m_locked){ 41 | m_mutex.unlock(); 42 | m_locked = false; 43 | } 44 | } 45 | 46 | T& getLock(){ 47 | return m_mutex; 48 | } 49 | 50 | private: 51 | T& m_mutex; 52 | bool m_locked; 53 | }; 54 | 55 | template 56 | class LockInterface : public noncopyable{ 57 | public: 58 | virtual void lock() = 0; 59 | virtual void unlock() = 0; 60 | M& getMutex() { return m_mutex; } 61 | protected: 62 | M m_mutex; 63 | }; 64 | 65 | 66 | /* 互斥锁 */ 67 | class MutexLock : public LockInterface{ 68 | public: 69 | typedef LockGuard RAIILock; 70 | MutexLock(){ 71 | pthread_mutex_init(&m_mutex, nullptr); 72 | } 73 | 74 | ~MutexLock(){ 75 | pthread_mutex_destroy(&m_mutex); 76 | } 77 | 78 | void lock() override{ 79 | pthread_mutex_lock(&m_mutex); 80 | } 81 | 82 | void unlock() override{ 83 | pthread_mutex_unlock(&m_mutex); 84 | } 85 | }; 86 | 87 | /* 自旋锁 */ 88 | class SpinLock : public LockInterface{ 89 | public: 90 | typedef LockGuard RAIILock; 91 | SpinLock(){ 92 | pthread_spin_init(&m_mutex, 0); 93 | } 94 | 95 | ~SpinLock(){ 96 | pthread_spin_destroy(&m_mutex); 97 | } 98 | 99 | void lock() override{ 100 | pthread_spin_lock(&m_mutex); 101 | } 102 | 103 | void unlock() override{ 104 | pthread_spin_unlock(&m_mutex); 105 | } 106 | }; 107 | 108 | /* CAS原子锁 */ 109 | class CASLock : public LockInterface{ 110 | public: 111 | typedef LockGuard RAIILock; 112 | CASLock(){ 113 | m_mutex.clear(); 114 | } 115 | 116 | ~CASLock(){} 117 | 118 | void lock() override{ 119 | while(std::atomic_flag_test_and_set_explicit(&m_mutex, std::memory_order_acquire)); 120 | } 121 | 122 | void unlock() override{ 123 | std::atomic_flag_clear_explicit(&m_mutex, std::memory_order_release); 124 | } 125 | }; 126 | 127 | 128 | } 129 | 130 | #endif -------------------------------------------------------------------------------- /haha_web/haha_json/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(haha_json) 3 | 4 | set(CMAKE_VERBOSE_MAKEFILE ON) # make 过程中显示信息 5 | 6 | if(NOT CMAKE_BUILD_TYPE) 7 | set(CMAKE_BUILD_TYPE "Release") 8 | endif() 9 | 10 | set(CXX_FLAGS 11 | -g 12 | # -MMD 13 | -std=c++2a 14 | -rdynamic 15 | # -DVALGRIND 16 | -DCHECK_PTHREAD_RETURN_VALUE 17 | -D_FILE_OFFSET_BITS=64 18 | -Wall 19 | -Wno-deprecated 20 | -Werror 21 | -Wno-unused-function 22 | -Wno-builtin-macro-redefined 23 | ) 24 | 25 | if(CMAKE_BUILD_TYPE AND (CMAKE_BUILD_TYPE STREQUAL "Debug")) 26 | set(CMAKE_CXX_FLAGS_DEBUG "-O0") 27 | message("Debug mode:${CMAKE_CXX_FLAGS_DEBUG}") 28 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin/debug) # 设置可执行文件的位置 29 | SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib/debug) # 设置动态链接库的位置 30 | elseif(CMAKE_BUILD_TYPE AND (CMAKE_BUILD_TYPE STREQUAL "Release")) 31 | set(CMAKE_CXX_FLAGS_RELEASE "-O2") 32 | message("Release mode:${CMAKE_CXX_FLAGS_RELEASE}") 33 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin/release) # 设置可执行文件的位置 34 | SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib/release) # 设置动态链接库的位置 35 | else() 36 | message("else:${CMAKE_BUILD_TYPE}") 37 | message("else:${CMAKE_CXX_FLAGS_RELEASE}") 38 | set(CMAKE_BUILD_TYPE "Release") 39 | set(CMAKE_CXX_FLAGS_RELEASE "-O2") 40 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin/release) # 设置可执行文件的位置 41 | SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib/release) # 设置动态链接库的位置 42 | endif() 43 | 44 | string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CXX_FLAGS}") 45 | message(STATUS "CMAKE_CXX_FLAGS = " ${CMAKE_CXX_FLAGS}) 46 | message(STATUS "CMAKE_BUILD_TYPE = " ${CMAKE_BUILD_TYPE}) 47 | 48 | # # 设置编译参数 49 | # set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -rdynamic -O0 -ggdb -std=c++2a -Wall -Wno-deprecated -Werror -Wno-unused-function \ 50 | # -Wno-builtin-macro-redefined") 51 | 52 | # # 设置编译参数 53 | # set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -rdynamic -O2 -ggdb -std=c++2a -Wall -Wno-deprecated -Werror -Wno-unused-function \ 54 | # -Wno-builtin-macro-redefined") 55 | 56 | include_directories(. ./haha_json) 57 | # include_directories(${YAML_CPP_INCLUE}) 58 | include_directories(/usr/local/include) # 添加头文件路径 59 | link_directories(/usr/local/lib) # 添加库文件路径 60 | 61 | 62 | aux_source_directory(${PROJECT_SOURCE_DIR}/haha_json/ SRC_LIST) # 将制定路径内的源文件全部加入变量中 63 | 64 | set( 65 | LIB_SRC 66 | ${SRC_LIST} 67 | ) 68 | 69 | # MESSAGE( STATUS "LIB_SRC = ${LIB_SRC}.") 70 | 71 | add_library(haha_json SHARED ${LIB_SRC}) # SHARED 指示生成so文件 即动态链接库 72 | 73 | set( 74 | ALL_LIBS 75 | haha_json 76 | pthread 77 | uuid 78 | ) 79 | 80 | add_executable(jsonParseTest.out tests/test_json.cpp) # 设置要生成可执行文件的代码 81 | add_dependencies(jsonParseTest.out haha_json) # 设置依赖关系 82 | target_link_libraries(jsonParseTest.out ${ALL_LIBS}) # 设置要链接的库 -------------------------------------------------------------------------------- /tests/test_lang.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define varName(x) #x 9 | #define typeName(x) typeid(x).name() 10 | 11 | class Data{ 12 | public: 13 | ~Data(){ 14 | std::cout << "~Data" << std::endl; 15 | } 16 | private: 17 | int cc = 0; 18 | }; 19 | 20 | class Heyhey{ 21 | public: 22 | Heyhey() = default; 23 | ~Heyhey(){ 24 | std::cout << "~Heyhey" << std::endl; 25 | } 26 | explicit Heyhey(int x) {x_ = x; std::cout << "int init" << std::endl;} 27 | explicit Heyhey(bool hehe) {x_ = 12345; std::cout << "bool init" << std::endl;} 28 | void oh(){ 29 | std::cout << "who are you ? " << x_ << std::endl; 30 | } 31 | 32 | void shit(){ 33 | std::function func = std::bind(&Heyhey::oh, this); 34 | func(); 35 | } 36 | 37 | private: 38 | int x_; 39 | Data data_; 40 | }; 41 | 42 | void test_pointer(){ 43 | // std::string s("haha"); 44 | // Heyhey hey; 45 | // Heyhey hey1(3); 46 | // Heyhey hey2(true); 47 | // std::unique_ptr ptr = std::make_unique(5); 48 | // std::function func = std::bind(&Heyhey::shit, ptr.get()); 49 | // hey.shit(); 50 | // func(); 51 | // std::shared_ptr hey3; 52 | // std::cout << (hey3 == nullptr) << std::endl; 53 | 54 | // Heyhey *hey = new Heyhey(); 55 | // std::shared_ptr eeee; 56 | // eeee.reset(hey); 57 | // std::shared_ptr oh; 58 | // oh = std::static_pointer_cast(eeee); 59 | // std::cout << eeee.use_count() << std::endl; 60 | } 61 | 62 | void test_filesystem(){ 63 | std::filesystem::path p("./resource/index.html"); 64 | bool ret = std::filesystem::exists(p); 65 | if(!ret){ 66 | std::cout << "no such file" << std::endl; 67 | } 68 | else{ 69 | auto fsz = std::filesystem::file_size(p); 70 | std::cout << "size: " << fsz << std::endl; 71 | std::cout << "full path: " << p << std::endl; 72 | std::cout << "file name: " << p.stem() << std::endl; 73 | std::cout << "file extention: " << p.extension() << std::endl; 74 | } 75 | } 76 | 77 | void test_stringview(){ 78 | std::string str("haha"); 79 | std::string_view view(str); 80 | std::cout << view << std::endl; 81 | str = "xixixixixixixixixixixixi"; 82 | std::cout << view << std::endl; 83 | } 84 | 85 | void test_var_type_name(){ 86 | Data d; 87 | auto vname = varName(1); 88 | auto tname = typeName(d); 89 | std::cout << vname << std::endl; 90 | std::cout << tname << std::endl; 91 | } 92 | 93 | void test_unique_move(){ 94 | std::unique_ptr p = std::make_unique(); 95 | auto t = std::move(p); 96 | std::cout << "---" << std::endl; 97 | } 98 | 99 | 100 | int main(){ 101 | 102 | // test_stringview(); 103 | // test_filesystem(); 104 | // test_var_type_name(); 105 | test_unique_move(); 106 | 107 | return 0; 108 | } -------------------------------------------------------------------------------- /haha_web/log/Logger.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_LOG_LOGGER_H__ 2 | #define __HAHA_LOG_LOGGER_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "base/Mutex.h" 8 | #include "base/Thread.h" 9 | #include "config/config.h" 10 | #include "log/LogInfo.h" 11 | #include "log/LogAppender.h" 12 | 13 | // %m -- 消息体 14 | // %p -- level 15 | // %r -- 启动后的时间 16 | // %c -- 日志名称 17 | // %t -- 线程id 18 | // %n -- 回车换行 19 | // %N -- 线程名称 20 | // %d -- 时间 21 | // %f -- 文件名 22 | // %l -- 行号 23 | // %T -- tab 24 | 25 | namespace haha{ 26 | 27 | namespace log{ 28 | 29 | // 日志器 30 | class Logger : public std::enable_shared_from_this{ 31 | public: 32 | typedef std::shared_ptr ptr; 33 | typedef MutexLock MutexType; 34 | 35 | Logger(const std::string &name = "root", LogAppender::ptr appender = nullptr):name_(name),level_(LogLevel::DEBUG){ 36 | formatter_.reset(new LogFormatter( 37 | haha::config::GET_CONFIG("log.default_format", "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%T[%p]%T[%c]%T%f:%l%T%m%n"))); 38 | 39 | if(appender){ 40 | if(!appender->getFormatter()){ 41 | appender->setFormatter(formatter_); 42 | } 43 | appenders_.push_back(appender); 44 | } 45 | } 46 | 47 | virtual ~Logger() {} 48 | 49 | virtual void log(LogInfo::ptr info) = 0; 50 | 51 | virtual void addAppender(LogAppender::ptr appender); 52 | void delAppender(LogAppender::ptr appender); 53 | void clearAppenders(); 54 | LogLevel::Level getLevel() const {return level_;} 55 | void setLevel(LogLevel::Level val){level_ = val;} 56 | const std::string& getName() const {return name_;} 57 | 58 | void setFormatter(LogFormatter::ptr val); 59 | void setFormatter(const std::string &val); 60 | LogFormatter::ptr getFormatter(); 61 | 62 | protected: 63 | std::string name_; // 日志名称 64 | LogLevel::Level level_; // 日志级别 65 | MutexType mutex_; 66 | std::list appenders_; // Appender集合 67 | LogFormatter::ptr formatter_; 68 | }; 69 | 70 | // 同步日志器 71 | class SyncLogger : public Logger{ 72 | public: 73 | SyncLogger(const std::string &name, SyncLogAppender::ptr appender = nullptr); 74 | void log(LogInfo::ptr info) override; 75 | protected: 76 | LogAppender::ptr defaultAppender_; 77 | }; 78 | 79 | // 异步日志器 80 | class AsyncLogger : public Logger{ 81 | public: 82 | typedef ConditionVariable::ptr Condition; 83 | 84 | AsyncLogger(const std::string &name, int flushInterval, AsyncLogAppender::ptr appender = nullptr); 85 | ~AsyncLogger(){ 86 | running_ = false; 87 | cond_->notify_all(); 88 | thread_->join(); 89 | } 90 | 91 | void log(LogInfo::ptr info) override; 92 | void addAppender(LogAppender::ptr appender) override; 93 | 94 | private: 95 | void task(); 96 | 97 | private: 98 | const int flushInterval_; 99 | MutexType mutex_; 100 | Condition cond_; 101 | std::atomic running_; 102 | std::unique_ptr thread_; 103 | }; 104 | 105 | } 106 | 107 | } 108 | 109 | #endif -------------------------------------------------------------------------------- /haha_web/http/HttpUrl.cpp: -------------------------------------------------------------------------------- 1 | #include "http/HttpUrl.h" 2 | 3 | namespace haha{ 4 | 5 | /* ************************************************HTTP URL************************************************ */ 6 | 7 | HttpUrl::HttpUrl(const std::string& url, bool decode){ 8 | if(decode){ 9 | url_ = EncodeUtil::urlDecode(url); 10 | } 11 | if(!url.empty() && url_.empty()){ 12 | state_ = PARSE_FAIL; 13 | } 14 | else{ 15 | state_ = parseFromUrl(); 16 | } 17 | } 18 | 19 | HttpUrl::STATE HttpUrl::parseFromUrl(){ 20 | // size_t n = url_.size(); 21 | std::string_view url_view(url_); 22 | size_t pos; 23 | 24 | if(url_view.compare(0, 4, "http") == 0){ 25 | // 有host 26 | // 解析scheme 27 | pos = url_view.find("://"); 28 | if(pos == url_view.npos){ 29 | return PARSE_FAIL; 30 | } 31 | std::string_view scheme_strview = url_view.substr(0, pos); 32 | if(scheme_strview == "http"){ 33 | scheme_ = HTTP; 34 | } 35 | else if(scheme_strview == "https"){ 36 | scheme_ = HTTPS; 37 | } 38 | else{ 39 | return PARSE_FAIL; 40 | } 41 | 42 | // 解析host 43 | url_view.remove_prefix(pos+3); 44 | if(url_view.size() < 2){ 45 | return PARSE_FAIL; 46 | } 47 | pos = url_view.find("/"); 48 | if(pos == url_view.npos){ 49 | host_ = std::string(url_view); 50 | hostName_ = host_; 51 | path_ = "/"; 52 | return PARSE_SUCCESS; 53 | } 54 | else{ 55 | host_ = std::string(url_view.substr(0, pos)); 56 | size_t pos1 = host_.find(":"); 57 | if(pos1 != host_.npos){ 58 | hostName_ = host_.substr(0, pos1); 59 | port_ = host_.substr(pos1+1); 60 | } 61 | else{ 62 | hostName_ = host_; 63 | } 64 | } 65 | url_view.remove_prefix(pos); // '/'要保留 66 | } 67 | else{ 68 | // 没host 69 | if(url_view.compare(0, 1, "/") != 0){ 70 | return PARSE_FAIL; 71 | } 72 | } 73 | 74 | // 解析path 75 | pos = url_view.find('?'); 76 | if(pos == url_view.npos){ 77 | path_ = std::string(url_view); 78 | return PARSE_SUCCESS; 79 | } 80 | else{ 81 | path_ = std::string(url_view.substr(0, pos)); 82 | } 83 | 84 | // 解析query 85 | url_view.remove_prefix(pos+1); 86 | std::string_view query_strview = url_view; 87 | std::string_view key; 88 | std::string_view val; 89 | size_t j = 0; 90 | size_t i = 0; 91 | for(i = 0; i < query_strview.size(); ++i){ 92 | if(query_strview[i] == '='){ 93 | key = query_strview.substr(j, i-j); 94 | j = i+1; 95 | } 96 | else if(query_strview[i] == '&'){ 97 | val = query_strview.substr(j, i-j); 98 | j = i+1; 99 | query_[std::string(key)] = std::string(val); 100 | key = ""; 101 | val = ""; 102 | } 103 | } 104 | if(!key.empty()){ 105 | val = query_strview.substr(j, i-j); 106 | query_[std::string(key)] = std::string(val); 107 | } 108 | 109 | return PARSE_SUCCESS; 110 | } 111 | 112 | } -------------------------------------------------------------------------------- /haha_web/http/HttpResponse.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_HTTPRESPONSE_H__ 2 | #define __HAHA_HTTPRESPONSE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "base/TimeStamp.h" 8 | #include "base/Buffer.h" 9 | #include "net/TcpConnection.h" 10 | #include "http/HttpUtil.h" 11 | #include "http/HttpUrl.h" 12 | #include "http/HttpHeader.h" 13 | #include "http/HttpSession.h" 14 | #include "http/HttpCookie.h" 15 | #include "haha_json/json.h" 16 | 17 | namespace haha{ 18 | 19 | class HttpResponse{ 20 | public: 21 | typedef std::shared_ptr ptr; 22 | 23 | HttpResponse(HttpVersion, TcpConnection::ptr); 24 | ~HttpResponse(); 25 | 26 | void setVersion(HttpVersion version) { version_ = version; } 27 | void setStatusCode(HttpStatus statusCode) { statusCode_ = statusCode; } 28 | void setKeepAlive(bool on) { 29 | if(on){ 30 | header_.setConnection("Keep-Alive"); 31 | } 32 | else{ 33 | header_.setConnection("Close"); 34 | } 35 | } 36 | void setContentType(HttpContentType contentType) { 37 | contentType_ = contentType; 38 | header_.setContentType(HttpContentType2Name.at(contentType)); 39 | } 40 | void setContentLength(size_t contentLength) { 41 | contentLength_ = contentLength; 42 | header_.setContentLength(std::to_string(contentLength)); 43 | } 44 | void setCookie(const HttpCookie &cookie) { 45 | header_.add("Set-Cookie", cookie.toString()); 46 | } 47 | void setCookie(const std::string &cookieStr){ 48 | header_.add("Set-Cookie", cookieStr); 49 | } 50 | void addAtrribute(const std::string &key, const std::string &val){ 51 | 52 | header_.add(key, val); 53 | } 54 | // 重定向 55 | void setRedirect(const std::string &url){ 56 | statusCode_ = HttpStatus::FOUND; 57 | header_.add("Location", url); 58 | } 59 | 60 | void appendBody(const std::string &data){ 61 | body_.append(data); 62 | } 63 | 64 | void setBody(const std::string &data){ 65 | buffer_->RetrieveAll(); 66 | body_.clear(); 67 | body_ = data; 68 | } 69 | 70 | // 任意文件 71 | void setFileBody(const char* file_path){ 72 | body_.clear(); 73 | isFileBody_ = true; 74 | file_path_ = file_path; 75 | conn_->setFileStream(file_path); 76 | } 77 | 78 | // html 79 | void setHtmlBody(const char* file_path){ 80 | header_.add("Content-Type", "text/html; charset=utf-8"); 81 | setFileBody(file_path); 82 | } 83 | 84 | // json 85 | void setJsonBody(haha::json::JsonNode::ptr node, const haha::json::PrintFormatter &fmt = {}){ 86 | setBody(node->toString(fmt)); 87 | } 88 | 89 | HttpStatus getStatusCode() const { return statusCode_; } 90 | 91 | bool hasSession() const { return hasSession_; } 92 | 93 | private: 94 | TcpConnection::ptr conn_; 95 | Buffer::ptr buffer_; 96 | 97 | HttpVersion version_; 98 | HttpStatus statusCode_; 99 | HttpHeader header_; 100 | HttpContentType contentType_; 101 | std::string body_; 102 | int contentLength_; 103 | bool keepAlive_; 104 | bool hasSession_; 105 | bool isFileBody_; 106 | std::string file_path_; 107 | }; 108 | 109 | } 110 | 111 | #endif -------------------------------------------------------------------------------- /haha_web/haha_json/haha_json/jsonUtil.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_JSON_UTIL_H__ 2 | #define __HAHA_JSON_UTIL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace haha 11 | { 12 | 13 | namespace json 14 | { 15 | 16 | namespace util 17 | { 18 | 19 | inline bool isLetter(char c) { 20 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); 21 | } 22 | inline bool isNumber(char c) { return c >= '0' && c <= '9'; } 23 | inline bool isNumberComponent(char c){ 24 | return isdigit(c) || c == 'e' || c == 'E' || c == '-' || c == '.'; 25 | } 26 | inline bool isHex(char c) { 27 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || 28 | (c >= 'A' && c <= 'F'); 29 | } 30 | inline bool isOct(char c) { return c >= '0' && c <= '7'; } 31 | inline bool isBin(char c) { return c == '0' || c == '1'; } 32 | inline bool isExponent(char c) { return c == 'e' || c == 'E'; } 33 | 34 | inline bool isSpace(char c) { return c == ' ' || c == '\t' || c == '\v'; } 35 | inline bool isCtrlAndSpace(char c) { return c <= 32; } 36 | inline bool isNewline(char c) { return c == '\n'; } 37 | 38 | inline bool is_objectbegin(char c) { return c == '{'; } 39 | inline bool is_objectend(char c) { return c == '}'; } 40 | inline bool is_arraybegin(char c) { return c == '['; } 41 | inline bool is_arrayend(char c) { return c == ']'; } 42 | inline bool is_string_begin_end(char c) { return c == '"'; } 43 | 44 | inline bool is_valid_next_escape_character(char c) { 45 | switch (c) 46 | { 47 | case 't': 48 | case 'n': 49 | case '0': 50 | case 'b': 51 | case 'f': 52 | case 'x': 53 | case 'u': 54 | case 'r': 55 | case '\"': 56 | case '\\': 57 | return true; 58 | default: 59 | break; 60 | } 61 | return false; 62 | } 63 | 64 | enum class JSON_TAG{ 65 | JSON_OBJECT_BEGIN = '{', 66 | JSON_OBJECT_END = '}', 67 | JSON_ARRAY_BEGIN = '[', 68 | JSON_ARRAY_END = ']', 69 | JSON_KEY_VALUE_SEP = ':', 70 | JSON_SEP = ',', 71 | JSON_STRING_TOKEN = '"', 72 | }; 73 | 74 | inline std::string repeat_char(size_t cnt, char c){ 75 | return std::string(cnt, c); 76 | } 77 | 78 | const char* skip_CtrlAndSpace(const char* str, size_t length, size_t offset=0); 79 | std::string_view skip_CtrlAndSpace(std::string_view str, size_t offset=0); 80 | 81 | const char* skip_utf8_bom(const char* str, size_t length, size_t offset=0); 82 | std::string_view skip_utf8_bom(std::string_view str, size_t offset=0); 83 | 84 | template inline T toBase(const std::string &s) { 85 | if (Base == 2){ 86 | return std::bitset<32>(s).to_ulong(); 87 | } 88 | 89 | std::stringstream ss; 90 | T val; 91 | if constexpr (Base == 16){ 92 | ss << std::hex; 93 | } 94 | else if constexpr (Base == 8){ 95 | ss << std::oct; 96 | } 97 | ss << s; 98 | ss >> val; 99 | return val; 100 | } 101 | 102 | unsigned int parse_hex4(const std::string &s); 103 | 104 | unsigned int parse_hex4(std::string_view input); 105 | 106 | unsigned int parse_hex4(const unsigned char * const input); 107 | 108 | std::string utf16_literal_to_utf8(std::string_view &str); 109 | 110 | std::string utf8_to_unicode(std::string_view &str); 111 | 112 | } 113 | 114 | } 115 | 116 | } 117 | 118 | #endif -------------------------------------------------------------------------------- /haha_web/config/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_CONFIG_H__ 2 | #define __HAHA_CONFIG_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "haha_json/json.h" 11 | #include "base/noncopyable.h" 12 | #include "base/RetOption.h" 13 | 14 | namespace haha 15 | { 16 | 17 | namespace config{ 18 | 19 | static const char* defaultConfigFile = "../configs/base_config.json"; 20 | 21 | // class ConfigVarBase{ 22 | // public: 23 | // typedef std::shared_ptr ptr; 24 | // explicit ConfigVarBase(const std::string &desc = ""):description_(desc){} 25 | // std::string getDescription() const { return description_; } 26 | // private: 27 | // std::string description_; 28 | // }; 29 | 30 | // template 31 | // class ConfigVar : public ConfigVarBase{ 32 | // public: 33 | // typedef std::shared_ptr ptr; 34 | // ConfigVar(const T& var, const std::string &desc = ""):ConfigVarBase(desc),val_(var){} 35 | // const T& getValue() const { return val_; } 36 | // protected: 37 | // T val_; 38 | // }; 39 | 40 | 41 | class Config : public noncopyable { 42 | public: 43 | using Values = std::variant>; 44 | 45 | static Config& getInstance(){ 46 | static Config config; 47 | return config; 48 | } 49 | 50 | /* 51 | 输入格式: config.server.port 52 | */ 53 | template const T& query(const char* query_str, const T& default_value); 54 | 55 | private: 56 | Config() { 57 | json_ = json::fromFile(defaultConfigFile); 58 | if(json_ == nullptr){ 59 | std::cout << "read file error" << std::endl; 60 | return; 61 | } 62 | if(json_->getType() != json::JsonType::Object){ 63 | std::cout << "json should be object" << std::endl; 64 | return; 65 | } 66 | std::list > all_nodes; 67 | ListAllMember("", json_, all_nodes); 68 | 69 | for(const auto &it : all_nodes){ 70 | map_.insert({it.first, it.second}); 71 | } 72 | 73 | std::cout << map_.size() << " configuration items detected" << std::endl; 74 | } 75 | 76 | // 生成形如a.b.c的键的键值对 77 | void ListAllMember(const std::string& prefix, 78 | json::JsonNode::ptr node, 79 | std::list >& output) 80 | { 81 | if(prefix.size()){ 82 | output.push_back({prefix, node}); 83 | } 84 | if(node->isObject()){ 85 | json::JsonObject::ptr obj = json::pointer_cast(node); 86 | for(const auto &it : *obj){ 87 | ListAllMember(prefix.empty() ? it.first.getValue() 88 | : prefix + "." + it.first.getValue(), it.second, output); 89 | } 90 | } 91 | } 92 | 93 | private: 94 | bool success_ = false; 95 | json::JsonNode::ptr json_; 96 | std::unordered_map map_; 97 | }; 98 | 99 | 100 | template 101 | const T& GET_CONFIG(const char* str, const T& default_val) { return Config::getInstance().query(str,default_val); } 102 | 103 | } 104 | 105 | } // namespace haha 106 | 107 | #endif -------------------------------------------------------------------------------- /haha_web/net/TcpConnection.cpp: -------------------------------------------------------------------------------- 1 | #include "TcpConnection.h" 2 | 3 | namespace haha{ 4 | 5 | TcpConnection::TcpConnection(const Socket &sock) 6 | :sock_(std::make_shared(sock)) 7 | ,recver_(std::make_shared()) 8 | ,sender_(std::make_shared()) 9 | ,fileSender_(nullptr) 10 | ,keepAlive_(false){ 11 | 12 | } 13 | 14 | TcpConnection::TcpConnection(Socket::ptr sock) 15 | :sock_(sock) 16 | ,recver_(std::make_shared()) 17 | ,sender_(std::make_shared()) 18 | ,fileSender_(nullptr) 19 | ,keepAlive_(false){ 20 | } 21 | 22 | 23 | TcpConnection::TcpConnection() 24 | :sock_(std::make_shared()) 25 | ,recver_(std::make_shared()) 26 | ,sender_(std::make_shared()) 27 | ,fileSender_(nullptr) 28 | ,keepAlive_(false){ 29 | } 30 | 31 | 32 | TcpConnection::status TcpConnection::recv(){ 33 | int len; 34 | int recvBytes = sock_->recv(recver_, &len); 35 | 36 | if(len > 0){ 37 | /* 阻塞情况下 len > 0 */ 38 | return status(recvBytes, errno, status::AGAIN); 39 | } 40 | else if(len < 0 && errno == EAGAIN){ 41 | /* 非阻塞情况下 len < 0 且 error == EAGAIN */ 42 | return status(recvBytes, errno, status::AGAIN); 43 | } 44 | else if(len == 0){ 45 | /* len == 0 说明连接关闭 */ 46 | return status(recvBytes, errno, status::CLOSED); 47 | } 48 | return status(recvBytes, errno, status::ERROR); 49 | } 50 | 51 | TcpConnection::status TcpConnection::send(){ 52 | int len = -10086; 53 | int sendBytes = 0; 54 | 55 | if(sender_->ReadableBytes() > 0){ 56 | sendBytes = sock_->send(sender_, &len); 57 | } 58 | if(fileSender_ && fileSender_->sendable() 59 | && sender_->ReadableBytes() == 0 60 | && (len > 0 || len == -10086)){ 61 | sendBytes = fileSender_->send(&len); 62 | } 63 | 64 | if(len > 0 && !sendable()){ 65 | return status(sendBytes, errno, status::COMPLETED); 66 | } 67 | else if(len < 0 && errno == EAGAIN && sendable() > 0){ 68 | /* 非阻塞情况下 len < 0 且 error == EAGAIN */ 69 | return status(sendBytes, errno, status::AGAIN); 70 | } 71 | else if(len == 0){ 72 | /* len == 0 说明连接关闭 */ 73 | return status(sendBytes, errno, status::CLOSED); 74 | } 75 | return status(sendBytes, errno, status::ERROR); 76 | } 77 | 78 | void TcpConnection::close(){ 79 | disconnected_ = true; 80 | channel_ = nullptr; 81 | } 82 | 83 | bool TcpConnection::sendable(){ 84 | if(fileSender_){ 85 | return fileSender_->sendable() || sender_->ReadableBytes(); 86 | } 87 | return sender_->ReadableBytes(); 88 | } 89 | 90 | void TcpConnection::retriveAll(){ 91 | retriveRecver(); 92 | retriveSender(); 93 | } 94 | 95 | void TcpConnection::retriveRecver(){ 96 | recver_->RetrieveAll(); 97 | } 98 | 99 | void TcpConnection::retriveSender(){ 100 | sender_->RetrieveAll(); 101 | } 102 | 103 | bool TcpConnection::isDisconnected() const { 104 | ReadWriteLock::RAIIReadLock rlock(disconnMtx_); 105 | return disconnected_; 106 | } 107 | 108 | void TcpConnection::setDisconnected(bool is){ 109 | ReadWriteLock::RAIIWriteLock wlock(disconnMtx_); 110 | disconnected_ = is; 111 | } 112 | 113 | void TcpConnection::setFileStream(const char *file_path){ 114 | fileSender_ = std::make_shared(file_path, sock_->getFd(), sock_->isBlocked()); 115 | } 116 | 117 | } -------------------------------------------------------------------------------- /haha_web/base/EncodeUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "base/EncodeUtil.h" 2 | 3 | namespace haha{ 4 | 5 | 6 | unsigned int EncodeUtil::murmurHash2(const std::string& s){ 7 | constexpr unsigned int m = 0x5bd1e995; 8 | constexpr int r = 24; 9 | // Initialize the hash to a 'random' value 10 | size_t len = s.size(); 11 | unsigned int h = 0xEE6B27EB ^ len; 12 | 13 | // Mix 4 bytes at a time into the hash 14 | 15 | const unsigned char *data = (const unsigned char *)s.data(); 16 | 17 | while (len >= 4) { 18 | unsigned int k = *(unsigned int *)data; 19 | 20 | k *= m; 21 | k ^= k >> r; 22 | k *= m; 23 | 24 | h *= m; 25 | h ^= k; 26 | 27 | data += 4; 28 | len -= 4; 29 | } 30 | 31 | // Handle the last few bytes of the input array 32 | switch (len) { 33 | case 3: 34 | h ^= data[2] << 16; 35 | case 2: 36 | h ^= data[1] << 8; 37 | case 1: 38 | h ^= data[0]; 39 | h *= m; 40 | }; 41 | 42 | // Do a few final mixes of the hash to ensure the last few 43 | // bytes are well-incorporated. 44 | h ^= h >> 13; 45 | h *= m; 46 | h ^= h >> 15; 47 | return h; 48 | } 49 | 50 | 51 | /* ************************************************url解码************************************************ */ 52 | 53 | std::string EncodeUtil::urlDecode(const char *value, size_t size) { 54 | std::string escaped; 55 | for (size_t i = 0; i < size; ++i) { 56 | if (value[i] == '%' && i + 2 < size && isxdigit(value[i + 1]) && 57 | isxdigit(value[i + 2])) { 58 | // merge two char to one byte 59 | // %AB => 0xAB 60 | unsigned char byte = 61 | ((unsigned char)hex2dec(value[i + 1]) << 4) | hex2dec(value[i + 2]); 62 | escaped += byte; 63 | i += 2; 64 | } 65 | else if(value[i] == '%'){ 66 | // 解码失败,返回空 67 | return ""; 68 | } 69 | else if(value[i] == '+'){ 70 | escaped += ' '; 71 | } 72 | else { 73 | escaped += value[i]; 74 | } 75 | } 76 | return escaped; 77 | } 78 | 79 | std::string EncodeUtil::urlDecode(std::string_view value) { 80 | return urlDecode(value.data(), value.size()); 81 | } 82 | 83 | std::string EncodeUtil::urlDecode(const char *value) { 84 | return urlDecode(value, strlen(value)); 85 | } 86 | 87 | 88 | /* ************************************************url编码************************************************ */ 89 | 90 | std::string EncodeUtil::urlEncode(const char *value, size_t size) { 91 | std::string escaped; 92 | 93 | for (size_t i = 0; i < size; i++) { 94 | unsigned char c = (unsigned char)value[i]; 95 | if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { 96 | escaped += c; 97 | } 98 | // 可以把' '转为'+',也可以选择直接转为十六进制编码 99 | // else if(c == ' '){ 100 | // escaped += '+'; 101 | // } 102 | else{ 103 | // split one byte to two char 104 | // 0xAB => %AB 105 | char buf[5]{0}; 106 | sprintf(buf, "%%%c%c", toupper(dec2hex(c >> 4)), toupper(dec2hex(c & 15))); 107 | escaped += buf; 108 | } 109 | } 110 | 111 | return escaped; 112 | } 113 | 114 | std::string EncodeUtil::urlEncode(std::string_view value) { 115 | return urlEncode(value.data(), value.size()); 116 | } 117 | 118 | std::string EncodeUtil::urlEncode(const char *value) { 119 | return urlEncode(value, strlen(value)); 120 | } 121 | 122 | } -------------------------------------------------------------------------------- /haha_web/net/TimerHeap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author : mark 3 | * @Date : 2020-06-17 4 | * @copyleft Apache 2.0 5 | */ 6 | #include "TimerHeap.h" 7 | #include "log/Log.h" 8 | 9 | namespace haha 10 | { 11 | 12 | void TimerHeap::siftup_(size_t i) { 13 | /* 上浮 */ 14 | assert(i >= 0 && i < heap_.size()); 15 | int j = (i - 1) / 2; 16 | while(j >= 0) { 17 | if(heap_[j] < heap_[i]) { break; } 18 | SwapNode_(i, j); 19 | i = j; 20 | j = (i - 1) / 2; 21 | } 22 | } 23 | 24 | bool TimerHeap::siftdown_(size_t index, size_t n) { 25 | /* 下沉 */ 26 | assert(index >= 0 && index < heap_.size()); 27 | assert(n >= 0 && n <= heap_.size()); 28 | size_t i = index; 29 | size_t j = i * 2 + 1; 30 | while(j < n) { 31 | if(j + 1 < n && heap_[j + 1] < heap_[j]) j++; 32 | if(heap_[i] < heap_[j]) break; 33 | SwapNode_(i, j); 34 | i = j; 35 | j = i * 2 + 1; 36 | } 37 | return i > index; 38 | } 39 | 40 | void TimerHeap::SwapNode_(size_t i, size_t j) { 41 | assert(i >= 0 && i < heap_.size()); 42 | assert(j >= 0 && j < heap_.size()); 43 | std::swap(heap_[i], heap_[j]); 44 | ref_[heap_[i].fd] = i; 45 | ref_[heap_[j].fd] = j; 46 | } 47 | 48 | void TimerHeap::del_(size_t index) { 49 | /* 删除指定位置的结点 */ 50 | assert(!heap_.empty() && index >= 0 && index < heap_.size()); 51 | /* 将要删除的结点换到队尾,然后调整堆 */ 52 | size_t i = index; 53 | size_t n = heap_.size() - 1; 54 | assert(i <= n); 55 | if(i < n) { 56 | SwapNode_(i, n); 57 | if(!siftdown_(i, n)) { 58 | siftup_(i); 59 | } 60 | } 61 | /* 队尾元素删除 */ 62 | int ret = ref_.erase(heap_.back().fd); 63 | assert(ret > 0); 64 | heap_.pop_back(); 65 | } 66 | 67 | void TimerHeap::push(const Timer& timer){ 68 | assertInLoopThread(); 69 | // ReadWriteLock::RAIIWriteLock lock_gaurd(mtx_); 70 | assert(timer.fd >= 0); 71 | size_t i; 72 | auto x = ref_.find(timer.fd); 73 | if(x == ref_.end()){ 74 | /* 新节点:堆尾插入,调整堆 */ 75 | i = heap_.size(); 76 | ref_[timer.fd] = i; 77 | heap_.push_back(timer); 78 | siftup_(i); 79 | } 80 | else{ 81 | /* 已有结点:调整堆 */ 82 | i = ref_[timer.fd]; 83 | heap_[i] = timer; 84 | if(!siftdown_(i, heap_.size())) { 85 | siftup_(i); 86 | } 87 | } 88 | } 89 | 90 | void TimerHeap::adjust(const Timer& timer){ 91 | assertInLoopThread(); 92 | // ReadWriteLock::RAIIWriteLock lock_gaurd(mtx_); 93 | auto x = ref_.find(timer.fd); 94 | if(x != ref_.end()){ 95 | int pos = x->second; 96 | heap_[pos].expire = timer.expire; 97 | siftdown_(pos, heap_.size()); 98 | } 99 | } 100 | 101 | void TimerHeap::remove(const Timer& timer){ 102 | assertInLoopThread(); 103 | // ReadWriteLock::RAIIWriteLock lock_gaurd(mtx_); 104 | auto x = ref_.find(timer.fd); 105 | if(x != ref_.end()){ 106 | int pos = x->second; 107 | del_(pos); 108 | } 109 | } 110 | 111 | void TimerHeap::pop() { 112 | assertInLoopThread(); 113 | // ReadWriteLock::RAIIWriteLock lock_gaurd(mtx_); 114 | assert(!heap_.empty()); 115 | del_(0); 116 | } 117 | 118 | void TimerHeap::clear() { 119 | assertInLoopThread(); 120 | // ReadWriteLock::RAIIWriteLock lock_gaurd(mtx_); 121 | ref_.clear(); 122 | heap_.clear(); 123 | } 124 | 125 | void TimerHeap::assertInLoopThread() const{ 126 | if(!isInLoopThread()){ 127 | HAHA_LOG_ERROR(HAHA_LOG_ASYNC_FILE_ROOT()) << "EventLoop was created in threadId_ = " 128 | << threadId_ << ", but current thread id = " << Thread::getCurrentThreadId(); 129 | assert(isInLoopThread()); 130 | } 131 | } 132 | 133 | } // namespace haha -------------------------------------------------------------------------------- /haha_web/log/LogStream.cpp: -------------------------------------------------------------------------------- 1 | #include "log/LogStream.h" 2 | 3 | namespace haha{ 4 | 5 | namespace log{ 6 | 7 | const char digits[] = "9876543210123456789"; 8 | const char* zero = digits + 9; 9 | static_assert(sizeof(digits) == 20, "wrong number of digits"); 10 | 11 | const char digitsHex[] = "0123456789ABCDEF"; 12 | static_assert(sizeof(digitsHex) == 17, "wrong number of digitsHex"); 13 | 14 | // Efficient Integer to String Conversions, by Matthew Wilson. 15 | template 16 | size_t convert(char buf[], T value) 17 | { 18 | T i = value; 19 | char* p = buf; 20 | 21 | do 22 | { 23 | int lsd = static_cast(i % 10); 24 | i /= 10; 25 | *p++ = zero[lsd]; 26 | } while (i != 0); 27 | 28 | if (value < 0) 29 | { 30 | *p++ = '-'; 31 | } 32 | *p = '\0'; 33 | std::reverse(buf, p); 34 | 35 | return p - buf; 36 | } 37 | 38 | size_t convertHex(char buf[], uintptr_t value) 39 | { 40 | uintptr_t i = value; 41 | char* p = buf; 42 | 43 | do 44 | { 45 | int lsd = static_cast(i % 16); 46 | i /= 16; 47 | *p++ = digitsHex[lsd]; 48 | } while (i != 0); 49 | 50 | *p = '\0'; 51 | std::reverse(buf, p); 52 | 53 | return p - buf; 54 | } 55 | 56 | void LogStream::staticCheck() 57 | { 58 | static_assert(kMaxNumericSize - 10 > std::numeric_limits::digits10, 59 | "kMaxNumericSize is large enough"); 60 | static_assert(kMaxNumericSize - 10 > std::numeric_limits::digits10, 61 | "kMaxNumericSize is large enough"); 62 | static_assert(kMaxNumericSize - 10 > std::numeric_limits::digits10, 63 | "kMaxNumericSize is large enough"); 64 | static_assert(kMaxNumericSize - 10 > std::numeric_limits::digits10, 65 | "kMaxNumericSize is large enough"); 66 | } 67 | 68 | template 69 | void LogStream::formatInteger(T v) 70 | { 71 | if (buffer_.avail() >= kMaxNumericSize) 72 | { 73 | size_t len = convert(buffer_.current(), v); 74 | buffer_.add(len); 75 | } 76 | } 77 | 78 | LogStream& LogStream::operator<<(short v) 79 | { 80 | *this << static_cast(v); 81 | return *this; 82 | } 83 | 84 | LogStream& LogStream::operator<<(unsigned short v) 85 | { 86 | *this << static_cast(v); 87 | return *this; 88 | } 89 | 90 | LogStream& LogStream::operator<<(int v) 91 | { 92 | formatInteger(v); 93 | return *this; 94 | } 95 | 96 | LogStream& LogStream::operator<<(unsigned int v) 97 | { 98 | formatInteger(v); 99 | return *this; 100 | } 101 | 102 | LogStream& LogStream::operator<<(long v) 103 | { 104 | formatInteger(v); 105 | return *this; 106 | } 107 | 108 | LogStream& LogStream::operator<<(unsigned long v) 109 | { 110 | formatInteger(v); 111 | return *this; 112 | } 113 | 114 | LogStream& LogStream::operator<<(long long v) 115 | { 116 | formatInteger(v); 117 | return *this; 118 | } 119 | 120 | LogStream& LogStream::operator<<(unsigned long long v) 121 | { 122 | formatInteger(v); 123 | return *this; 124 | } 125 | 126 | LogStream& LogStream::operator<<(const void* p) 127 | { 128 | uintptr_t v = reinterpret_cast(p); 129 | if (buffer_.avail() >= kMaxNumericSize) 130 | { 131 | char* buf = buffer_.current(); 132 | buf[0] = '0'; 133 | buf[1] = 'x'; 134 | size_t len = convertHex(buf+2, v); 135 | buffer_.add(len+2); 136 | } 137 | return *this; 138 | } 139 | 140 | // FIXME: replace this with Grisu3 by Florian Loitsch. 141 | LogStream& LogStream::operator<<(double v) 142 | { 143 | if (buffer_.avail() >= kMaxNumericSize) 144 | { 145 | int len = snprintf(buffer_.current(), kMaxNumericSize, "%.12g", v); 146 | buffer_.add(len); 147 | } 148 | return *this; 149 | } 150 | 151 | } 152 | 153 | } -------------------------------------------------------------------------------- /haha_web/http/HttpServer.cpp: -------------------------------------------------------------------------------- 1 | #include "http/HttpServer.h" 2 | 3 | namespace haha{ 4 | 5 | HttpServer::HttpServer() 6 | :sessionManger_(&HttpSessionManager::getInstance()) 7 | ,servletDispatcher_(std::make_shared()){ 8 | 9 | } 10 | 11 | HttpServer::MESSAGE_STATUS HttpServer::onMessage(TcpConnection::ptr conn){ 12 | std::shared_ptr request; 13 | if(conn->getContext() == nullptr){ 14 | request = std::make_shared(sessionManger_, conn); 15 | conn->setContext(request); 16 | } 17 | else{ 18 | request = std::static_pointer_cast(conn->getContext()); 19 | } 20 | 21 | auto ret = request->parseRequest(); 22 | 23 | HttpResponse::ptr response = std::make_shared(request->getVersion(), conn); 24 | 25 | switch (ret) 26 | { 27 | case haha::HttpRequest::RET_STATE::AGAIN_REQUEST: 28 | return MESSAGE_STATUS::AGAIN; 29 | break; 30 | case haha::HttpRequest::RET_STATE::OK_REQUEST: 31 | /* code */ 32 | break; 33 | case haha::HttpRequest::RET_STATE::BAD_REQUEST: 34 | conn->setKeepAlive(false); 35 | response->setStatusCode(HttpStatus::BAD_REQUEST); 36 | // response->addAtrribute("Connection", "close"); 37 | response->setKeepAlive(false); 38 | break; 39 | default: 40 | break; 41 | } 42 | 43 | if(servletDispatcher_){ 44 | servletDispatcher_->dispatch(request, response, 45 | [&](HttpRequest::ptr req, HttpResponse::ptr resp){ 46 | beforeServlet(req, resp); 47 | }, 48 | [&](HttpRequest::ptr req, HttpResponse::ptr resp){ 49 | afterServlet(req, resp); 50 | }); 51 | } 52 | 53 | conn->setContext(nullptr); 54 | 55 | return HttpServer::MESSAGE_STATUS::OK; 56 | } 57 | 58 | bool HttpServer::onCreateConnection(TcpConnection::ptr conn){ 59 | return true; 60 | } 61 | 62 | bool HttpServer::onCloseConntection(TcpConnection::ptr conn){ 63 | return true; 64 | } 65 | 66 | void HttpServer::beforeServlet(HttpRequest::ptr req, HttpResponse::ptr resp){ 67 | auto req_ssid = req->getCookie().getSessionId(); 68 | if(!req_ssid.empty()){ 69 | HttpCookie cookie; 70 | cookie.add("SESSIONID", req_ssid); 71 | resp->setCookie(cookie); 72 | } 73 | } 74 | 75 | void HttpServer::afterServlet(HttpRequest::ptr req, HttpResponse::ptr resp){ 76 | auto session = req->getSession(false); 77 | if(session){ 78 | // The old session has been set to an invalid state, so it is 79 | // automatically deleted by the timer, and a null pointer object is 80 | // returned 81 | if(!session->isDestroy()){ 82 | if(session->isNew()){ 83 | session->setStatus(HttpSession::Accessed); 84 | getMainLoop()->addTimer( 85 | Timer( 86 | EncodeUtil::murmurHash2(session->getId()), 87 | TimeStamp::nowSecond(session->getMaxInactiveInterval()), 88 | [&](){handleSessionTimeout(session);} 89 | )); 90 | } 91 | else{ 92 | getMainLoop()->adjustTimer( 93 | Timer( 94 | EncodeUtil::murmurHash2(session->getId()), 95 | TimeStamp::nowSecond(session->getMaxInactiveInterval()), 96 | nullptr 97 | )); 98 | } 99 | } 100 | } 101 | } 102 | 103 | void HttpServer::handleSessionTimeout(HttpSession::ptr session){ 104 | if(session){ 105 | session->setStatus(HttpSession::Destroy); 106 | sessionManger_->delSession(session->getId()); 107 | } 108 | } 109 | 110 | } -------------------------------------------------------------------------------- /haha_web/net/TcpConnection.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_TCPCONNECTION_H 2 | #define __HAHA_TCPCONNECTION_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "base/Mutex.h" 9 | #include "base/ReadWriteLock.h" 10 | #include "base/FileUtil.h" 11 | #include "log/Log.h" 12 | #include "InetAddress.h" 13 | #include "Socket.h" 14 | #include "Channel.h" 15 | 16 | namespace haha{ 17 | 18 | class TcpConnection{ 19 | public: 20 | typedef std::shared_ptr ptr; 21 | typedef std::weak_ptr weak_ptr; 22 | 23 | struct status{ 24 | int n; 25 | int errorNo; 26 | enum TYPE{ 27 | AGAIN, 28 | COMPLETED, 29 | ERROR, 30 | CLOSED, 31 | } type; 32 | status() = default; 33 | status(int n, int errorNo, TYPE type){ 34 | this->n = n; 35 | this->errorNo = errorNo; 36 | this->type = type; 37 | } 38 | }; 39 | 40 | TcpConnection(const Socket &sock); 41 | TcpConnection(Socket::ptr sock); 42 | TcpConnection(); 43 | 44 | ~TcpConnection() { 45 | // HAHA_LOG_DEBUG(HAHA_LOG_ASYNC_FILE_ROOT()) << "~TcpConnection"; 46 | } 47 | 48 | status recv(); 49 | status send(); 50 | void close(); 51 | 52 | void init(const Socket& sock) { 53 | sock_ = std::make_shared(sock); 54 | } 55 | 56 | void setChannel(Channel::ptr channel) { channel_ = channel; } 57 | void setRecvBuffer(Buffer::ptr buffer) { recver_ = buffer; } 58 | void setSendBuffer(Buffer::ptr buffer) { sender_ = buffer; } 59 | 60 | std::string getLocalIp() const noexcept { return sock_->getLocalAddress().getIp(); } 61 | uint16_t getLocalPort() const noexcept { return sock_->getLocalAddress().getPort(); } 62 | sockaddr *getLocalSockAddr() const noexcept { return sock_->getLocalAddress().getSockAddr(); } 63 | 64 | std::string geRemoteIp() const noexcept { return sock_->getRemoteAddress().getIp(); } 65 | uint16_t getRemotePort() const noexcept { return sock_->getRemoteAddress().getPort(); } 66 | sockaddr *getRemoteSockAddr() const noexcept { return sock_->getRemoteAddress().getSockAddr(); } 67 | 68 | int getFd() { return sock_->getFd(); } 69 | Channel::ptr getChannel() {return channel_;} 70 | void setEvents(uint32_t events) { channel_->setEvents(events); } 71 | 72 | bool isKeepAlive() const { return keepAlive_;} 73 | void setKeepAlive(bool is) { keepAlive_ = is; } 74 | 75 | bool isDisconnected() const; 76 | void setDisconnected(bool is); 77 | 78 | Buffer::ptr getRecver() { return recver_; } 79 | Buffer::ptr getSender() { return sender_; } 80 | 81 | /* 是否还有数据没发送 */ 82 | bool sendable(); 83 | 84 | // 重置接收缓冲区 85 | void retriveRecver(); 86 | // 重置发送缓冲区 87 | void retriveSender(); 88 | void retriveAll(); 89 | 90 | std::shared_ptr getContext() { return context_; } 91 | void setContext(std::shared_ptr context) { context_ = context; } 92 | void clearContext() { context_ = nullptr; } 93 | 94 | void setFileStream(const char *file_path); 95 | 96 | private: 97 | Socket::ptr sock_; // socket 98 | Buffer::ptr recver_; // 接收缓存 99 | Buffer::ptr sender_; // 发送缓存 100 | Channel::ptr channel_; // 事件回调器 101 | 102 | FileUtil::FileSendStream::ptr fileSender_; // 文件发送器 103 | 104 | bool disconnected_ = false; 105 | bool keepAlive_ = false; 106 | 107 | std::shared_ptr context_; // 连接的上下文数据,例如http的请求数据或响应数据 108 | 109 | mutable ReadWriteLock disconnMtx_; // 连接状态的读写锁 110 | 111 | 112 | // /* 已接收的字节数 */ 113 | // int receivedBytes_ = 0; 114 | 115 | // /* 已发送的字节数 */ 116 | // int sendBytes_ = 0; 117 | // /* 未发送的字节数 */ 118 | // int remainBytes_ = 0; 119 | }; 120 | 121 | } 122 | 123 | #endif -------------------------------------------------------------------------------- /haha_web/log/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "log/Logger.h" 2 | #include 3 | 4 | namespace haha{ 5 | 6 | namespace log{ 7 | 8 | void Logger::setFormatter(LogFormatter::ptr val){ 9 | MutexType::RAIILock lock(mutex_); 10 | formatter_ = val; 11 | 12 | for(auto &i : appenders_){ 13 | // appender还没设置格式,因此把logger的默认格式赋给它 14 | if(!i->hasFormatter()){ 15 | i->setFormatter(formatter_); 16 | } 17 | } 18 | } 19 | 20 | void Logger::setFormatter(const std::string &val){ 21 | LogFormatter::ptr new_val(new LogFormatter(val)); 22 | if(new_val->isError()){ 23 | std::cout << "Logger setFormatter name=" << name_ 24 | << " value=" << val << " invalid formatter" 25 | << std::endl; 26 | return; 27 | } 28 | // formatter_ = new_val; 29 | setFormatter(new_val); 30 | } 31 | 32 | LogFormatter::ptr Logger::getFormatter(){ 33 | MutexType::RAIILock lock(mutex_); 34 | return formatter_; 35 | } 36 | 37 | void Logger::addAppender(LogAppender::ptr appender){ 38 | MutexType::RAIILock lock(mutex_); 39 | if(!appender->getFormatter()){ 40 | appender->setFormatter(formatter_); 41 | } 42 | appenders_.push_back(appender); 43 | } 44 | 45 | void Logger::delAppender(LogAppender::ptr appender){ 46 | MutexType::RAIILock lock(mutex_); 47 | for(auto it = appenders_.begin(); it != appenders_.end(); ++it){ 48 | if(*it == appender){ 49 | appenders_.erase(it); 50 | break; 51 | } 52 | } 53 | } 54 | 55 | void Logger::clearAppenders(){ 56 | MutexType::RAIILock lock(mutex_); 57 | appenders_.clear(); 58 | } 59 | 60 | 61 | SyncLogger::SyncLogger(const std::string &name, SyncLogAppender::ptr appender) 62 | :Logger(name, appender) 63 | { 64 | defaultAppender_ = std::make_shared(); 65 | defaultAppender_->setFormatter(formatter_); 66 | } 67 | 68 | 69 | // void SyncLogger::log(LogInfo::ptr info){ 70 | // auto level = info->getLevel(); 71 | // if(level >= level_){ 72 | // if(!appenders_.empty()){ 73 | // for(auto &i : appenders_){ 74 | // i->append(info); 75 | // } 76 | // } 77 | // else if(defaultAppender_){ 78 | // defaultAppender_->append(info); 79 | // } 80 | // } 81 | // } 82 | 83 | void SyncLogger::log(LogInfo::ptr info){ 84 | auto level = info->getLevel(); 85 | if(level >= level_){ 86 | if(!appenders_.empty()){ 87 | for(auto &i : appenders_){ 88 | i->append(*info); 89 | } 90 | } 91 | else if(defaultAppender_){ 92 | defaultAppender_->append(*info); 93 | } 94 | } 95 | } 96 | 97 | 98 | AsyncLogger::AsyncLogger(const std::string &name, int flushInterval, AsyncLogAppender::ptr appender) 99 | :Logger(name, appender), 100 | flushInterval_(flushInterval), 101 | cond_(std::make_shared>()), 102 | running_(true), 103 | thread_(std::make_unique([this](){task();})) 104 | { 105 | 106 | } 107 | 108 | void AsyncLogger::log(LogInfo::ptr info){ 109 | auto level = info->getLevel(); 110 | if(level >= level_){ 111 | for(auto &i : appenders_){ 112 | i->append(*info); 113 | } 114 | } 115 | } 116 | 117 | void AsyncLogger::addAppender(LogAppender::ptr appender){ 118 | appender->setNotifyFunc([this](){cond_->notify_one();}); 119 | Logger::addAppender(appender); 120 | } 121 | 122 | void AsyncLogger::task(){ 123 | while (running_ == true) 124 | { 125 | MutexType::RAIILock lock(mutex_); 126 | cond_->waitForSeconds(mutex_, flushInterval_); 127 | for(auto &app : appenders_){ 128 | if(!app->empty()){ 129 | app->flush(); 130 | } 131 | } 132 | } 133 | 134 | } 135 | 136 | } 137 | 138 | } -------------------------------------------------------------------------------- /haha_web/haha_json/haha_json/jsonBuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_JSON_JsonBuffer_H__ 2 | #define __HAHA_JSON_JsonBuffer_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "jsonError.h" 9 | 10 | namespace haha 11 | { 12 | 13 | namespace json 14 | { 15 | 16 | class JsonBuffer { 17 | public: 18 | static const int max_size = 4 * 1000 * 1000; 19 | 20 | JsonBuffer(const JsonBuffer &) = delete; 21 | JsonBuffer(JsonBuffer &&) = delete; 22 | JsonBuffer &operator=(const JsonBuffer &) = delete; 23 | JsonBuffer &operator=(JsonBuffer &&) = delete; 24 | 25 | JsonBuffer() {} 26 | JsonBuffer(const std::string str) { readString(str); } 27 | 28 | ~JsonBuffer() { 29 | if (sbuffer_){ 30 | delete[] sbuffer_; 31 | } 32 | } 33 | 34 | bool readFile(const char *filePath){ 35 | bool exists = std::filesystem::exists(filePath); 36 | if(!exists){ 37 | return false; 38 | } 39 | int fsz = std::filesystem::file_size(filePath); 40 | if(fsz > max_size){ 41 | throw HAHA_JSON_ERROR("unsupport file size bigger than 4M !!!"); 42 | } 43 | 44 | if (sbuffer_) { 45 | delete[] sbuffer_; 46 | sbuffer_ = nullptr; 47 | } 48 | 49 | std::ifstream ifs(filePath); 50 | __len = fsz; 51 | __ptr = -1; 52 | sbuffer_ = new char[fsz+1]{0}; 53 | ifs.read(sbuffer_, fsz); 54 | return true; 55 | } 56 | 57 | void readString(const std::string &s) { 58 | if (sbuffer_) { 59 | delete[] sbuffer_; 60 | sbuffer_ = nullptr; 61 | } 62 | __len = s.length(); 63 | __ptr = -1; 64 | sbuffer_ = new char[__len]{0}; 65 | memcpy(sbuffer_, s.data(), __len * sizeof(char)); 66 | } 67 | 68 | bool writeFile(const char *filePath){ 69 | if(sbuffer_ == nullptr){ 70 | return false; 71 | } 72 | std::ofstream ofs(filePath); 73 | ofs << sbuffer_; 74 | return true; 75 | } 76 | 77 | void writeString(std::string &s){ 78 | s = sbuffer_; 79 | } 80 | 81 | char cur() { 82 | if (__ptr <= -1 || __ptr >= __len){ 83 | return EOF; 84 | } 85 | return sbuffer_[__ptr]; 86 | } 87 | char get() { 88 | if (++__ptr >= __len){ 89 | return EOF; 90 | } 91 | return sbuffer_[__ptr]; 92 | } 93 | char backc() { 94 | if (__ptr - 1 < 0){ 95 | return EOF; 96 | } 97 | return sbuffer_[__ptr - 1]; 98 | } 99 | 100 | void back() { --__ptr; } 101 | 102 | char peek() { 103 | if (__ptr + 1 >= __len){ 104 | return EOF; 105 | } 106 | return sbuffer_[__ptr + 1]; 107 | } 108 | 109 | std::pair geteq(int c) { 110 | char x = get(); 111 | if (x == c){ 112 | return std::make_pair(true, c); 113 | } 114 | back(); 115 | return std::make_pair(false, c); 116 | } 117 | 118 | std::string gets(int count) { 119 | std::string s; 120 | for (int i = 0; i < count; i++){ 121 | s.append(1, get()); 122 | } 123 | return s; 124 | } 125 | 126 | int len() { return __len; } 127 | int pos() { return __ptr; } 128 | bool eof() { return __ptr > __len - 1; } 129 | 130 | void reset() { __ptr = -1; } 131 | 132 | void clear() { 133 | reset(); 134 | memset(sbuffer_, 0, __len * sizeof(char)); 135 | } 136 | 137 | std::string_view to_stringview(){ 138 | return std::string_view(sbuffer_, __len); 139 | } 140 | 141 | private: 142 | char* sbuffer_ = nullptr; 143 | int __ptr = -1; 144 | int __len = 0; 145 | }; 146 | 147 | } // namespace json 148 | 149 | 150 | } // namespace haha 151 | 152 | 153 | #endif -------------------------------------------------------------------------------- /third_party/magic_enum/magic_enum_fuse.hpp: -------------------------------------------------------------------------------- 1 | // __ __ _ ______ _____ 2 | // | \/ | (_) | ____| / ____|_ _ 3 | // | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_ 4 | // | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _| 5 | // | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| 6 | // |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| 7 | // __/ | https://github.com/Neargye/magic_enum 8 | // |___/ version 0.8.0 9 | // 10 | // Licensed under the MIT License . 11 | // SPDX-License-Identifier: MIT 12 | // Copyright (c) 2019 - 2022 Daniil Goncharov . 13 | // 14 | // Permission is hereby granted, free of charge, to any person obtaining a copy 15 | // of this software and associated documentation files (the "Software"), to deal 16 | // in the Software without restriction, including without limitation the rights 17 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | // copies of the Software, and to permit persons to whom the Software is 19 | // furnished to do so, subject to the following conditions: 20 | // 21 | // The above copyright notice and this permission notice shall be included in all 22 | // copies or substantial portions of the Software. 23 | // 24 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | // SOFTWARE. 31 | 32 | #ifndef NEARGYE_MAGIC_ENUM_FUSE_HPP 33 | #define NEARGYE_MAGIC_ENUM_FUSE_HPP 34 | 35 | #include "magic_enum.hpp" 36 | 37 | namespace magic_enum { 38 | 39 | namespace detail { 40 | 41 | template 42 | constexpr optional fuse_one_enum(optional hash, E value) noexcept { 43 | if (hash) { 44 | if (const auto index = enum_index(value)) { 45 | return (*hash << log2(enum_count() + 1)) | *index; 46 | } 47 | } 48 | return {}; 49 | } 50 | 51 | template 52 | constexpr optional fuse_enum(E value) noexcept { 53 | return fuse_one_enum(0, value); 54 | } 55 | 56 | template 57 | constexpr optional fuse_enum(E head, Es... tail) noexcept { 58 | return fuse_one_enum(fuse_enum(tail...), head); 59 | } 60 | 61 | template 62 | constexpr auto typesafe_fuse_enum(Es... values) noexcept { 63 | enum class enum_fuse_t : std::uintmax_t; 64 | const auto fuse = fuse_enum(values...); 65 | if (fuse) { 66 | return optional{static_cast(*fuse)}; 67 | } 68 | return optional{}; 69 | } 70 | 71 | } // namespace magic_enum::detail 72 | 73 | // Returns a bijective mix of several enum values. This can be used to emulate 2D switch/case statements. 74 | template 75 | [[nodiscard]] constexpr auto enum_fuse(Es... values) noexcept { 76 | static_assert((std::is_enum_v> && ...), "magic_enum::enum_fuse requires enum type."); 77 | static_assert(sizeof...(Es) >= 2, "magic_enum::enum_fuse requires at least 2 values."); 78 | static_assert((detail::log2(enum_count>() + 1) + ...) <= (sizeof(std::uintmax_t) * 8), "magic_enum::enum_fuse does not work for large enums"); 79 | #if defined(MAGIC_ENUM_NO_TYPESAFE_ENUM_FUSE) 80 | const auto fuse = detail::fuse_enum...>(values...); 81 | #else 82 | const auto fuse = detail::typesafe_fuse_enum...>(values...); 83 | #endif 84 | return assert(fuse), fuse; 85 | } 86 | 87 | } // namespace magic_enum 88 | 89 | #endif // NEARGYE_MAGIC_ENUM_FUSE_HPP 90 | -------------------------------------------------------------------------------- /haha_web/http/HttpRequest.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_HTTPREQUEST_H__ 2 | #define __HAHA_HTTPREQUEST_H__ 3 | 4 | #include 5 | #include 6 | 7 | #include "haha_json/json.h" 8 | #include "base/Buffer.h" 9 | #include "net/TcpConnection.h" 10 | #include "http/HttpUtil.h" 11 | #include "http/HttpUrl.h" 12 | #include "http/HttpCookie.h" 13 | #include "http/HttpMultiPart.h" 14 | #include "log/Log.h" 15 | #include "http/HttpHeader.h" 16 | #include "http/HttpSession.h" 17 | // #include "base/StringView.h" 18 | 19 | namespace haha{ 20 | 21 | class HttpRequest{ 22 | public: 23 | typedef std::shared_ptr ptr; 24 | enum CHECK_STATE 25 | { 26 | CHECK_REQUESTLINE, 27 | CHECK_HEADER, 28 | CHECK_CONTENT, 29 | CHECK_DONE, 30 | }; 31 | enum RET_STATE 32 | { 33 | OK_REQUEST, 34 | AGAIN_REQUEST, 35 | BAD_REQUEST, 36 | }; 37 | enum REASON{ 38 | BAD_REQUESTLINE, 39 | BAD_HEADER, 40 | EXCEED_REQUESTLINE, 41 | EXCEED_HEADER, 42 | }; 43 | 44 | public: 45 | HttpRequest(HttpSessionManager*, TcpConnection::ptr); 46 | RET_STATE parseRequest(); 47 | 48 | HttpMethod getMethod() const { return method_; } 49 | const HttpUrl& getUrl() const { return reqUrl_; } 50 | HttpVersion getVersion() const { return version_; } 51 | const HttpHeader& getHeader() const { return header_; } 52 | Buffer::ptr getBuffer() const { return data_; } 53 | TcpConnection::ptr getConnection() const { return conn_; } 54 | const std::string& getBody() const { return body_; } 55 | 56 | bool getKeepAlive() const { return keepAlive_; } 57 | HttpContentType getContentType() const { return contentType_; } 58 | const std::string& getAcceptEncoding() const { return acceptEncoding_; } 59 | const HttpCookie& getCookie() const { return cookie_; } 60 | size_t getContentLength() const { return contentLength_; } 61 | const std::string& getTransferEncoding() const { return transferEncoding_; } 62 | 63 | CHECK_STATE getState() const { return state_; } 64 | RET_STATE getRetState() const { return retState_; } 65 | bool complete() const { return state_ == CHECK_DONE; } 66 | 67 | bool keepAlive() const { return keepAlive_; } 68 | bool hasCookies() const { return hasCookies_; } 69 | 70 | // 获取session 71 | HttpSession::ptr getSession(bool autoCreate = true); 72 | 73 | private: 74 | RET_STATE parseRequestLine(); 75 | RET_STATE parseRequestHeader(); 76 | RET_STATE parseRequestContent(); 77 | 78 | void parseKeepAlive(); 79 | void parseContentType(); 80 | void parseAcceptEncoding(); 81 | void parseCookies(); 82 | void parseContentLength(); 83 | void parseTransferEncoding(); 84 | void parseSession(); 85 | 86 | RET_STATE parseChunked(); 87 | 88 | private: 89 | HttpSessionManager *sessionManager_; 90 | TcpConnection::ptr conn_; 91 | Buffer::ptr data_; 92 | 93 | CHECK_STATE state_; 94 | RET_STATE retState_; 95 | 96 | // 记录上一次数据解析结束的位置 97 | size_t lastEnd_; 98 | 99 | const size_t max_requestLine_len_; 100 | const size_t max_requestHeader_len_; 101 | 102 | HttpMethod method_; 103 | HttpUrl reqUrl_; 104 | HttpVersion version_; 105 | 106 | HttpHeader header_; 107 | HttpCookie cookie_; 108 | HttpMultiPart multipart_; 109 | std::string body_; 110 | HttpForm form_; 111 | json::JsonNode::ptr json_; 112 | 113 | HttpSession::ptr session_; 114 | 115 | HttpContentType contentType_; 116 | size_t contentLength_; 117 | std::string acceptEncoding_; 118 | std::string transferEncoding_; 119 | bool chunked_; 120 | 121 | const size_t chunk_size_str_limit; 122 | HttpChunkedState cur_chunkedState_; 123 | int cur_chunk_size_; 124 | 125 | bool keepAlive_; 126 | bool compressed_; 127 | bool hasContentType_; 128 | bool hasContentLength_; 129 | bool hasCookies_; 130 | bool hasTransferEncoding_; 131 | }; 132 | 133 | } 134 | 135 | 136 | #endif -------------------------------------------------------------------------------- /haha_web/base/stringView.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_STRINGVIEW_H__ 2 | #define __HAHA_STRINGVIEW_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace haha{ 13 | 14 | class StringView{ 15 | 16 | friend std::ostream& operator<<(std::ostream& ostream, StringView view); 17 | 18 | public: 19 | static const size_t npos = -1; 20 | 21 | StringView() = delete; 22 | StringView(const char* begin, const char* end):begin_(begin),end_(end){} 23 | StringView(const char* begin, size_t len):begin_(begin),end_(begin+len){} 24 | 25 | /* 获取以pos开头的子串 */ 26 | StringView substr(size_t pos){ 27 | const char *begin1 = begin_ + pos; 28 | if(begin1 > end_){ 29 | throw std::out_of_range("StringView substr out of range"); 30 | } 31 | return StringView(begin1, end_); 32 | } 33 | 34 | /* 获取以pos开头,长度为len的子串 */ 35 | StringView substr(size_t pos, size_t len){ 36 | const char *begin1 = begin_ + pos; 37 | if(begin1 > end_){ 38 | throw std::out_of_range("StringView substr out of range"); 39 | } 40 | const char *end1 = begin1 + len - 1; 41 | if(end1 > end_){ 42 | throw std::out_of_range("StringView substr out of range"); 43 | } 44 | return StringView(begin1, end1); 45 | } 46 | 47 | /* 去掉前面的部分 */ 48 | void remove_prefix(size_t len){ 49 | size_t length = size(); 50 | begin_ = len > length ? end_ : begin_+len; 51 | } 52 | 53 | /* 去掉后面的部分 */ 54 | void remove_suffix(size_t len){ 55 | size_t length = size(); 56 | end_ = len > length ? begin_ : end_-len; 57 | } 58 | 59 | /* 判断str是否是其前缀 有范围限制 */ 60 | bool starts_with(const char *begin, const char *end){ 61 | size_t i = 0; 62 | while(begin+i != end && i < size() && begin[i] == begin_[i]){ 63 | ++i; 64 | } 65 | return i < size() ? false : true; 66 | } 67 | 68 | /* 判断str是否是其前缀 */ 69 | bool starts_with(const char *str){ 70 | size_t i = 0; 71 | while(str[i] != '\0' && i < size() && str[i] == begin_[i]){ 72 | ++i; 73 | } 74 | return i < size() ? false : true; 75 | } 76 | 77 | /* 判断str是否是其后缀 有范围限制 */ 78 | bool ends_with(const char *begin, const char *end){ 79 | if(begin == end)return true; 80 | if(begin < end || size() == 0 || (int)size() < end - begin)return false; 81 | int i = end - begin - 1; 82 | int j = (int)size() - 1; 83 | while(i >= 0 && j >= 0 && begin[i] == begin_[j]){ 84 | --i; 85 | --j; 86 | } 87 | return i >= 0 ? false : true; 88 | } 89 | 90 | /* 判断str是否是其后缀 */ 91 | bool ends_with(const char *str){ 92 | int i = strlen(str); 93 | if(i == 0)return true; 94 | if((int)size() == 0 || (int)size() < i)return false; 95 | --i; 96 | int j = (int)size() - 1; 97 | while(i >= 0 && j >= 0 && str[i] == begin_[j]){ 98 | --i; 99 | --j; 100 | } 101 | return i >= 0 ? false : true; 102 | } 103 | 104 | inline size_t find(const char *str, size_t pos = 0); 105 | 106 | inline size_t find(const char *begin, const char* end, size_t pos = 0); 107 | 108 | int compare(size_t start, size_t len, const char *str){ 109 | if(begin_ + len > end_){ 110 | throw std::out_of_range("len out of range"); 111 | } 112 | return strncmp(begin_+start, str, len); 113 | } 114 | 115 | 116 | inline size_t size() const {return static_cast(end_ - begin_);} 117 | 118 | const char& operator [](size_t pos) const{ 119 | if(begin_ + pos > end_){ 120 | throw std::out_of_range("index out of range"); 121 | } 122 | return begin_[pos]; 123 | } 124 | 125 | private: 126 | const char* begin_; 127 | const char* end_; 128 | size_t length_; 129 | }; 130 | 131 | inline std::ostream& operator<<(std::ostream& ostream, StringView view){ 132 | for(auto i = view.begin_; i != view.end_; ++i){ 133 | ostream << *i; 134 | } 135 | return ostream; 136 | } 137 | 138 | } 139 | 140 | #endif -------------------------------------------------------------------------------- /haha_web/haha_json/haha_json/stringView.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAHA_STRINGVIEW_H__ 2 | #define __HAHA_STRINGVIEW_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace haha{ 13 | 14 | namespace json{ 15 | 16 | class StringView{ 17 | 18 | friend std::ostream& operator<<(std::ostream& ostream, StringView view); 19 | 20 | public: 21 | static const size_t npos = -1; 22 | 23 | StringView() = delete; 24 | StringView(const char* begin, const char* end):begin_(begin),end_(end){} 25 | StringView(const char* begin, size_t len):begin_(begin),end_(begin+len){} 26 | 27 | /* 获取以pos开头的子串 */ 28 | StringView substr(size_t pos){ 29 | const char *begin1 = begin_ + pos; 30 | if(begin1 > end_){ 31 | throw std::out_of_range("StringView substr out of range"); 32 | } 33 | return StringView(begin1, end_); 34 | } 35 | 36 | /* 获取以pos开头,长度为len的子串 */ 37 | StringView substr(size_t pos, size_t len){ 38 | const char *begin1 = begin_ + pos; 39 | if(begin1 > end_){ 40 | throw std::out_of_range("StringView substr out of range"); 41 | } 42 | const char *end1 = begin1 + len - 1; 43 | if(end1 > end_){ 44 | throw std::out_of_range("StringView substr out of range"); 45 | } 46 | return StringView(begin1, end1); 47 | } 48 | 49 | /* 去掉前面的部分 */ 50 | void remove_prefix(size_t len){ 51 | size_t length = size(); 52 | begin_ = len > length ? end_ : begin_+len; 53 | } 54 | 55 | /* 去掉后面的部分 */ 56 | void remove_suffix(size_t len){ 57 | size_t length = size(); 58 | end_ = len > length ? begin_ : end_-len; 59 | } 60 | 61 | /* 判断str是否是其前缀 有范围限制 */ 62 | bool starts_with(const char *begin, const char *end){ 63 | size_t i = 0; 64 | while(begin+i != end && i < size() && begin[i] == begin_[i]){ 65 | ++i; 66 | } 67 | return i < size() ? false : true; 68 | } 69 | 70 | /* 判断str是否是其前缀 */ 71 | bool starts_with(const char *str){ 72 | size_t i = 0; 73 | while(str[i] != '\0' && i < size() && str[i] == begin_[i]){ 74 | ++i; 75 | } 76 | return i < size() ? false : true; 77 | } 78 | 79 | /* 判断str是否是其后缀 有范围限制 */ 80 | bool ends_with(const char *begin, const char *end){ 81 | if(begin == end)return true; 82 | if(begin < end || size() == 0 || (int)size() < end - begin)return false; 83 | int i = end - begin - 1; 84 | int j = (int)size() - 1; 85 | while(i >= 0 && j >= 0 && begin[i] == begin_[j]){ 86 | --i; 87 | --j; 88 | } 89 | return i >= 0 ? false : true; 90 | } 91 | 92 | /* 判断str是否是其后缀 */ 93 | bool ends_with(const char *str){ 94 | int i = strlen(str); 95 | if(i == 0)return true; 96 | if((int)size() == 0 || (int)size() < i)return false; 97 | --i; 98 | int j = (int)size() - 1; 99 | while(i >= 0 && j >= 0 && str[i] == begin_[j]){ 100 | --i; 101 | --j; 102 | } 103 | return i >= 0 ? false : true; 104 | } 105 | 106 | inline size_t find(const char *str, size_t pos = 0); 107 | 108 | inline size_t find(const char *begin, const char* end, size_t pos = 0); 109 | 110 | int compare(size_t start, size_t len, const char *str){ 111 | if(begin_ + len > end_){ 112 | throw std::out_of_range("len out of range"); 113 | } 114 | return strncmp(begin_+start, str, len); 115 | } 116 | 117 | 118 | inline size_t size() const {return static_cast(end_ - begin_);} 119 | 120 | const char& operator [](size_t pos) const{ 121 | if(begin_ + pos > end_){ 122 | throw std::out_of_range("index out of range"); 123 | } 124 | return begin_[pos]; 125 | } 126 | 127 | private: 128 | const char* begin_; 129 | const char* end_; 130 | size_t length_; 131 | }; 132 | 133 | inline std::ostream& operator<<(std::ostream& ostream, StringView view){ 134 | for(auto i = view.begin_; i != view.end_; ++i){ 135 | ostream << *i; 136 | } 137 | return ostream; 138 | } 139 | 140 | } 141 | 142 | } 143 | 144 | #endif -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(haha_web) 3 | 4 | set(CMAKE_VERBOSE_MAKEFILE ON) # make 过程中显示信息 5 | 6 | if(NOT CMAKE_BUILD_TYPE) 7 | set(CMAKE_BUILD_TYPE "Release") 8 | endif() 9 | 10 | set(CXX_FLAGS 11 | -g 12 | # -MMD 13 | -std=c++2a 14 | -rdynamic 15 | # -DVALGRIND 16 | -DCHECK_PTHREAD_RETURN_VALUE 17 | -D_FILE_OFFSET_BITS=64 18 | -Wall 19 | -Wno-deprecated 20 | -Werror 21 | -Wno-unused-function 22 | -Wno-builtin-macro-redefined 23 | ) 24 | 25 | if(CMAKE_BUILD_TYPE AND (CMAKE_BUILD_TYPE STREQUAL "Debug")) 26 | set(CMAKE_CXX_FLAGS_DEBUG "-O0") 27 | message("Debug mode:${CMAKE_CXX_FLAGS_DEBUG}") 28 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin/debug) # 设置可执行文件的位置 29 | SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib/debug) # 设置动态链接库的位置 30 | elseif(CMAKE_BUILD_TYPE AND (CMAKE_BUILD_TYPE STREQUAL "Release")) 31 | set(CMAKE_CXX_FLAGS_RELEASE "-O2") 32 | message("Release mode:${CMAKE_CXX_FLAGS_RELEASE}") 33 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin/release) # 设置可执行文件的位置 34 | SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib/release) # 设置动态链接库的位置 35 | else() 36 | message("else:${CMAKE_BUILD_TYPE}") 37 | message("else:${CMAKE_CXX_FLAGS_RELEASE}") 38 | set(CMAKE_BUILD_TYPE "Release") 39 | set(CMAKE_CXX_FLAGS_RELEASE "-O2") 40 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin/release) # 设置可执行文件的位置 41 | SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib/release) # 设置动态链接库的位置 42 | endif() 43 | 44 | string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CXX_FLAGS}") 45 | message(STATUS "CMAKE_CXX_FLAGS = " ${CMAKE_CXX_FLAGS}) 46 | message(STATUS "CMAKE_BUILD_TYPE = " ${CMAKE_BUILD_TYPE}) 47 | 48 | # # 设置编译参数 49 | # set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -rdynamic -O0 -ggdb -std=c++2a -Wall -Wno-deprecated -Werror -Wno-unused-function \ 50 | # -Wno-builtin-macro-redefined") 51 | 52 | # # 设置编译参数 53 | # set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -rdynamic -O2 -ggdb -std=c++2a -Wall -Wno-deprecated -Werror -Wno-unused-function \ 54 | # -Wno-builtin-macro-redefined") 55 | 56 | include_directories(. ./haha_web) 57 | # include_directories(${YAML_CPP_INCLUE}) 58 | include_directories(/usr/local/include) # 添加头文件路径 59 | link_directories(/usr/local/lib) # 添加库文件路径 60 | 61 | 62 | aux_source_directory(${PROJECT_SOURCE_DIR}/haha_web/base SRC_BASE_LIST) # 将制定路径内的源文件全部加入变量中 63 | aux_source_directory(${PROJECT_SOURCE_DIR}/haha_web/net SRC_NET_LIST) # 将制定路径内的源文件全部加入变量中 64 | aux_source_directory(${PROJECT_SOURCE_DIR}/haha_web/http SRC_HTTP_LIST) # 将制定路径内的源文件全部加入变量中 65 | aux_source_directory(${PROJECT_SOURCE_DIR}/haha_web/log SRC_LOG_LIST) # 将制定路径内的源文件全部加入变量中 66 | aux_source_directory(${PROJECT_SOURCE_DIR}/haha_web/config SRC_CONFIG_LIST) # 将制定路径内的源文件全部加入变量中 67 | 68 | add_subdirectory(${PROJECT_SOURCE_DIR}/haha_web/haha_json) # 添加子项目 69 | include_directories(${PROJECT_SOURCE_DIR}/haha_web/haha_json) # 添加子项目头文件 70 | 71 | set( 72 | LIB_SRC 73 | ${SRC_BASE_LIST} 74 | ${SRC_NET_LIST} 75 | ${SRC_HTTP_LIST} 76 | ${SRC_LOG_LIST} 77 | ${SRC_CONFIG_LIST} 78 | ) 79 | 80 | # MESSAGE( STATUS "LIB_SRC = ${LIB_SRC}.") 81 | 82 | add_library(haha_web SHARED ${LIB_SRC}) # SHARED 指示生成so文件 即动态链接库 83 | 84 | set( 85 | ALL_LIBS 86 | haha_web 87 | haha_json 88 | pthread 89 | uuid 90 | ssl # openssl的 91 | crypto # 这也是openssl的 92 | ) 93 | 94 | add_executable(httpServerTest.out tests/test_httpServer.cc) # 设置要生成可执行文件的代码 95 | add_dependencies(httpServerTest.out haha_web haha_json) # 设置依赖关系 96 | target_link_libraries(httpServerTest.out ${ALL_LIBS}) # 设置要链接的库 97 | 98 | add_executable(dogServletTest.out tests/test_servlet.cc) # 设置要生成可执行文件的代码 99 | add_dependencies(dogServletTest.out haha_web haha_json) # 设置依赖关系 100 | target_link_libraries(dogServletTest.out ${ALL_LIBS}) # 设置要链接的库 101 | 102 | # SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) # 设置可执行文件的位置 103 | # SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) # 设置动态链接库的位置 --------------------------------------------------------------------------------