├── .gitignore ├── LICENSE ├── Main.cpp ├── Makefile ├── README.md ├── base ├── Condition.h ├── CountDownLatch.cpp ├── CountDownLatch.h ├── CurrentThread.h ├── MutexLock.h ├── Thread.cpp ├── Thread.h ├── cJSON.cpp ├── cJSON.h └── noncopyable.h ├── bin └── .gitkeep ├── conf └── conf.json ├── docs └── image │ ├── ab-nginx-300.png │ ├── ab-nginx-500.png │ ├── ab-webserver-300.png │ ├── ab-webserver-500.png │ └── root.jpg └── src ├── Channel.cpp ├── Channel.h ├── Config.cpp ├── Config.h ├── Epoll.cpp ├── Epoll.h ├── EventLoop.cpp ├── EventLoop.h ├── EventLoopThread.cpp ├── EventLoopThread.h ├── EventLoopThreadPool.cpp ├── EventLoopThreadPool.h ├── FastCgi.cpp ├── FastCgi.h ├── HttpData.cpp ├── HttpData.h ├── Timer.cpp ├── Timer.h ├── Util.cpp ├── Util.h ├── WebServer.cpp └── WebServer.h /.gitignore: -------------------------------------------------------------------------------- 1 | /cmake-build-debug/ 2 | /CMakeLists.txt 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Icharle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Main.cpp: -------------------------------------------------------------------------------- 1 | #include "src/EventLoop.h" 2 | #include "src/WebServer.h" 3 | #include "src/Config.h" 4 | 5 | Config *config(Config::getInstance()); 6 | 7 | int main(int argc, char *argv[]) { 8 | int port; 9 | int threadNum; 10 | int backlog; 11 | // 读取配置文件 12 | Config::getInstance()->load("./conf/conf.json"); 13 | Config::getInstance()->getServerConfig(port, threadNum, backlog); 14 | 15 | EventLoop loop; 16 | WebServer webServer(&loop, threadNum, port, backlog); 17 | webServer.run(); 18 | loop.loop(); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT := $(shell pwd) 2 | MAIN := $(PROJECT)/Main.cpp 3 | SOURCE := $(wildcard $(PROJECT)/*.cpp $(PROJECT)/src/*.cpp $(PROJECT)/base/*.cpp) 4 | override SOURCE := $(filter-out $(MAIN), $(SOURCE)) 5 | OBJECT := $(patsubst %.cpp, %.o, $(SOURCE)) 6 | BIN := $(PROJECT)/bin 7 | TARGET := webserver 8 | CC := g++ 9 | CCFLAGS := -std=c++11 -g -O3 -Wall -D_PTHREADS 10 | CCLINK := -lpthread 11 | 12 | .PHONY: all 13 | all : $(BIN)/$(TARGET) 14 | 15 | $(BIN)/$(TARGET) : $(OBJECT) $(PROJECT)/Main.o 16 | $(CC) $(CCFLAGS) -o $@ $^ $(CCLINK) 17 | 18 | .PHONY: clean 19 | clean : 20 | rm $(OBJECT) $(BIN)/$(TARGET) $(PROJECT)/Main.o -rf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 基于Linux EPOLL多路复用的C++轻量级WebServer 2 | ## 功能 3 | * 可作为静态webserver(常规的html、图片、视频等) 4 | * 支持GET、POST(交由cgi处理)请求 5 | * 多站点支持 6 | * 支持fastcgi(目前已支持php-fpm) 7 | * 支持反向代理 8 | * 支持session、cookie 9 | 10 | **todo:** 11 | * 负载均衡(开发中,完善支持随机模式、hash模式、权重模式三种负载均衡策略) 12 | * 日志完善 13 | * 实现类似nginx中rewrite模块 14 | 15 | >[项目地址](https://github.com/icharle/WebServer) 欢迎PR、Issue、Star。 16 | 17 | ## 技术实现 18 | * 使用EPOLL边缘触发(ET)的IO多路复用技术,非阻塞IO+ET方式 19 | * 多线程(one loop per thread思想)充分利用CPU资源 20 | * 采用基于事件驱动的Reactor模式,主Reactor负责accept,子Reactor负责处理I/O,通过事件循环 21 | * FastCGI协议实现支持,可处理动态脚本语言 22 | * cJSON方式读入配置文件 23 | * 反向代理中域名自动解析IP地址 24 | * 状态机化HTTP处理过程 25 | 26 | ## ab压测(nginx vs webserver) 27 | ### ab -n 1000 -c 300 28 | * nginx 29 | 30 | ![ab-nginx-300](./docs/image/ab-nginx-300.png) 31 | 32 | * webserver 33 | 34 | ![ab-webserver-300](./docs/image/ab-webserver-300.png) 35 | 36 | ### ab -n 1000 -c 500 37 | * nginx 38 | 39 | ![ab-nginx-500](./docs/image/ab-nginx-500.png) 40 | 41 | * webserver 42 | 43 | ![ab-webserver-500](./docs/image/ab-webserver-500.png) 44 | 45 | ## 演示 46 | * [作为静态webserver](http://47.115.26.47/) 47 | 48 | >编译好的一个vue项目做为测试 49 | 50 | * [大文件测试](http://47.115.26.47/xingkong.jpg) 51 | 52 | >一个7m+大小的图片做为测试 53 | 54 | * 多host支持 55 | 56 | >由于服务器国内没有备案,使用curl模拟http请求在头部添加host测试 57 | 58 | ``` 59 | curl -H "Host: 1.icharle.com" http://47.115.26.47/ # 预期输出结果为:这里是1.icharle.com站点 60 | curl -H "Host: 2.icharle.com" http://47.115.26.47/ # 预期输出结果为:这里是2.icharle.com站点 61 | ``` 62 | 63 | * [反向代理](http://47.115.26.47:8085/) 64 | 65 | >采用新启动一个8085端口webserver**反向代理80端口**,配置文件示例如下 66 | 67 | ``` 68 | { 69 | "server": { 70 | "PORT": 8085, 71 | "ThreadNum": 4, 72 | "BackLog": 1024 73 | }, 74 | "host": [{ 75 | "default": { 76 | "root": "/home/www/default/", 77 | "access_log": "/www/log/default.log", 78 | "warn_log": "/www/log/default.warn.log", 79 | "proxy_pass": "http://127.0.0.1:80" # 反向代理80端口 80 | } 81 | }] 82 | } 83 | ``` 84 | 85 | * [CGI支持连接php-fpm](http://47.115.26.47/liuyanban/index.php) 86 | 87 | >简易的支持CURD的PHP项目做为测试(涉及GET、POST、AJAX、cookie、数据库交互) 88 | 89 | ## 食用 90 | **要求:** 91 | * Linux系统并已安装gcc/g++编译器 92 | * GCC >= 4.9 93 | 94 | ``` 95 | git clone https://github.com/icharle/WebServer.git 96 | cd WebServer 97 | make 98 | 99 | # 修改配置文件(见配置文件说明) 100 | 101 | # fastcgi使用(目前支持php-fpm) 102 | 编译安装PHP 103 | 修改php-fpm.conf中listen改用监听9000端口(如下为php-fpm.conf文件) 104 | 105 | [global] 106 | pid = xxxxx 107 | error_log = xxxxx 108 | log_level = notice 109 | 110 | [www] 111 | listen = 127.0.0.1:9000 # 需要修改的地方 112 | listen.backlog = 8192 113 | listen.allowed_clients = 127.0.0.1 114 | listen.owner = www # php-fpm运行的用户 115 | listen.group = www # php-fpm运行的用户组 116 | listen.mode = 0666 117 | user = www 118 | group = www 119 | pm = dynamic 120 | pm.status_path = /phpfpm_70_status 121 | pm.max_children = 80 122 | pm.start_servers = 5 123 | pm.min_spare_servers = 5 124 | pm.max_spare_servers = 20 125 | request_terminate_timeout = 100 126 | request_slowlog_timeout = 30 127 | slowlog = var/log/slow.log 128 | 129 | # 运行启动 130 | nohup ./bin/webserver >> web.log 2>&1 & 131 | ``` 132 | 133 | **注意:** 134 | 由于php-fpm默认运行在www用户及www用户组中,将root目录改为www用户,否则运行php脚本文件出现**权限错误**的问题。 135 | 136 | ``` 137 | # 创建不可登录的组帐户www 138 | groupadd www 139 | useradd www -s /sbin/nologin -g www 140 | 141 | # 修改root文件目录为www用户及组 142 | chown -R www:www /home/www/(修改为自己root目录) 143 | 144 | # 下图作者root文件目录示例 145 | ``` 146 | ![root文件目录示例](./docs/image/root.jpg) 147 | 148 | ## 配置文件说明 149 | ``` 150 | { 151 | "server": { 152 | "PORT": 80, // 运行端口 153 | "ThreadNum": 4, // 启动线程数 154 | "BackLog": 1024 // listen最大值 155 | }, 156 | "host": [ 157 | { 158 | "default": { // 默认站点(当host不匹配时候默认走default站点) 159 | "root": "/home/www/default/", // 该站点存放目录处 160 | "access_log": "/www/log/default.log", // 暂时无日志输出 161 | "warn_log": "/www/log/default.warn.log", // 暂时无日志输出 162 | "proxy_pass": "http://dududu.soarteam.cn" // 反向代理配置(仅支持HTTP代理) 不写端口时候默认反代80端口 163 | }, 164 | "1.icharle.com": { // host(即域名) 165 | "root": "/home/www/1.icharle.com/", 166 | "access_log": "/www/log/1.icharle.com.log", 167 | "warn_log": "/www/log/1.icharle.com.warn.log", 168 | "proxy_pass": "http://127.0.0.1:8043" // 反向代理(IP+端口、域名+端口) 169 | }, 170 | "2.icharle.com": { 171 | "root": "/home/www/2.icharle.com/", 172 | "access_log": "/www/log/2.icharle.com.log", 173 | "warn_log": "/www/log/2.icharle.com.warn.log" 174 | } 175 | } 176 | ] 177 | } 178 | ``` 179 | -------------------------------------------------------------------------------- /base/Condition.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/16. 3 | // 4 | 5 | #ifndef WEBNGINX_CONDITION_H 6 | #define WEBNGINX_CONDITION_H 7 | 8 | #include "noncopyable.h" 9 | #include "MutexLock.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class Condition : noncopyable { 16 | public: 17 | explicit Condition(MutexLock &mutex_) : 18 | mutex(mutex_) { 19 | pthread_cond_init(&cond, nullptr); 20 | } 21 | 22 | ~Condition() { 23 | pthread_cond_destroy(&cond); 24 | } 25 | 26 | void wait() { 27 | pthread_cond_wait(&cond, mutex.get()); 28 | } 29 | 30 | void notify() { 31 | pthread_cond_signal(&cond); 32 | } 33 | 34 | void notifyAll() { 35 | pthread_cond_broadcast(&cond); 36 | } 37 | 38 | bool waitForSeconds(int seconds) { 39 | struct timespec abstime; 40 | clock_gettime(CLOCK_REALTIME, &abstime); 41 | abstime.tv_sec += static_cast(seconds); 42 | return ETIMEDOUT == pthread_cond_timedwait(&cond, mutex.get(), &abstime); 43 | } 44 | 45 | private: 46 | MutexLock &mutex; 47 | pthread_cond_t cond; 48 | }; 49 | 50 | #endif //WEBNGINX_CONDITION_H 51 | -------------------------------------------------------------------------------- /base/CountDownLatch.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/16. 3 | // 4 | 5 | #include "CountDownLatch.h" 6 | 7 | CountDownLatch::CountDownLatch(int count_) : 8 | mutex(), 9 | condition(mutex), 10 | count(count_) { 11 | } 12 | 13 | void CountDownLatch::wait() { 14 | MutexLockGuard lock(mutex); 15 | while (count > 0) { 16 | condition.wait(); 17 | } 18 | } 19 | 20 | void CountDownLatch::countDown() { 21 | MutexLockGuard lock(mutex); 22 | count--; 23 | if (count == 0) { 24 | condition.notifyAll(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /base/CountDownLatch.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/16. 3 | // 4 | 5 | #ifndef WEBNGINX_COUNTDOWNLATCH_H 6 | #define WEBNGINX_COUNTDOWNLATCH_H 7 | 8 | #include "noncopyable.h" 9 | #include "Condition.h" 10 | #include "MutexLock.h" 11 | 12 | class CountDownLatch : noncopyable { 13 | public: 14 | explicit CountDownLatch(int count_); 15 | 16 | void wait(); 17 | 18 | void countDown(); 19 | 20 | private: 21 | mutable MutexLock mutex; 22 | Condition condition; 23 | int count; 24 | }; 25 | 26 | #endif //WEBNGINX_COUNTDOWNLATCH_H 27 | -------------------------------------------------------------------------------- /base/CurrentThread.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/16. 3 | // 4 | 5 | #ifndef WEBNGINX_CURRENTTHREAD_H 6 | #define WEBNGINX_CURRENTTHREAD_H 7 | 8 | #include 9 | 10 | namespace CurrentThread { 11 | extern __thread int t_cacheTid; 12 | extern __thread char t_tidString[32]; 13 | extern __thread int t_tidStringLength; 14 | extern __thread const char *t_threadName; 15 | 16 | void cacheTid(); 17 | 18 | inline int tid() { 19 | if (__builtin_expect(t_cacheTid == 0, 0)) { 20 | cacheTid(); 21 | } 22 | return t_cacheTid; 23 | } 24 | 25 | inline const char *tidString() { 26 | return t_tidString; 27 | } 28 | 29 | inline int tidStringLength() { 30 | return t_tidStringLength; 31 | } 32 | 33 | inline const char *name() { 34 | return t_threadName; 35 | } 36 | } 37 | 38 | #endif //WEBNGINX_CURRENTTHREAD_H 39 | -------------------------------------------------------------------------------- /base/MutexLock.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/16. 3 | // 4 | 5 | #ifndef WEBNGINX_MUTEXLOCK_H 6 | #define WEBNGINX_MUTEXLOCK_H 7 | 8 | #include "noncopyable.h" 9 | #include 10 | #include 11 | 12 | class MutexLock : noncopyable { 13 | public: 14 | MutexLock() { 15 | pthread_mutex_init(&mutex, nullptr); 16 | } 17 | 18 | ~MutexLock() { 19 | pthread_mutex_lock(&mutex); 20 | pthread_mutex_destroy(&mutex); 21 | } 22 | 23 | void lock() { 24 | pthread_mutex_lock(&mutex); 25 | } 26 | 27 | void unlock() { 28 | pthread_mutex_unlock(&mutex); 29 | } 30 | 31 | pthread_mutex_t *get() { 32 | return &mutex; 33 | } 34 | 35 | private: 36 | pthread_mutex_t mutex; 37 | 38 | private: 39 | friend class Condition; 40 | }; 41 | 42 | class MutexLockGuard : noncopyable { 43 | public: 44 | explicit MutexLockGuard(MutexLock &_mutex) : 45 | mutex(_mutex) { 46 | mutex.lock(); 47 | } 48 | 49 | ~MutexLockGuard() { 50 | mutex.unlock(); 51 | } 52 | 53 | private: 54 | MutexLock &mutex; 55 | }; 56 | 57 | #endif //WEBNGINX_MUTEXLOCK_H 58 | -------------------------------------------------------------------------------- /base/Thread.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/16. 3 | // 4 | 5 | #include "Thread.h" 6 | #include "CurrentThread.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | using namespace std; 18 | 19 | namespace CurrentThread { 20 | __thread int t_cacheTid = 0; 21 | __thread char t_tidString[32]; 22 | __thread int t_tidStringLength = 6; // tid length = 5 23 | __thread const char *t_threadName = "default"; 24 | } 25 | 26 | pid_t gettid() { 27 | return static_cast(::syscall(SYS_gettid)); 28 | } 29 | 30 | void CurrentThread::cacheTid() { 31 | if (t_cacheTid == 0) { 32 | t_cacheTid = gettid(); 33 | t_tidStringLength = snprintf(t_tidString, sizeof t_tidString, "%5d", t_cacheTid); 34 | } 35 | } 36 | 37 | struct TheadData { 38 | typedef Thread::ThreadFunc ThreadFunc; 39 | ThreadFunc func_; 40 | std::string name_; 41 | pid_t *tid_; 42 | CountDownLatch *latch_; 43 | 44 | TheadData(const ThreadFunc& func, const string& name, pid_t *tid, CountDownLatch* latch) : 45 | func_(func), 46 | name_(name), 47 | tid_(tid), 48 | latch_(latch) { 49 | 50 | } 51 | 52 | void runInThread() { 53 | *tid_ = CurrentThread::tid(); 54 | tid_ = nullptr; 55 | latch_->countDown(); 56 | latch_ = nullptr; 57 | 58 | CurrentThread::t_threadName = name_.empty() ? "Thread" : name_.c_str(); 59 | prctl(PR_SET_NAME, CurrentThread::t_threadName); 60 | 61 | func_(); 62 | CurrentThread::t_threadName = "finished"; 63 | } 64 | }; 65 | 66 | void *startThread(void *obj) { 67 | TheadData *data = static_cast(obj); 68 | data->runInThread(); 69 | delete data; 70 | return nullptr; 71 | } 72 | 73 | Thread::Thread(const ThreadFunc& func, const std::string& name) : 74 | started_(false), 75 | joined_(false), 76 | pthread_(0), 77 | tid_(0), 78 | func_(func), 79 | name_(name), 80 | latch_(1) { 81 | setDeafaultName(); 82 | } 83 | 84 | Thread::~Thread() { 85 | if (started_ && !joined_) { 86 | pthread_detach(pthread_); 87 | } 88 | } 89 | 90 | void Thread::setDeafaultName() { 91 | if (name_.empty()) { 92 | char buf[32]; 93 | snprintf(buf, sizeof buf, "Thread"); 94 | name_ = buf; 95 | } 96 | } 97 | 98 | void Thread::start() { 99 | assert(!started_); 100 | started_ = true; 101 | TheadData *data = new TheadData(func_, name_, &tid_, &latch_); 102 | if (pthread_create(&pthread_, nullptr, &startThread, data)) { 103 | started_ = false; 104 | delete data; 105 | } else { 106 | latch_.wait(); 107 | assert(tid_ > 0); 108 | } 109 | } 110 | 111 | int Thread::join() { 112 | assert(started_); 113 | assert(!joined_); 114 | joined_ = true; 115 | return pthread_join(pthread_, nullptr); 116 | } 117 | -------------------------------------------------------------------------------- /base/Thread.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/16. 3 | // 4 | 5 | #ifndef WEBNGINX_THREAD_H 6 | #define WEBNGINX_THREAD_H 7 | 8 | #include "CountDownLatch.h" 9 | #include "noncopyable.h" 10 | #include // for function 11 | #include 12 | #include 13 | #include 14 | #include // for SYS_** 15 | #include 16 | 17 | class Thread : noncopyable { 18 | public: 19 | typedef std::function ThreadFunc; 20 | 21 | explicit Thread(const ThreadFunc &, const std::string &name = std::string()); 22 | 23 | ~Thread(); 24 | 25 | void start(); 26 | 27 | int join(); 28 | 29 | bool started() const { return started_; } 30 | 31 | pid_t tid() const { return tid_; } 32 | 33 | const std::string &name() const { return name_; } 34 | 35 | private: 36 | void setDeafaultName(); 37 | 38 | bool started_; 39 | bool joined_; 40 | pthread_t pthread_; 41 | pid_t tid_; 42 | ThreadFunc func_; 43 | std::string name_; 44 | CountDownLatch latch_; 45 | }; 46 | 47 | #endif //WEBNGINX_THREAD_H 48 | -------------------------------------------------------------------------------- /base/cJSON.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009-2017 Dave Gamble and cJSON contributors 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* cJSON */ 24 | /* JSON parser in C. */ 25 | 26 | /* disable warnings about old C89 functions in MSVC */ 27 | #if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) 28 | #define _CRT_SECURE_NO_DEPRECATE 29 | #endif 30 | 31 | #ifdef __GNUC__ 32 | #pragma GCC visibility push(default) 33 | #endif 34 | #if defined(_MSC_VER) 35 | #pragma warning (push) 36 | /* disable warning about single line comments in system headers */ 37 | #pragma warning (disable : 4001) 38 | #endif 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #ifdef ENABLE_LOCALES 49 | #include 50 | #endif 51 | 52 | #if defined(_MSC_VER) 53 | #pragma warning (pop) 54 | #endif 55 | #ifdef __GNUC__ 56 | #pragma GCC visibility pop 57 | #endif 58 | 59 | #include "cJSON.h" 60 | 61 | /* define our own boolean type */ 62 | #ifdef true 63 | #undef true 64 | #endif 65 | #define true ((cJSON_bool)1) 66 | 67 | #ifdef false 68 | #undef false 69 | #endif 70 | #define false ((cJSON_bool)0) 71 | 72 | /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ 73 | #ifndef isinf 74 | #define isinf(d) (isnan((d - d)) && !isnan(d)) 75 | #endif 76 | #ifndef isnan 77 | #define isnan(d) (d != d) 78 | #endif 79 | 80 | #ifndef NAN 81 | #define NAN 0.0/0.0 82 | #endif 83 | 84 | typedef struct { 85 | const unsigned char *json; 86 | size_t position; 87 | } error; 88 | static error global_error = { NULL, 0 }; 89 | 90 | CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) 91 | { 92 | return (const char*) (global_error.json + global_error.position); 93 | } 94 | 95 | CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) 96 | { 97 | if (!cJSON_IsString(item)) 98 | { 99 | return NULL; 100 | } 101 | 102 | return item->valuestring; 103 | } 104 | 105 | CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item) 106 | { 107 | if (!cJSON_IsNumber(item)) 108 | { 109 | return NAN; 110 | } 111 | 112 | return item->valuedouble; 113 | } 114 | 115 | /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ 116 | #if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 13) 117 | #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. 118 | #endif 119 | 120 | CJSON_PUBLIC(const char*) cJSON_Version(void) 121 | { 122 | static char version[15]; 123 | sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); 124 | 125 | return version; 126 | } 127 | 128 | /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ 129 | static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) 130 | { 131 | if ((string1 == NULL) || (string2 == NULL)) 132 | { 133 | return 1; 134 | } 135 | 136 | if (string1 == string2) 137 | { 138 | return 0; 139 | } 140 | 141 | for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) 142 | { 143 | if (*string1 == '\0') 144 | { 145 | return 0; 146 | } 147 | } 148 | 149 | return tolower(*string1) - tolower(*string2); 150 | } 151 | 152 | typedef struct internal_hooks 153 | { 154 | void *(CJSON_CDECL *allocate)(size_t size); 155 | void (CJSON_CDECL *deallocate)(void *pointer); 156 | void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); 157 | } internal_hooks; 158 | 159 | #if defined(_MSC_VER) 160 | /* work around MSVC error C2322: '...' address of dllimport '...' is not static */ 161 | static void * CJSON_CDECL internal_malloc(size_t size) 162 | { 163 | return malloc(size); 164 | } 165 | static void CJSON_CDECL internal_free(void *pointer) 166 | { 167 | free(pointer); 168 | } 169 | static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) 170 | { 171 | return realloc(pointer, size); 172 | } 173 | #else 174 | #define internal_malloc malloc 175 | #define internal_free free 176 | #define internal_realloc realloc 177 | #endif 178 | 179 | /* strlen of character literals resolved at compile time */ 180 | #define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) 181 | 182 | static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; 183 | 184 | static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) 185 | { 186 | size_t length = 0; 187 | unsigned char *copy = NULL; 188 | 189 | if (string == NULL) 190 | { 191 | return NULL; 192 | } 193 | 194 | length = strlen((const char*)string) + sizeof(""); 195 | copy = (unsigned char*)hooks->allocate(length); 196 | if (copy == NULL) 197 | { 198 | return NULL; 199 | } 200 | memcpy(copy, string, length); 201 | 202 | return copy; 203 | } 204 | 205 | CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) 206 | { 207 | if (hooks == NULL) 208 | { 209 | /* Reset hooks */ 210 | global_hooks.allocate = malloc; 211 | global_hooks.deallocate = free; 212 | global_hooks.reallocate = realloc; 213 | return; 214 | } 215 | 216 | global_hooks.allocate = malloc; 217 | if (hooks->malloc_fn != NULL) 218 | { 219 | global_hooks.allocate = hooks->malloc_fn; 220 | } 221 | 222 | global_hooks.deallocate = free; 223 | if (hooks->free_fn != NULL) 224 | { 225 | global_hooks.deallocate = hooks->free_fn; 226 | } 227 | 228 | /* use realloc only if both free and malloc are used */ 229 | global_hooks.reallocate = NULL; 230 | if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) 231 | { 232 | global_hooks.reallocate = realloc; 233 | } 234 | } 235 | 236 | /* Internal constructor. */ 237 | static cJSON *cJSON_New_Item(const internal_hooks * const hooks) 238 | { 239 | cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); 240 | if (node) 241 | { 242 | memset(node, '\0', sizeof(cJSON)); 243 | } 244 | 245 | return node; 246 | } 247 | 248 | /* Delete a cJSON structure. */ 249 | CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) 250 | { 251 | cJSON *next = NULL; 252 | while (item != NULL) 253 | { 254 | next = item->next; 255 | if (!(item->type & cJSON_IsReference) && (item->child != NULL)) 256 | { 257 | cJSON_Delete(item->child); 258 | } 259 | if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) 260 | { 261 | global_hooks.deallocate(item->valuestring); 262 | } 263 | if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) 264 | { 265 | global_hooks.deallocate(item->string); 266 | } 267 | global_hooks.deallocate(item); 268 | item = next; 269 | } 270 | } 271 | 272 | /* get the decimal point character of the current locale */ 273 | static unsigned char get_decimal_point(void) 274 | { 275 | #ifdef ENABLE_LOCALES 276 | struct lconv *lconv = localeconv(); 277 | return (unsigned char) lconv->decimal_point[0]; 278 | #else 279 | return '.'; 280 | #endif 281 | } 282 | 283 | typedef struct 284 | { 285 | const unsigned char *content; 286 | size_t length; 287 | size_t offset; 288 | size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ 289 | internal_hooks hooks; 290 | } parse_buffer; 291 | 292 | /* check if the given size is left to read in a given parse buffer (starting with 1) */ 293 | #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) 294 | /* check if the buffer can be accessed at the given index (starting with 0) */ 295 | #define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) 296 | #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) 297 | /* get a pointer to the buffer at the position */ 298 | #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) 299 | 300 | /* Parse the input text to generate a number, and populate the result into item. */ 301 | static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) 302 | { 303 | double number = 0; 304 | unsigned char *after_end = NULL; 305 | unsigned char number_c_string[64]; 306 | unsigned char decimal_point = get_decimal_point(); 307 | size_t i = 0; 308 | 309 | if ((input_buffer == NULL) || (input_buffer->content == NULL)) 310 | { 311 | return false; 312 | } 313 | 314 | /* copy the number into a temporary buffer and replace '.' with the decimal point 315 | * of the current locale (for strtod) 316 | * This also takes care of '\0' not necessarily being available for marking the end of the input */ 317 | for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) 318 | { 319 | switch (buffer_at_offset(input_buffer)[i]) 320 | { 321 | case '0': 322 | case '1': 323 | case '2': 324 | case '3': 325 | case '4': 326 | case '5': 327 | case '6': 328 | case '7': 329 | case '8': 330 | case '9': 331 | case '+': 332 | case '-': 333 | case 'e': 334 | case 'E': 335 | number_c_string[i] = buffer_at_offset(input_buffer)[i]; 336 | break; 337 | 338 | case '.': 339 | number_c_string[i] = decimal_point; 340 | break; 341 | 342 | default: 343 | goto loop_end; 344 | } 345 | } 346 | loop_end: 347 | number_c_string[i] = '\0'; 348 | 349 | number = strtod((const char*)number_c_string, (char**)&after_end); 350 | if (number_c_string == after_end) 351 | { 352 | return false; /* parse_error */ 353 | } 354 | 355 | item->valuedouble = number; 356 | 357 | /* use saturation in case of overflow */ 358 | if (number >= INT_MAX) 359 | { 360 | item->valueint = INT_MAX; 361 | } 362 | else if (number <= (double)INT_MIN) 363 | { 364 | item->valueint = INT_MIN; 365 | } 366 | else 367 | { 368 | item->valueint = (int)number; 369 | } 370 | 371 | item->type = cJSON_Number; 372 | 373 | input_buffer->offset += (size_t)(after_end - number_c_string); 374 | return true; 375 | } 376 | 377 | /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ 378 | CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) 379 | { 380 | if (number >= INT_MAX) 381 | { 382 | object->valueint = INT_MAX; 383 | } 384 | else if (number <= (double)INT_MIN) 385 | { 386 | object->valueint = INT_MIN; 387 | } 388 | else 389 | { 390 | object->valueint = (int)number; 391 | } 392 | 393 | return object->valuedouble = number; 394 | } 395 | 396 | CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) 397 | { 398 | char *copy = NULL; 399 | /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ 400 | if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) 401 | { 402 | return NULL; 403 | } 404 | if (strlen(valuestring) <= strlen(object->valuestring)) 405 | { 406 | strcpy(object->valuestring, valuestring); 407 | return object->valuestring; 408 | } 409 | copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); 410 | if (copy == NULL) 411 | { 412 | return NULL; 413 | } 414 | if (object->valuestring != NULL) 415 | { 416 | cJSON_free(object->valuestring); 417 | } 418 | object->valuestring = copy; 419 | 420 | return copy; 421 | } 422 | 423 | typedef struct 424 | { 425 | unsigned char *buffer; 426 | size_t length; 427 | size_t offset; 428 | size_t depth; /* current nesting depth (for formatted printing) */ 429 | cJSON_bool noalloc; 430 | cJSON_bool format; /* is this print a formatted print */ 431 | internal_hooks hooks; 432 | } printbuffer; 433 | 434 | /* realloc printbuffer if necessary to have at least "needed" bytes more */ 435 | static unsigned char* ensure(printbuffer * const p, size_t needed) 436 | { 437 | unsigned char *newbuffer = NULL; 438 | size_t newsize = 0; 439 | 440 | if ((p == NULL) || (p->buffer == NULL)) 441 | { 442 | return NULL; 443 | } 444 | 445 | if ((p->length > 0) && (p->offset >= p->length)) 446 | { 447 | /* make sure that offset is valid */ 448 | return NULL; 449 | } 450 | 451 | if (needed > INT_MAX) 452 | { 453 | /* sizes bigger than INT_MAX are currently not supported */ 454 | return NULL; 455 | } 456 | 457 | needed += p->offset + 1; 458 | if (needed <= p->length) 459 | { 460 | return p->buffer + p->offset; 461 | } 462 | 463 | if (p->noalloc) { 464 | return NULL; 465 | } 466 | 467 | /* calculate new buffer size */ 468 | if (needed > (INT_MAX / 2)) 469 | { 470 | /* overflow of int, use INT_MAX if possible */ 471 | if (needed <= INT_MAX) 472 | { 473 | newsize = INT_MAX; 474 | } 475 | else 476 | { 477 | return NULL; 478 | } 479 | } 480 | else 481 | { 482 | newsize = needed * 2; 483 | } 484 | 485 | if (p->hooks.reallocate != NULL) 486 | { 487 | /* reallocate with realloc if available */ 488 | newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); 489 | if (newbuffer == NULL) 490 | { 491 | p->hooks.deallocate(p->buffer); 492 | p->length = 0; 493 | p->buffer = NULL; 494 | 495 | return NULL; 496 | } 497 | } 498 | else 499 | { 500 | /* otherwise reallocate manually */ 501 | newbuffer = (unsigned char*)p->hooks.allocate(newsize); 502 | if (!newbuffer) 503 | { 504 | p->hooks.deallocate(p->buffer); 505 | p->length = 0; 506 | p->buffer = NULL; 507 | 508 | return NULL; 509 | } 510 | if (newbuffer) 511 | { 512 | memcpy(newbuffer, p->buffer, p->offset + 1); 513 | } 514 | p->hooks.deallocate(p->buffer); 515 | } 516 | p->length = newsize; 517 | p->buffer = newbuffer; 518 | 519 | return newbuffer + p->offset; 520 | } 521 | 522 | /* calculate the new length of the string in a printbuffer and update the offset */ 523 | static void update_offset(printbuffer * const buffer) 524 | { 525 | const unsigned char *buffer_pointer = NULL; 526 | if ((buffer == NULL) || (buffer->buffer == NULL)) 527 | { 528 | return; 529 | } 530 | buffer_pointer = buffer->buffer + buffer->offset; 531 | 532 | buffer->offset += strlen((const char*)buffer_pointer); 533 | } 534 | 535 | /* securely comparison of floating-point variables */ 536 | static cJSON_bool compare_double(double a, double b) 537 | { 538 | double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); 539 | return (fabs(a - b) <= maxVal * DBL_EPSILON); 540 | } 541 | 542 | /* Render the number nicely from the given item into a string. */ 543 | static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) 544 | { 545 | unsigned char *output_pointer = NULL; 546 | double d = item->valuedouble; 547 | int length = 0; 548 | size_t i = 0; 549 | unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ 550 | unsigned char decimal_point = get_decimal_point(); 551 | double test = 0.0; 552 | 553 | if (output_buffer == NULL) 554 | { 555 | return false; 556 | } 557 | 558 | /* This checks for NaN and Infinity */ 559 | if (isnan(d) || isinf(d)) 560 | { 561 | length = sprintf((char*)number_buffer, "null"); 562 | } 563 | else 564 | { 565 | /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ 566 | length = sprintf((char*)number_buffer, "%1.15g", d); 567 | 568 | /* Check whether the original double can be recovered */ 569 | if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) 570 | { 571 | /* If not, print with 17 decimal places of precision */ 572 | length = sprintf((char*)number_buffer, "%1.17g", d); 573 | } 574 | } 575 | 576 | /* sprintf failed or buffer overrun occurred */ 577 | if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) 578 | { 579 | return false; 580 | } 581 | 582 | /* reserve appropriate space in the output */ 583 | output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); 584 | if (output_pointer == NULL) 585 | { 586 | return false; 587 | } 588 | 589 | /* copy the printed number to the output and replace locale 590 | * dependent decimal point with '.' */ 591 | for (i = 0; i < ((size_t)length); i++) 592 | { 593 | if (number_buffer[i] == decimal_point) 594 | { 595 | output_pointer[i] = '.'; 596 | continue; 597 | } 598 | 599 | output_pointer[i] = number_buffer[i]; 600 | } 601 | output_pointer[i] = '\0'; 602 | 603 | output_buffer->offset += (size_t)length; 604 | 605 | return true; 606 | } 607 | 608 | /* parse 4 digit hexadecimal number */ 609 | static unsigned parse_hex4(const unsigned char * const input) 610 | { 611 | unsigned int h = 0; 612 | size_t i = 0; 613 | 614 | for (i = 0; i < 4; i++) 615 | { 616 | /* parse digit */ 617 | if ((input[i] >= '0') && (input[i] <= '9')) 618 | { 619 | h += (unsigned int) input[i] - '0'; 620 | } 621 | else if ((input[i] >= 'A') && (input[i] <= 'F')) 622 | { 623 | h += (unsigned int) 10 + input[i] - 'A'; 624 | } 625 | else if ((input[i] >= 'a') && (input[i] <= 'f')) 626 | { 627 | h += (unsigned int) 10 + input[i] - 'a'; 628 | } 629 | else /* invalid */ 630 | { 631 | return 0; 632 | } 633 | 634 | if (i < 3) 635 | { 636 | /* shift left to make place for the next nibble */ 637 | h = h << 4; 638 | } 639 | } 640 | 641 | return h; 642 | } 643 | 644 | /* converts a UTF-16 literal to UTF-8 645 | * A literal can be one or two sequences of the form \uXXXX */ 646 | static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) 647 | { 648 | long unsigned int codepoint = 0; 649 | unsigned int first_code = 0; 650 | const unsigned char *first_sequence = input_pointer; 651 | unsigned char utf8_length = 0; 652 | unsigned char utf8_position = 0; 653 | unsigned char sequence_length = 0; 654 | unsigned char first_byte_mark = 0; 655 | 656 | if ((input_end - first_sequence) < 6) 657 | { 658 | /* input ends unexpectedly */ 659 | goto fail; 660 | } 661 | 662 | /* get the first utf16 sequence */ 663 | first_code = parse_hex4(first_sequence + 2); 664 | 665 | /* check that the code is valid */ 666 | if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) 667 | { 668 | goto fail; 669 | } 670 | 671 | /* UTF16 surrogate pair */ 672 | if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) 673 | { 674 | const unsigned char *second_sequence = first_sequence + 6; 675 | unsigned int second_code = 0; 676 | sequence_length = 12; /* \uXXXX\uXXXX */ 677 | 678 | if ((input_end - second_sequence) < 6) 679 | { 680 | /* input ends unexpectedly */ 681 | goto fail; 682 | } 683 | 684 | if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) 685 | { 686 | /* missing second half of the surrogate pair */ 687 | goto fail; 688 | } 689 | 690 | /* get the second utf16 sequence */ 691 | second_code = parse_hex4(second_sequence + 2); 692 | /* check that the code is valid */ 693 | if ((second_code < 0xDC00) || (second_code > 0xDFFF)) 694 | { 695 | /* invalid second half of the surrogate pair */ 696 | goto fail; 697 | } 698 | 699 | 700 | /* calculate the unicode codepoint from the surrogate pair */ 701 | codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); 702 | } 703 | else 704 | { 705 | sequence_length = 6; /* \uXXXX */ 706 | codepoint = first_code; 707 | } 708 | 709 | /* encode as UTF-8 710 | * takes at maximum 4 bytes to encode: 711 | * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ 712 | if (codepoint < 0x80) 713 | { 714 | /* normal ascii, encoding 0xxxxxxx */ 715 | utf8_length = 1; 716 | } 717 | else if (codepoint < 0x800) 718 | { 719 | /* two bytes, encoding 110xxxxx 10xxxxxx */ 720 | utf8_length = 2; 721 | first_byte_mark = 0xC0; /* 11000000 */ 722 | } 723 | else if (codepoint < 0x10000) 724 | { 725 | /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ 726 | utf8_length = 3; 727 | first_byte_mark = 0xE0; /* 11100000 */ 728 | } 729 | else if (codepoint <= 0x10FFFF) 730 | { 731 | /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ 732 | utf8_length = 4; 733 | first_byte_mark = 0xF0; /* 11110000 */ 734 | } 735 | else 736 | { 737 | /* invalid unicode codepoint */ 738 | goto fail; 739 | } 740 | 741 | /* encode as utf8 */ 742 | for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) 743 | { 744 | /* 10xxxxxx */ 745 | (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); 746 | codepoint >>= 6; 747 | } 748 | /* encode first byte */ 749 | if (utf8_length > 1) 750 | { 751 | (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); 752 | } 753 | else 754 | { 755 | (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); 756 | } 757 | 758 | *output_pointer += utf8_length; 759 | 760 | return sequence_length; 761 | 762 | fail: 763 | return 0; 764 | } 765 | 766 | /* Parse the input text into an unescaped cinput, and populate item. */ 767 | static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) 768 | { 769 | const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; 770 | const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; 771 | unsigned char *output_pointer = NULL; 772 | unsigned char *output = NULL; 773 | 774 | /* not a string */ 775 | if (buffer_at_offset(input_buffer)[0] != '\"') 776 | { 777 | goto fail; 778 | } 779 | 780 | { 781 | /* calculate approximate size of the output (overestimate) */ 782 | size_t allocation_length = 0; 783 | size_t skipped_bytes = 0; 784 | while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) 785 | { 786 | /* is escape sequence */ 787 | if (input_end[0] == '\\') 788 | { 789 | if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) 790 | { 791 | /* prevent buffer overflow when last input character is a backslash */ 792 | goto fail; 793 | } 794 | skipped_bytes++; 795 | input_end++; 796 | } 797 | input_end++; 798 | } 799 | if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) 800 | { 801 | goto fail; /* string ended unexpectedly */ 802 | } 803 | 804 | /* This is at most how much we need for the output */ 805 | allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; 806 | output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); 807 | if (output == NULL) 808 | { 809 | goto fail; /* allocation failure */ 810 | } 811 | } 812 | 813 | output_pointer = output; 814 | /* loop through the string literal */ 815 | while (input_pointer < input_end) 816 | { 817 | if (*input_pointer != '\\') 818 | { 819 | *output_pointer++ = *input_pointer++; 820 | } 821 | /* escape sequence */ 822 | else 823 | { 824 | unsigned char sequence_length = 2; 825 | if ((input_end - input_pointer) < 1) 826 | { 827 | goto fail; 828 | } 829 | 830 | switch (input_pointer[1]) 831 | { 832 | case 'b': 833 | *output_pointer++ = '\b'; 834 | break; 835 | case 'f': 836 | *output_pointer++ = '\f'; 837 | break; 838 | case 'n': 839 | *output_pointer++ = '\n'; 840 | break; 841 | case 'r': 842 | *output_pointer++ = '\r'; 843 | break; 844 | case 't': 845 | *output_pointer++ = '\t'; 846 | break; 847 | case '\"': 848 | case '\\': 849 | case '/': 850 | *output_pointer++ = input_pointer[1]; 851 | break; 852 | 853 | /* UTF-16 literal */ 854 | case 'u': 855 | sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); 856 | if (sequence_length == 0) 857 | { 858 | /* failed to convert UTF16-literal to UTF-8 */ 859 | goto fail; 860 | } 861 | break; 862 | 863 | default: 864 | goto fail; 865 | } 866 | input_pointer += sequence_length; 867 | } 868 | } 869 | 870 | /* zero terminate the output */ 871 | *output_pointer = '\0'; 872 | 873 | item->type = cJSON_String; 874 | item->valuestring = (char*)output; 875 | 876 | input_buffer->offset = (size_t) (input_end - input_buffer->content); 877 | input_buffer->offset++; 878 | 879 | return true; 880 | 881 | fail: 882 | if (output != NULL) 883 | { 884 | input_buffer->hooks.deallocate(output); 885 | } 886 | 887 | if (input_pointer != NULL) 888 | { 889 | input_buffer->offset = (size_t)(input_pointer - input_buffer->content); 890 | } 891 | 892 | return false; 893 | } 894 | 895 | /* Render the cstring provided to an escaped version that can be printed. */ 896 | static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) 897 | { 898 | const unsigned char *input_pointer = NULL; 899 | unsigned char *output = NULL; 900 | unsigned char *output_pointer = NULL; 901 | size_t output_length = 0; 902 | /* numbers of additional characters needed for escaping */ 903 | size_t escape_characters = 0; 904 | 905 | if (output_buffer == NULL) 906 | { 907 | return false; 908 | } 909 | 910 | /* empty string */ 911 | if (input == NULL) 912 | { 913 | output = ensure(output_buffer, sizeof("\"\"")); 914 | if (output == NULL) 915 | { 916 | return false; 917 | } 918 | strcpy((char*)output, "\"\""); 919 | 920 | return true; 921 | } 922 | 923 | /* set "flag" to 1 if something needs to be escaped */ 924 | for (input_pointer = input; *input_pointer; input_pointer++) 925 | { 926 | switch (*input_pointer) 927 | { 928 | case '\"': 929 | case '\\': 930 | case '\b': 931 | case '\f': 932 | case '\n': 933 | case '\r': 934 | case '\t': 935 | /* one character escape sequence */ 936 | escape_characters++; 937 | break; 938 | default: 939 | if (*input_pointer < 32) 940 | { 941 | /* UTF-16 escape sequence uXXXX */ 942 | escape_characters += 5; 943 | } 944 | break; 945 | } 946 | } 947 | output_length = (size_t)(input_pointer - input) + escape_characters; 948 | 949 | output = ensure(output_buffer, output_length + sizeof("\"\"")); 950 | if (output == NULL) 951 | { 952 | return false; 953 | } 954 | 955 | /* no characters have to be escaped */ 956 | if (escape_characters == 0) 957 | { 958 | output[0] = '\"'; 959 | memcpy(output + 1, input, output_length); 960 | output[output_length + 1] = '\"'; 961 | output[output_length + 2] = '\0'; 962 | 963 | return true; 964 | } 965 | 966 | output[0] = '\"'; 967 | output_pointer = output + 1; 968 | /* copy the string */ 969 | for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) 970 | { 971 | if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) 972 | { 973 | /* normal character, copy */ 974 | *output_pointer = *input_pointer; 975 | } 976 | else 977 | { 978 | /* character needs to be escaped */ 979 | *output_pointer++ = '\\'; 980 | switch (*input_pointer) 981 | { 982 | case '\\': 983 | *output_pointer = '\\'; 984 | break; 985 | case '\"': 986 | *output_pointer = '\"'; 987 | break; 988 | case '\b': 989 | *output_pointer = 'b'; 990 | break; 991 | case '\f': 992 | *output_pointer = 'f'; 993 | break; 994 | case '\n': 995 | *output_pointer = 'n'; 996 | break; 997 | case '\r': 998 | *output_pointer = 'r'; 999 | break; 1000 | case '\t': 1001 | *output_pointer = 't'; 1002 | break; 1003 | default: 1004 | /* escape and print as unicode codepoint */ 1005 | sprintf((char*)output_pointer, "u%04x", *input_pointer); 1006 | output_pointer += 4; 1007 | break; 1008 | } 1009 | } 1010 | } 1011 | output[output_length + 1] = '\"'; 1012 | output[output_length + 2] = '\0'; 1013 | 1014 | return true; 1015 | } 1016 | 1017 | /* Invoke print_string_ptr (which is useful) on an item. */ 1018 | static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) 1019 | { 1020 | return print_string_ptr((unsigned char*)item->valuestring, p); 1021 | } 1022 | 1023 | /* Predeclare these prototypes. */ 1024 | static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); 1025 | static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); 1026 | static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); 1027 | static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); 1028 | static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); 1029 | static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); 1030 | 1031 | /* Utility to jump whitespace and cr/lf */ 1032 | static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) 1033 | { 1034 | if ((buffer == NULL) || (buffer->content == NULL)) 1035 | { 1036 | return NULL; 1037 | } 1038 | 1039 | if (cannot_access_at_index(buffer, 0)) 1040 | { 1041 | return buffer; 1042 | } 1043 | 1044 | while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) 1045 | { 1046 | buffer->offset++; 1047 | } 1048 | 1049 | if (buffer->offset == buffer->length) 1050 | { 1051 | buffer->offset--; 1052 | } 1053 | 1054 | return buffer; 1055 | } 1056 | 1057 | /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ 1058 | static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) 1059 | { 1060 | if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) 1061 | { 1062 | return NULL; 1063 | } 1064 | 1065 | if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) 1066 | { 1067 | buffer->offset += 3; 1068 | } 1069 | 1070 | return buffer; 1071 | } 1072 | 1073 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) 1074 | { 1075 | size_t buffer_length; 1076 | 1077 | if (NULL == value) 1078 | { 1079 | return NULL; 1080 | } 1081 | 1082 | /* Adding null character size due to require_null_terminated. */ 1083 | buffer_length = strlen(value) + sizeof(""); 1084 | 1085 | return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); 1086 | } 1087 | 1088 | /* Parse an object - create a new root, and populate. */ 1089 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) 1090 | { 1091 | parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; 1092 | cJSON *item = NULL; 1093 | 1094 | /* reset error position */ 1095 | global_error.json = NULL; 1096 | global_error.position = 0; 1097 | 1098 | if (value == NULL || 0 == buffer_length) 1099 | { 1100 | goto fail; 1101 | } 1102 | 1103 | buffer.content = (const unsigned char*)value; 1104 | buffer.length = buffer_length; 1105 | buffer.offset = 0; 1106 | buffer.hooks = global_hooks; 1107 | 1108 | item = cJSON_New_Item(&global_hooks); 1109 | if (item == NULL) /* memory fail */ 1110 | { 1111 | goto fail; 1112 | } 1113 | 1114 | if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) 1115 | { 1116 | /* parse failure. ep is set. */ 1117 | goto fail; 1118 | } 1119 | 1120 | /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ 1121 | if (require_null_terminated) 1122 | { 1123 | buffer_skip_whitespace(&buffer); 1124 | if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') 1125 | { 1126 | goto fail; 1127 | } 1128 | } 1129 | if (return_parse_end) 1130 | { 1131 | *return_parse_end = (const char*)buffer_at_offset(&buffer); 1132 | } 1133 | 1134 | return item; 1135 | 1136 | fail: 1137 | if (item != NULL) 1138 | { 1139 | cJSON_Delete(item); 1140 | } 1141 | 1142 | if (value != NULL) 1143 | { 1144 | error local_error; 1145 | local_error.json = (const unsigned char*)value; 1146 | local_error.position = 0; 1147 | 1148 | if (buffer.offset < buffer.length) 1149 | { 1150 | local_error.position = buffer.offset; 1151 | } 1152 | else if (buffer.length > 0) 1153 | { 1154 | local_error.position = buffer.length - 1; 1155 | } 1156 | 1157 | if (return_parse_end != NULL) 1158 | { 1159 | *return_parse_end = (const char*)local_error.json + local_error.position; 1160 | } 1161 | 1162 | global_error = local_error; 1163 | } 1164 | 1165 | return NULL; 1166 | } 1167 | 1168 | /* Default options for cJSON_Parse */ 1169 | CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) 1170 | { 1171 | return cJSON_ParseWithOpts(value, 0, 0); 1172 | } 1173 | 1174 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) 1175 | { 1176 | return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); 1177 | } 1178 | 1179 | #define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) 1180 | 1181 | static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) 1182 | { 1183 | static const size_t default_buffer_size = 256; 1184 | printbuffer buffer[1]; 1185 | unsigned char *printed = NULL; 1186 | 1187 | memset(buffer, 0, sizeof(buffer)); 1188 | 1189 | /* create buffer */ 1190 | buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); 1191 | buffer->length = default_buffer_size; 1192 | buffer->format = format; 1193 | buffer->hooks = *hooks; 1194 | if (buffer->buffer == NULL) 1195 | { 1196 | goto fail; 1197 | } 1198 | 1199 | /* print the value */ 1200 | if (!print_value(item, buffer)) 1201 | { 1202 | goto fail; 1203 | } 1204 | update_offset(buffer); 1205 | 1206 | /* check if reallocate is available */ 1207 | if (hooks->reallocate != NULL) 1208 | { 1209 | printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); 1210 | if (printed == NULL) { 1211 | goto fail; 1212 | } 1213 | buffer->buffer = NULL; 1214 | } 1215 | else /* otherwise copy the JSON over to a new buffer */ 1216 | { 1217 | printed = (unsigned char*) hooks->allocate(buffer->offset + 1); 1218 | if (printed == NULL) 1219 | { 1220 | goto fail; 1221 | } 1222 | memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); 1223 | printed[buffer->offset] = '\0'; /* just to be sure */ 1224 | 1225 | /* free the buffer */ 1226 | hooks->deallocate(buffer->buffer); 1227 | } 1228 | 1229 | return printed; 1230 | 1231 | fail: 1232 | if (buffer->buffer != NULL) 1233 | { 1234 | hooks->deallocate(buffer->buffer); 1235 | } 1236 | 1237 | if (printed != NULL) 1238 | { 1239 | hooks->deallocate(printed); 1240 | } 1241 | 1242 | return NULL; 1243 | } 1244 | 1245 | /* Render a cJSON item/entity/structure to text. */ 1246 | CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) 1247 | { 1248 | return (char*)print(item, true, &global_hooks); 1249 | } 1250 | 1251 | CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) 1252 | { 1253 | return (char*)print(item, false, &global_hooks); 1254 | } 1255 | 1256 | CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) 1257 | { 1258 | printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; 1259 | 1260 | if (prebuffer < 0) 1261 | { 1262 | return NULL; 1263 | } 1264 | 1265 | p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); 1266 | if (!p.buffer) 1267 | { 1268 | return NULL; 1269 | } 1270 | 1271 | p.length = (size_t)prebuffer; 1272 | p.offset = 0; 1273 | p.noalloc = false; 1274 | p.format = fmt; 1275 | p.hooks = global_hooks; 1276 | 1277 | if (!print_value(item, &p)) 1278 | { 1279 | global_hooks.deallocate(p.buffer); 1280 | return NULL; 1281 | } 1282 | 1283 | return (char*)p.buffer; 1284 | } 1285 | 1286 | CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) 1287 | { 1288 | printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; 1289 | 1290 | if ((length < 0) || (buffer == NULL)) 1291 | { 1292 | return false; 1293 | } 1294 | 1295 | p.buffer = (unsigned char*)buffer; 1296 | p.length = (size_t)length; 1297 | p.offset = 0; 1298 | p.noalloc = true; 1299 | p.format = format; 1300 | p.hooks = global_hooks; 1301 | 1302 | return print_value(item, &p); 1303 | } 1304 | 1305 | /* Parser core - when encountering text, process appropriately. */ 1306 | static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) 1307 | { 1308 | if ((input_buffer == NULL) || (input_buffer->content == NULL)) 1309 | { 1310 | return false; /* no input */ 1311 | } 1312 | 1313 | /* parse the different types of values */ 1314 | /* null */ 1315 | if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) 1316 | { 1317 | item->type = cJSON_NULL; 1318 | input_buffer->offset += 4; 1319 | return true; 1320 | } 1321 | /* false */ 1322 | if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) 1323 | { 1324 | item->type = cJSON_False; 1325 | input_buffer->offset += 5; 1326 | return true; 1327 | } 1328 | /* true */ 1329 | if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) 1330 | { 1331 | item->type = cJSON_True; 1332 | item->valueint = 1; 1333 | input_buffer->offset += 4; 1334 | return true; 1335 | } 1336 | /* string */ 1337 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) 1338 | { 1339 | return parse_string(item, input_buffer); 1340 | } 1341 | /* number */ 1342 | if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) 1343 | { 1344 | return parse_number(item, input_buffer); 1345 | } 1346 | /* array */ 1347 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) 1348 | { 1349 | return parse_array(item, input_buffer); 1350 | } 1351 | /* object */ 1352 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) 1353 | { 1354 | return parse_object(item, input_buffer); 1355 | } 1356 | 1357 | return false; 1358 | } 1359 | 1360 | /* Render a value to text. */ 1361 | static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) 1362 | { 1363 | unsigned char *output = NULL; 1364 | 1365 | if ((item == NULL) || (output_buffer == NULL)) 1366 | { 1367 | return false; 1368 | } 1369 | 1370 | switch ((item->type) & 0xFF) 1371 | { 1372 | case cJSON_NULL: 1373 | output = ensure(output_buffer, 5); 1374 | if (output == NULL) 1375 | { 1376 | return false; 1377 | } 1378 | strcpy((char*)output, "null"); 1379 | return true; 1380 | 1381 | case cJSON_False: 1382 | output = ensure(output_buffer, 6); 1383 | if (output == NULL) 1384 | { 1385 | return false; 1386 | } 1387 | strcpy((char*)output, "false"); 1388 | return true; 1389 | 1390 | case cJSON_True: 1391 | output = ensure(output_buffer, 5); 1392 | if (output == NULL) 1393 | { 1394 | return false; 1395 | } 1396 | strcpy((char*)output, "true"); 1397 | return true; 1398 | 1399 | case cJSON_Number: 1400 | return print_number(item, output_buffer); 1401 | 1402 | case cJSON_Raw: 1403 | { 1404 | size_t raw_length = 0; 1405 | if (item->valuestring == NULL) 1406 | { 1407 | return false; 1408 | } 1409 | 1410 | raw_length = strlen(item->valuestring) + sizeof(""); 1411 | output = ensure(output_buffer, raw_length); 1412 | if (output == NULL) 1413 | { 1414 | return false; 1415 | } 1416 | memcpy(output, item->valuestring, raw_length); 1417 | return true; 1418 | } 1419 | 1420 | case cJSON_String: 1421 | return print_string(item, output_buffer); 1422 | 1423 | case cJSON_Array: 1424 | return print_array(item, output_buffer); 1425 | 1426 | case cJSON_Object: 1427 | return print_object(item, output_buffer); 1428 | 1429 | default: 1430 | return false; 1431 | } 1432 | } 1433 | 1434 | /* Build an array from input text. */ 1435 | static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) 1436 | { 1437 | cJSON *head = NULL; /* head of the linked list */ 1438 | cJSON *current_item = NULL; 1439 | 1440 | if (input_buffer->depth >= CJSON_NESTING_LIMIT) 1441 | { 1442 | return false; /* to deeply nested */ 1443 | } 1444 | input_buffer->depth++; 1445 | 1446 | if (buffer_at_offset(input_buffer)[0] != '[') 1447 | { 1448 | /* not an array */ 1449 | goto fail; 1450 | } 1451 | 1452 | input_buffer->offset++; 1453 | buffer_skip_whitespace(input_buffer); 1454 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) 1455 | { 1456 | /* empty array */ 1457 | goto success; 1458 | } 1459 | 1460 | /* check if we skipped to the end of the buffer */ 1461 | if (cannot_access_at_index(input_buffer, 0)) 1462 | { 1463 | input_buffer->offset--; 1464 | goto fail; 1465 | } 1466 | 1467 | /* step back to character in front of the first element */ 1468 | input_buffer->offset--; 1469 | /* loop through the comma separated array elements */ 1470 | do 1471 | { 1472 | /* allocate next item */ 1473 | cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); 1474 | if (new_item == NULL) 1475 | { 1476 | goto fail; /* allocation failure */ 1477 | } 1478 | 1479 | /* attach next item to list */ 1480 | if (head == NULL) 1481 | { 1482 | /* start the linked list */ 1483 | current_item = head = new_item; 1484 | } 1485 | else 1486 | { 1487 | /* add to the end and advance */ 1488 | current_item->next = new_item; 1489 | new_item->prev = current_item; 1490 | current_item = new_item; 1491 | } 1492 | 1493 | /* parse next value */ 1494 | input_buffer->offset++; 1495 | buffer_skip_whitespace(input_buffer); 1496 | if (!parse_value(current_item, input_buffer)) 1497 | { 1498 | goto fail; /* failed to parse value */ 1499 | } 1500 | buffer_skip_whitespace(input_buffer); 1501 | } 1502 | while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); 1503 | 1504 | if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') 1505 | { 1506 | goto fail; /* expected end of array */ 1507 | } 1508 | 1509 | success: 1510 | input_buffer->depth--; 1511 | 1512 | if (head != NULL) { 1513 | head->prev = current_item; 1514 | } 1515 | 1516 | item->type = cJSON_Array; 1517 | item->child = head; 1518 | 1519 | input_buffer->offset++; 1520 | 1521 | return true; 1522 | 1523 | fail: 1524 | if (head != NULL) 1525 | { 1526 | cJSON_Delete(head); 1527 | } 1528 | 1529 | return false; 1530 | } 1531 | 1532 | /* Render an array to text */ 1533 | static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) 1534 | { 1535 | unsigned char *output_pointer = NULL; 1536 | size_t length = 0; 1537 | cJSON *current_element = item->child; 1538 | 1539 | if (output_buffer == NULL) 1540 | { 1541 | return false; 1542 | } 1543 | 1544 | /* Compose the output array. */ 1545 | /* opening square bracket */ 1546 | output_pointer = ensure(output_buffer, 1); 1547 | if (output_pointer == NULL) 1548 | { 1549 | return false; 1550 | } 1551 | 1552 | *output_pointer = '['; 1553 | output_buffer->offset++; 1554 | output_buffer->depth++; 1555 | 1556 | while (current_element != NULL) 1557 | { 1558 | if (!print_value(current_element, output_buffer)) 1559 | { 1560 | return false; 1561 | } 1562 | update_offset(output_buffer); 1563 | if (current_element->next) 1564 | { 1565 | length = (size_t) (output_buffer->format ? 2 : 1); 1566 | output_pointer = ensure(output_buffer, length + 1); 1567 | if (output_pointer == NULL) 1568 | { 1569 | return false; 1570 | } 1571 | *output_pointer++ = ','; 1572 | if(output_buffer->format) 1573 | { 1574 | *output_pointer++ = ' '; 1575 | } 1576 | *output_pointer = '\0'; 1577 | output_buffer->offset += length; 1578 | } 1579 | current_element = current_element->next; 1580 | } 1581 | 1582 | output_pointer = ensure(output_buffer, 2); 1583 | if (output_pointer == NULL) 1584 | { 1585 | return false; 1586 | } 1587 | *output_pointer++ = ']'; 1588 | *output_pointer = '\0'; 1589 | output_buffer->depth--; 1590 | 1591 | return true; 1592 | } 1593 | 1594 | /* Build an object from the text. */ 1595 | static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) 1596 | { 1597 | cJSON *head = NULL; /* linked list head */ 1598 | cJSON *current_item = NULL; 1599 | 1600 | if (input_buffer->depth >= CJSON_NESTING_LIMIT) 1601 | { 1602 | return false; /* to deeply nested */ 1603 | } 1604 | input_buffer->depth++; 1605 | 1606 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) 1607 | { 1608 | goto fail; /* not an object */ 1609 | } 1610 | 1611 | input_buffer->offset++; 1612 | buffer_skip_whitespace(input_buffer); 1613 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) 1614 | { 1615 | goto success; /* empty object */ 1616 | } 1617 | 1618 | /* check if we skipped to the end of the buffer */ 1619 | if (cannot_access_at_index(input_buffer, 0)) 1620 | { 1621 | input_buffer->offset--; 1622 | goto fail; 1623 | } 1624 | 1625 | /* step back to character in front of the first element */ 1626 | input_buffer->offset--; 1627 | /* loop through the comma separated array elements */ 1628 | do 1629 | { 1630 | /* allocate next item */ 1631 | cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); 1632 | if (new_item == NULL) 1633 | { 1634 | goto fail; /* allocation failure */ 1635 | } 1636 | 1637 | /* attach next item to list */ 1638 | if (head == NULL) 1639 | { 1640 | /* start the linked list */ 1641 | current_item = head = new_item; 1642 | } 1643 | else 1644 | { 1645 | /* add to the end and advance */ 1646 | current_item->next = new_item; 1647 | new_item->prev = current_item; 1648 | current_item = new_item; 1649 | } 1650 | 1651 | /* parse the name of the child */ 1652 | input_buffer->offset++; 1653 | buffer_skip_whitespace(input_buffer); 1654 | if (!parse_string(current_item, input_buffer)) 1655 | { 1656 | goto fail; /* failed to parse name */ 1657 | } 1658 | buffer_skip_whitespace(input_buffer); 1659 | 1660 | /* swap valuestring and string, because we parsed the name */ 1661 | current_item->string = current_item->valuestring; 1662 | current_item->valuestring = NULL; 1663 | 1664 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) 1665 | { 1666 | goto fail; /* invalid object */ 1667 | } 1668 | 1669 | /* parse the value */ 1670 | input_buffer->offset++; 1671 | buffer_skip_whitespace(input_buffer); 1672 | if (!parse_value(current_item, input_buffer)) 1673 | { 1674 | goto fail; /* failed to parse value */ 1675 | } 1676 | buffer_skip_whitespace(input_buffer); 1677 | } 1678 | while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); 1679 | 1680 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) 1681 | { 1682 | goto fail; /* expected end of object */ 1683 | } 1684 | 1685 | success: 1686 | input_buffer->depth--; 1687 | 1688 | if (head != NULL) { 1689 | head->prev = current_item; 1690 | } 1691 | 1692 | item->type = cJSON_Object; 1693 | item->child = head; 1694 | 1695 | input_buffer->offset++; 1696 | return true; 1697 | 1698 | fail: 1699 | if (head != NULL) 1700 | { 1701 | cJSON_Delete(head); 1702 | } 1703 | 1704 | return false; 1705 | } 1706 | 1707 | /* Render an object to text. */ 1708 | static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) 1709 | { 1710 | unsigned char *output_pointer = NULL; 1711 | size_t length = 0; 1712 | cJSON *current_item = item->child; 1713 | 1714 | if (output_buffer == NULL) 1715 | { 1716 | return false; 1717 | } 1718 | 1719 | /* Compose the output: */ 1720 | length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ 1721 | output_pointer = ensure(output_buffer, length + 1); 1722 | if (output_pointer == NULL) 1723 | { 1724 | return false; 1725 | } 1726 | 1727 | *output_pointer++ = '{'; 1728 | output_buffer->depth++; 1729 | if (output_buffer->format) 1730 | { 1731 | *output_pointer++ = '\n'; 1732 | } 1733 | output_buffer->offset += length; 1734 | 1735 | while (current_item) 1736 | { 1737 | if (output_buffer->format) 1738 | { 1739 | size_t i; 1740 | output_pointer = ensure(output_buffer, output_buffer->depth); 1741 | if (output_pointer == NULL) 1742 | { 1743 | return false; 1744 | } 1745 | for (i = 0; i < output_buffer->depth; i++) 1746 | { 1747 | *output_pointer++ = '\t'; 1748 | } 1749 | output_buffer->offset += output_buffer->depth; 1750 | } 1751 | 1752 | /* print key */ 1753 | if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) 1754 | { 1755 | return false; 1756 | } 1757 | update_offset(output_buffer); 1758 | 1759 | length = (size_t) (output_buffer->format ? 2 : 1); 1760 | output_pointer = ensure(output_buffer, length); 1761 | if (output_pointer == NULL) 1762 | { 1763 | return false; 1764 | } 1765 | *output_pointer++ = ':'; 1766 | if (output_buffer->format) 1767 | { 1768 | *output_pointer++ = '\t'; 1769 | } 1770 | output_buffer->offset += length; 1771 | 1772 | /* print value */ 1773 | if (!print_value(current_item, output_buffer)) 1774 | { 1775 | return false; 1776 | } 1777 | update_offset(output_buffer); 1778 | 1779 | /* print comma if not last */ 1780 | length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); 1781 | output_pointer = ensure(output_buffer, length + 1); 1782 | if (output_pointer == NULL) 1783 | { 1784 | return false; 1785 | } 1786 | if (current_item->next) 1787 | { 1788 | *output_pointer++ = ','; 1789 | } 1790 | 1791 | if (output_buffer->format) 1792 | { 1793 | *output_pointer++ = '\n'; 1794 | } 1795 | *output_pointer = '\0'; 1796 | output_buffer->offset += length; 1797 | 1798 | current_item = current_item->next; 1799 | } 1800 | 1801 | output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); 1802 | if (output_pointer == NULL) 1803 | { 1804 | return false; 1805 | } 1806 | if (output_buffer->format) 1807 | { 1808 | size_t i; 1809 | for (i = 0; i < (output_buffer->depth - 1); i++) 1810 | { 1811 | *output_pointer++ = '\t'; 1812 | } 1813 | } 1814 | *output_pointer++ = '}'; 1815 | *output_pointer = '\0'; 1816 | output_buffer->depth--; 1817 | 1818 | return true; 1819 | } 1820 | 1821 | /* Get Array size/item / object item. */ 1822 | CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) 1823 | { 1824 | cJSON *child = NULL; 1825 | size_t size = 0; 1826 | 1827 | if (array == NULL) 1828 | { 1829 | return 0; 1830 | } 1831 | 1832 | child = array->child; 1833 | 1834 | while(child != NULL) 1835 | { 1836 | size++; 1837 | child = child->next; 1838 | } 1839 | 1840 | /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ 1841 | 1842 | return (int)size; 1843 | } 1844 | 1845 | static cJSON* get_array_item(const cJSON *array, size_t index) 1846 | { 1847 | cJSON *current_child = NULL; 1848 | 1849 | if (array == NULL) 1850 | { 1851 | return NULL; 1852 | } 1853 | 1854 | current_child = array->child; 1855 | while ((current_child != NULL) && (index > 0)) 1856 | { 1857 | index--; 1858 | current_child = current_child->next; 1859 | } 1860 | 1861 | return current_child; 1862 | } 1863 | 1864 | CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) 1865 | { 1866 | if (index < 0) 1867 | { 1868 | return NULL; 1869 | } 1870 | 1871 | return get_array_item(array, (size_t)index); 1872 | } 1873 | 1874 | static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) 1875 | { 1876 | cJSON *current_element = NULL; 1877 | 1878 | if ((object == NULL) || (name == NULL)) 1879 | { 1880 | return NULL; 1881 | } 1882 | 1883 | current_element = object->child; 1884 | if (case_sensitive) 1885 | { 1886 | while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) 1887 | { 1888 | current_element = current_element->next; 1889 | } 1890 | } 1891 | else 1892 | { 1893 | while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) 1894 | { 1895 | current_element = current_element->next; 1896 | } 1897 | } 1898 | 1899 | if ((current_element == NULL) || (current_element->string == NULL)) { 1900 | return NULL; 1901 | } 1902 | 1903 | return current_element; 1904 | } 1905 | 1906 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) 1907 | { 1908 | return get_object_item(object, string, false); 1909 | } 1910 | 1911 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) 1912 | { 1913 | return get_object_item(object, string, true); 1914 | } 1915 | 1916 | CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) 1917 | { 1918 | return cJSON_GetObjectItem(object, string) ? 1 : 0; 1919 | } 1920 | 1921 | /* Utility for array list handling. */ 1922 | static void suffix_object(cJSON *prev, cJSON *item) 1923 | { 1924 | prev->next = item; 1925 | item->prev = prev; 1926 | } 1927 | 1928 | /* Utility for handling references. */ 1929 | static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) 1930 | { 1931 | cJSON *reference = NULL; 1932 | if (item == NULL) 1933 | { 1934 | return NULL; 1935 | } 1936 | 1937 | reference = cJSON_New_Item(hooks); 1938 | if (reference == NULL) 1939 | { 1940 | return NULL; 1941 | } 1942 | 1943 | memcpy(reference, item, sizeof(cJSON)); 1944 | reference->string = NULL; 1945 | reference->type |= cJSON_IsReference; 1946 | reference->next = reference->prev = NULL; 1947 | return reference; 1948 | } 1949 | 1950 | static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) 1951 | { 1952 | cJSON *child = NULL; 1953 | 1954 | if ((item == NULL) || (array == NULL) || (array == item)) 1955 | { 1956 | return false; 1957 | } 1958 | 1959 | child = array->child; 1960 | /* 1961 | * To find the last item in array quickly, we use prev in array 1962 | */ 1963 | if (child == NULL) 1964 | { 1965 | /* list is empty, start new one */ 1966 | array->child = item; 1967 | item->prev = item; 1968 | item->next = NULL; 1969 | } 1970 | else 1971 | { 1972 | /* append to the end */ 1973 | if (child->prev) 1974 | { 1975 | suffix_object(child->prev, item); 1976 | array->child->prev = item; 1977 | } 1978 | else 1979 | { 1980 | while (child->next) 1981 | { 1982 | child = child->next; 1983 | } 1984 | suffix_object(child, item); 1985 | array->child->prev = item; 1986 | } 1987 | } 1988 | 1989 | return true; 1990 | } 1991 | 1992 | /* Add item to array/object. */ 1993 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) 1994 | { 1995 | return add_item_to_array(array, item); 1996 | } 1997 | 1998 | #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) 1999 | #pragma GCC diagnostic push 2000 | #endif 2001 | #ifdef __GNUC__ 2002 | #pragma GCC diagnostic ignored "-Wcast-qual" 2003 | #endif 2004 | /* helper function to cast away const */ 2005 | static void* cast_away_const(const void* string) 2006 | { 2007 | return (void*)string; 2008 | } 2009 | #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) 2010 | #pragma GCC diagnostic pop 2011 | #endif 2012 | 2013 | 2014 | static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) 2015 | { 2016 | char *new_key = NULL; 2017 | int new_type = cJSON_Invalid; 2018 | 2019 | if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) 2020 | { 2021 | return false; 2022 | } 2023 | 2024 | if (constant_key) 2025 | { 2026 | new_key = (char*)cast_away_const(string); 2027 | new_type = item->type | cJSON_StringIsConst; 2028 | } 2029 | else 2030 | { 2031 | new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); 2032 | if (new_key == NULL) 2033 | { 2034 | return false; 2035 | } 2036 | 2037 | new_type = item->type & ~cJSON_StringIsConst; 2038 | } 2039 | 2040 | if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) 2041 | { 2042 | hooks->deallocate(item->string); 2043 | } 2044 | 2045 | item->string = new_key; 2046 | item->type = new_type; 2047 | 2048 | return add_item_to_array(object, item); 2049 | } 2050 | 2051 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) 2052 | { 2053 | return add_item_to_object(object, string, item, &global_hooks, false); 2054 | } 2055 | 2056 | /* Add an item to an object with constant string as key */ 2057 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) 2058 | { 2059 | return add_item_to_object(object, string, item, &global_hooks, true); 2060 | } 2061 | 2062 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) 2063 | { 2064 | if (array == NULL) 2065 | { 2066 | return false; 2067 | } 2068 | 2069 | return add_item_to_array(array, create_reference(item, &global_hooks)); 2070 | } 2071 | 2072 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) 2073 | { 2074 | if ((object == NULL) || (string == NULL)) 2075 | { 2076 | return false; 2077 | } 2078 | 2079 | return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); 2080 | } 2081 | 2082 | CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) 2083 | { 2084 | cJSON *null = cJSON_CreateNull(); 2085 | if (add_item_to_object(object, name, null, &global_hooks, false)) 2086 | { 2087 | return null; 2088 | } 2089 | 2090 | cJSON_Delete(null); 2091 | return NULL; 2092 | } 2093 | 2094 | CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) 2095 | { 2096 | cJSON *true_item = cJSON_CreateTrue(); 2097 | if (add_item_to_object(object, name, true_item, &global_hooks, false)) 2098 | { 2099 | return true_item; 2100 | } 2101 | 2102 | cJSON_Delete(true_item); 2103 | return NULL; 2104 | } 2105 | 2106 | CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) 2107 | { 2108 | cJSON *false_item = cJSON_CreateFalse(); 2109 | if (add_item_to_object(object, name, false_item, &global_hooks, false)) 2110 | { 2111 | return false_item; 2112 | } 2113 | 2114 | cJSON_Delete(false_item); 2115 | return NULL; 2116 | } 2117 | 2118 | CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) 2119 | { 2120 | cJSON *bool_item = cJSON_CreateBool(boolean); 2121 | if (add_item_to_object(object, name, bool_item, &global_hooks, false)) 2122 | { 2123 | return bool_item; 2124 | } 2125 | 2126 | cJSON_Delete(bool_item); 2127 | return NULL; 2128 | } 2129 | 2130 | CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) 2131 | { 2132 | cJSON *number_item = cJSON_CreateNumber(number); 2133 | if (add_item_to_object(object, name, number_item, &global_hooks, false)) 2134 | { 2135 | return number_item; 2136 | } 2137 | 2138 | cJSON_Delete(number_item); 2139 | return NULL; 2140 | } 2141 | 2142 | CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) 2143 | { 2144 | cJSON *string_item = cJSON_CreateString(string); 2145 | if (add_item_to_object(object, name, string_item, &global_hooks, false)) 2146 | { 2147 | return string_item; 2148 | } 2149 | 2150 | cJSON_Delete(string_item); 2151 | return NULL; 2152 | } 2153 | 2154 | CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) 2155 | { 2156 | cJSON *raw_item = cJSON_CreateRaw(raw); 2157 | if (add_item_to_object(object, name, raw_item, &global_hooks, false)) 2158 | { 2159 | return raw_item; 2160 | } 2161 | 2162 | cJSON_Delete(raw_item); 2163 | return NULL; 2164 | } 2165 | 2166 | CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) 2167 | { 2168 | cJSON *object_item = cJSON_CreateObject(); 2169 | if (add_item_to_object(object, name, object_item, &global_hooks, false)) 2170 | { 2171 | return object_item; 2172 | } 2173 | 2174 | cJSON_Delete(object_item); 2175 | return NULL; 2176 | } 2177 | 2178 | CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) 2179 | { 2180 | cJSON *array = cJSON_CreateArray(); 2181 | if (add_item_to_object(object, name, array, &global_hooks, false)) 2182 | { 2183 | return array; 2184 | } 2185 | 2186 | cJSON_Delete(array); 2187 | return NULL; 2188 | } 2189 | 2190 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) 2191 | { 2192 | if ((parent == NULL) || (item == NULL)) 2193 | { 2194 | return NULL; 2195 | } 2196 | 2197 | if (item != parent->child) 2198 | { 2199 | /* not the first element */ 2200 | item->prev->next = item->next; 2201 | } 2202 | if (item->next != NULL) 2203 | { 2204 | /* not the last element */ 2205 | item->next->prev = item->prev; 2206 | } 2207 | 2208 | if (item == parent->child) 2209 | { 2210 | /* first element */ 2211 | parent->child = item->next; 2212 | } 2213 | else if (item->next == NULL) 2214 | { 2215 | /* last element */ 2216 | parent->child->prev = item->prev; 2217 | } 2218 | 2219 | /* make sure the detached item doesn't point anywhere anymore */ 2220 | item->prev = NULL; 2221 | item->next = NULL; 2222 | 2223 | return item; 2224 | } 2225 | 2226 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) 2227 | { 2228 | if (which < 0) 2229 | { 2230 | return NULL; 2231 | } 2232 | 2233 | return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); 2234 | } 2235 | 2236 | CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) 2237 | { 2238 | cJSON_Delete(cJSON_DetachItemFromArray(array, which)); 2239 | } 2240 | 2241 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) 2242 | { 2243 | cJSON *to_detach = cJSON_GetObjectItem(object, string); 2244 | 2245 | return cJSON_DetachItemViaPointer(object, to_detach); 2246 | } 2247 | 2248 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) 2249 | { 2250 | cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); 2251 | 2252 | return cJSON_DetachItemViaPointer(object, to_detach); 2253 | } 2254 | 2255 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) 2256 | { 2257 | cJSON_Delete(cJSON_DetachItemFromObject(object, string)); 2258 | } 2259 | 2260 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) 2261 | { 2262 | cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); 2263 | } 2264 | 2265 | /* Replace array/object items with new ones. */ 2266 | CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) 2267 | { 2268 | cJSON *after_inserted = NULL; 2269 | 2270 | if (which < 0) 2271 | { 2272 | return false; 2273 | } 2274 | 2275 | after_inserted = get_array_item(array, (size_t)which); 2276 | if (after_inserted == NULL) 2277 | { 2278 | return add_item_to_array(array, newitem); 2279 | } 2280 | 2281 | newitem->next = after_inserted; 2282 | newitem->prev = after_inserted->prev; 2283 | after_inserted->prev = newitem; 2284 | if (after_inserted == array->child) 2285 | { 2286 | array->child = newitem; 2287 | } 2288 | else 2289 | { 2290 | newitem->prev->next = newitem; 2291 | } 2292 | return true; 2293 | } 2294 | 2295 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) 2296 | { 2297 | if ((parent == NULL) || (replacement == NULL) || (item == NULL)) 2298 | { 2299 | return false; 2300 | } 2301 | 2302 | if (replacement == item) 2303 | { 2304 | return true; 2305 | } 2306 | 2307 | replacement->next = item->next; 2308 | replacement->prev = item->prev; 2309 | 2310 | if (replacement->next != NULL) 2311 | { 2312 | replacement->next->prev = replacement; 2313 | } 2314 | if (parent->child == item) 2315 | { 2316 | if (parent->child->prev == parent->child) 2317 | { 2318 | replacement->prev = replacement; 2319 | } 2320 | parent->child = replacement; 2321 | } 2322 | else 2323 | { /* 2324 | * To find the last item in array quickly, we use prev in array. 2325 | * We can't modify the last item's next pointer where this item was the parent's child 2326 | */ 2327 | if (replacement->prev != NULL) 2328 | { 2329 | replacement->prev->next = replacement; 2330 | } 2331 | if (replacement->next == NULL) 2332 | { 2333 | parent->child->prev = replacement; 2334 | } 2335 | } 2336 | 2337 | item->next = NULL; 2338 | item->prev = NULL; 2339 | cJSON_Delete(item); 2340 | 2341 | return true; 2342 | } 2343 | 2344 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) 2345 | { 2346 | if (which < 0) 2347 | { 2348 | return false; 2349 | } 2350 | 2351 | return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); 2352 | } 2353 | 2354 | static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) 2355 | { 2356 | if ((replacement == NULL) || (string == NULL)) 2357 | { 2358 | return false; 2359 | } 2360 | 2361 | /* replace the name in the replacement */ 2362 | if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) 2363 | { 2364 | cJSON_free(replacement->string); 2365 | } 2366 | replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); 2367 | replacement->type &= ~cJSON_StringIsConst; 2368 | 2369 | return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); 2370 | } 2371 | 2372 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) 2373 | { 2374 | return replace_item_in_object(object, string, newitem, false); 2375 | } 2376 | 2377 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) 2378 | { 2379 | return replace_item_in_object(object, string, newitem, true); 2380 | } 2381 | 2382 | /* Create basic types: */ 2383 | CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) 2384 | { 2385 | cJSON *item = cJSON_New_Item(&global_hooks); 2386 | if(item) 2387 | { 2388 | item->type = cJSON_NULL; 2389 | } 2390 | 2391 | return item; 2392 | } 2393 | 2394 | CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) 2395 | { 2396 | cJSON *item = cJSON_New_Item(&global_hooks); 2397 | if(item) 2398 | { 2399 | item->type = cJSON_True; 2400 | } 2401 | 2402 | return item; 2403 | } 2404 | 2405 | CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) 2406 | { 2407 | cJSON *item = cJSON_New_Item(&global_hooks); 2408 | if(item) 2409 | { 2410 | item->type = cJSON_False; 2411 | } 2412 | 2413 | return item; 2414 | } 2415 | 2416 | CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) 2417 | { 2418 | cJSON *item = cJSON_New_Item(&global_hooks); 2419 | if(item) 2420 | { 2421 | item->type = boolean ? cJSON_True : cJSON_False; 2422 | } 2423 | 2424 | return item; 2425 | } 2426 | 2427 | CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) 2428 | { 2429 | cJSON *item = cJSON_New_Item(&global_hooks); 2430 | if(item) 2431 | { 2432 | item->type = cJSON_Number; 2433 | item->valuedouble = num; 2434 | 2435 | /* use saturation in case of overflow */ 2436 | if (num >= INT_MAX) 2437 | { 2438 | item->valueint = INT_MAX; 2439 | } 2440 | else if (num <= (double)INT_MIN) 2441 | { 2442 | item->valueint = INT_MIN; 2443 | } 2444 | else 2445 | { 2446 | item->valueint = (int)num; 2447 | } 2448 | } 2449 | 2450 | return item; 2451 | } 2452 | 2453 | CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) 2454 | { 2455 | cJSON *item = cJSON_New_Item(&global_hooks); 2456 | if(item) 2457 | { 2458 | item->type = cJSON_String; 2459 | item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); 2460 | if(!item->valuestring) 2461 | { 2462 | cJSON_Delete(item); 2463 | return NULL; 2464 | } 2465 | } 2466 | 2467 | return item; 2468 | } 2469 | 2470 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) 2471 | { 2472 | cJSON *item = cJSON_New_Item(&global_hooks); 2473 | if (item != NULL) 2474 | { 2475 | item->type = cJSON_String | cJSON_IsReference; 2476 | item->valuestring = (char*)cast_away_const(string); 2477 | } 2478 | 2479 | return item; 2480 | } 2481 | 2482 | CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) 2483 | { 2484 | cJSON *item = cJSON_New_Item(&global_hooks); 2485 | if (item != NULL) { 2486 | item->type = cJSON_Object | cJSON_IsReference; 2487 | item->child = (cJSON*)cast_away_const(child); 2488 | } 2489 | 2490 | return item; 2491 | } 2492 | 2493 | CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { 2494 | cJSON *item = cJSON_New_Item(&global_hooks); 2495 | if (item != NULL) { 2496 | item->type = cJSON_Array | cJSON_IsReference; 2497 | item->child = (cJSON*)cast_away_const(child); 2498 | } 2499 | 2500 | return item; 2501 | } 2502 | 2503 | CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) 2504 | { 2505 | cJSON *item = cJSON_New_Item(&global_hooks); 2506 | if(item) 2507 | { 2508 | item->type = cJSON_Raw; 2509 | item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); 2510 | if(!item->valuestring) 2511 | { 2512 | cJSON_Delete(item); 2513 | return NULL; 2514 | } 2515 | } 2516 | 2517 | return item; 2518 | } 2519 | 2520 | CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) 2521 | { 2522 | cJSON *item = cJSON_New_Item(&global_hooks); 2523 | if(item) 2524 | { 2525 | item->type=cJSON_Array; 2526 | } 2527 | 2528 | return item; 2529 | } 2530 | 2531 | CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) 2532 | { 2533 | cJSON *item = cJSON_New_Item(&global_hooks); 2534 | if (item) 2535 | { 2536 | item->type = cJSON_Object; 2537 | } 2538 | 2539 | return item; 2540 | } 2541 | 2542 | /* Create Arrays: */ 2543 | CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) 2544 | { 2545 | size_t i = 0; 2546 | cJSON *n = NULL; 2547 | cJSON *p = NULL; 2548 | cJSON *a = NULL; 2549 | 2550 | if ((count < 0) || (numbers == NULL)) 2551 | { 2552 | return NULL; 2553 | } 2554 | 2555 | a = cJSON_CreateArray(); 2556 | for(i = 0; a && (i < (size_t)count); i++) 2557 | { 2558 | n = cJSON_CreateNumber(numbers[i]); 2559 | if (!n) 2560 | { 2561 | cJSON_Delete(a); 2562 | return NULL; 2563 | } 2564 | if(!i) 2565 | { 2566 | a->child = n; 2567 | } 2568 | else 2569 | { 2570 | suffix_object(p, n); 2571 | } 2572 | p = n; 2573 | } 2574 | 2575 | return a; 2576 | } 2577 | 2578 | CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) 2579 | { 2580 | size_t i = 0; 2581 | cJSON *n = NULL; 2582 | cJSON *p = NULL; 2583 | cJSON *a = NULL; 2584 | 2585 | if ((count < 0) || (numbers == NULL)) 2586 | { 2587 | return NULL; 2588 | } 2589 | 2590 | a = cJSON_CreateArray(); 2591 | 2592 | for(i = 0; a && (i < (size_t)count); i++) 2593 | { 2594 | n = cJSON_CreateNumber((double)numbers[i]); 2595 | if(!n) 2596 | { 2597 | cJSON_Delete(a); 2598 | return NULL; 2599 | } 2600 | if(!i) 2601 | { 2602 | a->child = n; 2603 | } 2604 | else 2605 | { 2606 | suffix_object(p, n); 2607 | } 2608 | p = n; 2609 | } 2610 | 2611 | return a; 2612 | } 2613 | 2614 | CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) 2615 | { 2616 | size_t i = 0; 2617 | cJSON *n = NULL; 2618 | cJSON *p = NULL; 2619 | cJSON *a = NULL; 2620 | 2621 | if ((count < 0) || (numbers == NULL)) 2622 | { 2623 | return NULL; 2624 | } 2625 | 2626 | a = cJSON_CreateArray(); 2627 | 2628 | for(i = 0;a && (i < (size_t)count); i++) 2629 | { 2630 | n = cJSON_CreateNumber(numbers[i]); 2631 | if(!n) 2632 | { 2633 | cJSON_Delete(a); 2634 | return NULL; 2635 | } 2636 | if(!i) 2637 | { 2638 | a->child = n; 2639 | } 2640 | else 2641 | { 2642 | suffix_object(p, n); 2643 | } 2644 | p = n; 2645 | } 2646 | 2647 | return a; 2648 | } 2649 | 2650 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) 2651 | { 2652 | size_t i = 0; 2653 | cJSON *n = NULL; 2654 | cJSON *p = NULL; 2655 | cJSON *a = NULL; 2656 | 2657 | if ((count < 0) || (strings == NULL)) 2658 | { 2659 | return NULL; 2660 | } 2661 | 2662 | a = cJSON_CreateArray(); 2663 | 2664 | for (i = 0; a && (i < (size_t)count); i++) 2665 | { 2666 | n = cJSON_CreateString(strings[i]); 2667 | if(!n) 2668 | { 2669 | cJSON_Delete(a); 2670 | return NULL; 2671 | } 2672 | if(!i) 2673 | { 2674 | a->child = n; 2675 | } 2676 | else 2677 | { 2678 | suffix_object(p,n); 2679 | } 2680 | p = n; 2681 | } 2682 | 2683 | return a; 2684 | } 2685 | 2686 | /* Duplication */ 2687 | CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) 2688 | { 2689 | cJSON *newitem = NULL; 2690 | cJSON *child = NULL; 2691 | cJSON *next = NULL; 2692 | cJSON *newchild = NULL; 2693 | 2694 | /* Bail on bad ptr */ 2695 | if (!item) 2696 | { 2697 | goto fail; 2698 | } 2699 | /* Create new item */ 2700 | newitem = cJSON_New_Item(&global_hooks); 2701 | if (!newitem) 2702 | { 2703 | goto fail; 2704 | } 2705 | /* Copy over all vars */ 2706 | newitem->type = item->type & (~cJSON_IsReference); 2707 | newitem->valueint = item->valueint; 2708 | newitem->valuedouble = item->valuedouble; 2709 | if (item->valuestring) 2710 | { 2711 | newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); 2712 | if (!newitem->valuestring) 2713 | { 2714 | goto fail; 2715 | } 2716 | } 2717 | if (item->string) 2718 | { 2719 | newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); 2720 | if (!newitem->string) 2721 | { 2722 | goto fail; 2723 | } 2724 | } 2725 | /* If non-recursive, then we're done! */ 2726 | if (!recurse) 2727 | { 2728 | return newitem; 2729 | } 2730 | /* Walk the ->next chain for the child. */ 2731 | child = item->child; 2732 | while (child != NULL) 2733 | { 2734 | newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ 2735 | if (!newchild) 2736 | { 2737 | goto fail; 2738 | } 2739 | if (next != NULL) 2740 | { 2741 | /* If newitem->child already set, then crosswire ->prev and ->next and move on */ 2742 | next->next = newchild; 2743 | newchild->prev = next; 2744 | next = newchild; 2745 | } 2746 | else 2747 | { 2748 | /* Set newitem->child and move to it */ 2749 | newitem->child = newchild; 2750 | next = newchild; 2751 | } 2752 | child = child->next; 2753 | } 2754 | 2755 | return newitem; 2756 | 2757 | fail: 2758 | if (newitem != NULL) 2759 | { 2760 | cJSON_Delete(newitem); 2761 | } 2762 | 2763 | return NULL; 2764 | } 2765 | 2766 | static void skip_oneline_comment(char **input) 2767 | { 2768 | *input += static_strlen("//"); 2769 | 2770 | for (; (*input)[0] != '\0'; ++(*input)) 2771 | { 2772 | if ((*input)[0] == '\n') { 2773 | *input += static_strlen("\n"); 2774 | return; 2775 | } 2776 | } 2777 | } 2778 | 2779 | static void skip_multiline_comment(char **input) 2780 | { 2781 | *input += static_strlen("/*"); 2782 | 2783 | for (; (*input)[0] != '\0'; ++(*input)) 2784 | { 2785 | if (((*input)[0] == '*') && ((*input)[1] == '/')) 2786 | { 2787 | *input += static_strlen("*/"); 2788 | return; 2789 | } 2790 | } 2791 | } 2792 | 2793 | static void minify_string(char **input, char **output) { 2794 | (*output)[0] = (*input)[0]; 2795 | *input += static_strlen("\""); 2796 | *output += static_strlen("\""); 2797 | 2798 | 2799 | for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { 2800 | (*output)[0] = (*input)[0]; 2801 | 2802 | if ((*input)[0] == '\"') { 2803 | (*output)[0] = '\"'; 2804 | *input += static_strlen("\""); 2805 | *output += static_strlen("\""); 2806 | return; 2807 | } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { 2808 | (*output)[1] = (*input)[1]; 2809 | *input += static_strlen("\""); 2810 | *output += static_strlen("\""); 2811 | } 2812 | } 2813 | } 2814 | 2815 | CJSON_PUBLIC(void) cJSON_Minify(char *json) 2816 | { 2817 | char *into = json; 2818 | 2819 | if (json == NULL) 2820 | { 2821 | return; 2822 | } 2823 | 2824 | while (json[0] != '\0') 2825 | { 2826 | switch (json[0]) 2827 | { 2828 | case ' ': 2829 | case '\t': 2830 | case '\r': 2831 | case '\n': 2832 | json++; 2833 | break; 2834 | 2835 | case '/': 2836 | if (json[1] == '/') 2837 | { 2838 | skip_oneline_comment(&json); 2839 | } 2840 | else if (json[1] == '*') 2841 | { 2842 | skip_multiline_comment(&json); 2843 | } else { 2844 | json++; 2845 | } 2846 | break; 2847 | 2848 | case '\"': 2849 | minify_string(&json, (char**)&into); 2850 | break; 2851 | 2852 | default: 2853 | into[0] = json[0]; 2854 | json++; 2855 | into++; 2856 | } 2857 | } 2858 | 2859 | /* and null-terminate. */ 2860 | *into = '\0'; 2861 | } 2862 | 2863 | CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) 2864 | { 2865 | if (item == NULL) 2866 | { 2867 | return false; 2868 | } 2869 | 2870 | return (item->type & 0xFF) == cJSON_Invalid; 2871 | } 2872 | 2873 | CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) 2874 | { 2875 | if (item == NULL) 2876 | { 2877 | return false; 2878 | } 2879 | 2880 | return (item->type & 0xFF) == cJSON_False; 2881 | } 2882 | 2883 | CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) 2884 | { 2885 | if (item == NULL) 2886 | { 2887 | return false; 2888 | } 2889 | 2890 | return (item->type & 0xff) == cJSON_True; 2891 | } 2892 | 2893 | 2894 | CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) 2895 | { 2896 | if (item == NULL) 2897 | { 2898 | return false; 2899 | } 2900 | 2901 | return (item->type & (cJSON_True | cJSON_False)) != 0; 2902 | } 2903 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) 2904 | { 2905 | if (item == NULL) 2906 | { 2907 | return false; 2908 | } 2909 | 2910 | return (item->type & 0xFF) == cJSON_NULL; 2911 | } 2912 | 2913 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) 2914 | { 2915 | if (item == NULL) 2916 | { 2917 | return false; 2918 | } 2919 | 2920 | return (item->type & 0xFF) == cJSON_Number; 2921 | } 2922 | 2923 | CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) 2924 | { 2925 | if (item == NULL) 2926 | { 2927 | return false; 2928 | } 2929 | 2930 | return (item->type & 0xFF) == cJSON_String; 2931 | } 2932 | 2933 | CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) 2934 | { 2935 | if (item == NULL) 2936 | { 2937 | return false; 2938 | } 2939 | 2940 | return (item->type & 0xFF) == cJSON_Array; 2941 | } 2942 | 2943 | CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) 2944 | { 2945 | if (item == NULL) 2946 | { 2947 | return false; 2948 | } 2949 | 2950 | return (item->type & 0xFF) == cJSON_Object; 2951 | } 2952 | 2953 | CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) 2954 | { 2955 | if (item == NULL) 2956 | { 2957 | return false; 2958 | } 2959 | 2960 | return (item->type & 0xFF) == cJSON_Raw; 2961 | } 2962 | 2963 | CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) 2964 | { 2965 | if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) 2966 | { 2967 | return false; 2968 | } 2969 | 2970 | /* check if type is valid */ 2971 | switch (a->type & 0xFF) 2972 | { 2973 | case cJSON_False: 2974 | case cJSON_True: 2975 | case cJSON_NULL: 2976 | case cJSON_Number: 2977 | case cJSON_String: 2978 | case cJSON_Raw: 2979 | case cJSON_Array: 2980 | case cJSON_Object: 2981 | break; 2982 | 2983 | default: 2984 | return false; 2985 | } 2986 | 2987 | /* identical objects are equal */ 2988 | if (a == b) 2989 | { 2990 | return true; 2991 | } 2992 | 2993 | switch (a->type & 0xFF) 2994 | { 2995 | /* in these cases and equal type is enough */ 2996 | case cJSON_False: 2997 | case cJSON_True: 2998 | case cJSON_NULL: 2999 | return true; 3000 | 3001 | case cJSON_Number: 3002 | if (compare_double(a->valuedouble, b->valuedouble)) 3003 | { 3004 | return true; 3005 | } 3006 | return false; 3007 | 3008 | case cJSON_String: 3009 | case cJSON_Raw: 3010 | if ((a->valuestring == NULL) || (b->valuestring == NULL)) 3011 | { 3012 | return false; 3013 | } 3014 | if (strcmp(a->valuestring, b->valuestring) == 0) 3015 | { 3016 | return true; 3017 | } 3018 | 3019 | return false; 3020 | 3021 | case cJSON_Array: 3022 | { 3023 | cJSON *a_element = a->child; 3024 | cJSON *b_element = b->child; 3025 | 3026 | for (; (a_element != NULL) && (b_element != NULL);) 3027 | { 3028 | if (!cJSON_Compare(a_element, b_element, case_sensitive)) 3029 | { 3030 | return false; 3031 | } 3032 | 3033 | a_element = a_element->next; 3034 | b_element = b_element->next; 3035 | } 3036 | 3037 | /* one of the arrays is longer than the other */ 3038 | if (a_element != b_element) { 3039 | return false; 3040 | } 3041 | 3042 | return true; 3043 | } 3044 | 3045 | case cJSON_Object: 3046 | { 3047 | cJSON *a_element = NULL; 3048 | cJSON *b_element = NULL; 3049 | cJSON_ArrayForEach(a_element, a) 3050 | { 3051 | /* TODO This has O(n^2) runtime, which is horrible! */ 3052 | b_element = get_object_item(b, a_element->string, case_sensitive); 3053 | if (b_element == NULL) 3054 | { 3055 | return false; 3056 | } 3057 | 3058 | if (!cJSON_Compare(a_element, b_element, case_sensitive)) 3059 | { 3060 | return false; 3061 | } 3062 | } 3063 | 3064 | /* doing this twice, once on a and b to prevent true comparison if a subset of b 3065 | * TODO: Do this the proper way, this is just a fix for now */ 3066 | cJSON_ArrayForEach(b_element, b) 3067 | { 3068 | a_element = get_object_item(a, b_element->string, case_sensitive); 3069 | if (a_element == NULL) 3070 | { 3071 | return false; 3072 | } 3073 | 3074 | if (!cJSON_Compare(b_element, a_element, case_sensitive)) 3075 | { 3076 | return false; 3077 | } 3078 | } 3079 | 3080 | return true; 3081 | } 3082 | 3083 | default: 3084 | return false; 3085 | } 3086 | } 3087 | 3088 | CJSON_PUBLIC(void *) cJSON_malloc(size_t size) 3089 | { 3090 | return global_hooks.allocate(size); 3091 | } 3092 | 3093 | CJSON_PUBLIC(void) cJSON_free(void *object) 3094 | { 3095 | global_hooks.deallocate(object); 3096 | } 3097 | -------------------------------------------------------------------------------- /base/cJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009-2017 Dave Gamble and cJSON contributors 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef cJSON__h 24 | #define cJSON__h 25 | 26 | #ifdef __cplusplus 27 | extern "C" 28 | { 29 | #endif 30 | 31 | #if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) 32 | #define __WINDOWS__ 33 | #endif 34 | 35 | #ifdef __WINDOWS__ 36 | 37 | /* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: 38 | 39 | CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols 40 | CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) 41 | CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol 42 | 43 | For *nix builds that support visibility attribute, you can define similar behavior by 44 | 45 | setting default visibility to hidden by adding 46 | -fvisibility=hidden (for gcc) 47 | or 48 | -xldscope=hidden (for sun cc) 49 | to CFLAGS 50 | 51 | then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does 52 | 53 | */ 54 | 55 | #define CJSON_CDECL __cdecl 56 | #define CJSON_STDCALL __stdcall 57 | 58 | /* export symbols by default, this is necessary for copy pasting the C and header file */ 59 | #if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) 60 | #define CJSON_EXPORT_SYMBOLS 61 | #endif 62 | 63 | #if defined(CJSON_HIDE_SYMBOLS) 64 | #define CJSON_PUBLIC(type) type CJSON_STDCALL 65 | #elif defined(CJSON_EXPORT_SYMBOLS) 66 | #define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL 67 | #elif defined(CJSON_IMPORT_SYMBOLS) 68 | #define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL 69 | #endif 70 | #else /* !__WINDOWS__ */ 71 | #define CJSON_CDECL 72 | #define CJSON_STDCALL 73 | 74 | #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) 75 | #define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type 76 | #else 77 | #define CJSON_PUBLIC(type) type 78 | #endif 79 | #endif 80 | 81 | /* project version */ 82 | #define CJSON_VERSION_MAJOR 1 83 | #define CJSON_VERSION_MINOR 7 84 | #define CJSON_VERSION_PATCH 13 85 | 86 | #include 87 | 88 | /* cJSON Types: */ 89 | #define cJSON_Invalid (0) 90 | #define cJSON_False (1 << 0) 91 | #define cJSON_True (1 << 1) 92 | #define cJSON_NULL (1 << 2) 93 | #define cJSON_Number (1 << 3) 94 | #define cJSON_String (1 << 4) 95 | #define cJSON_Array (1 << 5) 96 | #define cJSON_Object (1 << 6) 97 | #define cJSON_Raw (1 << 7) /* raw json */ 98 | 99 | #define cJSON_IsReference 256 100 | #define cJSON_StringIsConst 512 101 | 102 | /* The cJSON structure: */ 103 | typedef struct cJSON 104 | { 105 | /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ 106 | struct cJSON *next; 107 | struct cJSON *prev; 108 | /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ 109 | struct cJSON *child; 110 | 111 | /* The type of the item, as above. */ 112 | int type; 113 | 114 | /* The item's string, if type==cJSON_String and type == cJSON_Raw */ 115 | char *valuestring; 116 | /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ 117 | int valueint; 118 | /* The item's number, if type==cJSON_Number */ 119 | double valuedouble; 120 | 121 | /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ 122 | char *string; 123 | } cJSON; 124 | 125 | typedef struct cJSON_Hooks 126 | { 127 | /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ 128 | void *(CJSON_CDECL *malloc_fn)(size_t sz); 129 | void (CJSON_CDECL *free_fn)(void *ptr); 130 | } cJSON_Hooks; 131 | 132 | typedef int cJSON_bool; 133 | 134 | /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. 135 | * This is to prevent stack overflows. */ 136 | #ifndef CJSON_NESTING_LIMIT 137 | #define CJSON_NESTING_LIMIT 1000 138 | #endif 139 | 140 | /* returns the version of cJSON as a string */ 141 | CJSON_PUBLIC(const char*) cJSON_Version(void); 142 | 143 | /* Supply malloc, realloc and free functions to cJSON */ 144 | CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); 145 | 146 | /* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ 147 | /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ 148 | cJSON * cJSON_Parse(const char *value); 149 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); 150 | /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ 151 | /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ 152 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); 153 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); 154 | 155 | /* Render a cJSON entity to text for transfer/storage. */ 156 | CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); 157 | /* Render a cJSON entity to text for transfer/storage without any formatting. */ 158 | CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); 159 | /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ 160 | CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); 161 | /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ 162 | /* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ 163 | CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); 164 | /* Delete a cJSON entity and all subentities. */ 165 | CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); 166 | 167 | /* Returns the number of items in an array (or object). */ 168 | CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); 169 | /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ 170 | CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); 171 | /* Get item "string" from object. Case insensitive. */ 172 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); 173 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); 174 | CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); 175 | /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ 176 | CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); 177 | 178 | /* Check item type and return its value */ 179 | CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); 180 | CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item); 181 | 182 | /* These functions check the type of an item */ 183 | CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); 184 | CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); 185 | CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); 186 | CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); 187 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); 188 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); 189 | CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); 190 | CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); 191 | CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); 192 | CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); 193 | 194 | /* These calls create a cJSON item of the appropriate type. */ 195 | CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); 196 | CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); 197 | CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); 198 | CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); 199 | CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); 200 | CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); 201 | /* raw json */ 202 | CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); 203 | CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); 204 | CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); 205 | 206 | /* Create a string where valuestring references a string so 207 | * it will not be freed by cJSON_Delete */ 208 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); 209 | /* Create an object/array that only references it's elements so 210 | * they will not be freed by cJSON_Delete */ 211 | CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); 212 | CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); 213 | 214 | /* These utilities create an Array of count items. 215 | * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ 216 | CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); 217 | CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); 218 | CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); 219 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); 220 | 221 | /* Append item to the specified array/object. */ 222 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); 223 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); 224 | /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. 225 | * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before 226 | * writing to `item->string` */ 227 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); 228 | /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ 229 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); 230 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); 231 | 232 | /* Remove/Detach items from Arrays/Objects. */ 233 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); 234 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); 235 | CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); 236 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); 237 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); 238 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); 239 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); 240 | 241 | /* Update array items. */ 242 | CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ 243 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); 244 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); 245 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); 246 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); 247 | 248 | /* Duplicate a cJSON item */ 249 | CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); 250 | /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will 251 | * need to be released. With recurse!=0, it will duplicate any children connected to the item. 252 | * The item->next and ->prev pointers are always zero on return from Duplicate. */ 253 | /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. 254 | * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ 255 | CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); 256 | 257 | /* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. 258 | * The input pointer json cannot point to a read-only address area, such as a string constant, 259 | * but should point to a readable and writable adress area. */ 260 | CJSON_PUBLIC(void) cJSON_Minify(char *json); 261 | 262 | /* Helper functions for creating and adding items to an object at the same time. 263 | * They return the added item or NULL on failure. */ 264 | CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); 265 | CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); 266 | CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); 267 | CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); 268 | CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); 269 | CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); 270 | CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); 271 | CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); 272 | CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); 273 | 274 | /* When assigning an integer value, it needs to be propagated to valuedouble too. */ 275 | #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) 276 | /* helper for the cJSON_SetNumberValue macro */ 277 | CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); 278 | #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) 279 | /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ 280 | CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); 281 | 282 | /* Macro for iterating over an array or object */ 283 | #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) 284 | 285 | /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ 286 | CJSON_PUBLIC(void *) cJSON_malloc(size_t size); 287 | CJSON_PUBLIC(void) cJSON_free(void *object); 288 | 289 | #ifdef __cplusplus 290 | } 291 | #endif 292 | 293 | #endif 294 | -------------------------------------------------------------------------------- /base/noncopyable.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/16. 3 | // 4 | 5 | #ifndef WEBNGINX_NONCOPYABLE_H 6 | #define WEBNGINX_NONCOPYABLE_H 7 | 8 | class noncopyable { 9 | protected: 10 | noncopyable() {}; 11 | 12 | ~noncopyable() {}; 13 | 14 | private: 15 | noncopyable(const noncopyable &); 16 | 17 | const noncopyable &operator=(const noncopyable &); 18 | }; 19 | 20 | #endif //WEBNGINX_NONCOPYABLE_H 21 | -------------------------------------------------------------------------------- /bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icharle/WebServer/207b26f44534e25623ace89a982c3984babdde8a/bin/.gitkeep -------------------------------------------------------------------------------- /conf/conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "PORT": 80, 4 | "ThreadNum": 4, 5 | "BackLog": 1024 6 | }, 7 | "host": [ 8 | { 9 | "default": { 10 | "root": "/home/www/default/", 11 | "access_log": "/www/log/default.log", 12 | "warn_log": "/www/log/default.warn.log" 13 | }, 14 | "1.icharle.com": { 15 | "root": "/home/www/1.icharle.com/", 16 | "access_log": "/www/log/1.icharle.com.log", 17 | "warn_log": "/www/log/1.icharle.com.warn.log" 18 | }, 19 | "2.icharle.com": { 20 | "root": "/home/www/2.icharle.com/", 21 | "access_log": "/www/log/2.icharle.com.log", 22 | "warn_log": "/www/log/2.icharle.com.warn.log" 23 | } 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /docs/image/ab-nginx-300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icharle/WebServer/207b26f44534e25623ace89a982c3984babdde8a/docs/image/ab-nginx-300.png -------------------------------------------------------------------------------- /docs/image/ab-nginx-500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icharle/WebServer/207b26f44534e25623ace89a982c3984babdde8a/docs/image/ab-nginx-500.png -------------------------------------------------------------------------------- /docs/image/ab-webserver-300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icharle/WebServer/207b26f44534e25623ace89a982c3984babdde8a/docs/image/ab-webserver-300.png -------------------------------------------------------------------------------- /docs/image/ab-webserver-500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icharle/WebServer/207b26f44534e25623ace89a982c3984babdde8a/docs/image/ab-webserver-500.png -------------------------------------------------------------------------------- /docs/image/root.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icharle/WebServer/207b26f44534e25623ace89a982c3984babdde8a/docs/image/root.jpg -------------------------------------------------------------------------------- /src/Channel.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/15. 3 | // 4 | 5 | #include "Channel.h" 6 | #include "EventLoop.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | Channel::Channel(EventLoop *loop_) : 13 | loop(loop_), 14 | events(0), 15 | lastEvents(0) { 16 | } 17 | 18 | Channel::Channel(EventLoop *loop_, int fd_) : 19 | loop(loop_), 20 | fd(fd_), 21 | events(0), 22 | lastEvents(0) { 23 | } 24 | 25 | int Channel::getFd() { 26 | return fd; 27 | } 28 | 29 | void Channel::setFd(int fd_) { 30 | fd = fd_; 31 | } 32 | 33 | void Channel::setHolder(std::shared_ptr holder_) { 34 | holder = holder_; 35 | } 36 | 37 | void Channel::setReadHandler(CallBack &&readHandler_) { 38 | readHandler = readHandler_; 39 | } 40 | 41 | void Channel::setWriteHandler(CallBack &&writeHandler_) { 42 | writeHandler = writeHandler_; 43 | } 44 | 45 | void Channel::setErrorHandler(CallBack &&errorHandler_) { 46 | errorHandler = errorHandler_; 47 | } 48 | 49 | void Channel::handleEvents() { 50 | events = 0; 51 | if ((revents & EPOLLHUP) && !(revents & EPOLLIN)) { 52 | events = 0; 53 | return; 54 | } 55 | if (revents & EPOLLERR) { 56 | if (errorHandler) { 57 | errorHandler(); 58 | } 59 | events = 0; 60 | return; 61 | } 62 | if (revents & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)) { 63 | handleRead(); 64 | } 65 | if (revents & EPOLLOUT) { 66 | handleWrite(); 67 | } 68 | handleConn(); 69 | } 70 | 71 | void Channel::setConnHandler(CallBack &&connHandler_) { 72 | connHandler = connHandler_; 73 | } 74 | 75 | void Channel::handleRead() { 76 | if (readHandler) { 77 | readHandler(); 78 | } 79 | } 80 | 81 | void Channel::handleWrite() { 82 | if (writeHandler) { 83 | writeHandler(); 84 | } 85 | } 86 | 87 | void Channel::handleConn() { 88 | if (connHandler) { 89 | connHandler(); 90 | } 91 | } 92 | 93 | Channel::~Channel() = default; -------------------------------------------------------------------------------- /src/Channel.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/15. 3 | // 4 | 5 | #ifndef WEBNGINX_CHANNEL_H 6 | #define WEBNGINX_CHANNEL_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class EventLoop; 15 | 16 | class HttpData; 17 | 18 | class Channel { 19 | private: 20 | // 回调函数 21 | typedef std::function CallBack; 22 | EventLoop *loop; 23 | int fd; 24 | __uint32_t events; 25 | __uint32_t revents; 26 | __uint32_t lastEvents; 27 | // channel持有者 28 | std::weak_ptr holder; 29 | 30 | public: 31 | Channel(EventLoop *loop_); 32 | 33 | Channel(EventLoop *loop_, int fd_); 34 | 35 | ~Channel(); 36 | 37 | int getFd(); 38 | 39 | void setFd(int fd_); 40 | 41 | void setHolder(std::shared_ptr holder_); 42 | 43 | std::shared_ptr getHolder() { 44 | std::shared_ptr res(holder.lock()); 45 | return res; 46 | } 47 | 48 | void setReadHandler(CallBack &&readHandler_); 49 | 50 | void setWriteHandler(CallBack &&writeHandler_); 51 | 52 | void setErrorHandler(CallBack &&errorHandler_); 53 | 54 | void setConnHandler(CallBack &&connHandler_); 55 | 56 | // 读 写 异常 连接事件分发处理 57 | void handleEvents(); 58 | 59 | void setRevents(__uint32_t ev) { 60 | revents = ev; 61 | } 62 | 63 | void setEvents(__uint32_t ev) { 64 | events = ev; 65 | } 66 | 67 | uint32_t &getEvents() { 68 | return events; 69 | } 70 | 71 | __uint32_t getLastEvents() { 72 | return lastEvents; 73 | } 74 | 75 | bool EqualAndUpdateLastEvents() { 76 | bool res = (lastEvents == events); 77 | lastEvents = events; 78 | return res; 79 | } 80 | 81 | void handleRead(); 82 | 83 | void handleWrite(); 84 | 85 | void handleError(int fd, int code, std::string msg); 86 | 87 | void handleConn(); 88 | 89 | private: 90 | // 处理请求行 91 | int parse_URI(); 92 | 93 | // 处理请求头 94 | int parse_Headers(); 95 | 96 | // 读取具体文件 97 | int analysisRequest(); 98 | 99 | CallBack readHandler; 100 | CallBack writeHandler; 101 | CallBack errorHandler; 102 | CallBack connHandler; 103 | }; 104 | 105 | typedef std::shared_ptr shareChannel; 106 | 107 | #endif //WEBNGINX_CHANNEL_H 108 | -------------------------------------------------------------------------------- /src/Config.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/21. 3 | // 4 | 5 | #include "Config.h" 6 | #include 7 | #include "../base/cJSON.h" 8 | #include "../base/MutexLock.h" 9 | #include 10 | 11 | Config *Config::instance = nullptr; 12 | MutexLock Config::mutex; 13 | 14 | Config::Config() {} 15 | 16 | Config *Config::getInstance() { 17 | if (instance == nullptr) { 18 | MutexLockGuard lock(mutex); 19 | if (instance == nullptr) { 20 | instance = new Config(); 21 | static Recovery recov; 22 | } 23 | } 24 | return instance; 25 | } 26 | 27 | void Config::load(const std::string &filePath) { 28 | FILE *fp = fopen(filePath.c_str(), "r"); 29 | if (fp == NULL) { 30 | //todo 日志 31 | abort(); 32 | } 33 | int i = 0; 34 | char ch; 35 | while ((ch = fgetc(fp)) != EOF) { 36 | configBuf[i++] = ch; 37 | } 38 | fclose(fp); 39 | } 40 | 41 | std::string Config::getHostRoot(const char *host) { 42 | cJSON *root = NULL; 43 | cJSON *hosts = NULL; 44 | cJSON *item = NULL; 45 | root = cJSON_Parse(configBuf); 46 | 47 | hosts = cJSON_GetObjectItem(root, "host"); 48 | item = cJSON_GetArrayItem(hosts, 0); 49 | if (cJSON_HasObjectItem(item, host) == 1) { 50 | item = cJSON_GetObjectItem(item, host); 51 | } else { 52 | // 返回默认站点 53 | item = cJSON_GetObjectItem(item, "default"); 54 | } 55 | item = cJSON_GetObjectItem(item, "root"); 56 | return item->valuestring; 57 | } 58 | 59 | std::string Config::getPassProxy(const char *host) { 60 | cJSON *root = NULL; 61 | cJSON *hosts = NULL; 62 | cJSON *item = NULL; 63 | root = cJSON_Parse(configBuf); 64 | 65 | hosts = cJSON_GetObjectItem(root, "host"); 66 | item = cJSON_GetArrayItem(hosts, 0); 67 | if (cJSON_HasObjectItem(item, host) == 1) { 68 | item = cJSON_GetObjectItem(item, host); 69 | if (cJSON_HasObjectItem(item, "proxy_pass") == 1) { 70 | item = cJSON_GetObjectItem(item, "proxy_pass"); 71 | return item->valuestring; 72 | } 73 | } else { 74 | // 返回默认站点 75 | item = cJSON_GetObjectItem(item, "default"); 76 | if (cJSON_HasObjectItem(item, "proxy_pass") == 1) { 77 | item = cJSON_GetObjectItem(item, "proxy_pass"); 78 | return item->valuestring; 79 | } 80 | } 81 | return ""; 82 | } 83 | 84 | void Config::getServerConfig(int &port, int &threadNum, int &backlog) { 85 | cJSON *root = NULL; 86 | cJSON *server = NULL; 87 | root = cJSON_Parse(configBuf); 88 | server = cJSON_GetObjectItem(root, "server"); 89 | port = cJSON_GetObjectItem(server, "PORT")->valueint; 90 | threadNum = cJSON_GetObjectItem(server, "ThreadNum")->valueint; 91 | backlog = cJSON_GetObjectItem(server, "BackLog")->valueint; 92 | } 93 | 94 | Config::~Config() { 95 | } -------------------------------------------------------------------------------- /src/Config.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/21. 3 | // 4 | 5 | #ifndef WEBNGINX_CONFIG_H 6 | #define WEBNGINX_CONFIG_H 7 | 8 | #include "../base/MutexLock.h" 9 | #include 10 | 11 | class Config { 12 | private: 13 | static MutexLock mutex; 14 | static Config *instance; 15 | 16 | Config(); 17 | 18 | class Recovery { 19 | public: 20 | ~Recovery() { 21 | if (instance != nullptr) { 22 | delete instance; 23 | } 24 | } 25 | }; 26 | 27 | ~Config(); 28 | 29 | public: 30 | static Config *getInstance(); 31 | 32 | void load(const std::string &filePath); 33 | 34 | std::string getHostRoot(const char *host); 35 | 36 | std::string getPassProxy(const char *host); 37 | 38 | void getServerConfig(int &port, int &threadNum, int &backlog); 39 | 40 | public: 41 | char configBuf[4096]{}; 42 | }; 43 | 44 | #endif //WEBNGINX_CONFIG_H 45 | -------------------------------------------------------------------------------- /src/Epoll.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/15. 3 | // 4 | 5 | #include "Epoll.h" 6 | #include 7 | #include 8 | #include "Util.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define MAXEVENTS 1024 18 | #define EPOLLWAIT_TIME 10000 19 | typedef std::shared_ptr shareChannel; 20 | 21 | Epoll::Epoll() : 22 | epollFd(epoll_create1(EPOLL_CLOEXEC)), 23 | events(MAXEVENTS) { 24 | assert(epollFd > 0); 25 | } 26 | 27 | Epoll::~Epoll() { 28 | 29 | } 30 | 31 | void Epoll::add(shareChannel request, int timeout) { 32 | int fd = request->getFd(); 33 | if (timeout > 0) { 34 | add_timer(request, timeout); 35 | fd2http[fd] = request->getHolder(); 36 | } 37 | struct epoll_event event; 38 | event.data.fd = fd; 39 | event.events = request->getEvents(); 40 | 41 | request->EqualAndUpdateLastEvents(); 42 | 43 | fd2chan[fd] = request; 44 | if (epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &event) < 0) { 45 | fd2chan[fd].reset(); 46 | } 47 | } 48 | 49 | void Epoll::mod(shareChannel request, int timeout) { 50 | int fd = request->getFd(); 51 | if (timeout > 0) { 52 | add_timer(request, timeout); 53 | } 54 | if (!request->EqualAndUpdateLastEvents()) { 55 | struct epoll_event event; 56 | event.data.fd = fd; 57 | event.events = request->getEvents(); 58 | if (epoll_ctl(epollFd, EPOLL_CTL_MOD, fd, &event) < 0) { 59 | fd2chan[fd].reset(); 60 | } 61 | } 62 | } 63 | 64 | void Epoll::del(shareChannel request) { 65 | int fd = request->getFd(); 66 | struct epoll_event event; 67 | event.data.fd = fd; 68 | event.events = request->getLastEvents(); 69 | 70 | if (epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, &event) < 0) { 71 | // todo 日志 72 | } 73 | fd2chan[fd].reset(); 74 | fd2http[fd].reset(); 75 | } 76 | 77 | std::vector Epoll::poll() { 78 | while (true) { 79 | int event_count = epoll_wait(epollFd, &*events.begin(), events.size(), EPOLLWAIT_TIME); 80 | if (event_count < 0) { 81 | // todo 日志 82 | } 83 | std::vector req_data = getEventsRequest(event_count);; 84 | if (!req_data.empty()) { 85 | return req_data; 86 | } 87 | } 88 | } 89 | 90 | void Epoll::handleExpired() { 91 | timerManager.handleExpiredEvent(); 92 | } 93 | 94 | void Epoll::add_timer(shareChannel request_data, int timeout) { 95 | std::shared_ptr t = request_data->getHolder(); 96 | if (t) { 97 | timerManager.addTimer(t, timeout); 98 | } else { 99 | //todo 日志 100 | } 101 | } 102 | 103 | std::vector Epoll::getEventsRequest(int events_num) { 104 | std::vector req_data; 105 | for (int i = 0; i < events_num; ++i) { 106 | int fd = events[i].data.fd; 107 | shareChannel cur_req = fd2chan[fd]; 108 | if (cur_req) { 109 | cur_req->setRevents(events[i].events); 110 | cur_req->setEvents(0); 111 | 112 | req_data.push_back(cur_req); 113 | } else { 114 | // todo 日志 115 | } 116 | } 117 | return req_data; 118 | } -------------------------------------------------------------------------------- /src/Epoll.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/15. 3 | // 4 | 5 | #ifndef WEBNGINX_EPOLL_H 6 | #define WEBNGINX_EPOLL_H 7 | 8 | #define MAXEVENTS 1024 9 | 10 | #include 11 | #include "Channel.h" 12 | #include "Timer.h" 13 | #include "HttpData.h" 14 | #include 15 | #include 16 | #include 17 | 18 | class Epoll { 19 | public: 20 | Epoll(); 21 | 22 | void add(shareChannel request, int timeout); 23 | 24 | void mod(shareChannel request, int timeout); 25 | 26 | void del(shareChannel request); 27 | 28 | std::vector> poll(); 29 | 30 | std::vector> getEventsRequest(int events_num); 31 | 32 | void add_timer(std::shared_ptr request_data, int timeout); 33 | 34 | int getEpollFd() { 35 | return epollFd; 36 | } 37 | 38 | void handleExpired(); 39 | 40 | ~Epoll(); 41 | 42 | private: 43 | int epollFd; 44 | std::vector events; 45 | std::shared_ptr fd2chan[MAXEVENTS]; 46 | std::shared_ptr fd2http[MAXEVENTS]; 47 | TimerManager timerManager; 48 | }; 49 | 50 | #endif //WEBNGINX_EPOLL_H 51 | -------------------------------------------------------------------------------- /src/EventLoop.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/15. 3 | // 4 | 5 | #include "EventLoop.h" 6 | #include 7 | #include "sys/eventfd.h" 8 | #include 9 | 10 | __thread EventLoop *t_loopInThisThread = 0; 11 | 12 | int createEventfd() { 13 | int evtfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); 14 | if (evtfd < 0) { 15 | abort(); 16 | } 17 | return evtfd; 18 | } 19 | 20 | EventLoop::EventLoop() : 21 | looping_(false), 22 | poller_(new Epoll()), 23 | wakeupFd_(createEventfd()), 24 | quit_(false), 25 | eventHandling_(false), 26 | callingPendingFunctors_(false), 27 | threadId_(CurrentThread::tid()), 28 | pwakeupChannel_(new Channel(this, wakeupFd_)) { 29 | if (t_loopInThisThread) { 30 | // todo 日志 31 | } else { 32 | t_loopInThisThread = this; 33 | } 34 | pwakeupChannel_->setEvents(EPOLLIN | EPOLLET); 35 | pwakeupChannel_->setReadHandler(std::bind(&EventLoop::handleRead, this)); 36 | pwakeupChannel_->setConnHandler(std::bind(&EventLoop::handleConn, this)); 37 | poller_->add(pwakeupChannel_, 0); 38 | } 39 | 40 | EventLoop::~EventLoop() { 41 | close(wakeupFd_); 42 | t_loopInThisThread = nullptr; 43 | } 44 | 45 | void EventLoop::handleConn() { 46 | updatePoller(pwakeupChannel_, 0); 47 | } 48 | 49 | void EventLoop::wakeup() { 50 | uint64_t one = 1; 51 | ssize_t n = writen(wakeupFd_, (char *) (&one), sizeof(one)); 52 | if (n != sizeof(one)) { 53 | //todo 日志 54 | } 55 | } 56 | 57 | void EventLoop::handleRead() { 58 | uint64_t one = 1; 59 | ssize_t n = readn(wakeupFd_, (char *) &one, sizeof(one)); 60 | if (n != sizeof(one)) { 61 | //todo 日志 62 | } 63 | pwakeupChannel_->setEvents(EPOLLIN | EPOLLET); 64 | } 65 | 66 | void EventLoop::runInLoop(Functor &&cb) { 67 | if (isInLoopThread()) { 68 | cb(); 69 | } else { 70 | queueInLoop(std::move(cb)); 71 | } 72 | } 73 | 74 | void EventLoop::queueInLoop(Functor &&cb) { 75 | { 76 | MutexLockGuard lock(mutex_); 77 | pendingFunctors_.emplace_back(std::move(cb)); 78 | } 79 | if (!isInLoopThread() || callingPendingFunctors_) { 80 | wakeup(); 81 | } 82 | } 83 | 84 | void EventLoop::loop() { 85 | assert(!looping_); 86 | assert(isInLoopThread()); 87 | looping_ = true; 88 | quit_ = false; 89 | std::vector ret; 90 | while (!quit_) { 91 | ret.clear(); 92 | ret = poller_->poll(); 93 | eventHandling_ = true; 94 | for (auto &it : ret) { 95 | it->handleEvents(); 96 | } 97 | eventHandling_ = false; 98 | doPendingFunctors(); 99 | poller_->handleExpired(); 100 | } 101 | looping_ = false; 102 | } 103 | 104 | void EventLoop::doPendingFunctors() { 105 | std::vector functors; 106 | callingPendingFunctors_ = true; 107 | { 108 | MutexLockGuard lock(mutex_); 109 | functors.swap(pendingFunctors_); 110 | } 111 | 112 | for (size_t i = 0; i < functors.size(); ++i) { 113 | functors[i](); 114 | } 115 | callingPendingFunctors_ = false; 116 | } 117 | 118 | void EventLoop::quit() { 119 | quit_ = true; 120 | if (!isInLoopThread()) { 121 | wakeup(); 122 | } 123 | } -------------------------------------------------------------------------------- /src/EventLoop.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/15. 3 | // 4 | 5 | #ifndef WEBNGINX_EVENTLOOP_H 6 | #define WEBNGINX_EVENTLOOP_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "Util.h" 13 | #include "../base/MutexLock.h" 14 | #include "../base/CurrentThread.h" 15 | #include "../base/Thread.h" 16 | #include "Epoll.h" 17 | #include "Channel.h" 18 | 19 | class EventLoop { 20 | public: 21 | typedef std::function Functor; 22 | 23 | EventLoop(); 24 | 25 | ~EventLoop(); 26 | 27 | void loop(); 28 | 29 | void quit(); 30 | 31 | void runInLoop(Functor &&cb); 32 | 33 | void queueInLoop(Functor &&cb); 34 | 35 | bool isInLoopThread() const { 36 | return threadId_ == CurrentThread::tid(); 37 | } 38 | 39 | void assertInLoopThread() const { 40 | assert(isInLoopThread()); 41 | } 42 | 43 | void shutdown(std::shared_ptr channel) { 44 | shutDownWR(channel->getFd()); 45 | } 46 | 47 | void removeFromPoller(std::shared_ptr channel) { 48 | poller_->del(channel); 49 | } 50 | 51 | void updatePoller(std::shared_ptr channel, int timeout = 0) { 52 | poller_->mod(channel, timeout); 53 | } 54 | 55 | void addPoller(std::shared_ptr channel, int timeout = 0) { 56 | poller_->add(channel, timeout); 57 | } 58 | 59 | private: 60 | bool looping_; 61 | std::shared_ptr poller_; 62 | int wakeupFd_; 63 | bool quit_; 64 | bool eventHandling_; 65 | mutable MutexLock mutex_; 66 | 67 | std::vector pendingFunctors_; 68 | bool callingPendingFunctors_; 69 | const pid_t threadId_; 70 | std::shared_ptr pwakeupChannel_; 71 | 72 | void wakeup(); 73 | 74 | void handleRead(); 75 | 76 | void doPendingFunctors(); 77 | 78 | void handleConn(); 79 | }; 80 | 81 | #endif //WEBNGINX_EVENTLOOP_H 82 | -------------------------------------------------------------------------------- /src/EventLoopThread.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/17. 3 | // 4 | 5 | #include "EventLoopThread.h" 6 | #include 7 | 8 | EventLoopThread::EventLoopThread() : 9 | loop(nullptr), 10 | existing(false), 11 | thread(std::bind(&EventLoopThread::threadFunc, this)), 12 | mutex(), 13 | condition(mutex) { 14 | } 15 | 16 | EventLoopThread::~EventLoopThread() { 17 | existing = true; 18 | if (loop != nullptr) { 19 | loop->quit(); 20 | thread.join(); 21 | } 22 | } 23 | 24 | EventLoop *EventLoopThread::startLoop() { 25 | assert(!thread.started()); 26 | thread.start(); 27 | { 28 | MutexLockGuard lock(mutex); 29 | while (loop == nullptr) { 30 | condition.wait(); 31 | } 32 | } 33 | return loop; 34 | } 35 | 36 | void EventLoopThread::threadFunc() { 37 | EventLoop loop_; 38 | { 39 | MutexLockGuard lock(mutex); 40 | loop = &loop_; 41 | condition.notify(); 42 | } 43 | loop_.loop(); 44 | loop = nullptr; 45 | } -------------------------------------------------------------------------------- /src/EventLoopThread.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/17. 3 | // 4 | 5 | #ifndef WEBNGINX_EVENTLOOPTHREAD_H 6 | #define WEBNGINX_EVENTLOOPTHREAD_H 7 | 8 | #include "../base/noncopyable.h" 9 | #include "../base/Thread.h" 10 | #include "../base/Condition.h" 11 | #include "../base/MutexLock.h" 12 | #include "EventLoop.h" 13 | 14 | class EventLoopThread : noncopyable { 15 | public: 16 | EventLoopThread(); 17 | 18 | ~EventLoopThread(); 19 | 20 | EventLoop *startLoop(); 21 | 22 | private: 23 | void threadFunc(); 24 | 25 | EventLoop *loop; 26 | 27 | bool existing; 28 | 29 | Thread thread; 30 | 31 | MutexLock mutex; 32 | 33 | Condition condition; 34 | }; 35 | 36 | #endif //WEBNGINX_EVENTLOOPTHREAD_H 37 | -------------------------------------------------------------------------------- /src/EventLoopThreadPool.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/17. 3 | // 4 | 5 | #include "EventLoopThreadPool.h" 6 | 7 | EventLoopThreadPool::EventLoopThreadPool(EventLoop *baseloop, int numThreads) : 8 | baseLoop(baseloop), 9 | started(false), 10 | numThreads(numThreads), 11 | next(0) { 12 | if (numThreads <= 0) { 13 | abort(); 14 | } 15 | } 16 | 17 | EventLoopThreadPool::~EventLoopThreadPool() { 18 | //todo 日志 19 | } 20 | 21 | void EventLoopThreadPool::start() { 22 | baseLoop->assertInLoopThread(); 23 | started = true; 24 | for (int i = 0; i < numThreads; ++i) { 25 | std::shared_ptr t(new EventLoopThread()); 26 | threads.push_back(t); 27 | loops.push_back(t->startLoop()); 28 | } 29 | } 30 | 31 | EventLoop *EventLoopThreadPool::getNextLoop() { 32 | baseLoop->assertInLoopThread(); 33 | assert(started); 34 | EventLoop *loop = baseLoop; 35 | if (!loops.empty()) { 36 | loop = loops[next]; 37 | next = (next + 1) % numThreads; 38 | } 39 | return loop; 40 | } -------------------------------------------------------------------------------- /src/EventLoopThreadPool.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/17. 3 | // 4 | 5 | #ifndef WEBNGINX_EVENTLOOPTHREADPOOL_H 6 | #define WEBNGINX_EVENTLOOPTHREADPOOL_H 7 | 8 | #include "../base/noncopyable.h" 9 | #include "EventLoop.h" 10 | #include "EventLoopThread.h" 11 | #include 12 | #include 13 | #include 14 | 15 | class EventLoopThreadPool : noncopyable { 16 | public: 17 | EventLoopThreadPool(EventLoop *baseloop, int numThreads); 18 | 19 | ~EventLoopThreadPool(); 20 | 21 | void start(); 22 | 23 | EventLoop *getNextLoop(); 24 | 25 | private: 26 | EventLoop *baseLoop; 27 | bool started; 28 | int numThreads; 29 | int next; 30 | std::vector> threads; 31 | std::vector loops; 32 | }; 33 | 34 | #endif //WEBNGINX_EVENTLOOPTHREADPOOL_H 35 | -------------------------------------------------------------------------------- /src/FastCgi.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/24. 3 | // 4 | 5 | #include "FastCgi.h" 6 | #include 7 | #include "Util.h" 8 | #include 9 | #include 10 | #include 11 | 12 | static const int PARAMS_BUFF_LEN = 2048; //环境参数buffer的大小 13 | static const int CONTENT_BUFF_LEN = 2048; //内容buffer的大小 14 | #define CR '\r' 15 | #define LF '\n' 16 | 17 | FastCgi::FastCgi() : 18 | socketFd(0), 19 | requestId(0) {} 20 | 21 | FastCgi::~FastCgi() { 22 | socketFd = 0; 23 | requestId = 0; 24 | } 25 | 26 | FASTCGI_Header FastCgi::makeHeader(int type, int requestId, int contentLength, int paddingLength) { 27 | FASTCGI_Header header{}; 28 | header.version = FASTCGI_VERSION; 29 | header.type = (unsigned char) type; 30 | 31 | header.requestIdB1 = (unsigned char) ((requestId >> 8) & 0xff); 32 | header.requestIdB0 = (unsigned char) (requestId & 0xff); 33 | 34 | header.contentLengthB1 = (unsigned char) ((contentLength >> 8) & 0xff); 35 | header.contentLengthB0 = (unsigned char) ((contentLength & 0xff)); 36 | 37 | header.paddingLength = (unsigned char) paddingLength; 38 | 39 | header.reserved = 0; 40 | 41 | return header; 42 | } 43 | 44 | FastCgiBeginRequestBody FastCgi::makeBeginRequestBody(int role, int keepConnection) { 45 | 46 | FastCgiBeginRequestBody body{}; 47 | 48 | body.roleB1 = (unsigned char) ((role >> 8) & 0xff); 49 | body.roleB0 = (unsigned char) (role & 0xff); 50 | 51 | body.flags = (unsigned char) ((keepConnection > 0 ? FASTCGI_KEEP_CONN : 0)); 52 | memset(body.reserved, 0, sizeof(body.reserved)); 53 | return body; 54 | } 55 | 56 | void FastCgi::connectFpm() { 57 | socketFd = proxySocket(DEFAULT_ADDR, DEFAULT_PORT); 58 | } 59 | 60 | int FastCgi::sendStartRequestRecord() { 61 | int ret; 62 | FastCgiBeginRequestRecord beginRequestRecord{}; 63 | beginRequestRecord.header = makeHeader(FASTCGI_BEGIN_REQUEST, requestId, sizeof(beginRequestRecord.body), 0); 64 | beginRequestRecord.body = makeBeginRequestBody(FASTCGI_RESPONDER, 0); 65 | 66 | ret = writen(socketFd, (char *) &beginRequestRecord, sizeof(beginRequestRecord)); 67 | 68 | if (ret == sizeof(beginRequestRecord)) { 69 | return 0; 70 | } else { 71 | abort(); 72 | return -1; 73 | } 74 | } 75 | 76 | int FastCgi::sendParams(char *name, char *value) { 77 | int nlen = strlen(name); 78 | int vlen = strlen(value); 79 | int ret, bodylen = nlen + vlen; 80 | (nlen < 128) ? (++bodylen) : (bodylen + 4); 81 | (vlen < 128) ? (++bodylen) : (bodylen + 4); 82 | 83 | auto *buf = static_cast(malloc(bodylen + FASTCGI_HEADER_LENGTH)); 84 | 85 | // header 86 | FASTCGI_Header nameValueHeader = makeHeader(FASTCGI_PARAMS, requestId, bodylen, 0); 87 | memcpy(buf, (char *) &nameValueHeader, FASTCGI_HEADER_LENGTH); 88 | // header - end 89 | 90 | // body 91 | unsigned char bodyBuff[bodylen]; 92 | int j = 0; 93 | /* 如果 nameLen 小于128字节 */ 94 | if (nlen < 128) { 95 | bodyBuff[j++] = (unsigned char) nlen; //nameLen用1个字节保存 96 | } else { 97 | /* nameLen 用 4 个字节保存 */ 98 | bodyBuff[j++] = (unsigned char) ((nlen >> 24) | 0x80); 99 | bodyBuff[j++] = (unsigned char) (nlen >> 16); 100 | bodyBuff[j++] = (unsigned char) (nlen >> 8); 101 | bodyBuff[j++] = (unsigned char) nlen; 102 | } 103 | 104 | /* valueLen 小于 128 就用一个字节保存 */ 105 | if (vlen < 128) { 106 | bodyBuff[j++] = (unsigned char) vlen; 107 | } else { 108 | /* valueLen 用 4 个字节保存 */ 109 | bodyBuff[j++] = (unsigned char) ((vlen >> 24) | 0x80); 110 | bodyBuff[j++] = (unsigned char) (vlen >> 16); 111 | bodyBuff[j++] = (unsigned char) (vlen >> 8); 112 | bodyBuff[j++] = (unsigned char) vlen; 113 | } 114 | 115 | for (size_t i = 0; i < strlen(name); i++) { 116 | bodyBuff[j++] = name[i]; 117 | } 118 | 119 | for (size_t i = 0; i < strlen(value); i++) { 120 | bodyBuff[j++] = value[i]; 121 | } 122 | memcpy(buf + FASTCGI_HEADER_LENGTH, bodyBuff, bodylen); 123 | // body - end 124 | ret = write(socketFd, buf, bodylen + FASTCGI_HEADER_LENGTH); 125 | assert(ret == bodylen + FASTCGI_HEADER_LENGTH); 126 | return 1; 127 | } 128 | 129 | int FastCgi::sendPostStdinRecord(char *data, int len) { 130 | int bodylen, ret; 131 | 132 | while (len > 0) { 133 | 134 | if (len > FASTCGI_MAX_LENGTH) { 135 | bodylen = FASTCGI_MAX_LENGTH; 136 | } 137 | 138 | FASTCGI_Header header = makeHeader(FASTCGI_STDIN, requestId, bodylen, 0); 139 | 140 | ret = writen(socketFd, (char *) &header, FASTCGI_HEADER_LENGTH); 141 | if (ret != FASTCGI_HEADER_LENGTH) { 142 | return -1; 143 | } 144 | 145 | ret = writen(socketFd, data, bodylen); 146 | if (ret != bodylen) { 147 | return -1; 148 | } 149 | 150 | len -= bodylen; 151 | data += bodylen; 152 | } 153 | return 0; 154 | } 155 | 156 | int FastCgi::sendEndPostStdinRecord() { 157 | int ret; 158 | FASTCGI_Header header = makeHeader(FASTCGI_STDIN, requestId, 0, 0); 159 | ret = writen(socketFd, (char *) &header, FASTCGI_HEADER_LENGTH); 160 | 161 | if (ret == FASTCGI_HEADER_LENGTH) { 162 | return 0; 163 | } else { 164 | return -1; 165 | } 166 | } 167 | 168 | int FastCgi::sendEndRequestRecord() { 169 | int ret; 170 | FASTCGI_Header endHeader{}; 171 | endHeader = makeHeader(FASTCGI_PARAMS, requestId, 0, 0); 172 | 173 | ret = writen(socketFd, (char *) &endHeader, sizeof(endHeader)); 174 | if (ret == sizeof(endHeader)) { 175 | return 0; 176 | } else { 177 | abort(); 178 | return -1; 179 | } 180 | } 181 | 182 | void FastCgi::recvRecord(std::string &header, std::string &body) { 183 | FASTCGI_Header respHeader{}; 184 | 185 | int contentLen; 186 | char paddingBuf[8]; 187 | char *data = nullptr; 188 | int ret; 189 | 190 | while (read(socketFd, &respHeader, FASTCGI_HEADER_LENGTH) > 0) { 191 | if (respHeader.type == FASTCGI_STDOUT) { 192 | contentLen = (respHeader.contentLengthB1 << 8) + (respHeader.contentLengthB0); 193 | data = (char *) malloc(contentLen); 194 | ret = readn(socketFd, data, contentLen); 195 | headerAndContent(data, ret, header, body); 196 | if (respHeader.paddingLength > 0) { 197 | read(socketFd, paddingBuf, respHeader.paddingLength); 198 | } 199 | } else if (respHeader.type == FASTCGI_STDERR) { 200 | contentLen = (respHeader.contentLengthB1 << 8) + (respHeader.contentLengthB0); 201 | ret = readn(socketFd, data, contentLen); 202 | headerAndContent(data, ret, header, body); 203 | 204 | if (respHeader.paddingLength > 0) { 205 | read(socketFd, paddingBuf, respHeader.paddingLength); 206 | } 207 | } else if (respHeader.type == FASTCGI_END_REQUEST) { 208 | FastCgiEndRequestBody endRequestBody{}; 209 | read(socketFd, &endRequestBody, sizeof(endRequestBody)); 210 | } 211 | } 212 | free(data); 213 | } 214 | 215 | void FastCgi::headerAndContent(char *p, int len, std::string &outHeader, std::string &outBody) { 216 | 217 | enum headerState { 218 | H_START, 219 | H_KEY, 220 | H_SPACES_AFTER_COLON, 221 | H_VALUE, 222 | H_CR, 223 | H_LF, 224 | H_END_CR, 225 | H_END_LF 226 | } hState; 227 | 228 | char *key_start, *key_end, *value_start, *value_end; 229 | std::string header; 230 | bool isFinish = false; 231 | hState = H_START; 232 | char *cur = p; 233 | for (; (*cur != '\0' && !isFinish); cur++) { 234 | switch (hState) { 235 | case H_START: 236 | if (*cur == CR || *cur == LF) { 237 | break; 238 | } 239 | key_start = cur; 240 | hState = H_KEY; 241 | break; 242 | 243 | case H_KEY: 244 | if (*cur == ':') { 245 | key_end = cur; 246 | hState = H_SPACES_AFTER_COLON; 247 | break; 248 | } 249 | break; 250 | 251 | case H_SPACES_AFTER_COLON: 252 | if (*cur == ' ') { 253 | break; 254 | } 255 | hState = H_VALUE; 256 | value_start = cur; 257 | break; 258 | 259 | case H_VALUE: 260 | if (*cur == CR) { 261 | value_end = cur; 262 | hState = H_CR; 263 | } 264 | break; 265 | case H_CR: 266 | if (*cur == LF) { 267 | // key - start 268 | while (key_start != key_end) { 269 | header += *key_start; 270 | key_start++; 271 | } 272 | header += ": "; 273 | // key - end 274 | // value - start 275 | while (value_start != value_end) { 276 | header += *value_start; 277 | value_start++; 278 | } 279 | header += "\r\n"; 280 | // value - end 281 | hState = H_LF; 282 | } else { 283 | isFinish = true; 284 | } 285 | break; 286 | case H_LF: 287 | if (*cur == CR) { 288 | hState = H_END_CR; 289 | } else { 290 | key_start = cur; 291 | hState = H_KEY; 292 | } 293 | break; 294 | case H_END_CR: 295 | if (*cur == LF) { 296 | hState = H_END_LF; 297 | } else { 298 | isFinish = true; 299 | } 300 | break; 301 | case H_END_LF: 302 | isFinish = true; 303 | break; 304 | } 305 | } 306 | outHeader = header; 307 | outBody = value_end; 308 | // 截取body部分, 由于上一步read可能出现直到'\0'才结束,导致实际读取长度超过指定长度,超过部分字符异常(越界导致) 309 | outBody = outBody.substr(2, len - outHeader.size()); 310 | } -------------------------------------------------------------------------------- /src/FastCgi.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/24. 3 | // https://blog.csdn.net/shreck66/article/details/50355729 4 | // 5 | #include 6 | #ifndef WEBNGINX_FASTCGI_H 7 | #define WEBNGINX_FASTCGI_H 8 | 9 | // 默认地址 10 | #define DEFAULT_ADDR "127.0.0.1" 11 | // 端口 12 | #define DEFAULT_PORT 9000 13 | 14 | struct FASTCGI_Header { 15 | unsigned char version; 16 | unsigned char type; 17 | unsigned char requestIdB1; 18 | unsigned char requestIdB0; 19 | unsigned char contentLengthB1; 20 | unsigned char contentLengthB0; 21 | unsigned char paddingLength; 22 | unsigned char reserved; 23 | }; 24 | 25 | // 允许传送的最大数据 65536 26 | #define FASTCGI_MAX_LENGTH 0xffff 27 | // fast_cgi header长度 28 | #define FASTCGI_HEADER_LENGTH 8 29 | // fast_cgi 版本 30 | #define FASTCGI_VERSION 1 31 | 32 | enum TypeState { 33 | FASTCGI_BEGIN_REQUEST = 1, 34 | FASTCGI_ABORT_REQUEST, 35 | FASTCGI_END_REQUEST, 36 | FASTCGI_PARAMS, 37 | FASTCGI_STDIN, 38 | FASTCGI_STDOUT, 39 | FASTCGI_STDERR, 40 | FASTCGI_DATA, 41 | FASTCGI_GET_VALUES, 42 | FASTCGI_GET_VALUES_RESULT, 43 | FASTCGI_UNKNOWN_TYPES 44 | }; 45 | 46 | enum RoleState { 47 | FASTCGI_KEEP_CONN = 1, 48 | FASTCGI_RESPONDER = 1, 49 | FASTCGI_AUTHORIZER, 50 | FASTCGI_FILTER 51 | }; 52 | 53 | enum EndState { 54 | FASTCGI_REQUEST_COMPLETE, 55 | FASTCGI_CANT_MPX_XONN, 56 | FASTCGI_OVERLOADED, 57 | FASTCGI_UNKNOWN_ROLE 58 | }; 59 | 60 | struct FastCgiBeginRequestBody { 61 | unsigned char roleB1; 62 | unsigned char roleB0; 63 | unsigned char flags; 64 | unsigned char reserved[5]; 65 | }; 66 | 67 | struct FastCgiBeginRequestRecord { 68 | FASTCGI_Header header; 69 | FastCgiBeginRequestBody body; 70 | }; 71 | 72 | struct FastCgiEndRequestBody { 73 | unsigned char appStatusB3; 74 | unsigned char appStatusB2; 75 | unsigned char appStatusB1; 76 | unsigned char appStatusB0; 77 | unsigned char protocolStatus; 78 | unsigned char reserved[3]; 79 | }; 80 | 81 | struct FastCgiEndRequestRecord { 82 | FASTCGI_Header header; 83 | FastCgiEndRequestBody body; 84 | }; 85 | 86 | class FastCgi { 87 | public: 88 | FastCgi(); 89 | 90 | ~FastCgi(); 91 | 92 | FASTCGI_Header makeHeader(int type, int requestId, int contentLength, int paddingLength); 93 | 94 | FastCgiBeginRequestBody makeBeginRequestBody(int role, int keepConnection); 95 | 96 | void setRequestId(int requestId_) { 97 | requestId = requestId_; 98 | } 99 | 100 | void connectFpm(); 101 | 102 | int sendStartRequestRecord(); 103 | 104 | int sendParams(char *name, char *value); 105 | 106 | int sendPostStdinRecord(char *data, int len); 107 | 108 | int sendEndPostStdinRecord(); 109 | 110 | int sendEndRequestRecord(); 111 | 112 | void recvRecord(std::string &header, std::string &body); 113 | 114 | void headerAndContent(char *p, int len, std::string &header, std::string &body); 115 | 116 | private: 117 | int socketFd; 118 | int requestId; 119 | }; 120 | 121 | #endif //WEBNGINX_FASTCGI_H 122 | -------------------------------------------------------------------------------- /src/HttpData.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/15. 3 | // 4 | 5 | #include "HttpData.h" 6 | #include 7 | #include "Channel.h" 8 | #include "EventLoop.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "time.h" 14 | #include "Util.h" 15 | #include 16 | #include 17 | #include "Config.h" 18 | #include 19 | 20 | extern Config *config; 21 | const __uint32_t DEFALT_EVENT = EPOLLIN | EPOLLET | EPOLLONESHOT; 22 | const int DEFAULT_EXPIRED_TIME = 2000; // ms 23 | const int DEFAULT_KEEP_ALIVE_TIME = 5 * 60 * 1000; // ms 5min 24 | 25 | const std::unordered_map HttpData::MimeType = { 26 | {".html", "text/html"}, 27 | {".css", "text/css"}, 28 | {".xml", "text/xml"}, 29 | {".xhtml", "application/xhtml+xml"}, 30 | {".txt", "text/plain"}, 31 | {".png", "image/png"}, 32 | {".gif", "image/gif"}, 33 | {".jpg", "image/jpeg"}, 34 | {".jpeg", "image/jpeg"}, 35 | {".txt", "text/plain"}, 36 | {".rtf", "application/rtf"}, 37 | {".pdf", "application/pdf"}, 38 | {".word", "application/nsword"}, 39 | {".au", "audio/basic"}, 40 | {".mpeg", "video/mpeg"}, 41 | {".mpg", "video/mpeg"}, 42 | {".avi", "video/x-msvideo"}, 43 | {".gz", "application/x-gzip"}, 44 | {".tar", "application/x-tar"}, 45 | }; 46 | 47 | HttpData::HttpData(EventLoop *loop, int connfd) : 48 | loop(loop), 49 | fastCgi(new FastCgi()), 50 | fd(connfd), 51 | channel(new Channel(loop, connfd)), 52 | method(METHOD_GET), 53 | version(HTTP1_1), 54 | connectionState(H_CONNECTED), 55 | httpProcessState(PARSE_URI), 56 | parseState(H_START), 57 | nowReadPos(0), 58 | keepAlive(false), 59 | isPHP(false), 60 | error(false) { 61 | channel->setReadHandler(std::bind(&HttpData::handleRead, this)); 62 | channel->setWriteHandler(std::bind(&HttpData::handleWrite, this)); 63 | channel->setConnHandler(std::bind(&HttpData::handleConn, this)); 64 | } 65 | 66 | HttpData::~HttpData() { 67 | close(fd); 68 | } 69 | 70 | void HttpData::reset() { 71 | fileName.clear(); 72 | root.clear(); 73 | nowReadPos = 0; 74 | httpProcessState = PARSE_URI; 75 | parseState = H_START; 76 | headers.clear(); 77 | if (timer.lock()) { 78 | std::shared_ptr m_timer(timer.lock()); 79 | m_timer->clearReq(); 80 | timer.reset(); 81 | } 82 | } 83 | 84 | void HttpData::seperateTimer() { 85 | if (timer.lock()) { 86 | std::shared_ptr m_timer(timer.lock()); 87 | m_timer->clearReq(); 88 | timer.reset(); 89 | } 90 | } 91 | 92 | HeaderState HttpData::parseHeaders() { 93 | std::string &str = inBuffer; 94 | int key_start = -1, key_end = -1, value_start = -1, value_end = -1; 95 | int now_read_line_begin = 0; 96 | bool notFinish = true; 97 | size_t i = 0; 98 | 99 | for (; i < str.size() && notFinish; i++) { 100 | switch (parseState) { 101 | case H_START: { 102 | if (str[i] == '\n' || str[i] == '\r') { 103 | break; 104 | } 105 | parseState = H_KEY; 106 | key_start = i; 107 | now_read_line_begin = i; 108 | break; 109 | } 110 | case H_KEY: { 111 | if (str[i] == ':') { 112 | key_end = i; 113 | if (key_end - key_start <= 0) { 114 | return PARSE_HEADER_ERROR; 115 | } 116 | parseState = H_COLON; 117 | } else if (str[i] == '\n' || str[i] == '\r') { 118 | return PARSE_HEADER_ERROR; 119 | } 120 | break; 121 | } 122 | case H_COLON: { 123 | if (str[i] == ' ') { 124 | parseState = H_SPACES_AFTER_COLON; 125 | } else { 126 | return PARSE_HEADER_ERROR; 127 | } 128 | break; 129 | } 130 | case H_SPACES_AFTER_COLON: { 131 | parseState = H_VALUE; 132 | value_start = i; 133 | break; 134 | } 135 | case H_VALUE: { 136 | if (str[i] == '\r') { 137 | parseState = H_CR; 138 | value_end = i; 139 | if (value_end - value_start <= 0) { 140 | return PARSE_HEADER_ERROR; 141 | } 142 | } else if (i - value_start > 255) { 143 | return PARSE_HEADER_ERROR; 144 | } 145 | break; 146 | } 147 | case H_CR: { 148 | if (str[i] == '\n') { 149 | parseState = H_LF; 150 | std::string key(str.begin() + key_start, str.begin() + key_end); 151 | std::string value(str.begin() + value_start, str.begin() + value_end); 152 | headers[key] = value; 153 | now_read_line_begin = i; 154 | } else { 155 | return PARSE_HEADER_ERROR; 156 | } 157 | break; 158 | } 159 | case H_LF: { 160 | if (str[i] == '\r') { 161 | parseState = H_END_CR; 162 | } else { 163 | key_start = i; 164 | parseState = H_KEY; 165 | } 166 | break; 167 | } 168 | case H_END_CR: { 169 | if (str[i] == '\n') { 170 | parseState = H_END_LF; 171 | } else { 172 | return PARSE_HEADER_ERROR; 173 | } 174 | break; 175 | } 176 | case H_END_LF: { 177 | notFinish = false; 178 | key_start = i; 179 | now_read_line_begin = i; 180 | break; 181 | } 182 | } 183 | } 184 | if (parseState == H_END_LF) { 185 | // 解决x-www-form-urlencoded方式中截取错误问题 186 | if (method == METHOD_POST) { 187 | str = str.substr(now_read_line_begin); 188 | } else { 189 | str = str.substr(i); 190 | } 191 | return PARSE_HEADER_SUCC; 192 | } 193 | str = str.substr(now_read_line_begin); 194 | return PARSE_HEADER_AGAIN; 195 | } 196 | 197 | void HttpData::handleRead() { 198 | __uint32_t &events = channel->getEvents(); 199 | do { 200 | bool zero = false; 201 | int read_num = readn(fd, inBuffer, zero); 202 | std::cout << "\nRequest: \n" << inBuffer << std::endl; 203 | if (connectionState == H_DISCONNECTING) { 204 | inBuffer.clear(); 205 | break; 206 | } 207 | if (read_num < 0) { 208 | perror("handleRead: 1"); 209 | error = true; 210 | handleError(fd, 400, "Bad Request"); 211 | break; 212 | } else if (zero) { 213 | connectionState = H_DISCONNECTING; 214 | if (read_num == 0) { 215 | break; 216 | } 217 | } 218 | 219 | if (httpProcessState == PARSE_URI) { 220 | URIState flag = this->parseURI(); 221 | if (flag == PARSE_URI_AGAIN) { 222 | break; 223 | } else if (flag == PARSE_URI_ERROR) { 224 | perror("handleRead: 2"); 225 | inBuffer.clear(); 226 | error = true; 227 | handleError(fd, 400, "Bad Request"); 228 | break; 229 | } else { 230 | httpProcessState = PARSE_HEADERS; 231 | } 232 | } 233 | 234 | if (httpProcessState == PARSE_HEADERS) { 235 | HeaderState flag = this->parseHeaders(); 236 | if (flag == PARSE_HEADER_AGAIN) { 237 | break; 238 | } else if (flag == PARSE_HEADER_ERROR) { 239 | perror("handleRead: 3"); 240 | error = true; 241 | handleError(fd, 400, "Bad Request"); 242 | break; 243 | } 244 | if (method == METHOD_POST) { 245 | httpProcessState = RECV_BODY; 246 | } else { 247 | httpProcessState = ANALYSIS; 248 | } 249 | // 处理root目录 反向代理URL 250 | if (headers.find("Host") != headers.end()) { 251 | root = config->getHostRoot(headers["Host"].c_str()); 252 | passProxy = config->getPassProxy(headers["Host"].c_str()); 253 | } else { 254 | root = config->getHostRoot("default"); 255 | passProxy = config->getPassProxy("default"); 256 | } 257 | } 258 | 259 | if (httpProcessState == RECV_BODY) { 260 | int content_length = -1; 261 | if (headers.find("Content-Length") != headers.end()) { 262 | content_length = stoi(headers["Content-Length"]); 263 | } else { 264 | error = true; 265 | handleError(fd, 400, "Bad Request: lost of Content-length"); 266 | break; 267 | } 268 | if (static_cast(inBuffer.size()) < content_length) { 269 | break; 270 | } 271 | httpProcessState = ANALYSIS; 272 | } 273 | 274 | if (httpProcessState == ANALYSIS) { 275 | AnalysisState flag = this->analysisRequest(); 276 | if (flag == ANALYSIS_SUCC) { 277 | httpProcessState = FINISH; 278 | break; 279 | } else { 280 | error = true; 281 | break; 282 | } 283 | } 284 | } while (false); 285 | 286 | if (!error) { 287 | if (outBuffer.size() > 0) { 288 | handleWrite(); 289 | } 290 | if (!error && httpProcessState == FINISH) { 291 | this->reset(); 292 | if (inBuffer.size() > 0) { 293 | if (connectionState != H_DISCONNECTING) { 294 | handleRead(); 295 | } 296 | } 297 | } else if (!error && connectionState != H_DISCONNECTED) { 298 | events |= EPOLLIN; 299 | } 300 | } 301 | } 302 | 303 | void HttpData::handleWrite() { 304 | if (!error && connectionState != H_DISCONNECTED) { 305 | __uint32_t &events = channel->getEvents(); 306 | if (writen(fd, outBuffer) < 0) { 307 | perror("handleWrite: writen"); 308 | // todo 日志 309 | events = 0; 310 | error = true; 311 | } 312 | if (!outBuffer.empty()) { 313 | events |= EPOLLOUT; 314 | } 315 | } 316 | } 317 | 318 | 319 | void HttpData::handleConn() { 320 | seperateTimer(); 321 | __uint32_t &events = channel->getEvents(); 322 | if (!error && connectionState == H_CONNECTED) { 323 | if (events != 0) { 324 | int timeout = DEFAULT_EXPIRED_TIME; 325 | if (keepAlive) { 326 | timeout = DEFAULT_KEEP_ALIVE_TIME; 327 | } 328 | if ((events & EPOLLIN) && (events & EPOLLOUT)) { 329 | events = __uint32_t(0); 330 | events |= EPOLLOUT; 331 | } 332 | events |= EPOLLET; 333 | loop->updatePoller(channel, timeout); 334 | } else if (keepAlive) { 335 | events |= (EPOLLIN | EPOLLET); 336 | int timeout = DEFAULT_KEEP_ALIVE_TIME; 337 | loop->updatePoller(channel, timeout); 338 | } else { 339 | events |= (EPOLLIN | EPOLLET); 340 | int timeout = (DEFAULT_KEEP_ALIVE_TIME >> 1); 341 | loop->updatePoller(channel, timeout); 342 | } 343 | } else if (!error && connectionState == H_DISCONNECTING && (events & EPOLLOUT)) { 344 | events = (EPOLLOUT | EPOLLET); 345 | } else { 346 | loop->runInLoop(std::bind(&HttpData::handleClose, shared_from_this())); 347 | } 348 | } 349 | 350 | void HttpData::handleError(int fd, int code, std::string msg) { 351 | std::string header_buff, body_buff; 352 | char output[4 * 1024]; 353 | // body 354 | body_buff += "Error"; 355 | body_buff += ""; 356 | body_buff += std::to_string(code) + " : " + msg + "\n"; 357 | body_buff += "

" + msg + "

"; 358 | body_buff += "
WebServer"; 359 | 360 | // header 361 | header_buff += "HTTP/1.1 " + std::to_string(code) + " " + msg + "\r\n"; 362 | header_buff += "Server: WebServer\r\n"; 363 | header_buff += "Content-type: text/html\r\n"; 364 | header_buff += "Connection: close\r\n"; 365 | header_buff += "Content-length: " + std::to_string(body_buff.size()) + "\r\n\r\n"; 366 | 367 | sprintf(output, "%s", header_buff.c_str()); 368 | writen(fd, output, strlen(output)); 369 | sprintf(output, "%s", body_buff.c_str()); 370 | writen(fd, output, strlen(output)); 371 | } 372 | 373 | URIState HttpData::parseURI() { 374 | std::string &str = inBuffer; 375 | std::string cop = str; 376 | size_t pos = str.find('\r', nowReadPos); 377 | 378 | if (pos < 0) { 379 | return PARSE_URI_AGAIN; 380 | } 381 | std::string request_line = str.substr(0, pos); 382 | if (str.size() > pos + 1) { 383 | str = str.substr(pos + 1); 384 | } else { 385 | str.clear(); 386 | } 387 | int posGet = request_line.find("GET"); 388 | int posPost = request_line.find("POST"); 389 | 390 | if (posGet >= 0) { 391 | pos = posGet; 392 | method = METHOD_GET; 393 | } else if (posPost >= 0) { 394 | pos = posPost; 395 | method = METHOD_POST; 396 | } else { 397 | return PARSE_URI_ERROR; 398 | } 399 | 400 | pos = request_line.find("/", pos); 401 | if (pos < 0) { 402 | fileName = "index.html"; 403 | version = HTTP1_1; 404 | return PARSE_URI_SUCC; 405 | } else { 406 | size_t pos_ = request_line.find(' ', pos); 407 | if (pos_ < 0) { 408 | return PARSE_URI_ERROR; 409 | } else { 410 | if (pos_ - pos > 1) { 411 | fileName = request_line.substr(pos + 1, pos_ - pos - 1); 412 | size_t pos__ = fileName.find("?"); 413 | if (pos__ != std::string::npos) { 414 | query = fileName.substr(pos__ + 1, pos_); 415 | fileName = fileName.substr(0, pos__); 416 | } 417 | } else { 418 | fileName = "index.html"; 419 | } 420 | } 421 | pos = pos_; 422 | } 423 | // 是否需要php-fpm处理 424 | auto idx = fileName.find_last_of("."); 425 | if (idx != std::string::npos) { 426 | isPHP = fileName.substr(idx) == ".php"; 427 | } 428 | 429 | pos = request_line.find("/", pos); 430 | if (pos < 0) { 431 | return PARSE_URI_ERROR; 432 | } else { 433 | if (request_line.size() - pos <= 3) { 434 | return PARSE_URI_ERROR; 435 | } else { 436 | std::string version_ = request_line.substr(pos + 1, 3); 437 | if (version_ == "1.0") { 438 | version = HTTP1_0; 439 | } else if (version_ == "1.1") { 440 | version = HTTP1_1; 441 | } else { 442 | return PARSE_URI_ERROR; 443 | } 444 | } 445 | } 446 | return PARSE_URI_SUCC; 447 | } 448 | 449 | AnalysisState HttpData::analysisRequest() { 450 | if (!passProxy.empty()) { 451 | // 处理反向代理 分离IP、Port、host 452 | int port; 453 | std::string host; 454 | std::string ip; 455 | regexUrl(passProxy, host, port, ip); 456 | // 反向代理 457 | int proxyFd = proxySocket(ip, port); 458 | std::string header_buff; 459 | header_buff += 460 | (method == METHOD_GET ? "GET /" : "POST /") + fileName + 461 | (version == HTTP1_1 ? " HTTP/1.1" : " HTTP/1.0") + 462 | "\r\n"; 463 | header_buff += "Host: " + host + "\r\n"; 464 | header_buff += "User-Agent: " + headers["User-Agent"] + "\r\n"; 465 | header_buff += "Content-Length: " + headers["Content-Length"] + "\r\n"; 466 | header_buff += "Content-Type: " + headers["Content-Type"] + "\r\n"; 467 | header_buff += "\r\n"; 468 | sendn(proxyFd, header_buff); 469 | recvn(proxyFd, outBuffer); 470 | return ANALYSIS_SUCC; 471 | } 472 | // 判断文件是否存在 473 | fileName = root + fileName; 474 | struct stat sbuf; 475 | if (stat(fileName.c_str(), &sbuf) < 0) { 476 | handleError(fd, 404, "NOT FOUND!"); 477 | return ANALYSIS_ERR; 478 | } 479 | std::string header, body, outheader, outbody, filetype; 480 | // 反向代理 or 负载均衡 481 | // php-fpm动态处理 or 静态资源文件 482 | if (method == METHOD_POST) { 483 | fastCgi->setRequestId(1); 484 | fastCgi->connectFpm(); 485 | fastCgi->sendStartRequestRecord(); 486 | fastCgi->sendParams(const_cast("SCRIPT_FILENAME"), const_cast(fileName.c_str())); 487 | fastCgi->sendParams(const_cast("REQUEST_METHOD"), const_cast("POST")); 488 | fastCgi->sendParams(const_cast("CONTENT_LENGTH"), 489 | const_cast(headers["Content-Length"].c_str())); 490 | 491 | fastCgi->sendParams(const_cast("CONTENT_TYPE"), 492 | const_cast(headers["Content-Type"].c_str())); 493 | if (!query.empty()) { 494 | fastCgi->sendParams(const_cast("QUERY_STRING"), const_cast(query.c_str())); 495 | } 496 | if (headers.find("Cookie") != headers.end()) { 497 | fastCgi->sendParams(const_cast("HTTP_COOKIE"), const_cast(headers["Cookie"].c_str())); 498 | } 499 | fastCgi->sendEndRequestRecord(); 500 | fastCgi->sendPostStdinRecord(const_cast(inBuffer.c_str()), stoi(headers["Content-Length"])); 501 | fastCgi->sendEndPostStdinRecord(); 502 | fastCgi->recvRecord(outheader, outbody); 503 | header += "HTTP/1.1 200 OK\r\n"; 504 | if (headers.find("Connection") != headers.end() && 505 | (headers["Connection"] == "Keep-Alive" || headers["Connection"] == "keep-alive")) { 506 | keepAlive = true; 507 | header += std::string("Connection: Keep-Alive\r\n"); 508 | header += "Keep-Alive: timeout=" + std::to_string(DEFAULT_KEEP_ALIVE_TIME) + "\r\n"; 509 | } 510 | header += outheader; 511 | header += "Content-length: " + std::to_string(outbody.size()) + "\r\n"; 512 | header += "Server: WebServer\r\n"; 513 | header += "\r\n"; 514 | outBuffer += header; 515 | outBuffer += outbody; 516 | outbody.clear(); 517 | outheader.clear(); 518 | return ANALYSIS_SUCC; 519 | } else if (method == METHOD_GET) { 520 | if (isPHP) { 521 | fastCgi->setRequestId(1); 522 | fastCgi->connectFpm(); 523 | fastCgi->sendStartRequestRecord(); 524 | // params 525 | fastCgi->sendParams(const_cast("SCRIPT_FILENAME"), const_cast(fileName.c_str())); 526 | fastCgi->sendParams(const_cast("REQUEST_METHOD"), const_cast("GET")); 527 | fastCgi->sendParams(const_cast("CONTENT_LENGTH"), const_cast("0")); 528 | fastCgi->sendParams(const_cast("CONTENT_TYPE"), 529 | const_cast("text/html")); 530 | if (!query.empty()) { 531 | fastCgi->sendParams(const_cast("QUERY_STRING"), const_cast(query.c_str())); 532 | } 533 | if (headers.find("Cookie") != headers.end()) { 534 | fastCgi->sendParams(const_cast("HTTP_COOKIE"), const_cast(headers["Cookie"].c_str())); 535 | } 536 | fastCgi->sendEndRequestRecord(); 537 | fastCgi->recvRecord(outheader, outbody); 538 | } else { 539 | size_t dot_pos = fileName.find_last_of("."); 540 | if (dot_pos == std::string::npos) { 541 | filetype = "text/html"; 542 | } else { 543 | std::string suffix = fileName.substr(dot_pos); 544 | auto it = MimeType.find(suffix); 545 | if (it == MimeType.cend()) { 546 | filetype = "text/html"; 547 | } else { 548 | filetype = it->second; 549 | } 550 | } 551 | 552 | int src_fd = open(fileName.c_str(), O_RDONLY, 0); 553 | if (src_fd < 0) { 554 | outBuffer.clear(); 555 | handleError(fd, 404, "NOT FOUND!"); 556 | return ANALYSIS_ERR; 557 | } 558 | void *mmapRet = mmap(nullptr, sbuf.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0); 559 | close(src_fd); 560 | if (mmapRet == (void *) (-1)) { 561 | munmap(mmapRet, sbuf.st_size); 562 | outBuffer.clear(); 563 | handleError(fd, 404, "NOT FOUND!"); 564 | return ANALYSIS_ERR; 565 | } 566 | char *src_addr = static_cast(mmapRet); 567 | body += std::string(src_addr, src_addr + sbuf.st_size); 568 | munmap(mmapRet, sbuf.st_size); 569 | } 570 | 571 | header += "HTTP/1.1 200 OK\r\n"; 572 | header += outheader; 573 | body += outbody; 574 | if (headers.find("Connection") != headers.end() && 575 | (headers["Connection"] == "Keep-Alive" || headers["Connection"] == "keep-alive")) { 576 | keepAlive = true; 577 | header += std::string("Connection: Keep-Alive\r\n"); 578 | header += "Keep-Alive: timeout=" + std::to_string(DEFAULT_KEEP_ALIVE_TIME) + "\r\n"; 579 | } 580 | header += "Content-type: " + filetype + "\r\n"; 581 | header += "Content-length: " + std::to_string(body.size()) + "\r\n"; 582 | header += "Server: WebServer\r\n"; 583 | header += "\r\n"; 584 | outBuffer += header; 585 | outBuffer += body; 586 | outbody.clear(); 587 | outheader.clear(); 588 | return ANALYSIS_SUCC; 589 | } 590 | return ANALYSIS_ERR; 591 | } 592 | 593 | void HttpData::handleClose() { 594 | connectionState = H_DISCONNECTED; 595 | std::shared_ptr guard(shared_from_this()); 596 | loop->removeFromPoller(channel); 597 | } 598 | 599 | void HttpData::newEvent() { 600 | channel->setEvents(DEFALT_EVENT); 601 | loop->addPoller(channel, DEFAULT_EXPIRED_TIME); 602 | } -------------------------------------------------------------------------------- /src/HttpData.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/15. 3 | // 4 | 5 | #ifndef WEBNGINX_HTTPDATA_H 6 | #define WEBNGINX_HTTPDATA_H 7 | 8 | #include 9 | #include "Timer.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "FastCgi.h" 18 | 19 | class Channel; 20 | 21 | class TimerNode; 22 | 23 | class EventLoop; 24 | 25 | enum HttpVersion { 26 | HTTP1_0, 27 | HTTP1_1 28 | }; 29 | 30 | enum HttpMethod { 31 | METHOD_GET, 32 | METHOD_POST 33 | }; 34 | 35 | enum HttpProcessState { 36 | PARSE_URI, 37 | PARSE_HEADERS, 38 | RECV_BODY, 39 | ANALYSIS, 40 | FINISH 41 | }; 42 | 43 | enum URIState { 44 | PARSE_URI_AGAIN, 45 | PARSE_URI_ERROR, 46 | PARSE_URI_SUCC 47 | }; 48 | 49 | enum HeaderState { 50 | PARSE_HEADER_SUCC, 51 | PARSE_HEADER_AGAIN, 52 | PARSE_HEADER_ERROR 53 | }; 54 | 55 | enum ParseState { 56 | H_START, 57 | H_KEY, 58 | H_COLON, 59 | H_SPACES_AFTER_COLON, 60 | H_VALUE, 61 | H_CR, 62 | H_LF, 63 | H_END_CR, 64 | H_END_LF 65 | }; 66 | 67 | enum ConnectionState { 68 | H_CONNECTED, 69 | H_DISCONNECTING, 70 | H_DISCONNECTED 71 | }; 72 | 73 | enum AnalysisState { 74 | ANALYSIS_SUCC, 75 | ANALYSIS_ERR 76 | }; 77 | 78 | class HttpData : public std::enable_shared_from_this { 79 | public: 80 | HttpData(EventLoop *loop, int connfd); 81 | 82 | ~HttpData(); 83 | 84 | void reset(); 85 | 86 | void seperateTimer(); 87 | 88 | void linkTimer(std::shared_ptr mtimer) { 89 | timer = mtimer; 90 | } 91 | 92 | std::shared_ptr getChannel() { 93 | return channel; 94 | } 95 | 96 | EventLoop *getLoop() { 97 | return loop; 98 | } 99 | 100 | void handleClose(); 101 | 102 | void newEvent(); 103 | 104 | private: 105 | EventLoop *loop; 106 | std::shared_ptr fastCgi; 107 | int fd; 108 | std::shared_ptr channel; 109 | HttpMethod method; 110 | HttpVersion version; 111 | ConnectionState connectionState; 112 | HttpProcessState httpProcessState; 113 | ParseState parseState; 114 | int nowReadPos; 115 | bool keepAlive; 116 | std::string inBuffer; 117 | std::string outBuffer; 118 | std::string fileName; 119 | std::string query; 120 | bool isPHP; 121 | bool error; 122 | std::string root; 123 | std::string passProxy; 124 | const static std::unordered_map MimeType; 125 | std::map headers; 126 | std::weak_ptr timer; 127 | 128 | void handleRead(); 129 | 130 | void handleWrite(); 131 | 132 | void handleError(int fd, int code, std::string msg); 133 | 134 | void handleConn(); 135 | 136 | URIState parseURI(); 137 | 138 | HeaderState parseHeaders(); 139 | 140 | AnalysisState analysisRequest(); 141 | 142 | }; 143 | 144 | #endif //WEBNGINX_HTTPDATA_H 145 | -------------------------------------------------------------------------------- /src/Timer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/18. 3 | // 4 | 5 | #include "Timer.h" 6 | #include 7 | #include 8 | #include "HttpData.h" 9 | 10 | TimerNode::TimerNode(std::shared_ptr requestData, int timeout) : 11 | deleted(false), 12 | shareHttpData(requestData) { 13 | struct timeval now; 14 | gettimeofday(&now, nullptr); 15 | expiredTime = (((now.tv_sec % 10000) * 1000) + (now.tv_usec / 1000)) + timeout; 16 | } 17 | 18 | TimerNode::TimerNode(TimerNode &timerNode) : 19 | shareHttpData(timerNode.shareHttpData) { 20 | 21 | } 22 | 23 | TimerNode::~TimerNode() { 24 | if (shareHttpData) { 25 | shareHttpData->handleClose(); 26 | } 27 | } 28 | 29 | void TimerNode::update(int timeout) { 30 | struct timeval now; 31 | gettimeofday(&now, nullptr); 32 | expiredTime = (((now.tv_sec % 10000) * 1000) + (now.tv_usec / 1000)) + timeout; 33 | } 34 | 35 | bool TimerNode::isValid() { 36 | struct timeval now; 37 | gettimeofday(&now, nullptr); 38 | size_t diff = (((now.tv_sec % 10000) * 1000) + (now.tv_usec / 1000));; 39 | if (diff < expiredTime) { 40 | return true; 41 | } else { 42 | this->setDeleted(); 43 | return false; 44 | } 45 | } 46 | 47 | void TimerNode::clearReq() { 48 | shareHttpData.reset(); 49 | this->setDeleted(); 50 | } 51 | 52 | TimerManager::TimerManager() = default; 53 | 54 | TimerManager::~TimerManager() = default; 55 | 56 | void TimerManager::addTimer(std::shared_ptr shareHttpData, int timeout) { 57 | shareTimerNode new_node(new TimerNode(shareHttpData, timeout)); 58 | timerNodeQueue.push(new_node); 59 | shareHttpData->linkTimer(new_node); 60 | } 61 | 62 | void TimerManager::handleExpiredEvent() { 63 | while (!timerNodeQueue.empty()) { 64 | shareTimerNode ptimer_now = timerNodeQueue.top(); 65 | if (ptimer_now->isDeleted()) { 66 | timerNodeQueue.pop(); 67 | } else if (!ptimer_now->isValid()) { 68 | timerNodeQueue.pop(); 69 | } else { 70 | break; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/Timer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/18. 3 | // 4 | 5 | #ifndef WEBNGINX_TIMER_H 6 | #define WEBNGINX_TIMER_H 7 | 8 | #include "HttpData.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class HttpData; 15 | 16 | class TimerNode { 17 | public: 18 | TimerNode(std::shared_ptr requestData, int timeout); 19 | 20 | TimerNode(TimerNode &timerNode); 21 | 22 | ~TimerNode(); 23 | 24 | void update(int timeout); 25 | 26 | bool isValid(); 27 | 28 | void clearReq(); 29 | 30 | void setDeleted() { 31 | deleted = true; 32 | } 33 | 34 | bool isDeleted() const { 35 | return deleted; 36 | } 37 | 38 | size_t getExpTime() const { 39 | return expiredTime; 40 | } 41 | 42 | private: 43 | bool deleted; 44 | size_t expiredTime; 45 | std::shared_ptr shareHttpData; 46 | }; 47 | 48 | struct TimerCmp { 49 | bool operator()(std::shared_ptr &first, std::shared_ptr &second) const { 50 | return first->getExpTime() > second->getExpTime(); 51 | } 52 | }; 53 | 54 | class TimerManager { 55 | public: 56 | TimerManager(); 57 | 58 | ~TimerManager(); 59 | 60 | void addTimer(std::shared_ptr shareHttpData, int timeout); 61 | 62 | void handleExpiredEvent(); 63 | 64 | private: 65 | typedef std::shared_ptr shareTimerNode; 66 | std::priority_queue, TimerCmp> timerNodeQueue; 67 | }; 68 | 69 | #endif //WEBNGINX_TIMER_H 70 | -------------------------------------------------------------------------------- /src/Util.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/17. 3 | // 4 | 5 | #include "Util.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define MAX_BUFF 4096 18 | 19 | ssize_t readn(int fd, void *buff, size_t n) { 20 | ssize_t nleft = n; 21 | ssize_t nread = 0; 22 | ssize_t readSum = 0; 23 | 24 | char *ptr = (char *) buff; 25 | while (nleft > 0) { 26 | if ((nread = read(fd, ptr, nleft)) < 0) { 27 | if (errno == EINTR) { 28 | nread = 0; 29 | } else if (errno == EAGAIN) { 30 | return readSum; 31 | } else { 32 | perror("read error"); 33 | return -1; 34 | } 35 | } else if (nread == 0) { 36 | break; 37 | } 38 | readSum += nread; 39 | nleft -= nread; 40 | ptr += nread; 41 | } 42 | return readSum; 43 | } 44 | 45 | ssize_t readn(int fd, std::string &inBuffer, bool &zero) { 46 | ssize_t nread = 0; 47 | ssize_t readSum = 0; 48 | while (true) { 49 | char buff[MAX_BUFF]; 50 | if ((nread = read(fd, buff, MAX_BUFF)) < 0) { 51 | if (errno == EINTR) { 52 | continue; 53 | } else if (errno == EAGAIN) { 54 | return readSum; 55 | } else { 56 | perror("read error"); 57 | return -1; 58 | } 59 | } else if (nread == 0) { 60 | zero = true; 61 | break; 62 | } 63 | readSum += nread; 64 | inBuffer += std::string(buff, buff + nread); 65 | } 66 | return readSum; 67 | } 68 | 69 | ssize_t readn(int fd, std::string &inBuffer) { 70 | ssize_t nread = 0; 71 | ssize_t readSum = 0; 72 | while (true) { 73 | char buff[MAX_BUFF]; 74 | if ((nread = read(fd, buff, MAX_BUFF)) < 0) { 75 | if (errno == EINTR) { 76 | nread = 0; 77 | } else if (errno == EAGAIN) { 78 | return readSum; 79 | } else { 80 | perror("read error"); 81 | return -1; 82 | } 83 | } else if (nread == 0) { 84 | break; 85 | } 86 | readSum += nread; 87 | inBuffer += std::string(buff, buff + nread); 88 | } 89 | return readSum; 90 | } 91 | 92 | ssize_t writen(int fd, void *buff, size_t n) { 93 | ssize_t nleft = n; 94 | ssize_t nwritten = 0; 95 | ssize_t writeSum = 0; 96 | char *ptr = (char *) buff; 97 | while (nleft > 0) { 98 | if ((nwritten = write(fd, ptr, nleft)) <= 0) { 99 | if (nwritten < 0) { 100 | if (errno == EINTR) { 101 | nwritten = 0; 102 | } else if (errno == EAGAIN) { 103 | return writeSum; 104 | } else { 105 | return -1; 106 | } 107 | } 108 | } 109 | writeSum += nwritten; 110 | nleft -= nwritten; 111 | ptr += nwritten; 112 | } 113 | return writeSum; 114 | } 115 | 116 | size_t writen(int fd, std::string &sbuff) { 117 | ssize_t nleft = sbuff.size(); 118 | ssize_t nwritten = 0; 119 | ssize_t writeSum = 0; 120 | 121 | const char *ptr = sbuff.c_str(); 122 | while (nleft > 0) { 123 | if ((nwritten = write(fd, ptr, nleft)) <= 0) { 124 | if (nwritten < 0) { 125 | if (errno == EINTR) { 126 | nwritten = 0; 127 | } else if (errno == EAGAIN) { 128 | break; 129 | } else { 130 | return -1; 131 | } 132 | } 133 | } 134 | writeSum += nwritten; 135 | nleft -= nwritten; 136 | ptr += nwritten; 137 | } 138 | if (writeSum == static_cast(sbuff.size())) { 139 | sbuff.clear(); 140 | } else { 141 | sbuff = sbuff.substr(writeSum); 142 | } 143 | return writeSum; 144 | } 145 | 146 | void shutDownWR(int fd) { 147 | shutdown(fd, SHUT_WR); 148 | } 149 | 150 | int proxySocket(std::string addr, int port) { 151 | struct sockaddr_in proxyaddr; 152 | 153 | int proxyFd = socket(AF_INET, SOCK_STREAM, 0); 154 | 155 | memset(&proxyaddr, 0, sizeof(proxyaddr)); 156 | proxyaddr.sin_family = AF_INET; 157 | proxyaddr.sin_port = htons(port); 158 | inet_aton(addr.c_str(), &proxyaddr.sin_addr); 159 | 160 | if (connect(proxyFd, (struct sockaddr *) &proxyaddr, sizeof(proxyaddr)) == -1) { 161 | perror("proxy socket"); 162 | abort(); 163 | } 164 | return proxyFd; 165 | } 166 | 167 | size_t sendn(int fd, std::string &inBuffer) { 168 | ssize_t nleft = inBuffer.size(); 169 | ssize_t nwritten = 0; 170 | ssize_t writeSum = 0; 171 | const char *ptr = inBuffer.c_str(); 172 | while (nleft > 0) { 173 | if ((nwritten = send(fd, ptr, MAX_BUFF, 0)) <= 0) { 174 | if (nwritten < 0) { 175 | if (errno == EINTR) { 176 | nwritten = 0; 177 | } else if (errno == EAGAIN) { 178 | break; 179 | } else { 180 | return -1; 181 | } 182 | } 183 | } 184 | writeSum += nwritten; 185 | nleft -= nwritten; 186 | ptr += nwritten; 187 | } 188 | return writeSum; 189 | } 190 | 191 | size_t recvn(int fd, std::string &outBuffer) { 192 | ssize_t nread = 0; 193 | ssize_t readSum = 0; 194 | while (true) { 195 | char buff[MAX_BUFF]; 196 | if ((nread = recv(fd, buff, MAX_BUFF, 0)) < 0) { 197 | if (errno == EINTR) { 198 | continue; 199 | } else if (errno == EAGAIN) { 200 | return readSum; 201 | } else { 202 | perror("read error"); 203 | return -1; 204 | } 205 | } else if (nread == 0) { 206 | break; 207 | } 208 | readSum += nread; 209 | outBuffer += std::string(buff, buff + nread); 210 | } 211 | return readSum; 212 | } 213 | 214 | void regexUrl(std::string url, std::string &host, int &port, std::string &ip) { 215 | std::regex exUrl("(\\w+:\\/\\/)([^/:]+)(:\\d*)?([/s/S]*)"); 216 | std::regex exIp("((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}"); 217 | std::cmatch m; 218 | if (std::regex_match(url.c_str(), m, exUrl)) { 219 | host = std::string(m[2].first, m[2].second); 220 | port = std::string(m[3].first, m[3].second).empty() ? 80 : std::stoi(std::string(m[3].first+1, m[3].second)); 221 | if (std::regex_match(host.c_str(), exIp)) { 222 | // 默认为点分十进制数IP地址格式 223 | ip = host; 224 | } else { 225 | // 转换成点分十进制数IP地址 226 | struct hostent *hostent = gethostbyname(host.c_str()); 227 | ip = inet_ntoa(*(in_addr *) *hostent->h_addr_list); 228 | } 229 | } 230 | } -------------------------------------------------------------------------------- /src/Util.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/17. 3 | // 4 | 5 | #ifndef WEBNGINX_UTIL_H 6 | #define WEBNGINX_UTIL_H 7 | 8 | #include 9 | #include 10 | 11 | 12 | ssize_t readn(int fd, void *buff, size_t n); 13 | 14 | ssize_t readn(int fd, std::string &inBuffer, bool &zero); 15 | 16 | ssize_t readn(int fd, std::string &inBuffer); 17 | 18 | ssize_t writen(int fd, void *buff, size_t n); 19 | 20 | size_t writen(int fd, std::string &sbuff); 21 | 22 | void shutDownWR(int fd); 23 | 24 | int proxySocket(std::string addr, int port); 25 | 26 | size_t sendn(int fd, std::string &inBuffer); 27 | 28 | size_t recvn(int fd, std::string &outBuffer); 29 | 30 | void regexUrl(std::string url, std::string &host, int &port, std::string &ip); 31 | 32 | #endif //WEBNGINX_UTIL_H 33 | -------------------------------------------------------------------------------- /src/WebServer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/15. 3 | // 4 | 5 | #include "WebServer.h" 6 | #include "HttpData.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | WebServer::WebServer(EventLoop *loop_, int threadNum_, int port_, int backlog_) : 16 | loop(loop_), 17 | port(port_), 18 | backlog(backlog_), 19 | threadNum(threadNum_), 20 | eventLoopThreadPool(new EventLoopThreadPool(loop_, threadNum_)), 21 | acceptChannel(new Channel(loop_)) { 22 | if (!initSocket()) { 23 | // todo 日志 24 | abort(); 25 | } 26 | acceptChannel->setFd(socketFd); 27 | } 28 | 29 | void WebServer::run() { 30 | eventLoopThreadPool->start(); 31 | acceptChannel->setEvents(EPOLLIN | EPOLLET); 32 | acceptChannel->setReadHandler(std::bind(&WebServer::acceptConnection, this)); 33 | acceptChannel->setConnHandler(std::bind(&WebServer::handleThisConn, this)); 34 | loop->addPoller(acceptChannel, 0); 35 | } 36 | 37 | void WebServer::acceptConnection() { 38 | struct sockaddr_in client_addr; 39 | memset(&client_addr, 0, sizeof(struct sockaddr_in)); 40 | socklen_t client_addrlen = sizeof(client_addr); 41 | int connect_fd = 0; 42 | while ((connect_fd = accept(socketFd, (struct sockaddr *) &client_addr, &client_addrlen)) > 0) { 43 | EventLoop *loop_ = eventLoopThreadPool->getNextLoop(); 44 | if (setSocketNonBlock(connect_fd) < 0) { 45 | // todo 日志 46 | return; 47 | } 48 | std::shared_ptr req_info(new HttpData(loop_, connect_fd)); 49 | req_info->getChannel()->setHolder(req_info); 50 | loop_->queueInLoop(std::bind(&HttpData::newEvent, req_info)); 51 | } 52 | acceptChannel->setEvents(EPOLLIN | EPOLLET); 53 | } 54 | 55 | bool WebServer::initSocket() { 56 | struct sockaddr_in sockaddr; 57 | 58 | socketFd = socket(AF_INET, SOCK_STREAM, 0); 59 | if (socketFd == -1) { 60 | //todo 日志 61 | return false; 62 | } 63 | // 端口复用 64 | int opt = 1; 65 | setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, (const void *) &opt, sizeof(opt)); 66 | // 地址端口 67 | memset(&sockaddr, 0, sizeof(sockaddr)); 68 | sockaddr.sin_family = AF_INET; 69 | sockaddr.sin_addr.s_addr = htons(INADDR_ANY); 70 | sockaddr.sin_port = htons(port); 71 | // 设置非阻塞 72 | if (setSocketNonBlock(socketFd) == -1) { 73 | // todo 日志 74 | return false; 75 | } 76 | if (bind(socketFd, (struct sockaddr *) &sockaddr, sizeof(sockaddr)) == -1) { 77 | //todo 日志 78 | return false; 79 | } 80 | if (listen(socketFd, backlog) == -1) { 81 | // todo 日志 82 | return false; 83 | } 84 | return true; 85 | } 86 | 87 | int WebServer::setSocketNonBlock(int socketFd) { 88 | int flag = fcntl(socketFd, F_GETFL, 0); 89 | if (flag == -1) { 90 | return -1; 91 | } 92 | flag |= O_NONBLOCK; 93 | if (fcntl(socketFd, F_SETFL, flag) < 0) { 94 | return -1; 95 | } 96 | return 0; 97 | } 98 | 99 | void WebServer::handleThisConn() { 100 | loop->updatePoller(acceptChannel); 101 | } 102 | 103 | WebServer::~WebServer() = default; -------------------------------------------------------------------------------- /src/WebServer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Icharle on 2020/5/15. 3 | // 4 | 5 | #ifndef WEBNGINX_WEBSERVER_H 6 | #define WEBNGINX_WEBSERVER_H 7 | 8 | #include "EventLoop.h" 9 | #include "Channel.h" 10 | #include "EventLoopThreadPool.h" 11 | #include 12 | 13 | class WebServer { 14 | public: 15 | WebServer(EventLoop *loop_, int threadNum_, int port_, int backlog_); 16 | 17 | void run(); 18 | 19 | void acceptConnection(); 20 | 21 | void handleThisConn(); 22 | 23 | ~WebServer(); 24 | 25 | private: 26 | bool initSocket(); 27 | 28 | int setSocketNonBlock(int socketFd); 29 | 30 | private: 31 | EventLoop *loop; 32 | int port; 33 | int backlog; 34 | int threadNum; 35 | std::shared_ptr eventLoopThreadPool; 36 | std::shared_ptr acceptChannel; 37 | int socketFd; 38 | }; 39 | 40 | #endif //WEBNGINX_WEBSERVER_H 41 | --------------------------------------------------------------------------------