├── .gitignore ├── .travis.yml ├── LICENSE.md ├── Makefile ├── README.md ├── base64.cpp ├── base64.h ├── conn_status.h ├── connection.cpp ├── connection.h ├── connection_pool.cpp ├── connection_pool.h ├── event_loop.cpp ├── event_loop.h ├── event_loop_base.cpp ├── event_loop_base.h ├── event_loop_base_epoll.cpp ├── event_loop_base_kqueue.cpp ├── examples ├── chat_room.cpp ├── form_action.cpp ├── hello_world.cpp └── tls_server.cpp ├── http_client.cpp ├── http_client.h ├── http_server.cpp ├── http_server.h ├── lock_guard.cpp ├── lock_guard.h ├── mevent.h ├── request.cpp ├── request.h ├── response.cpp ├── response.h ├── ternary_search_tree.cpp ├── ternary_search_tree.h ├── util.cpp ├── util.h ├── websocket.cpp └── websocket.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | 3 | examples/host.key 4 | examples/host.crt 5 | 6 | examples/tls_server 7 | examples/hello_world 8 | examples/form_action 9 | examples/chat_room 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | os: 4 | - linux 5 | - osx 6 | 7 | compiler: 8 | - gcc 9 | - clang 10 | 11 | install: 12 | - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi 13 | 14 | addons: 15 | apt: 16 | packages: 17 | - libcurl4-gnutls-dev 18 | - libssl-dev 19 | 20 | before_install: 21 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi 22 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install openssl; fi 23 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install curl; fi 24 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew link openssl --force; fi 25 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew link curl --force; fi 26 | 27 | script: 28 | - make 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 looyao 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | ifdef DEBUG 3 | CXXFLAGS = -g -Wall -Wextra -std=c++0x -I/usr/local/opt/openssl/include -I/usr/local/opt/curl/include 4 | else 5 | CXXFLAGS = -O2 -Wall -Wextra -std=c++0x -I/usr/local/opt/openssl/include -I/usr/local/opt/curl/include 6 | endif 7 | LDFLAGS = -L/usr/local/opt/openssl/lib -L/usr/local/opt/curl/lib 8 | LIBS = -lpthread -lssl -lcrypto -lcurl 9 | 10 | OBJS = http_server.o \ 11 | event_loop.o \ 12 | util.o \ 13 | request.o \ 14 | response.o \ 15 | connection.o \ 16 | connection_pool.o \ 17 | base64.o \ 18 | ternary_search_tree.o \ 19 | websocket.o \ 20 | lock_guard.o \ 21 | http_client.o \ 22 | event_loop_base.o 23 | 24 | all : examples/chat_room \ 25 | examples/hello_world \ 26 | examples/form_action \ 27 | examples/tls_server 28 | 29 | examples/chat_room: $(OBJS) examples/chat_room.o 30 | $(CXX) $(CXXFLAGS) $^ -o $@ $(LDFLAGS) $(LIBS) 31 | examples/chat_room.o : examples/chat_room.cpp 32 | $(CXX) $(CXXFLAGS) -c $< -o $@ 33 | 34 | examples/hello_world: $(OBJS) examples/hello_world.o 35 | $(CXX) $(CXXFLAGS) $^ -o $@ $(LDFLAGS) $(LIBS) 36 | examples/hello_world.o : examples/hello_world.cpp 37 | $(CXX) $(CXXFLAGS) -c $< -o $@ 38 | 39 | examples/form_action: $(OBJS) examples/form_action.o 40 | $(CXX) $(CXXFLAGS) $^ -o $@ $(LDFLAGS) $(LIBS) 41 | examples/form_action.o : examples/form_action.cpp 42 | $(CXX) $(CXXFLAGS) -c $< -o $@ 43 | 44 | examples/tls_server: $(OBJS) examples/tls_server.o 45 | $(CXX) $(CXXFLAGS) $^ -o $@ $(LDFLAGS) $(LIBS) 46 | examples/tls_server.o : examples/tls_server.cpp 47 | $(CXX) $(CXXFLAGS) -c $< -o $@ 48 | 49 | 50 | 51 | http_server.o : http_server.cpp http_server.h 52 | $(CXX) $(CXXFLAGS) -c $< -o $@ 53 | event_loop.o : event_loop.cpp event_loop.h 54 | $(CXX) $(CXXFLAGS) -c $< -o $@ 55 | util.o : util.cpp util.h 56 | $(CXX) $(CXXFLAGS) -c $< -o $@ 57 | request.o : request.cpp request.h 58 | $(CXX) $(CXXFLAGS) -c $< -o $@ 59 | response.o : response.cpp response.h 60 | $(CXX) $(CXXFLAGS) -c $< -o $@ 61 | connection.o : connection.cpp connection.h conn_status.h 62 | $(CXX) $(CXXFLAGS) -c $< -o $@ 63 | connection_pool.o : connection_pool.cpp connection_pool.h 64 | $(CXX) $(CXXFLAGS) -c $< -o $@ 65 | base64.o : base64.cpp base64.h 66 | $(CXX) $(CXXFLAGS) -c $< -o $@ 67 | ternary_search_tree.o : ternary_search_tree.cpp ternary_search_tree.h 68 | $(CXX) $(CXXFLAGS) -c $< -o $@ 69 | websocket.o : websocket.cpp websocket.h 70 | $(CXX) $(CXXFLAGS) -c $< -o $@ 71 | lock_guard.o : lock_guard.cpp lock_guard.h 72 | $(CXX) $(CXXFLAGS) -c $< -o $@ 73 | http_client.o : http_client.cpp http_client.h 74 | $(CXX) $(CXXFLAGS) -c $< -o $@ 75 | event_loop_base.o : event_loop_base.cpp event_loop_base.h 76 | $(CXX) $(CXXFLAGS) -c $< -o $@ 77 | 78 | 79 | .PHONY : clean 80 | clean : 81 | rm -f *.o 82 | rm -f examples/*.o 83 | rm -f examples/chat_room 84 | rm -f examples/form_action 85 | rm -f examples/hello_world 86 | rm -f examples/tls_server 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mevent 2 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/looyao/mevent/master/LICENSE.md) 3 | [![Build Status](https://travis-ci.org/looyao/mevent.svg?branch=master)](https://travis-ci.org/looyao/mevent) 4 | 5 | Mevent is a tiny HTTP/WebSocket server library, can be used on Linux and OSX. 6 | 7 | ## Features 8 | 9 | - TLS (https/wss) support 10 | - `ping`/`pong` support 11 | - Asynchronous and non-blocking, I/O Multiplexing using epoll and kqueue 12 | - Supports Linux, OSX 13 | - Thread-safe 14 | 15 | ## Integration 16 | 17 | You should have libssl, libcurl installed into your system, and your compiler should support C++11. 18 | 19 | #### Debian and Ubuntu users 20 | ``` 21 | apt-get install libssl-dev libcurl4-gnutls-dev 22 | ``` 23 | 24 | #### Fedora and RedHat users 25 | ``` 26 | yum install openssl-devel libcurl-devel 27 | ``` 28 | 29 | #### OSX users 30 | ``` 31 | brew update 32 | brew install openssl curl 33 | ``` 34 | 35 | #### Example 36 | 37 | ```cpp 38 | #include "mevent/http_server.h" 39 | 40 | using namespace mevent; 41 | 42 | class HelloWorld { 43 | public: 44 | void Index(Connection *conn) { 45 | conn->Resp()->SetHeader("Content-Type", "text/html"); 46 | conn->Resp()->WriteString("hello world!"); 47 | } 48 | }; 49 | 50 | int main() { 51 | HelloWorld hello; 52 | 53 | HTTPServer *server = new HTTPServer(); 54 | server->SetHandler("/", std::bind(&::HelloWorld::Index, &hello, std::placeholders::_1)); 55 | 56 | server->SetWorkerThreads(4); 57 | server->SetIdleTimeout(60); 58 | server->SetMaxWorkerConnections(8192); 59 | 60 | server->ListenAndServe("0.0.0.0", 80); 61 | 62 | return 0; 63 | } 64 | ``` 65 | 66 | More examples can be found in examples directory. 67 | 68 | ## Author 69 | 70 | Luyao Teng 71 | -------------------------------------------------------------------------------- /base64.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | base64.cpp and base64.h 3 | 4 | Copyright (C) 2004-2008 René Nyffenegger 5 | 6 | This source code is provided 'as-is', without any express or implied 7 | warranty. In no event will the author be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this source code must not be misrepresented; you must not 15 | claim that you wrote the original source code. If you use this source code 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original source code. 21 | 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | René Nyffenegger rene.nyffenegger@adp-gmbh.ch 25 | 26 | */ 27 | 28 | #include "base64.h" 29 | 30 | namespace mevent { 31 | 32 | static const std::string base64_chars = 33 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 34 | "abcdefghijklmnopqrstuvwxyz" 35 | "0123456789+/"; 36 | 37 | 38 | static inline bool IsBase64(unsigned char c) { 39 | return (isalnum(c) || (c == '+') || (c == '/')); 40 | } 41 | 42 | std::string Base64Encode(unsigned char const* bytes_to_encode, unsigned int in_len) { 43 | std::string ret; 44 | int i = 0; 45 | int j = 0; 46 | unsigned char char_array_3[3]; 47 | unsigned char char_array_4[4]; 48 | 49 | while (in_len--) { 50 | char_array_3[i++] = *(bytes_to_encode++); 51 | if (3 == i) { 52 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 53 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 54 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 55 | char_array_4[3] = char_array_3[2] & 0x3f; 56 | 57 | for(i = 0; (i <4) ; i++) 58 | ret += base64_chars[char_array_4[i]]; 59 | i = 0; 60 | } 61 | } 62 | 63 | if (i) { 64 | for(j = i; j < 3; j++) 65 | char_array_3[j] = '\0'; 66 | 67 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 68 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 69 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 70 | char_array_4[3] = char_array_3[2] & 0x3f; 71 | 72 | for (j = 0; (j < i + 1); j++) 73 | ret += base64_chars[char_array_4[j]]; 74 | 75 | while((i++ < 3)) 76 | ret += '='; 77 | 78 | } 79 | 80 | return ret; 81 | 82 | } 83 | 84 | std::string Base64Decode(std::string const& encoded_string) { 85 | std::size_t in_len = encoded_string.size(); 86 | int i = 0; 87 | int j = 0; 88 | int in_ = 0; 89 | unsigned char char_array_4[4], char_array_3[3]; 90 | std::string ret; 91 | 92 | while (in_len-- && ( encoded_string[in_] != '=') && IsBase64(encoded_string[in_])) { 93 | char_array_4[i++] = encoded_string[in_]; in_++; 94 | if (4 == i) { 95 | for (i = 0; i < 4; i++) 96 | char_array_4[i] = base64_chars.find(char_array_4[i]); 97 | 98 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 99 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 100 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 101 | 102 | for (i = 0; (i < 3); i++) 103 | ret += char_array_3[i]; 104 | i = 0; 105 | } 106 | } 107 | 108 | if (i) { 109 | for (j = i; j < 4; j++) 110 | char_array_4[j] = 0; 111 | 112 | for (j = 0; j < 4; j++) 113 | char_array_4[j] = base64_chars.find(char_array_4[j]); 114 | 115 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 116 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 117 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 118 | 119 | for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; 120 | } 121 | 122 | return ret; 123 | } 124 | 125 | }//namespace mevent -------------------------------------------------------------------------------- /base64.h: -------------------------------------------------------------------------------- 1 | #ifndef _BASE64_H 2 | #define _BASE64_H 3 | 4 | #include 5 | 6 | namespace mevent { 7 | 8 | std::string Base64Encode(unsigned char const* , unsigned int len); 9 | std::string Base64Decode(std::string const& s); 10 | 11 | }//namespace mevent 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /conn_status.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONN_STATUS_H 2 | #define _CONN_STATUS_H 3 | 4 | #include 5 | 6 | namespace mevent { 7 | 8 | enum class ConnStatus : uint8_t { 9 | AGAIN, 10 | UPGRADE, 11 | END, 12 | ERROR, 13 | CLOSE 14 | }; 15 | 16 | }//namespace mevent 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /connection.cpp: -------------------------------------------------------------------------------- 1 | #include "connection.h" 2 | #include "util.h" 3 | #include "websocket.h" 4 | #include "event_loop.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace mevent { 10 | 11 | Connection::Connection() : req_(this), resp_(this), ws_(this) { 12 | fd_ = -1; 13 | 14 | active_time_ = 0; 15 | 16 | free_next_ = NULL; 17 | active_next_ = NULL; 18 | active_prev_ = NULL; 19 | elp_ = NULL; 20 | 21 | ev_writable_ = false; 22 | 23 | if (pthread_mutex_init(&mtx_, NULL) != 0) { 24 | MEVENT_LOG_DEBUG_EXIT(NULL); 25 | } 26 | 27 | ssl_ = NULL; 28 | } 29 | 30 | Connection::~Connection() { 31 | if (pthread_mutex_destroy(&mtx_) != 0) { 32 | MEVENT_LOG_DEBUG_EXIT(NULL); 33 | } 34 | } 35 | 36 | ConnStatus Connection::Flush() { 37 | if (fd_ < 0) { 38 | return ConnStatus::CLOSE; 39 | } 40 | 41 | while (!write_buffer_chain_.empty()) { 42 | auto wb = write_buffer_chain_.front(); 43 | 44 | ssize_t n = Writen(wb->str.c_str() + wb->write_len, wb->len - wb->write_len); 45 | if (n >= 0) { 46 | wb->write_len += n; 47 | if (wb->write_len == wb->len) { 48 | write_buffer_chain_.pop(); 49 | } else { 50 | return ConnStatus::AGAIN; 51 | } 52 | } else { 53 | return ConnStatus::ERROR; 54 | } 55 | } 56 | 57 | if (req_.status_ == RequestStatus::UPGRADE) { 58 | return ConnStatus::UPGRADE; 59 | } 60 | 61 | if (resp_.finish_) { 62 | return ConnStatus::END; 63 | } 64 | 65 | return ConnStatus::AGAIN; 66 | } 67 | 68 | ConnStatus Connection::ReadData() { 69 | if (fd_ < 0) { 70 | return ConnStatus::CLOSE; 71 | } 72 | 73 | active_time_ = time(NULL); 74 | 75 | ConnStatus status = ConnStatus::AGAIN; 76 | 77 | if (req_.status_ == RequestStatus::UPGRADE) { 78 | status = ws_.ReadData(); 79 | } else { 80 | status = req_.ReadData(); 81 | } 82 | 83 | return status; 84 | } 85 | 86 | void Connection::Keepalive() { 87 | if (fd_ < 0) { 88 | return; 89 | } 90 | 91 | req_.Keepalive(); 92 | 93 | resp_.Reset(); 94 | } 95 | 96 | void Connection::Close() { 97 | elp_->ResetConnection(this); 98 | } 99 | 100 | void Connection::Reset() { 101 | if (ssl_) { 102 | SSL_free(ssl_); 103 | ssl_ = NULL; 104 | } 105 | 106 | if (fd_ > 0) { 107 | close(fd_); 108 | fd_ = -1; 109 | } 110 | 111 | active_time_ = 0; 112 | 113 | elp_ = NULL; 114 | 115 | req_.Reset(); 116 | resp_.Reset(); 117 | ws_.Reset(); 118 | 119 | write_buffer_chain_ = {}; 120 | 121 | ev_writable_ = false; 122 | } 123 | 124 | void Connection::ShutdownSocket(int how) { 125 | if (fd_ > 0) { 126 | shutdown(fd_, how); 127 | } 128 | } 129 | 130 | void Connection::WriteString(const std::string &str) { 131 | if (str.empty()) { 132 | return; 133 | } 134 | 135 | if (fd_ < 0) { 136 | return; 137 | } 138 | 139 | std::shared_ptr wb = std::make_shared(str); 140 | write_buffer_chain_.push(wb); 141 | } 142 | 143 | void Connection::WriteData(const std::vector &data) { 144 | if (data.empty()) { 145 | return; 146 | } 147 | 148 | if (fd_ < 0) { 149 | return; 150 | } 151 | 152 | std::string str; 153 | str.insert(str.end(), data.begin(), data.end()); 154 | 155 | std::shared_ptr wb = std::make_shared(str); 156 | write_buffer_chain_.push(wb); 157 | } 158 | 159 | ssize_t Connection::Writen(const void *buf, size_t len) { 160 | ssize_t nwrite, n = 0; 161 | 162 | while (n < (ssize_t)len) { 163 | if (ssl_) { 164 | nwrite = SSL_write(ssl_, (char *)buf + n, static_cast(len)); 165 | if (nwrite <= 0) { 166 | int sslerr = SSL_get_error(ssl_, static_cast(nwrite)); 167 | switch (sslerr) { 168 | case SSL_ERROR_WANT_READ: 169 | case SSL_ERROR_WANT_WRITE: 170 | case SSL_ERROR_SSL: 171 | return n; 172 | case SSL_ERROR_SYSCALL: 173 | break; 174 | default: 175 | return -1; 176 | } 177 | } 178 | } else { 179 | nwrite = write(fd_, (char *)buf + n, len - n); 180 | } 181 | 182 | if (nwrite > 0) { 183 | n += nwrite; 184 | } else if (nwrite < 0) { 185 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 186 | return n; 187 | } else { 188 | return -1; 189 | } 190 | } else { 191 | return -1; 192 | } 193 | } 194 | 195 | return n; 196 | } 197 | 198 | ssize_t Connection::Readn(void *buf, size_t len) { 199 | ssize_t nread, n = 0; 200 | 201 | while (n < (ssize_t)len) { 202 | if (ssl_) { 203 | nread = SSL_read(ssl_, (char *)buf + n, static_cast(len)); 204 | if (nread <= 0) { 205 | int sslerr = SSL_get_error(ssl_, static_cast(nread)); 206 | switch (sslerr) { 207 | case SSL_ERROR_WANT_READ: 208 | case SSL_ERROR_WANT_WRITE: 209 | case SSL_ERROR_SSL: 210 | return n; 211 | case SSL_ERROR_SYSCALL: 212 | break; 213 | default: 214 | return -1; 215 | } 216 | } 217 | } else { 218 | nread = read(fd_, (char *)buf + n, len - n); 219 | } 220 | 221 | if (nread > 0) { 222 | n += nread; 223 | } else if (nread < 0) { 224 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 225 | return n; 226 | } else { 227 | return -1; 228 | } 229 | } else { 230 | return -1; 231 | } 232 | } 233 | 234 | return n; 235 | } 236 | 237 | void Connection::TaskPush() { 238 | elp_->TaskPush(this); 239 | } 240 | 241 | void Connection::WebSocketTaskPush(WebSocketOpcodeType opcode, const std::string &msg) { 242 | elp_->WebSocketTaskPush(&ws_, opcode, msg); 243 | } 244 | 245 | Request *Connection::Req() { 246 | return &req_; 247 | } 248 | 249 | Response *Connection::Resp() { 250 | return &resp_; 251 | } 252 | 253 | WebSocket *Connection::WS() { 254 | return &ws_; 255 | } 256 | 257 | bool Connection::CreateSSL(SSL_CTX *ssl_ctx) { 258 | ssl_ = SSL_new(ssl_ctx); 259 | if (!ssl_) { 260 | MEVENT_LOG_DEBUG("SSL_new() failed"); 261 | return false; 262 | } 263 | 264 | if (SSL_set_fd(ssl_, fd_) == 0) { 265 | MEVENT_LOG_DEBUG("SSL_set_fd() failed"); 266 | return false; 267 | } 268 | 269 | SSL_set_accept_state(ssl_); 270 | 271 | return true; 272 | } 273 | 274 | }//namespace mevent 275 | -------------------------------------------------------------------------------- /connection.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONNECTION_H 2 | #define _CONNECTION_H 3 | 4 | #include "request.h" 5 | #include "response.h" 6 | #include "websocket.h" 7 | #include "conn_status.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace mevent { 20 | 21 | class EventLoop; 22 | class ConnectionPool; 23 | 24 | struct WriteBuffer { 25 | std::string str; 26 | std::size_t len; 27 | std::size_t write_len; 28 | 29 | WriteBuffer(const std::string &s) : str(s), len(s.length()), write_len(0) {} 30 | ~WriteBuffer() {} 31 | }; 32 | 33 | class Connection { 34 | public: 35 | Connection(); 36 | virtual ~Connection(); 37 | 38 | void Close(); 39 | 40 | Request *Req(); 41 | Response *Resp(); 42 | WebSocket *WS(); 43 | 44 | private: 45 | friend class ConnectionPool; 46 | friend class EventLoop; 47 | friend class Request; 48 | friend class Response; 49 | friend class WebSocket; 50 | 51 | void WriteString(const std::string &str); 52 | void WriteData(const std::vector &data); 53 | 54 | ssize_t Writen(const void *buf, size_t len); 55 | ssize_t Readn(void *buf, size_t len); 56 | 57 | void TaskPush(); 58 | 59 | void WebSocketTaskPush(WebSocketOpcodeType opcode, const std::string &msg); 60 | 61 | ConnStatus Flush(); 62 | 63 | ConnStatus ReadData(); 64 | 65 | void Keepalive(); 66 | 67 | void Reset(); 68 | 69 | void ShutdownSocket(int how); 70 | 71 | bool CreateSSL(SSL_CTX *ssl_ctx); 72 | 73 | int fd_; 74 | 75 | pthread_mutex_t mtx_; 76 | 77 | std::queue> write_buffer_chain_; 78 | 79 | Request req_; 80 | Response resp_; 81 | WebSocket ws_; 82 | 83 | Connection *free_next_; 84 | 85 | Connection *active_next_; 86 | Connection *active_prev_; 87 | 88 | EventLoop *elp_; 89 | 90 | time_t active_time_; 91 | 92 | bool ev_writable_; 93 | 94 | SSL *ssl_; 95 | }; 96 | 97 | }//namespace mevent 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /connection_pool.cpp: -------------------------------------------------------------------------------- 1 | #include "connection_pool.h" 2 | #include "util.h" 3 | #include "lock_guard.h" 4 | 5 | namespace mevent { 6 | 7 | ConnectionPool::ConnectionPool(int max_conn) 8 | : free_list_head_(NULL), 9 | active_list_head_(NULL), 10 | active_list_tail_(NULL), 11 | active_list_lookup_cursor_(NULL), 12 | count_(0), 13 | max_conn_(max_conn) { 14 | 15 | if (pthread_mutex_init(&free_list_mtx_, NULL) != 0) { 16 | MEVENT_LOG_DEBUG_EXIT(NULL); 17 | } 18 | 19 | if (pthread_mutex_init(&active_list_mtx_, NULL) != 0) { 20 | MEVENT_LOG_DEBUG_EXIT(NULL); 21 | } 22 | } 23 | 24 | void ConnectionPool::Reserve(int n) { 25 | if (n < 1) { 26 | return; 27 | } 28 | 29 | Connection *conn = new Connection(); 30 | 31 | free_list_head_ = conn; 32 | 33 | Connection *tail = conn; 34 | count_++; 35 | for (int i = 1; i < n; i++, count_++) { 36 | conn = new Connection(); 37 | 38 | tail->free_next_ = conn; 39 | tail = conn; 40 | } 41 | } 42 | 43 | Connection *ConnectionPool::FreeListPop() { 44 | LockGuard lock_guard(free_list_mtx_); 45 | 46 | Connection *conn = NULL; 47 | 48 | if (free_list_head_) { 49 | conn = free_list_head_; 50 | free_list_head_ = free_list_head_->free_next_; 51 | } else { 52 | if (count_ > max_conn_) { 53 | return NULL; 54 | } 55 | 56 | conn = new Connection(); 57 | count_++; 58 | } 59 | 60 | return conn; 61 | } 62 | 63 | void ConnectionPool::FreeListPush(Connection *conn) { 64 | LockGuard lock_guard(free_list_mtx_); 65 | 66 | conn->free_next_ = free_list_head_; 67 | free_list_head_ = conn; 68 | } 69 | 70 | void ConnectionPool::ActiveListPush(Connection *conn) { 71 | LockGuard lock_guard(active_list_mtx_); 72 | 73 | if (NULL == active_list_tail_) { 74 | active_list_tail_ = conn; 75 | active_list_head_ = active_list_tail_; 76 | } else { 77 | active_list_tail_->active_next_ = conn; 78 | conn->active_prev_ = active_list_tail_; 79 | conn->active_next_ = NULL; 80 | active_list_tail_ = conn; 81 | } 82 | } 83 | 84 | void ConnectionPool::ActiveListUpdate(Connection *conn) { 85 | ActiveListErase(conn); 86 | ActiveListPush(conn); 87 | } 88 | 89 | bool ConnectionPool::ActiveListEmpty() { 90 | LockGuard lock_guard(active_list_mtx_); 91 | if (active_list_head_) { 92 | return false; 93 | } 94 | 95 | return true; 96 | } 97 | 98 | void ConnectionPool::ActiveListErase(Connection *conn) { 99 | LockGuard lock_guard(active_list_mtx_); 100 | 101 | if (conn == active_list_lookup_cursor_) { 102 | active_list_lookup_cursor_ = conn->active_next_; 103 | } 104 | 105 | if (active_list_tail_ == active_list_head_) { 106 | active_list_tail_ = NULL; 107 | active_list_head_ = NULL; 108 | } else { 109 | if (conn->active_prev_) { 110 | conn->active_prev_->active_next_ = conn->active_next_; 111 | } else { 112 | active_list_head_ = conn->active_next_; 113 | if (active_list_head_) { 114 | active_list_head_->active_prev_ = NULL; 115 | } 116 | } 117 | 118 | if (conn->active_next_) { 119 | conn->active_next_->active_prev_ = conn->active_prev_; 120 | } else { 121 | active_list_tail_ = conn->active_prev_; 122 | if (active_list_tail_) { 123 | active_list_tail_->active_next_ = NULL; 124 | } 125 | } 126 | } 127 | 128 | conn->active_next_ = NULL; 129 | conn->active_prev_ = NULL; 130 | } 131 | 132 | void ConnectionPool::ActiveListTimeoutCheck() { 133 | LockGuard lock_guard(active_list_mtx_); 134 | active_list_lookup_cursor_ = active_list_head_; 135 | } 136 | 137 | Connection *ConnectionPool::ActiveListCheckNext() { 138 | LockGuard lock_guard(active_list_mtx_); 139 | 140 | Connection *conn = active_list_lookup_cursor_; 141 | if (active_list_lookup_cursor_) { 142 | active_list_lookup_cursor_ = active_list_lookup_cursor_->active_next_; 143 | } 144 | 145 | return conn; 146 | } 147 | 148 | ConnectionPool::~ConnectionPool() { 149 | pthread_mutex_destroy(&free_list_mtx_); 150 | pthread_mutex_destroy(&active_list_mtx_); 151 | } 152 | 153 | }//namespace mevent 154 | -------------------------------------------------------------------------------- /connection_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONNECTION_POOL_H 2 | #define _CONNECTION_POOL_H 3 | 4 | #include "connection.h" 5 | 6 | #include 7 | 8 | namespace mevent { 9 | 10 | class ConnectionPool { 11 | public: 12 | ConnectionPool(int max_conn); 13 | virtual ~ConnectionPool(); 14 | 15 | void Reserve(int n); 16 | 17 | Connection *FreeListPop(); 18 | void FreeListPush(Connection *c); 19 | 20 | void ActiveListPush(Connection *c); 21 | void ActiveListErase(Connection *c); 22 | void ActiveListUpdate(Connection *c); 23 | bool ActiveListEmpty(); 24 | 25 | void ActiveListTimeoutCheck(); 26 | Connection *ActiveListCheckNext(); 27 | 28 | void ResetConnection(Connection *c); 29 | 30 | private: 31 | pthread_mutex_t free_list_mtx_; 32 | Connection *free_list_head_; 33 | 34 | pthread_mutex_t active_list_mtx_; 35 | Connection *active_list_head_; 36 | Connection *active_list_tail_; 37 | Connection *active_list_lookup_cursor_; 38 | 39 | int count_; 40 | int max_conn_; 41 | }; 42 | 43 | }//namespace mevent 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /event_loop.cpp: -------------------------------------------------------------------------------- 1 | #include "event_loop.h" 2 | #include "util.h" 3 | #include "lock_guard.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #define set_nonblock(fd) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) 12 | 13 | namespace mevent { 14 | 15 | void HTTPHandler::SetHandleFunc(const std::string &path, HTTPHandleFunc func) { 16 | if (path.empty()) { 17 | return; 18 | } 19 | 20 | tst::NodeData data; 21 | data.str = path; 22 | data.func = func; 23 | 24 | func_tree_.Insert(&data, NULL); 25 | } 26 | 27 | HTTPHandleFunc HTTPHandler::GetHandleFunc(const std::string &path) { 28 | HTTPHandleFunc func = nullptr; 29 | 30 | if (path.empty()) { 31 | return nullptr; 32 | } 33 | 34 | tst::NodeData data = func_tree_.SearchOne(path.c_str()); 35 | 36 | if (data.func) { 37 | func = data.func; 38 | } 39 | 40 | return func; 41 | } 42 | 43 | 44 | ////////////////// 45 | 46 | EventLoop::EventLoop() { 47 | worker_threads_ = 1; 48 | max_worker_connections_ = 1024; 49 | idle_timeout_ = 30; 50 | max_post_size_ = 8192; 51 | max_header_size_ = 2048; 52 | 53 | handler_ = nullptr; 54 | ssl_ctx_ = NULL; 55 | 56 | if (pthread_cond_init(&task_cond_, NULL) != 0) { 57 | MEVENT_LOG_DEBUG_EXIT(NULL); 58 | } 59 | 60 | if (pthread_mutex_init(&task_cond_mtx_, NULL) != 0) { 61 | MEVENT_LOG_DEBUG_EXIT(NULL); 62 | } 63 | 64 | if (pthread_cond_init(&ws_task_cond_, NULL) != 0) { 65 | MEVENT_LOG_DEBUG_EXIT(NULL); 66 | } 67 | 68 | if (pthread_mutex_init(&ws_task_cond_mtx_, NULL) != 0) { 69 | MEVENT_LOG_DEBUG_EXIT(NULL); 70 | } 71 | } 72 | 73 | void EventLoop::SetHandler(mevent::HTTPHandler *handler) { 74 | handler_ = handler; 75 | } 76 | 77 | void EventLoop::SetSslCtx(SSL_CTX *ssl_ctx) { 78 | ssl_ctx_ = ssl_ctx; 79 | } 80 | 81 | void EventLoop::Loop(int listen_fd) { 82 | listen_fd_ = listen_fd; 83 | 84 | conn_pool_ = new ConnectionPool(max_worker_connections_); 85 | // conn_pool_->Reserve(worker_connections_); 86 | 87 | evfd_ = Create(); 88 | 89 | if (evfd_ < 0) { 90 | MEVENT_LOG_DEBUG_EXIT(NULL); 91 | } 92 | 93 | if (set_nonblock(listen_fd) < 0) { 94 | MEVENT_LOG_DEBUG_EXIT(NULL); 95 | } 96 | 97 | listen_c_.fd_ = listen_fd_; 98 | if (Add(evfd_, listen_fd, MEVENT_IN, &listen_c_) == -1) { 99 | MEVENT_LOG_DEBUG_EXIT(NULL); 100 | } 101 | 102 | pthread_t tid; 103 | if (pthread_create(&tid, NULL, CheckConnectionTimeout, (void *)this) != 0) { 104 | MEVENT_LOG_DEBUG_EXIT(NULL); 105 | } 106 | 107 | if (pthread_detach(tid) != 0) { 108 | MEVENT_LOG_DEBUG_EXIT(NULL); 109 | } 110 | 111 | 112 | for (int i = 0; i < worker_threads_; i++) { 113 | if (pthread_create(&tid, NULL, WorkerThread, (void *)this) != 0) { 114 | MEVENT_LOG_DEBUG_EXIT(NULL); 115 | } 116 | 117 | if (pthread_detach(tid) != 0) { 118 | MEVENT_LOG_DEBUG_EXIT(NULL); 119 | } 120 | } 121 | 122 | for (int i = 0; i < worker_threads_; i++) { 123 | if (pthread_create(&tid, NULL, WebSocketWorkerThread, (void *)this) != 0) { 124 | MEVENT_LOG_DEBUG_EXIT(NULL); 125 | } 126 | 127 | if (pthread_detach(tid) != 0) { 128 | MEVENT_LOG_DEBUG_EXIT(NULL); 129 | } 130 | } 131 | 132 | int nfds; 133 | Connection *conn; 134 | 135 | while (1) { 136 | nfds = Poll(evfd_, events_, 512, NULL); 137 | 138 | for (int n = 0; n < nfds; n++) { 139 | conn = (Connection *)events_[n].data.ptr; 140 | 141 | if (conn->fd_ == listen_fd) { 142 | Accept(); 143 | } else if (events_[n].mask & MEVENT_IN) { 144 | conn_pool_->ActiveListUpdate(conn); 145 | 146 | LockGuard lock_guard(conn->mtx_); 147 | OnRead(conn); 148 | } else if (events_[n].mask & MEVENT_OUT) { 149 | LockGuard lock_guard(conn->mtx_); 150 | OnWrite(conn); 151 | } 152 | } 153 | } 154 | } 155 | 156 | void EventLoop::Accept() { 157 | struct sockaddr_in cliaddr; 158 | socklen_t clilen = sizeof(cliaddr); 159 | 160 | for (;;) { 161 | int clifd = accept(listen_fd_, (struct sockaddr *)&cliaddr, &clilen); 162 | 163 | if (clifd < 0) { 164 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 165 | break; 166 | } else { 167 | MEVENT_LOG_DEBUG_EXIT(NULL); 168 | return; 169 | } 170 | } 171 | 172 | Connection *conn = conn_pool_->FreeListPop(); 173 | 174 | if (!conn) { 175 | MEVENT_LOG_DEBUG("worker connections reach max limit"); 176 | close(clifd); 177 | return; 178 | } 179 | 180 | 181 | LockGuard lock_guard(conn->mtx_); 182 | 183 | conn->active_time_ = time(NULL); 184 | conn->fd_ = clifd; 185 | conn->elp_ = this; 186 | 187 | Request *req = conn->Req(); 188 | req->addr_ = cliaddr.sin_addr; 189 | 190 | bool status = true; 191 | 192 | do { 193 | if (set_nonblock(clifd) < 0) { 194 | MEVENT_LOG_DEBUG(NULL); 195 | status = false; 196 | break; 197 | } 198 | 199 | int enable = 1; 200 | if (setsockopt(clifd, IPPROTO_TCP, TCP_NODELAY, (void*)&enable, sizeof(enable)) < 0) { 201 | MEVENT_LOG_DEBUG(NULL); 202 | status = false; 203 | break; 204 | } 205 | 206 | if (Add(evfd_, clifd, MEVENT_IN, conn) == -1) { 207 | MEVENT_LOG_DEBUG(NULL); 208 | status = false; 209 | break; 210 | } 211 | 212 | if (ssl_ctx_) { 213 | if (!conn->CreateSSL(ssl_ctx_)) { 214 | status = false; 215 | break; 216 | } 217 | } 218 | } while (0); 219 | 220 | if (!status) { 221 | conn->Reset(); 222 | conn_pool_->FreeListPush(conn); 223 | continue; 224 | } 225 | 226 | conn_pool_->ActiveListPush(conn); 227 | 228 | OnAccept(conn); 229 | } 230 | } 231 | 232 | void *EventLoop::CheckConnectionTimeout(void *arg) { 233 | EventLoop *elp = (EventLoop *)arg; 234 | 235 | for (;;) { 236 | int64_t now = time(NULL); 237 | 238 | elp->conn_pool_->ActiveListTimeoutCheck(); 239 | 240 | Connection *conn = elp->conn_pool_->ActiveListCheckNext(); 241 | 242 | if (conn) { 243 | time_t active_time = 0; 244 | { 245 | LockGuard lock_guard(conn->mtx_); 246 | active_time = conn->active_time_; 247 | } 248 | if (active_time > 0 && now - active_time < elp->idle_timeout_) { 249 | sleep(static_cast(elp->idle_timeout_ - (now - active_time))); 250 | continue; 251 | } 252 | } else { 253 | if (elp->conn_pool_->ActiveListEmpty()) { 254 | sleep(elp->idle_timeout_); 255 | } 256 | continue; 257 | } 258 | 259 | while (conn) { 260 | { 261 | LockGuard lock_guard(conn->mtx_); 262 | 263 | if (conn->active_time_ > 0) { 264 | if ((now - conn->active_time_) >= elp->idle_timeout_) { 265 | if (conn->Req()->status_ == RequestStatus::UPGRADE) { 266 | MEVENT_LOG_DEBUG("client(websocket):%s timeout", conn->Req()->RemoteAddr().c_str()); 267 | } else { 268 | MEVENT_LOG_DEBUG("client:%s timeout", conn->Req()->RemoteAddr().c_str()); 269 | } 270 | elp->ResetConnection(conn); 271 | } else { 272 | break; 273 | } 274 | } 275 | } 276 | 277 | conn = elp->conn_pool_->ActiveListCheckNext(); 278 | } 279 | } 280 | 281 | return (void *)0; 282 | } 283 | 284 | void EventLoop::ResetConnection(Connection *conn) { 285 | if (conn->fd_ > 0) { 286 | OnClose(conn); 287 | 288 | conn->Reset(); 289 | 290 | conn_pool_->FreeListPush(conn); 291 | conn_pool_->ActiveListErase(conn); 292 | } 293 | } 294 | 295 | void EventLoop::SetIdleTimeout(int secs) { 296 | if (secs < 1) { 297 | return; 298 | } 299 | 300 | idle_timeout_ = secs; 301 | } 302 | 303 | void EventLoop::SetMaxWorkerConnections(int num) { 304 | if (num < 1) { 305 | return; 306 | } 307 | 308 | max_worker_connections_ = num; 309 | } 310 | 311 | void EventLoop::SetMaxPostSize(size_t size) { 312 | max_post_size_ = size; 313 | } 314 | 315 | void EventLoop::SetMaxHeaderSize(size_t size) { 316 | max_header_size_ = size; 317 | } 318 | 319 | void EventLoop::OnAccept(Connection *conn) { 320 | (void)conn;//avoid unused parameter warning 321 | // conn->elp_ = this; 322 | } 323 | 324 | void EventLoop::OnRead(Connection *conn) { 325 | ConnStatus status = conn->ReadData(); 326 | 327 | if (status == ConnStatus::ERROR) { 328 | if (conn->Req()->error_code_ != 0) { 329 | conn->Resp()->WriteErrorMessage(conn->Req()->error_code_); 330 | status = conn->Flush(); 331 | } 332 | } 333 | 334 | if (status != ConnStatus::AGAIN) { 335 | ResetConnection(conn); 336 | } 337 | } 338 | 339 | void EventLoop::OnWrite(Connection *conn) { 340 | ConnStatus status = conn->Flush(); 341 | if (status != ConnStatus::AGAIN) { 342 | ResetConnection(conn); 343 | } 344 | } 345 | 346 | void EventLoop::OnClose(Connection *conn) { 347 | if (conn->Req()->status_ == RequestStatus::UPGRADE) { 348 | conn->WS()->on_close_func_(conn->WS()); 349 | } 350 | } 351 | 352 | void *EventLoop::WorkerThread(void *arg) { 353 | EventLoop *elp = (EventLoop *)arg; 354 | 355 | for (;;) { 356 | Connection *conn; 357 | 358 | { 359 | LockGuard lock_guard(elp->task_cond_mtx_); 360 | 361 | if (elp->task_que_.empty()) { 362 | pthread_cond_wait(&elp->task_cond_, &elp->task_cond_mtx_); 363 | continue; 364 | } 365 | 366 | conn = elp->task_que_.front(); 367 | elp->task_que_.pop(); 368 | } 369 | 370 | { 371 | LockGuard lock_guard(conn->mtx_); 372 | 373 | if (conn->fd_ < 0) { 374 | continue; 375 | } 376 | 377 | Request *req = conn->Req(); 378 | Response *resp = conn->Resp(); 379 | 380 | HTTPHandleFunc func; 381 | if ((func = elp->handler_->GetHandleFunc(req->path_))) { 382 | func(conn); 383 | } else { 384 | resp->WriteErrorMessage(404); 385 | } 386 | 387 | if (req->status_ != RequestStatus::UPGRADE) { 388 | resp->Flush(); 389 | } 390 | 391 | ConnStatus status = conn->Flush(); 392 | 393 | if (status == ConnStatus::AGAIN) { 394 | if (!conn->ev_writable_) { 395 | elp->Modify(elp->evfd_, conn->fd_, MEVENT_IN | MEVENT_OUT, conn); 396 | conn->ev_writable_ = true; 397 | } 398 | } else if (status != ConnStatus::UPGRADE) { 399 | elp->ResetConnection(conn); 400 | } 401 | } 402 | } 403 | 404 | return (void *)0; 405 | } 406 | 407 | void *EventLoop::WebSocketWorkerThread(void *arg) { 408 | EventLoop *elp = (EventLoop *)arg; 409 | 410 | for (;;) { 411 | std::shared_ptr item; 412 | 413 | { 414 | LockGuard lock_guard(elp->ws_task_cond_mtx_); 415 | 416 | if (elp->ws_task_que_.empty()) { 417 | pthread_cond_wait(&elp->ws_task_cond_, &elp->ws_task_cond_mtx_); 418 | continue; 419 | } 420 | 421 | item = elp->ws_task_que_.front(); 422 | elp->ws_task_que_.pop(); 423 | } 424 | 425 | { 426 | LockGuard lock_guard(item->ws->Conn()->mtx_); 427 | 428 | if (item->ws->Conn()->fd_ < 0) { 429 | continue; 430 | } 431 | 432 | if (item->opcode == WebSocketOpcodeType::BINARY_FRAME 433 | || item->opcode == WebSocketOpcodeType::CONTINUATION 434 | || item->opcode == WebSocketOpcodeType::TEXT_FRAME) { 435 | if (item->ws->on_message_func_) { 436 | item->ws->on_message_func_(item->ws, item->msg); 437 | } 438 | } else if (item->opcode == WebSocketOpcodeType::PING) { 439 | if (item->ws->ping_func_) { 440 | item->ws->ping_func_(item->ws, item->msg); 441 | } 442 | } else if (item->opcode == WebSocketOpcodeType::PONG) { 443 | if (item->ws->pong_func_) { 444 | item->ws->pong_func_(item->ws, item->msg); 445 | } 446 | } else if (item->opcode == WebSocketOpcodeType::CLOSE) { 447 | if (item->ws->on_close_func_) { 448 | item->ws->on_close_func_(item->ws); 449 | } 450 | } 451 | 452 | ConnStatus status = item->ws->Conn()->Flush(); 453 | 454 | if (status == ConnStatus::AGAIN) { 455 | if (!item->ws->Conn()->ev_writable_) { 456 | elp->Modify(elp->evfd_, item->ws->Conn()->fd_, MEVENT_IN | MEVENT_OUT, item->ws->Conn()); 457 | item->ws->Conn()->ev_writable_ = true; 458 | } 459 | } else if (status != ConnStatus::UPGRADE) { 460 | elp->ResetConnection(item->ws->Conn()); 461 | } 462 | } 463 | } 464 | 465 | return (void *)0; 466 | } 467 | 468 | void EventLoop::TaskPush(Connection *conn) { 469 | LockGuard cond_lock_guard(task_cond_mtx_); 470 | task_que_.push(conn); 471 | pthread_cond_signal(&task_cond_); 472 | } 473 | 474 | void EventLoop::WebSocketTaskPush(WebSocket *ws, WebSocketOpcodeType opcode, const std::string &msg) { 475 | LockGuard lock_guard(ws_task_cond_mtx_); 476 | std::shared_ptr item = std::make_shared(ws, opcode, msg); 477 | ws_task_que_.push(item); 478 | pthread_cond_signal(&ws_task_cond_); 479 | } 480 | 481 | EventLoop::~EventLoop() { 482 | delete conn_pool_; 483 | } 484 | 485 | }//namespace mevent 486 | -------------------------------------------------------------------------------- /event_loop.h: -------------------------------------------------------------------------------- 1 | #ifndef _EVENT_LOOP 2 | #define _EVENT_LOOP 3 | 4 | #include "connection_pool.h" 5 | #include "event_loop_base.h" 6 | #include "ternary_search_tree.h" 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace mevent { 16 | 17 | class HTTPHandler { 18 | public: 19 | HTTPHandler() {}; 20 | virtual ~HTTPHandler() {}; 21 | 22 | void SetHandleFunc(const std::string &path, HTTPHandleFunc func); 23 | HTTPHandleFunc GetHandleFunc(const std::string &path); 24 | 25 | private: 26 | tst::TernarySearchTree func_tree_; 27 | }; 28 | 29 | class EventLoop : public EventLoopBase { 30 | public: 31 | EventLoop(); 32 | virtual ~EventLoop(); 33 | 34 | void ResetConnection(Connection *conn); 35 | 36 | void Loop(int listen_fd); 37 | 38 | void SetHandler(HTTPHandler *handler); 39 | 40 | void SetSslCtx(SSL_CTX *ssl_ctx); 41 | 42 | void SetMaxWorkerConnections(int num); 43 | void SetIdleTimeout(int secs); 44 | void SetMaxPostSize(size_t size); 45 | void SetMaxHeaderSize(size_t size); 46 | 47 | void TaskPush(Connection *conn); 48 | 49 | void WebSocketTaskPush(WebSocket *ws, WebSocketOpcodeType opcode, const std::string &msg); 50 | 51 | private: 52 | friend class Request; 53 | friend class Connection; 54 | 55 | void OnClose(Connection *conn); 56 | 57 | void OnRead(Connection *conn); 58 | void OnWrite(Connection *conn); 59 | void OnAccept(Connection *conn); 60 | 61 | void Accept(); 62 | 63 | static void *CheckConnectionTimeout(void *arg); 64 | 65 | static void *WorkerThread(void *arg); 66 | static void *WebSocketWorkerThread(void *arg); 67 | 68 | int evfd_; 69 | int listen_fd_; 70 | Connection listen_c_; 71 | 72 | SSL_CTX *ssl_ctx_; 73 | 74 | pthread_mutex_t task_cond_mtx_; 75 | pthread_cond_t task_cond_; 76 | std::queue task_que_; 77 | 78 | HTTPHandler *handler_; 79 | 80 | EventLoopBase::Event events_[512]; 81 | 82 | int worker_threads_; 83 | int max_worker_connections_; 84 | int idle_timeout_; 85 | size_t max_post_size_; 86 | size_t max_header_size_; 87 | 88 | ConnectionPool *conn_pool_; 89 | 90 | pthread_mutex_t ws_task_cond_mtx_; 91 | pthread_cond_t ws_task_cond_; 92 | std::queue> ws_task_que_; 93 | }; 94 | 95 | }//namespace mevent 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /event_loop_base.cpp: -------------------------------------------------------------------------------- 1 | #if defined(__APPLE__) || defined(__FreeBSD__) 2 | #include "event_loop_base_kqueue.cpp" 3 | #elif defined(__linux__) 4 | #include "event_loop_base_epoll.cpp" 5 | #endif 6 | -------------------------------------------------------------------------------- /event_loop_base.h: -------------------------------------------------------------------------------- 1 | #ifndef _EVENT_LOOP_BASE_H 2 | #define _EVENT_LOOP_BASE_H 3 | 4 | #include 5 | 6 | namespace mevent { 7 | 8 | #define MEVENT_IN 1 9 | #define MEVENT_OUT 2 10 | #define MEVENT_ERR 4 11 | 12 | class EventLoopBase { 13 | protected: 14 | struct Event { 15 | int mask; 16 | union { 17 | int fd; 18 | void *ptr; 19 | } data; 20 | }; 21 | 22 | int Create(); 23 | int Add(int evfd, int fd, int mask, void *data); 24 | int Modify(int evfd, int fd, int mask, void *data); 25 | 26 | int Poll(int evfd, Event *events, int size, struct timeval *tv); 27 | }; 28 | 29 | }//namespace mevent 30 | 31 | #endif -------------------------------------------------------------------------------- /event_loop_base_epoll.cpp: -------------------------------------------------------------------------------- 1 | #include "event_loop_base.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace mevent { 8 | 9 | int EventLoopBase::Create() { 10 | return epoll_create(512); 11 | } 12 | 13 | int EventLoopBase::Add(int evfd, int fd, int mask, void *data) { 14 | struct epoll_event ev; 15 | ev.events = 0; 16 | 17 | if (mask & MEVENT_IN) { 18 | ev.events |= EPOLLIN; 19 | } 20 | 21 | if (mask & MEVENT_OUT) { 22 | ev.events |= EPOLLOUT; 23 | } 24 | 25 | ev.events |= EPOLLET; 26 | 27 | ev.data.ptr = data; 28 | 29 | return epoll_ctl(evfd, EPOLL_CTL_ADD, fd, &ev); 30 | } 31 | 32 | int EventLoopBase::Modify(int evfd, int fd, int mask, void *data) { 33 | struct epoll_event ev; 34 | ev.events = 0; 35 | 36 | if (mask & MEVENT_IN) { 37 | ev.events |= EPOLLIN; 38 | } 39 | 40 | if (mask & MEVENT_OUT) { 41 | ev.events |= EPOLLOUT; 42 | } 43 | 44 | ev.events |= EPOLLET; 45 | 46 | ev.data.ptr = data; 47 | 48 | return epoll_ctl(evfd, EPOLL_CTL_MOD, fd, &ev); 49 | } 50 | 51 | int EventLoopBase::Poll(int evfd, EventLoopBase::Event *events, int size, struct timeval *tv) { 52 | struct epoll_event evs[size]; 53 | int nfds; 54 | 55 | if (tv) { 56 | nfds = epoll_wait(evfd, evs, size, (tv->tv_sec * 1000) + (tv->tv_usec / 1000)); 57 | } else { 58 | nfds = epoll_wait(evfd, evs, size, -1); 59 | } 60 | 61 | if (nfds > 0) { 62 | for (int i = 0; i < nfds; i++) { 63 | events[i].data.ptr = evs[i].data.ptr; 64 | 65 | events[i].mask = 0; 66 | 67 | if (evs[i].events & EPOLLIN) { 68 | events[i].mask |= MEVENT_IN; 69 | } 70 | 71 | if (evs[i].events & EPOLLHUP) { 72 | events[i].mask |= MEVENT_OUT; 73 | } 74 | 75 | if (evs[i].events & EPOLLERR) { 76 | events[i].mask |= MEVENT_OUT; 77 | } 78 | 79 | if (evs[i].events & EPOLLOUT) { 80 | events[i].mask |= MEVENT_OUT; 81 | } 82 | } 83 | } 84 | 85 | return nfds; 86 | } 87 | 88 | }//namespace mevent -------------------------------------------------------------------------------- /event_loop_base_kqueue.cpp: -------------------------------------------------------------------------------- 1 | #include "event_loop_base.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace mevent { 7 | 8 | int EventLoopBase::Create() { 9 | return kqueue(); 10 | } 11 | 12 | int EventLoopBase::Add(int evfd, int fd, int mask, void *data) { 13 | struct kevent ev; 14 | 15 | if (mask & MEVENT_IN) { 16 | EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, data); 17 | kevent(evfd, &ev, 1, NULL, 0, NULL); 18 | } 19 | 20 | if (mask & MEVENT_OUT) { 21 | EV_SET(&ev, fd, EVFILT_WRITE, EV_ADD, 0, 0, data); 22 | kevent(evfd, &ev, 1, NULL, 0, NULL); 23 | } 24 | 25 | return 0; 26 | } 27 | 28 | int EventLoopBase::Modify(int evfd, int fd, int mask, void *data) { 29 | return Add(evfd, fd, mask, data); 30 | } 31 | 32 | int EventLoopBase::Poll(int evfd, EventLoopBase::Event *events, int size, struct timeval *tv) { 33 | struct kevent evs[size]; 34 | int nfds; 35 | 36 | if (tv) { 37 | struct timespec timeout; 38 | timeout.tv_sec = tv->tv_sec; 39 | timeout.tv_nsec = tv->tv_usec * 1000; 40 | 41 | nfds = kevent(evfd, NULL, 0, evs, size, &timeout); 42 | } else { 43 | nfds = kevent(evfd, NULL, 0, evs, size, NULL); 44 | } 45 | 46 | if (nfds > 0) { 47 | for (int i = 0; i < nfds; i++) { 48 | events[i].data.ptr = evs[i].udata; 49 | events[i].mask = 0; 50 | 51 | if (evs[i].filter == EVFILT_READ) { 52 | events[i].mask |= MEVENT_IN; 53 | } 54 | 55 | if (evs[i].filter == EVFILT_WRITE) { 56 | events[i].mask |= MEVENT_OUT; 57 | } 58 | } 59 | } 60 | 61 | return nfds; 62 | } 63 | 64 | }//namespace mevent -------------------------------------------------------------------------------- /examples/chat_room.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../http_server.h" 5 | #include "../util.h" 6 | #include "../http_client.h" 7 | #include "../lock_guard.h" 8 | 9 | using namespace mevent; 10 | using namespace mevent::util; 11 | 12 | 13 | const char *index_html = 14 | "\n" 16 | "\n" 17 | "\n" 18 | " \n" 19 | " mevent - chat room\n" 20 | "\n" 21 | "\n" 22 | "

\n" 23 | " Chat:\n" 24 | " \n" 25 | "

\n" 26 | "

\n" 27 | " Status:\n" 28 | " online\n" 29 | " offline\n" 30 | "

\n" 31 | "

\n" 32 | "

\n" 35 | "

\n" 36 | " Nick:\n" 37 | " \n" 38 | "

\n" 39 | "

\n" 40 | " Message:\n" 41 | " \n" 42 | "

\n" 43 | "

\n" 44 | " \n" 45 | " \n" 46 | "

\n" 47 | "\n" 48 | "\n" 119 | "\n" 120 | "\n"; 121 | 122 | class ChatRoom { 123 | public: 124 | struct Task { 125 | std::string channel_name; 126 | std::vector data; 127 | }; 128 | 129 | ChatRoom() { 130 | pthread_mutex_init(&task_mtx_, NULL); 131 | pthread_cond_init(&task_cond_, NULL); 132 | 133 | pthread_mutex_init(&clients_mtx_, NULL); 134 | 135 | pthread_t tid; 136 | pthread_create(&tid, NULL, TaskThread, this); 137 | pthread_detach(tid); 138 | } 139 | 140 | void Index(Connection *conn) { 141 | conn->Resp()->SetHeader("Content-Type", "text/html"); 142 | conn->Resp()->WriteString(std::string(index_html)); 143 | } 144 | 145 | void OnClose(WebSocket *ws) { 146 | LockGuard lock_guard(clients_mtx_); 147 | 148 | auto it = channel_index_map_.find(ws); 149 | if (it == channel_index_map_.end()) { 150 | return; 151 | } 152 | 153 | auto client_it = clients_map_.find(it->second); 154 | if (client_it == clients_map_.end()) { 155 | return; 156 | } 157 | 158 | client_it->second.erase(ws); 159 | if (client_it->second.empty()) { 160 | clients_map_.erase(client_it); 161 | } 162 | } 163 | 164 | void OnMessage(WebSocket *ws, const std::string &msg) { 165 | if (msg.empty()) { 166 | ws->WriteString(msg); 167 | } else { 168 | TaskPush(ws, msg); 169 | } 170 | } 171 | 172 | void Publish(Connection *conn) { 173 | Request *req = conn->Req(); 174 | if (req->Method() != RequestMethod::POST) { 175 | return; 176 | } 177 | 178 | req->ParsePostForm(); 179 | 180 | std::string nick = req->PostFormValue("nick"); 181 | std::string msg = req->PostFormValue("msg"); 182 | std::string room = req->PostFormValue("room"); 183 | 184 | std::string str = nick + ":" + msg; 185 | 186 | TaskPush(room, str); 187 | 188 | Response *resp = conn->Resp(); 189 | resp->WriteString("ok"); 190 | } 191 | 192 | void Ping(WebSocket *ws, const std::string &msg) { 193 | (void)msg;//avoid unused parameter warning 194 | ws->SendPong(""); 195 | } 196 | 197 | void Subscribe(Connection *conn) { 198 | conn->Req()->ParseQueryString(); 199 | std::string channel_name = conn->Req()->QueryStringValue("room"); 200 | if (channel_name.empty()) { 201 | channel_name = "default"; 202 | } 203 | 204 | if (!conn->WS()->Upgrade()) { 205 | conn->Resp()->WriteErrorMessage(500); 206 | return; 207 | } 208 | 209 | conn->WS()->SetPingHandler(std::bind(&::ChatRoom::Ping, this, std::placeholders::_1, std::placeholders::_2)); 210 | conn->WS()->SetOnCloseHandler(std::bind(&::ChatRoom::OnClose, this, std::placeholders::_1)); 211 | conn->WS()->SetOnMessageHandler(std::bind(&::ChatRoom::OnMessage, this, std::placeholders::_1, std::placeholders::_2)); 212 | 213 | conn->WS()->SetMaxBufferSize(100000); 214 | 215 | { 216 | LockGuard lock_guard(clients_mtx_); 217 | 218 | channel_index_map_[conn->WS()] = channel_name; 219 | 220 | auto it = clients_map_.find(channel_name); 221 | if (it == clients_map_.end()) { 222 | std::set clients; 223 | clients.insert(conn->WS()); 224 | clients_map_[channel_name] = clients; 225 | } else { 226 | it->second.insert(conn->WS()); 227 | } 228 | } 229 | } 230 | 231 | void TaskPush(WebSocket *ws, const std::string &msg) { 232 | LockGuard lock_guard(task_mtx_); 233 | 234 | auto it = channel_index_map_.find(ws); 235 | if (it == channel_index_map_.end()) { 236 | return; 237 | } 238 | 239 | std::vector data; 240 | ws->MakeFrame(data, msg, WebSocketOpcodeType::TEXT_FRAME); 241 | 242 | std::shared_ptr task_ptr = std::make_shared(); 243 | 244 | task_ptr->data = data; 245 | task_ptr->channel_name = it->second; 246 | task_que_.push(task_ptr); 247 | 248 | pthread_cond_signal(&task_cond_); 249 | } 250 | 251 | void TaskPush(const std::string &channel_name, const std::string &msg) { 252 | LockGuard lock_guard(task_mtx_); 253 | 254 | std::vector data; 255 | WebSocket::MakeFrame(data, msg, WebSocketOpcodeType::TEXT_FRAME); 256 | 257 | std::shared_ptr task_ptr = std::make_shared(); 258 | 259 | task_ptr->data = data; 260 | task_ptr->channel_name = channel_name; 261 | task_que_.push(task_ptr); 262 | 263 | pthread_cond_signal(&task_cond_); 264 | } 265 | 266 | static void *TaskThread(void *arg) { 267 | ChatRoom *chat = static_cast(arg); 268 | while (true) { 269 | std::shared_ptr task_ptr; 270 | { 271 | LockGuard lock_guard(chat->task_mtx_); 272 | if (chat->task_que_.empty()) { 273 | pthread_cond_wait(&chat->task_cond_, &chat->task_mtx_); 274 | continue; 275 | } 276 | task_ptr = chat->task_que_.front(); 277 | chat->task_que_.pop(); 278 | } 279 | 280 | std::set clients; 281 | { 282 | LockGuard lock_guard(chat->clients_mtx_); 283 | 284 | auto it = chat->clients_map_.find(task_ptr->channel_name); 285 | if (it == chat->clients_map_.end()) { 286 | continue; 287 | } 288 | 289 | clients = it->second; 290 | } 291 | 292 | auto it = clients.begin(); 293 | for (; it != clients.end(); it++) { 294 | (*it)->WriteRawDataSafe(task_ptr->data); 295 | } 296 | } 297 | 298 | return (void *)0; 299 | } 300 | 301 | pthread_mutex_t clients_mtx_; 302 | 303 | pthread_cond_t task_cond_; 304 | pthread_mutex_t task_mtx_; 305 | std::queue> task_que_; 306 | 307 | std::map channel_index_map_; 308 | std::map> clients_map_; 309 | }; 310 | 311 | int main() { 312 | ChatRoom chat; 313 | 314 | HTTPServer *server = new HTTPServer(); 315 | server->SetHandler("/", std::bind(&ChatRoom::Index, &chat, std::placeholders::_1)); 316 | server->SetHandler("/ws", std::bind(&ChatRoom::Subscribe, &chat, std::placeholders::_1)); 317 | 318 | //curl -XPOST http://localhost/pub -d 'nick=looyao&msg=hello&room=10086' 319 | server->SetHandler("/pub", std::bind(&ChatRoom::Publish, &chat, std::placeholders::_1)); 320 | 321 | server->SetWorkerThreads(4); 322 | server->SetIdleTimeout(60); 323 | server->SetMaxWorkerConnections(8192); 324 | 325 | server->ListenAndServe("0.0.0.0", 80); 326 | 327 | //HTTPS WSS 328 | // server->ListenAndServeTLS("0.0.0.0", 443, "host.crt", "host.key"); 329 | 330 | return 0; 331 | } 332 | -------------------------------------------------------------------------------- /examples/form_action.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../http_server.h" 4 | 5 | using namespace mevent; 6 | 7 | const char *index_html = 8 | "\n" 10 | "\n" 11 | "\n" 12 | " \n" 13 | " mevent - form test\n" 14 | "\n" 15 | "\n" 16 | "
\n" 17 | "

First name:

\n" 18 | "

Last name:

\n" 19 | " \n" 20 | "
\n" 21 | "
\n" 22 | "
\n" 23 | "

First name:

\n" 24 | "

Last name:

\n" 25 | " \n" 26 | "
\n" 27 | "\n" 28 | "\n"; 29 | 30 | class FormAction { 31 | public: 32 | void Index(Connection *conn) { 33 | Response *resp = conn->Resp(); 34 | resp->SetHeader("Content-Type", "text/html"); 35 | resp->WriteString(index_html); 36 | } 37 | 38 | void Action(Connection *conn) { 39 | Request *req = conn->Req(); 40 | Response *resp = conn->Resp(); 41 | 42 | std::string str; 43 | if (req->Method() == RequestMethod::GET) { 44 | req->ParseQueryString(); 45 | str = "GET:"; 46 | str += req->QueryString() + "\n"; 47 | str += "First name:" + req->QueryStringValue("fname") + "\n"; 48 | str += "Last name:" + req->QueryStringValue("lname") + "\n"; 49 | } else if (req->Method() == RequestMethod::POST) { 50 | req->ParsePostForm(); 51 | str = "POST:"; 52 | str += req->Body() + "\n"; 53 | str += "First name:" + req->PostFormValue("fname") + "\n"; 54 | str += "Last name:" + req->PostFormValue("lname") + "\n"; 55 | } 56 | 57 | str += "Content-Length:" + std::to_string(req->ContentLength()) + "\n"; 58 | str += "RemoteAddr:" + req->RemoteAddr() + "\n"; 59 | 60 | req->ParseHeader(); 61 | str += "Content-Type:" + req->HeaderValue("Content-Type") + "\n"; 62 | 63 | resp->SetHeader("Content-Type", "text/plain"); 64 | resp->WriteString(str); 65 | } 66 | }; 67 | 68 | int main() { 69 | FormAction action; 70 | 71 | HTTPServer *server = new HTTPServer(); 72 | server->SetHandler("/", std::bind(&FormAction::Index, &action, std::placeholders::_1)); 73 | server->SetHandler("/form_action", std::bind(&FormAction::Action, &action, std::placeholders::_1)); 74 | 75 | server->SetWorkerThreads(4); 76 | server->SetIdleTimeout(60); 77 | server->SetMaxWorkerConnections(8192); 78 | 79 | server->ListenAndServe("0.0.0.0", 80); 80 | 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /examples/hello_world.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../http_server.h" 4 | 5 | using namespace mevent; 6 | 7 | class HelloWorld { 8 | public: 9 | void Index(Connection *conn) { 10 | conn->Resp()->SetHeader("Content-Type", "text/html"); 11 | conn->Resp()->WriteString("hello"); 12 | conn->Resp()->WriteString(" world!"); 13 | } 14 | }; 15 | 16 | int main() { 17 | HelloWorld hello; 18 | 19 | HTTPServer *server = new HTTPServer(); 20 | server->SetHandler("/", std::bind(&::HelloWorld::Index, &hello, std::placeholders::_1)); 21 | 22 | server->SetWorkerThreads(4); 23 | server->SetIdleTimeout(60); 24 | server->SetMaxWorkerConnections(8192); 25 | 26 | server->ListenAndServe("0.0.0.0", 80); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /examples/tls_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../http_server.h" 4 | #include "../util.h" 5 | 6 | using namespace mevent; 7 | 8 | class TLS { 9 | public: 10 | void Index(Connection *conn) { 11 | conn->Resp()->SetHeader("Content-Type", "text/html"); 12 | conn->Resp()->WriteString("hello world!"); 13 | } 14 | }; 15 | 16 | int main() { 17 | TLS tls; 18 | 19 | HTTPServer *server = new HTTPServer(); 20 | server->SetHandler("/", std::bind(&::TLS::Index, &tls, std::placeholders::_1)); 21 | 22 | server->SetWorkerThreads(4); 23 | server->SetIdleTimeout(60); 24 | server->SetMaxWorkerConnections(8192); 25 | 26 | server->ListenAndServeTLS("0.0.0.0", 443, "host.crt", "host.key"); 27 | 28 | fflush(NULL); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /http_client.cpp: -------------------------------------------------------------------------------- 1 | #include "http_client.h" 2 | 3 | namespace mevent { 4 | namespace util { 5 | 6 | #define REQUEST_TIMEOUT 30 7 | 8 | bool HTTPClient::Request(const std::string &host) { 9 | CURL *curl_ = curl_easy_init(); 10 | 11 | if (!curl_) { 12 | return false; 13 | } 14 | 15 | if (curl_easy_setopt(curl_, CURLOPT_URL, host.c_str()) != CURLE_OK) { 16 | return false; 17 | } 18 | 19 | curl_easy_setopt(curl_, CURLOPT_TIMEOUT, REQUEST_TIMEOUT); 20 | 21 | curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L); 22 | 23 | curl_easy_setopt(curl_, CURLOPT_FOLLOWLOCATION, 1L); 24 | curl_easy_setopt(curl_, CURLOPT_MAXREDIRS, 5L); 25 | curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback); 26 | curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &data_); 27 | 28 | curl_easy_perform(curl_); 29 | 30 | curl_easy_cleanup(curl_); 31 | 32 | return true; 33 | } 34 | 35 | std::string HTTPClient::ResponseData() { 36 | return data_; 37 | } 38 | 39 | size_t HTTPClient::WriteCallback(char *ptr, size_t size, size_t nmemb, void *userdata) { 40 | size_t len = size * nmemb; 41 | static_cast(userdata)->append(ptr, len); 42 | return len; 43 | } 44 | 45 | }//namespace util 46 | }//namespace mevent -------------------------------------------------------------------------------- /http_client.h: -------------------------------------------------------------------------------- 1 | #ifndef _HTTP_CLIENT_H 2 | #define _HTTP_CLIENT_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace mevent { 9 | namespace util { 10 | 11 | class HTTPClient { 12 | public: 13 | HTTPClient() {}; 14 | ~HTTPClient() {}; 15 | 16 | bool Request(const std::string &host); 17 | 18 | std::string ResponseData(); 19 | 20 | private: 21 | static size_t WriteCallback(char *ptr, size_t size, size_t nmemb, void *userdata); 22 | 23 | std::string data_; 24 | }; 25 | 26 | }//namespace util 27 | }//namespace mevent 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /http_server.cpp: -------------------------------------------------------------------------------- 1 | #include "http_server.h" 2 | #include "util.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #ifdef __APPLE__ 17 | #include 18 | #endif 19 | 20 | #define LISTENQ 512 21 | 22 | namespace mevent { 23 | 24 | HTTPServer::HTTPServer() { 25 | rlimit_nofile_ = 0; 26 | worker_threads_ = 1; 27 | max_worker_connections_ = 1024; 28 | idle_timeout_ = 30; 29 | max_header_size_ = 2048; 30 | max_post_size_ = 8192; 31 | ssl_ctx_ = NULL; 32 | } 33 | 34 | void *HTTPServer::EventLoopThread(void *arg) { 35 | HTTPServer *server = (HTTPServer *)arg; 36 | 37 | EventLoop *elp = new EventLoop(); 38 | elp->SetHandler(&server->handler_); 39 | elp->SetSslCtx(server->ssl_ctx_); 40 | elp->SetMaxWorkerConnections(server->max_worker_connections_); 41 | elp->SetIdleTimeout(server->idle_timeout_); 42 | elp->SetMaxHeaderSize(server->max_header_size_); 43 | elp->SetMaxPostSize(server->max_post_size_); 44 | 45 | elp->Loop(server->listen_fd_); 46 | 47 | return (void *)0; 48 | } 49 | 50 | void HTTPServer::ListenAndServe(const std::string &ip, int port) { 51 | curl_global_init(CURL_GLOBAL_ALL); 52 | 53 | signal(SIGPIPE, SIG_IGN); 54 | 55 | struct sockaddr_in servaddr; 56 | int reuse = 1; 57 | 58 | listen_fd_ = socket(AF_INET, SOCK_STREAM, 0); 59 | if (listen_fd_ < 0) { 60 | MEVENT_LOG_DEBUG_EXIT(NULL); 61 | } 62 | 63 | if (setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { 64 | MEVENT_LOG_DEBUG_EXIT(NULL); 65 | } 66 | 67 | memset(&servaddr, 0, sizeof(servaddr)); 68 | servaddr.sin_family = AF_INET; 69 | servaddr.sin_port = htons(port); 70 | if (ip.empty()) { 71 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 72 | } else { 73 | inet_pton(AF_INET, ip.c_str(), &servaddr.sin_addr); 74 | } 75 | 76 | if (bind(listen_fd_, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { 77 | MEVENT_LOG_DEBUG_EXIT(NULL); 78 | } 79 | 80 | if (listen(listen_fd_, LISTENQ) < 0) { 81 | MEVENT_LOG_DEBUG_EXIT(NULL); 82 | } 83 | 84 | rlimit_nofile_ = worker_threads_ * max_worker_connections_; 85 | 86 | if (rlimit_nofile_ > 0) { 87 | struct rlimit limit; 88 | limit.rlim_cur = rlimit_nofile_; 89 | #ifdef __APPLE__ 90 | if (limit.rlim_cur > OPEN_MAX) { 91 | limit.rlim_cur = OPEN_MAX; 92 | } 93 | #endif 94 | limit.rlim_max = limit.rlim_cur; 95 | 96 | if (setrlimit(RLIMIT_NOFILE, &limit) < 0) { 97 | MEVENT_LOG_DEBUG(NULL); 98 | } 99 | } 100 | 101 | if (!user_.empty()) { 102 | int gid = util::GetSysGid(user_.c_str()); 103 | if (gid != -1) { 104 | setgid(gid); 105 | } 106 | 107 | int uid = util::GetSysUid(user_.c_str()); 108 | if (uid != -1) { 109 | setuid(uid); 110 | } 111 | } 112 | 113 | pthread_t tid; 114 | 115 | for (int i = 0; i < worker_threads_; i++) { 116 | if (pthread_create(&tid, NULL, EventLoopThread, (void *)this) != 0) { 117 | MEVENT_LOG_DEBUG_EXIT(NULL); 118 | } 119 | 120 | if (pthread_detach(tid) != 0) { 121 | MEVENT_LOG_DEBUG_EXIT(NULL); 122 | } 123 | } 124 | 125 | pause(); 126 | } 127 | 128 | void HTTPServer::ListenAndServeTLS(const std::string &ip, int port, const std::string &cert_file, const std::string &key_file) { 129 | if (cert_file.empty() || key_file.empty()) { 130 | MEVENT_LOG_DEBUG_EXIT("cert_file or key_file can't be empty"); 131 | } 132 | 133 | ssl_mutex_ = static_cast(malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks())); 134 | assert(ssl_mutex_ != NULL); 135 | for (int i = 0; i < CRYPTO_num_locks(); i++) { 136 | pthread_mutex_init(&ssl_mutex_[i], NULL); 137 | } 138 | 139 | CRYPTO_set_locking_callback(SSLLockingCb); 140 | CRYPTO_set_id_callback(SSLIdCb); 141 | 142 | OPENSSL_config(NULL); 143 | 144 | SSL_library_init(); 145 | SSL_load_error_strings(); 146 | OpenSSL_add_all_algorithms(); 147 | 148 | ssl_ctx_ = SSL_CTX_new(SSLv23_server_method()); 149 | 150 | if (!ssl_ctx_) { 151 | MEVENT_LOG_DEBUG_EXIT("Unable to create SSL context"); 152 | } 153 | 154 | std::string self_path = util::ExecutablePath(); 155 | std::string ssl_cert_file; 156 | if (cert_file[0] != '/') { 157 | ssl_cert_file = self_path + cert_file; 158 | } else { 159 | ssl_cert_file = cert_file; 160 | } 161 | 162 | if (SSL_CTX_use_certificate_file(ssl_ctx_, ssl_cert_file.c_str(), SSL_FILETYPE_PEM) != 1) { 163 | MEVENT_LOG_DEBUG_EXIT("SSL_CTX_use_certificate_file(%s) failed", ssl_cert_file.c_str()); 164 | } 165 | 166 | std::string ssl_key_file; 167 | if (key_file[0] != '/') { 168 | ssl_key_file = self_path + key_file; 169 | } else { 170 | ssl_key_file = key_file; 171 | } 172 | 173 | if (SSL_CTX_use_PrivateKey_file(ssl_ctx_, ssl_key_file.c_str(), SSL_FILETYPE_PEM) != 1) { 174 | MEVENT_LOG_DEBUG_EXIT("SSL_CTX_use_PrivateKey_file(%s)", ssl_key_file.c_str()); 175 | } 176 | 177 | if(SSL_CTX_check_private_key(ssl_ctx_) != 1) { 178 | MEVENT_LOG_DEBUG_EXIT("SSL_CTX_check_private_key(%s)", ssl_key_file.c_str()); 179 | } 180 | 181 | SSL_CTX_set_read_ahead(ssl_ctx_, 1); 182 | 183 | SSL_CTX_set_options(ssl_ctx_, SSL_OP_NO_SSLv2); 184 | SSL_CTX_set_options(ssl_ctx_, SSL_OP_NO_SSLv3); 185 | SSL_CTX_set_options(ssl_ctx_, SSL_OP_NO_TLSv1); 186 | 187 | int nid; 188 | EC_KEY *ecdh; 189 | 190 | nid = OBJ_sn2nid("prime256v1"); 191 | 192 | if (nid == 0) { 193 | MEVENT_LOG_DEBUG_EXIT("OBJ_sn2nid(prime256v1) failed"); 194 | } 195 | 196 | ecdh = EC_KEY_new_by_curve_name(nid); 197 | if (ecdh == NULL) { 198 | MEVENT_LOG_DEBUG_EXIT("EC_KEY_new_by_curve_name(%d)", nid); 199 | } 200 | 201 | SSL_CTX_set_options(ssl_ctx_, SSL_OP_SINGLE_ECDH_USE); 202 | SSL_CTX_set_tmp_ecdh(ssl_ctx_, ecdh); 203 | 204 | EC_KEY_free(ecdh); 205 | 206 | ListenAndServe(ip, port); 207 | 208 | SSL_CTX_free(ssl_ctx_); 209 | EVP_cleanup(); 210 | } 211 | 212 | void HTTPServer::SetHandler(const std::string &name, HTTPHandleFunc func) { 213 | handler_.SetHandleFunc(name, func); 214 | } 215 | 216 | void HTTPServer::SetRlimitNofile(int num) { 217 | rlimit_nofile_ = num; 218 | } 219 | 220 | void HTTPServer::SetUser(const std::string &user) { 221 | user_ = user; 222 | } 223 | 224 | void HTTPServer::SetWorkerThreads(int num) { 225 | if (num < 1) { 226 | return; 227 | } 228 | worker_threads_ = num; 229 | } 230 | 231 | void HTTPServer::SetMaxWorkerConnections(int num) { 232 | if (num < 1) { 233 | return; 234 | } 235 | 236 | max_worker_connections_ = num; 237 | } 238 | 239 | void HTTPServer::SetIdleTimeout(int secs) { 240 | if (secs < 1) { 241 | return; 242 | } 243 | 244 | idle_timeout_ = secs; 245 | } 246 | 247 | void HTTPServer::SetMaxHeaderSize(size_t size) { 248 | max_header_size_ = size; 249 | } 250 | 251 | void HTTPServer::SetMaxPostSize(size_t size) { 252 | max_post_size_ = size; 253 | } 254 | 255 | void HTTPServer::Daemonize(const std::string &working_dir) { 256 | util::Daemonize(working_dir); 257 | } 258 | 259 | pthread_mutex_t *HTTPServer::ssl_mutex_ = NULL; 260 | 261 | void HTTPServer::SSLLockingCb(int mode, int type, const char *file, int line) { 262 | (void)file;//avoid unused parameter warning 263 | (void)line;//avoid unused parameter warning 264 | if (mode & CRYPTO_LOCK) { 265 | pthread_mutex_lock(&ssl_mutex_[type]); 266 | } else { 267 | pthread_mutex_unlock(&ssl_mutex_[type]); 268 | } 269 | } 270 | 271 | unsigned long HTTPServer::SSLIdCb() { 272 | return (unsigned long)pthread_self(); 273 | } 274 | 275 | }//namespace mevent 276 | -------------------------------------------------------------------------------- /http_server.h: -------------------------------------------------------------------------------- 1 | #ifndef _HTTP_SERVER_H 2 | #define _HTTP_SERVER_H 3 | 4 | #include "event_loop.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace mevent { 10 | 11 | class HTTPServer { 12 | public: 13 | HTTPServer(); 14 | ~HTTPServer() {}; 15 | 16 | void ListenAndServe(const std::string &ip, int port); 17 | 18 | void ListenAndServeTLS(const std::string &ip, int port, const std::string &cert_file, const std::string &key_file); 19 | 20 | void SetHandler(const std::string &name, HTTPHandleFunc func); 21 | 22 | //Settings 23 | void SetRlimitNofile(int num); 24 | void SetUser(const std::string &user); 25 | void SetWorkerThreads(int num); 26 | void SetMaxWorkerConnections(int num); 27 | void SetIdleTimeout(int secs); 28 | 29 | //Default 8192 bytes 30 | void SetMaxPostSize(size_t size); 31 | 32 | //Default 2048 bytes 33 | void SetMaxHeaderSize(size_t size); 34 | 35 | void Daemonize(const std::string &working_dir); 36 | 37 | private: 38 | static void *EventLoopThread(void *arg); 39 | 40 | static void SSLLockingCb(int mode, int type, const char* file, int line); 41 | static unsigned long SSLIdCb(); 42 | 43 | int listen_fd_; 44 | 45 | HTTPHandler handler_; 46 | 47 | std::string user_; 48 | int rlimit_nofile_; 49 | int worker_threads_; 50 | int max_worker_connections_; 51 | int idle_timeout_; 52 | size_t max_post_size_; 53 | size_t max_header_size_; 54 | 55 | SSL_CTX *ssl_ctx_; 56 | static pthread_mutex_t *ssl_mutex_; 57 | }; 58 | 59 | }//namespace mevent 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /lock_guard.cpp: -------------------------------------------------------------------------------- 1 | #include "lock_guard.h" 2 | #include "util.h" 3 | 4 | namespace mevent { 5 | 6 | LockGuard::LockGuard(pthread_mutex_t &mutex) : mtx(mutex) { 7 | if (pthread_mutex_lock(&mtx) != 0) { 8 | MEVENT_LOG_DEBUG_EXIT(NULL); 9 | } 10 | } 11 | 12 | LockGuard::~LockGuard() { 13 | if (pthread_mutex_unlock(&mtx) != 0) { 14 | MEVENT_LOG_DEBUG_EXIT(NULL); 15 | } 16 | } 17 | 18 | }//namespace mevent 19 | -------------------------------------------------------------------------------- /lock_guard.h: -------------------------------------------------------------------------------- 1 | #ifndef _RAII_LOCK_GUARD_H 2 | #define _RAII_LOCK_GUARD_H 3 | 4 | #include 5 | 6 | namespace mevent { 7 | 8 | class LockGuard { 9 | public: 10 | LockGuard(pthread_mutex_t &mutex); 11 | ~LockGuard(); 12 | private: 13 | pthread_mutex_t &mtx; 14 | }; 15 | 16 | }//namespace mevent 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /mevent.h: -------------------------------------------------------------------------------- 1 | #ifndef _MEVENT_H 2 | #define _MEVENT_H 3 | 4 | #define CRLF "\r\n" 5 | 6 | #define SERVER "mevent" 7 | 8 | #endif -------------------------------------------------------------------------------- /request.cpp: -------------------------------------------------------------------------------- 1 | #include "request.h" 2 | #include "connection.h" 3 | #include "util.h" 4 | #include "event_loop.h" 5 | 6 | #include 7 | 8 | #include 9 | 10 | namespace mevent { 11 | 12 | //https://github.com/nodejs/http-parser/blob/master/http_parser.c 13 | static const char tokens[256] = { 14 | /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 15 | 0, 0, 0, 0, 0, 0, 0, 0, 16 | /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 17 | 0, 0, '\n', 0, 0, '\r', 0, 0, 18 | /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 19 | 0, 0, 0, 0, 0, 0, 0, 0, 20 | /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 21 | 0, 0, 0, 0, 0, 0, 0, 0, 22 | /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ 23 | 0, '!', '"', '#', '$', '%', '&', '\'', 24 | /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 25 | '(', ')', '*', '+', ',', '-', '.', '/', 26 | /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ 27 | '0', '1', '2', '3', '4', '5', '6', '7', 28 | /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ 29 | '8', '9', ':', ';', '<', '=', '>', '?', 30 | /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ 31 | '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 32 | /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 33 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 34 | /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 35 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 36 | /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 37 | 'x', 'y', 'z', '[', 0, ']', '^', '_', 38 | /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ 39 | '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 40 | /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 41 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 42 | /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 43 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 44 | /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 45 | 'x', 'y', 'z', '{', '|', '}', '~', 0 }; 46 | 47 | 48 | 49 | static const uint8_t normal_url_char[32] = { 50 | /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 51 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, 52 | /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 53 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, 54 | /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 55 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, 56 | /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 57 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, 58 | /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ 59 | 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, 60 | /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 61 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 62 | /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ 63 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 64 | /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ 65 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, 66 | /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ 67 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 68 | /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 69 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 70 | /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 71 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 72 | /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 73 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 74 | /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ 75 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 76 | /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 77 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 78 | /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 79 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 80 | /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 81 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; 82 | 83 | #define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) 84 | 85 | #define IS_NUM(c) ((c) >= '0' && (c) <= '9') 86 | 87 | #define CONTENT_LENGTH "content-length" 88 | #define UPGRADE "upgrade" 89 | #define SEC_WEBSOCKET_KEY "sec-websocket-key" 90 | #define CONTENT_TYPE "content-type" 91 | 92 | #define METHOD_GET "get" 93 | #define METHOD_POST "post" 94 | #define METHOD_HEAD "head" 95 | #define METHOD_PUT "put" 96 | #define METHOD_DELETE "delete" 97 | #define METHOD_CONNECT "connect" 98 | #define METHOD_OPTION "options" 99 | #define METHOD_TRACE "trace" 100 | #define METHOD_PATCH "patch" 101 | 102 | #define CR '\r' 103 | #define LF '\n' 104 | 105 | #ifndef BIT_AT 106 | # define BIT_AT(a, i) \ 107 | (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ 108 | (1 << ((unsigned int) (i) & 7)))) 109 | #endif 110 | 111 | #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) 112 | 113 | 114 | #define READ_BUFFER_SIZE 2048 115 | 116 | Request::Request(Connection *conn) : conn_(conn) { 117 | Reset(); 118 | } 119 | 120 | void Request::ParseHeader() { 121 | char *pos = strchr(const_cast(rbuf_.c_str()), '\n'); 122 | bool is_field = true; 123 | 124 | std::string field; 125 | std::string value; 126 | for (; *pos; pos++) { 127 | if (*pos == ':' && is_field) { 128 | is_field = false; 129 | continue; 130 | } else if (*pos == '\r') { 131 | continue; 132 | } else if (*pos == '\n') { 133 | if (!field.empty() && !value.empty()) { 134 | header_map_[field] = value; 135 | } 136 | field.clear(); 137 | value.clear(); 138 | is_field = true; 139 | continue; 140 | } 141 | 142 | if (is_field) { 143 | field.push_back(*pos); 144 | } else { 145 | if (value.empty() && *pos == ' ') { //trim 146 | continue; 147 | } 148 | value.push_back(*pos); 149 | } 150 | } 151 | } 152 | 153 | std::string Request::HeaderValue(const std::string &field) { 154 | std::string value; 155 | 156 | auto it = header_map_.find(field); 157 | if (it != header_map_.end()) { 158 | value = it->second; 159 | } 160 | 161 | return value; 162 | } 163 | 164 | void Request::ParseQueryString() { 165 | if (query_string_.empty()) { 166 | return; 167 | } 168 | 169 | ParseFormUrlencoded(get_form_map_, query_string_); 170 | } 171 | 172 | std::string Request::QueryStringValue(const std::string &field) { 173 | std::string value; 174 | 175 | auto it = get_form_map_.find(field); 176 | if (it != get_form_map_.end()) { 177 | value = it->second; 178 | } 179 | 180 | return value; 181 | } 182 | 183 | void Request::ParsePostForm() { 184 | if (content_type_ != "application/x-www-form-urlencoded") { 185 | return; 186 | } 187 | 188 | ParseFormUrlencoded(post_form_map_, rbuf_.substr(header_len_)); 189 | } 190 | 191 | std::string Request::PostFormValue(const std::string &field) { 192 | std::string value; 193 | 194 | auto it = post_form_map_.find(field); 195 | if (it != post_form_map_.end()) { 196 | value = it->second; 197 | } 198 | 199 | return value; 200 | } 201 | 202 | uint32_t Request::ContentLength() { 203 | return content_length_; 204 | } 205 | 206 | std::string Request::Body() { 207 | return rbuf_.substr(header_len_); 208 | } 209 | 210 | std::string Request::Path() { 211 | return path_; 212 | } 213 | 214 | std::string Request::QueryString() { 215 | return query_string_; 216 | } 217 | 218 | RequestMethod Request::Method() { 219 | return method_; 220 | } 221 | 222 | std::string Request::RemoteAddr() { 223 | char buf[INET_ADDRSTRLEN]; 224 | inet_ntop(AF_INET, &addr_, buf, INET_ADDRSTRLEN); 225 | return std::string(buf); 226 | } 227 | 228 | void Request::ParseFormUrlencoded(std::map &m, const std::string &str) { 229 | bool is_field = true; 230 | 231 | std::string field; 232 | std::string value; 233 | const char *pos = str.c_str(); 234 | for (; *pos; pos++) { 235 | if (*pos == '=') { 236 | is_field = false; 237 | continue; 238 | } else if (*pos == '&') { 239 | if (!field.empty() && !value.empty()) { 240 | m[field] = util::URLDecode(value); 241 | } 242 | field.clear(); 243 | value.clear(); 244 | is_field = true; 245 | continue; 246 | } 247 | 248 | if (is_field) { 249 | field.push_back(*pos); 250 | } else { 251 | value.push_back(*pos); 252 | } 253 | } 254 | 255 | if (!field.empty() && !value.empty()) { 256 | m[field] = util::URLDecode(value); 257 | } 258 | } 259 | 260 | void Request::Reset() { 261 | status_ = RequestStatus::HEADER_RECEIVING; 262 | 263 | bzero(&addr_, sizeof(addr_)); 264 | 265 | rbuf_.clear(); 266 | std::string().swap(rbuf_); 267 | rbuf_len_ = 0; 268 | 269 | method_ = RequestMethod::UNKNOWN; 270 | 271 | path_.clear(); 272 | std::string().swap(path_); 273 | query_string_.clear(); 274 | std::string().swap(query_string_); 275 | 276 | content_length_ = 0; 277 | header_len_ = 0; 278 | 279 | content_type_.clear(); 280 | std::string().swap(content_type_); 281 | 282 | parse_status_ = RequestParseStatus::S_START; 283 | parse_offset_ = 0; 284 | parse_match_ = 0; 285 | 286 | error_code_ = 0; 287 | 288 | sec_websocket_key_.clear(); 289 | std::string().swap(sec_websocket_key_); 290 | 291 | header_map_.clear(); 292 | std::map().swap(header_map_); 293 | get_form_map_.clear(); 294 | std::map().swap(get_form_map_); 295 | post_form_map_.clear(); 296 | std::map().swap(post_form_map_); 297 | } 298 | 299 | void Request::Keepalive() 300 | { 301 | status_ = RequestStatus::HEADER_RECEIVING; 302 | rbuf_.clear(); 303 | rbuf_len_ = 0; 304 | } 305 | 306 | ConnStatus Request::ReadData() { 307 | char buf[READ_BUFFER_SIZE]; 308 | ssize_t n = 0; 309 | 310 | if (status_ != RequestStatus::HEADER_RECEIVING && status_ != RequestStatus::BODY_RECEIVING) { 311 | return ConnStatus::ERROR; 312 | } 313 | 314 | if (status_ == RequestStatus::HEADER_RECEIVING) { 315 | do { 316 | n = conn_->Readn(buf, READ_BUFFER_SIZE); 317 | if (n > 0) { 318 | rbuf_len_ += n; 319 | if (rbuf_len_ > conn_->elp_->max_header_size_) { 320 | error_code_ = 500; 321 | return ConnStatus::ERROR; 322 | } 323 | 324 | rbuf_.append(buf, n); 325 | 326 | HTTPParserStatus parse_status = Parse(); 327 | if (parse_status == HTTPParserStatus::FINISHED) { 328 | status_ = RequestStatus::BODY_RECEIVING; 329 | break; 330 | } else if (parse_status == HTTPParserStatus::ERROR) { 331 | error_code_ = 400; 332 | return ConnStatus::ERROR; 333 | } 334 | } else if (n < 0) { 335 | return ConnStatus::CLOSE; 336 | } else { 337 | break; 338 | } 339 | } while (n == READ_BUFFER_SIZE); 340 | } 341 | 342 | if (status_ == RequestStatus::BODY_RECEIVING) { 343 | if (content_length_ > conn_->elp_->max_post_size_) { 344 | error_code_ = 500; 345 | return ConnStatus::ERROR; 346 | } 347 | 348 | if (rbuf_len_ - header_len_ >= content_length_) { 349 | status_ = RequestStatus::BODY_RECEIVED; 350 | } else { 351 | do { 352 | n = conn_->Readn(buf, READ_BUFFER_SIZE); 353 | if (n > 0) { 354 | rbuf_len_ += n; 355 | rbuf_.append(buf, n); 356 | 357 | if (rbuf_len_ - header_len_ >= content_length_) { 358 | status_ = RequestStatus::BODY_RECEIVED; 359 | break; 360 | } 361 | } else if (n < 0) { 362 | return ConnStatus::CLOSE; 363 | } else { 364 | break; 365 | } 366 | } while (n == READ_BUFFER_SIZE); 367 | } 368 | } 369 | 370 | if (status_ == RequestStatus::BODY_RECEIVED) { 371 | conn_->TaskPush(); 372 | } 373 | 374 | return ConnStatus::AGAIN; 375 | } 376 | 377 | HTTPParserStatus Request::Parse() { 378 | HTTPParserStatus status = HTTPParserStatus::CONTINUE; 379 | 380 | while (parse_offset_ < rbuf_len_) { 381 | char ch = rbuf_[parse_offset_]; 382 | 383 | char c = TOKEN(ch); 384 | if (!c) { 385 | status = HTTPParserStatus::ERROR; 386 | break; 387 | } 388 | 389 | if (parse_status_ == RequestParseStatus::S_EOL) { 390 | if (c == LF) { 391 | if (rbuf_[parse_offset_ - 1] == CR) { 392 | parse_status_ = RequestParseStatus::S_HEADER_FIELD; 393 | parse_match_ = parse_offset_; 394 | } else { 395 | status = HTTPParserStatus::ERROR; 396 | break; 397 | } 398 | } 399 | } else if (parse_status_ == RequestParseStatus::S_START) { 400 | parse_status_ = RequestParseStatus::S_METHOD; 401 | if (c == 'g') { 402 | method_ = RequestMethod::GET; 403 | } else if (c == 'p') { 404 | parse_status_ = RequestParseStatus::S_METHOD_P; 405 | } else if (c == 'h') { 406 | method_ = RequestMethod::HEAD; 407 | } else if (c == 'd') { 408 | method_ = RequestMethod::DELETE; 409 | } else if (c == 'c') { 410 | method_ = RequestMethod::CONNECT; 411 | } else if (c == 'o') { 412 | method_ = RequestMethod::OPTIONS; 413 | } else if (c == 't') { 414 | method_ = RequestMethod::TRACE; 415 | } else if (c == 'p') { 416 | method_ = RequestMethod::PATCH; 417 | } else { 418 | parse_status_ = RequestParseStatus::S_START; 419 | status = HTTPParserStatus::ERROR; 420 | break; 421 | } 422 | } else if (parse_status_ == RequestParseStatus::S_METHOD) { 423 | const char *method = NULL; 424 | if (method_ == RequestMethod::GET) { 425 | method = METHOD_GET; 426 | } else if (method_ == RequestMethod::POST) { 427 | method = METHOD_POST; 428 | } else if (method_ == RequestMethod::HEAD) { 429 | method = METHOD_HEAD; 430 | } else if (method_ == RequestMethod::PUT) { 431 | method = METHOD_PUT; 432 | } else if (method_ == RequestMethod::DELETE) { 433 | method = METHOD_DELETE; 434 | } else if (method_ == RequestMethod::CONNECT) { 435 | method = METHOD_CONNECT; 436 | } else if (method_ == RequestMethod::OPTIONS) { 437 | method = METHOD_OPTION; 438 | } else if (method_ == RequestMethod::TRACE) { 439 | method = METHOD_TRACE; 440 | } else if (method_ == RequestMethod::PATCH) { 441 | method = METHOD_PATCH; 442 | } else { 443 | status = HTTPParserStatus::ERROR; 444 | break; 445 | } 446 | 447 | size_t len = strlen(method); 448 | 449 | if (parse_offset_ > len - 1) { 450 | if (c != ' ') { 451 | if (parse_offset_ == len) { 452 | status = HTTPParserStatus::ERROR; 453 | break; 454 | } else { 455 | parse_status_ = RequestParseStatus::S_PATH; 456 | parse_match_ = parse_offset_; 457 | continue; 458 | } 459 | } 460 | } else { 461 | if (method[parse_offset_ - parse_match_] != c) { 462 | status = HTTPParserStatus::ERROR; 463 | break; 464 | } 465 | } 466 | } else if (parse_status_ == RequestParseStatus::S_METHOD_P) { 467 | parse_status_ = RequestParseStatus::S_METHOD; 468 | if (c == 'o') { 469 | method_ = RequestMethod::POST; 470 | } else if (c == 'u') { 471 | method_ = RequestMethod::PUT; 472 | } else if (c == 'a') { 473 | method_ = RequestMethod::PATCH; 474 | } else { 475 | status = HTTPParserStatus::ERROR; 476 | break; 477 | } 478 | } else if (parse_status_ == RequestParseStatus::S_PATH) { 479 | if (ch != ' ') { 480 | if (IS_URL_CHAR(ch)) { 481 | path_.push_back(ch); 482 | } else { 483 | if (ch == '?') { 484 | parse_status_ = RequestParseStatus::S_QUERY_STRING; 485 | parse_offset_++; 486 | continue; 487 | } else { 488 | status = HTTPParserStatus::ERROR; 489 | break; 490 | } 491 | } 492 | } else { 493 | if (!path_.empty()) { 494 | parse_status_ = RequestParseStatus::S_EOL; 495 | } else { 496 | status = HTTPParserStatus::ERROR; 497 | break; 498 | } 499 | } 500 | } else if (parse_status_ == RequestParseStatus::S_QUERY_STRING) { 501 | if (ch != ' ') { 502 | if (IS_URL_CHAR(ch)) { 503 | query_string_.push_back(ch); 504 | } else { 505 | status = HTTPParserStatus::ERROR; 506 | break; 507 | } 508 | } else { 509 | parse_status_ = RequestParseStatus::S_EOL; 510 | } 511 | } else if (parse_status_ == RequestParseStatus::S_HEADER_FIELD) { 512 | if (c == CR) { 513 | parse_status_ = RequestParseStatus::S_EOH; 514 | } else if (c == 'c') { 515 | if (rbuf_len_ - parse_offset_ < 9) { 516 | parse_offset_++; 517 | continue; 518 | } 519 | 520 | if (TOKEN(rbuf_[parse_offset_ + 1]) == 'o' 521 | && TOKEN(rbuf_[parse_offset_ + 2]) == 'n' 522 | && TOKEN(rbuf_[parse_offset_ + 3]) == 't' 523 | && TOKEN(rbuf_[parse_offset_ + 4]) == 'e' 524 | && TOKEN(rbuf_[parse_offset_ + 5]) == 'n' 525 | && TOKEN(rbuf_[parse_offset_ + 6]) == 't' 526 | && TOKEN(rbuf_[parse_offset_ + 7]) == '-') { 527 | 528 | if (TOKEN(rbuf_[parse_offset_ + 8]) == 'l') { 529 | parse_status_ = RequestParseStatus::S_CONTENT_LENGTH; 530 | parse_match_ = parse_offset_; 531 | } else if (TOKEN(rbuf_[parse_offset_ + 8]) == 't') { 532 | parse_status_ = RequestParseStatus::S_CONTENT_TYPE; 533 | parse_match_ = parse_offset_; 534 | } else { 535 | parse_status_ = RequestParseStatus::S_EOL; 536 | } 537 | } else { 538 | parse_status_ = RequestParseStatus::S_EOL; 539 | } 540 | } else if (c == 's') { 541 | parse_status_ = RequestParseStatus::S_SEC_WEBSOCKET_KEY; 542 | parse_match_ = parse_offset_; 543 | } else { 544 | parse_status_ = RequestParseStatus::S_EOL; 545 | } 546 | } else if (parse_status_ == RequestParseStatus::S_EOH) { 547 | if (c == LF) { 548 | header_len_ = parse_offset_ + 1; 549 | status = HTTPParserStatus::FINISHED; 550 | } else { 551 | status = HTTPParserStatus::ERROR; 552 | } 553 | break; 554 | } else if (parse_status_ == RequestParseStatus::S_CONTENT_LENGTH) { 555 | if (parse_offset_ - parse_match_ > 13) { 556 | if (c != ' ' && c != ':') { 557 | parse_status_ = RequestParseStatus::S_CONTNET_LENGTH_V; 558 | parse_match_ = parse_offset_; 559 | continue; 560 | } 561 | } else { 562 | if (CONTENT_LENGTH[parse_offset_ - parse_match_] != c) { 563 | parse_status_ = RequestParseStatus::S_EOL; 564 | } 565 | } 566 | } else if (parse_status_ == RequestParseStatus::S_CONTNET_LENGTH_V) { 567 | if (c == ' ' || c == CR) { 568 | parse_status_ = RequestParseStatus::S_EOL; 569 | } else { 570 | if (!IS_NUM(c)) { 571 | status = HTTPParserStatus::ERROR; 572 | break; 573 | } 574 | 575 | content_length_ *= 10; 576 | content_length_ += c - '0'; 577 | } 578 | } else if (parse_status_ == RequestParseStatus::S_SEC_WEBSOCKET_KEY) { 579 | if (parse_offset_ - parse_match_ > 16) { 580 | if (c != ' ' && c != ':') { 581 | parse_status_ = RequestParseStatus::S_SEC_WEBSOCKET_KEY_V; 582 | parse_match_ = parse_offset_; 583 | continue; 584 | } 585 | } else { 586 | if (SEC_WEBSOCKET_KEY[parse_offset_ - parse_match_] != c) { 587 | parse_status_ = RequestParseStatus::S_EOL; 588 | } 589 | } 590 | } else if (parse_status_ == RequestParseStatus::S_SEC_WEBSOCKET_KEY_V) { 591 | if (c == ' ' || c == CR) { 592 | parse_status_ = RequestParseStatus::S_EOL; 593 | } else { 594 | sec_websocket_key_.push_back(ch); 595 | } 596 | } else if (parse_status_ == RequestParseStatus::S_CONTENT_TYPE) { 597 | if (parse_offset_ - parse_match_ > 11) { 598 | if (c != ' ' && c != ':') { 599 | parse_status_ = RequestParseStatus::S_CONTENT_TYPE_V; 600 | parse_match_ = parse_offset_; 601 | continue; 602 | } 603 | } else { 604 | if (CONTENT_TYPE[parse_offset_ - parse_match_] != c) { 605 | parse_status_ = RequestParseStatus::S_EOL; 606 | } 607 | } 608 | } else if (parse_status_ == RequestParseStatus::S_CONTENT_TYPE_V) { 609 | if (c == ' ' || c == CR) { 610 | parse_status_ = RequestParseStatus::S_EOL; 611 | } else { 612 | content_type_.push_back(ch); 613 | } 614 | } 615 | 616 | parse_offset_++; 617 | } 618 | 619 | return status; 620 | } 621 | 622 | }//namespace mevent 623 | -------------------------------------------------------------------------------- /request.h: -------------------------------------------------------------------------------- 1 | #ifndef _REQUEST_H 2 | #define _REQUEST_H 3 | 4 | #include "conn_status.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | namespace mevent { 13 | 14 | enum class RequestMethod : uint8_t { 15 | GET, 16 | POST, 17 | HEAD, 18 | PUT, 19 | DELETE, 20 | CONNECT, 21 | OPTIONS, 22 | TRACE, 23 | PATCH, 24 | UNKNOWN 25 | }; 26 | 27 | enum class RequestStatus : uint8_t { 28 | HEADER_RECEIVING, 29 | BODY_RECEIVING, 30 | BODY_RECEIVED, 31 | UPGRADE, 32 | }; 33 | 34 | enum class RequestParseStatus : uint8_t { 35 | S_START, 36 | S_METHOD, 37 | S_METHOD_P, 38 | S_PATH, 39 | S_QUERY_STRING, 40 | S_CONTENT_LENGTH, 41 | S_CONTNET_LENGTH_V, 42 | S_CONTENT_TYPE, 43 | S_CONTENT_TYPE_V, 44 | S_SEC_WEBSOCKET_KEY, 45 | S_SEC_WEBSOCKET_KEY_V, 46 | S_UPGRADE, 47 | S_EOL, 48 | S_HEADER_FIELD, 49 | S_EOH 50 | }; 51 | 52 | enum class HTTPParserStatus : uint8_t { 53 | CONTINUE, 54 | ERROR, 55 | FINISHED 56 | }; 57 | 58 | class Connection; 59 | class WebSocket; 60 | class EventLoop; 61 | 62 | class Request { 63 | public: 64 | Request(Connection *conn); 65 | virtual ~Request() {}; 66 | 67 | void ParseHeader(); 68 | std::string HeaderValue(const std::string &field); 69 | 70 | void ParseQueryString(); 71 | std::string QueryStringValue(const std::string &field); 72 | 73 | void ParsePostForm(); 74 | std::string PostFormValue(const std::string &field); 75 | 76 | // void ParseCookie(); 77 | // void std::string CookieValue(const std::string &field); 78 | 79 | uint32_t ContentLength(); 80 | 81 | std::string Body(); 82 | 83 | std::string Path(); 84 | std::string QueryString(); 85 | 86 | RequestMethod Method(); 87 | 88 | std::string RemoteAddr(); 89 | 90 | private: 91 | friend class Connection; 92 | friend class WebSocket; 93 | friend class EventLoop; 94 | 95 | void ParseFormUrlencoded(std::map &m, const std::string &str); 96 | 97 | void Reset(); 98 | 99 | void Keepalive(); 100 | 101 | ConnStatus ReadData(); 102 | 103 | HTTPParserStatus Parse(); 104 | 105 | RequestStatus status_; 106 | 107 | struct in_addr addr_; 108 | 109 | std::string rbuf_; 110 | uint32_t rbuf_len_; 111 | 112 | RequestMethod method_; 113 | 114 | std::string path_; 115 | std::string query_string_; 116 | 117 | uint32_t content_length_; 118 | size_t header_len_; 119 | 120 | RequestParseStatus parse_status_; 121 | uint32_t parse_offset_; 122 | uint32_t parse_match_; 123 | 124 | Connection *conn_; 125 | 126 | int error_code_; 127 | 128 | std::string content_type_; 129 | 130 | std::string sec_websocket_key_; 131 | 132 | std::map header_map_; 133 | 134 | std::map get_form_map_; 135 | std::map post_form_map_; 136 | }; 137 | 138 | } 139 | 140 | #endif 141 | -------------------------------------------------------------------------------- /response.cpp: -------------------------------------------------------------------------------- 1 | #include "response.h" 2 | #include "mevent.h" 3 | #include "util.h" 4 | #include "connection.h" 5 | 6 | namespace mevent { 7 | 8 | #define HTTP_200_HEAD "HTTP/1.1 200 OK" CRLF 9 | #define HTTP_400_HEAD "HTTP/1.1 400 Bad Request" CRLF 10 | #define HTTP_403_HEAD "HTTP/1.1 403 Forbidden" CRLF 11 | #define HTTP_404_HEAD "HTTP/1.1 404 Not Found" CRLF 12 | #define HTTP_500_HEAD "HTTP/1.1 500 Internal Server Error" CRLF 13 | 14 | #define HTTP_400_MSG "400 Bad Request

400 Bad Request


" SERVER "
" 15 | #define HTTP_403_MSG "403 Forbidden

403 Forbidden


" SERVER "
" 16 | #define HTTP_404_MSG "404 Not Found

404 Not Found


" SERVER "
" 17 | #define HTTP_500_MSG "500 Internal Server Error

500 Internal Server Error


" SERVER "
" 18 | 19 | Response::Response(Connection *conn) : conn_(conn) { 20 | Reset(); 21 | } 22 | 23 | void Response::Reset() { 24 | header_map_.clear(); 25 | 26 | wbuf_.clear(); 27 | wbuf_.shrink_to_fit(); 28 | 29 | finish_ = false; 30 | } 31 | 32 | void Response::WriteErrorMessage(int code) { 33 | std::string str; 34 | 35 | switch (code) { 36 | case 400: 37 | str = HTTP_400_HEAD "Server: " SERVER CRLF "Content-Type: text/html" CRLF "Content-Length: " + std::to_string(sizeof(HTTP_403_MSG) - 1) + CRLF "Date: " + util::GetGMTimeStr() + CRLF "Connection: close" CRLF CRLF HTTP_400_MSG; 38 | break; 39 | case 403: 40 | str = HTTP_403_HEAD "Server: " SERVER CRLF "Content-Type: text/html" CRLF "Content-Length: " + std::to_string(sizeof(HTTP_403_MSG) - 1) + CRLF "Date: " + util::GetGMTimeStr() + CRLF "Connection: close" CRLF CRLF HTTP_403_MSG; 41 | break; 42 | case 404: 43 | str = HTTP_404_HEAD "Server: " SERVER CRLF "Content-Type: text/html" CRLF "Content-Length: " + std::to_string(sizeof(HTTP_404_MSG) - 1) + CRLF "Date: " + util::GetGMTimeStr() + CRLF "Connection: close" CRLF CRLF HTTP_404_MSG; 44 | break; 45 | default: 46 | str = HTTP_500_HEAD "Server: " SERVER CRLF "Content-Type: text/html" CRLF "Content-Length: " + std::to_string(sizeof(HTTP_500_MSG) - 1) + CRLF "Date: " + util::GetGMTimeStr() + CRLF "Connection: close" CRLF CRLF HTTP_500_MSG; 47 | break; 48 | } 49 | 50 | conn_->WriteString(str); 51 | 52 | wbuf_.clear(); 53 | 54 | finish_ = true; 55 | } 56 | 57 | void Response::WriteData(const std::vector &data) { 58 | wbuf_.insert(wbuf_.end(), data.begin(), data.end()); 59 | } 60 | 61 | void Response::WriteString(const std::string &str) { 62 | wbuf_.insert(wbuf_.end(), str.begin(), str.end()); 63 | } 64 | 65 | void Response::Flush() { 66 | finish_ = true; 67 | 68 | if (wbuf_.empty()) { 69 | return; 70 | } 71 | 72 | header_map_["Content-Length"] = std::vector{std::to_string(wbuf_.length())}; 73 | 74 | conn_->WriteString(MakeHeader()); 75 | conn_->WriteString(wbuf_); 76 | } 77 | 78 | void Response::SetHeader(const std::string &field, const std::string &value) { 79 | if (field.empty() || value.empty()) { 80 | return; 81 | } 82 | 83 | header_map_[field] = std::vector{value}; 84 | } 85 | 86 | void Response::AddHeader(const std::string &field, const std::string &value ) { 87 | if (field.empty() || value.empty()) { 88 | return; 89 | } 90 | 91 | header_map_[field].push_back(value); 92 | } 93 | 94 | void Response::DelHeader(const std::string &field) { 95 | header_map_.erase(field); 96 | } 97 | 98 | std::string Response::MakeHeader() { 99 | std::string str = HTTP_200_HEAD; 100 | 101 | auto it = header_map_.find("Server"); 102 | if (it == header_map_.end()) { 103 | str += "Server: " SERVER CRLF; 104 | } 105 | 106 | it = header_map_.find("Date"); 107 | if (it == header_map_.end()) { 108 | str += "Date: " + util::GetGMTimeStr() + CRLF; 109 | } 110 | 111 | it = header_map_.find("Content-Type"); 112 | if (it == header_map_.end()) { 113 | str += "Content-Type: application/octet-stream" CRLF; 114 | } 115 | 116 | header_map_["Connection"] = std::vector{"close"}; 117 | 118 | for (it = header_map_.begin(); it != header_map_.end(); it++) { 119 | for (auto vit = it->second.begin(); vit != it->second.end(); vit++) { 120 | str += it->first + ": " + *vit + CRLF; 121 | } 122 | } 123 | 124 | str += CRLF; 125 | 126 | return str; 127 | } 128 | 129 | }//namespace mevent 130 | -------------------------------------------------------------------------------- /response.h: -------------------------------------------------------------------------------- 1 | #ifndef _RESPONSE_H 2 | #define _RESPONSE_H 3 | 4 | #include "conn_status.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace mevent { 13 | 14 | class Connection; 15 | 16 | class Response { 17 | public: 18 | Response(Connection *conn); 19 | ~Response() {} 20 | 21 | void Reset(); 22 | 23 | void WriteErrorMessage(int code); 24 | 25 | void WriteString(const std::string &str); 26 | void WriteData(const std::vector &data); 27 | 28 | void SetHeader(const std::string &field, const std::string &value); 29 | void AddHeader(const std::string &field, const std::string &value); 30 | void DelHeader(const std::string &field); 31 | 32 | private: 33 | friend class Connection; 34 | friend class EventLoop; 35 | 36 | std::string MakeHeader(); 37 | void Flush(); 38 | 39 | Connection *conn_; 40 | std::map> header_map_; 41 | 42 | std::string wbuf_; 43 | 44 | bool finish_; 45 | }; 46 | 47 | }//namespace mevent 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /ternary_search_tree.cpp: -------------------------------------------------------------------------------- 1 | #include "ternary_search_tree.h" 2 | #include "util.h" 3 | 4 | #define TST_MAX_RANK 0xffffffff 5 | 6 | namespace mevent { 7 | namespace tst { 8 | 9 | TernarySearchTree::TernarySearchTree() { 10 | root_ = NULL; 11 | } 12 | 13 | TernarySearchTree::~TernarySearchTree() { 14 | Clean(root_); 15 | } 16 | 17 | Node *TernarySearchTree::Insert(const NodeData *data, Node **npp) { 18 | root_ = Insert(root_, data->str.c_str(), data, npp); 19 | return root_; 20 | } 21 | 22 | Node *TernarySearchTree::Insert(Node *np, const char *pos, const NodeData *data, Node **npp) { 23 | if (!np) { 24 | np = new Node(); 25 | 26 | np->c = *pos; 27 | np->left = NULL; 28 | np->center = NULL; 29 | np->right = NULL; 30 | np->data = NULL; 31 | } 32 | 33 | if (*pos < np->c) { 34 | np->left = Insert(np->left, pos, data, npp); 35 | } else if (*pos > np->c) { 36 | np->right = Insert(np->right, pos, data, npp); 37 | } else { 38 | if (*(pos + 1) == 0) { 39 | if (!np->data) { 40 | np->data = new NodeData(*data); 41 | 42 | if (npp) { 43 | *npp = np; 44 | } 45 | } else { 46 | if (npp) { 47 | *npp = NULL; 48 | } 49 | } 50 | } else { 51 | np->center = Insert(np->center, ++pos, data, npp); 52 | } 53 | } 54 | 55 | return np; 56 | } 57 | 58 | void TernarySearchTree::Traverse(Node *np, Result *rp) { 59 | if (!np) { 60 | return; 61 | } 62 | 63 | Traverse(np->left, rp); 64 | 65 | if (!np->data) { 66 | Traverse(np->center, rp); 67 | } else { 68 | if (rp) { 69 | ResultAdd(rp, np->data); 70 | } 71 | 72 | if (rp->count > rp->limit) { 73 | return; 74 | } 75 | 76 | if (np->center != 0) { 77 | Traverse(np->center, rp); 78 | } 79 | } 80 | 81 | Traverse(np->right, rp); 82 | } 83 | 84 | void TernarySearchTree::Traverse(Result *tsrp) { 85 | Traverse(root_, tsrp); 86 | } 87 | 88 | Result *TernarySearchTree::ResultInit() { 89 | Result *result = new Result(); 90 | 91 | result->count = 0; 92 | result->list = NULL; 93 | result->tail = NULL; 94 | 95 | return result; 96 | } 97 | 98 | void TernarySearchTree::Search(Node *np, const char *pos, Result *rp) { 99 | if (!np) { 100 | return; 101 | } 102 | 103 | if (*pos < np->c) { 104 | Search(np->left, pos, rp); 105 | } else if (*pos > np->c) { 106 | Search(np->right, pos, rp); 107 | } else { 108 | if (*(pos + 1) == 0) { 109 | if (np->data) { 110 | ResultAdd(rp, np->data); 111 | } 112 | 113 | Traverse(np->center, rp); 114 | } else { 115 | Search(np->center, ++pos, rp); 116 | } 117 | } 118 | 119 | } 120 | 121 | void TernarySearchTree::Search(Node *np, const char *pos, NodeData *ndp) { 122 | if (!np) { 123 | return; 124 | } 125 | 126 | if (*pos < np->c) { 127 | Search(np->left, pos, ndp); 128 | } else if (*pos > np->c) { 129 | Search(np->right, pos, ndp); 130 | } else { 131 | if (np->data) { 132 | ndp->func = np->data->func; 133 | ndp->str = np->data->str; 134 | } 135 | 136 | if (*(pos + 1)) { 137 | Search(np->center, ++pos, ndp); 138 | } 139 | } 140 | } 141 | 142 | Result *TernarySearchTree::Search(const char *str, int limit) { 143 | Result *result = ResultInit(); 144 | result->limit = limit; 145 | if (!result) { 146 | return NULL; 147 | } 148 | 149 | Search(root_, str, result); 150 | 151 | return result; 152 | } 153 | 154 | NodeData TernarySearchTree::SearchOne(const char *str) { 155 | NodeData result; 156 | 157 | Search(root_, str, &result); 158 | 159 | return result; 160 | } 161 | 162 | void TernarySearchTree::ResultAdd(Result *rp, const NodeData *data) { 163 | if (!data) { 164 | return; 165 | } 166 | 167 | ResultNode *node = new ResultNode(); 168 | 169 | node->data = new NodeData(*data); 170 | 171 | node->next = NULL; 172 | node->prev = NULL; 173 | 174 | if (!rp->tail) { 175 | rp->list = node; 176 | rp->tail = node; 177 | } else { 178 | node->prev = rp->tail; 179 | rp->tail->next = node; 180 | rp->tail = node; 181 | } 182 | 183 | rp->count++; 184 | } 185 | 186 | void TernarySearchTree::Erase(const char *str) { 187 | Node *last_np = NULL, *np = root_, *first_np = NULL; 188 | const char *s = str, *last_s = s; 189 | 190 | if (!str) { 191 | return; 192 | } 193 | 194 | while (np) { 195 | if (*s == np->c) { 196 | if (!first_np) { 197 | first_np = np; 198 | } 199 | 200 | if (*++s == 0) { 201 | break; 202 | } 203 | 204 | np = np->center; 205 | if (!np) { 206 | return; 207 | } 208 | } else { 209 | last_s = s; 210 | 211 | if (*s < np->c) { 212 | np = np->left; 213 | } else { 214 | np = np->right; 215 | } 216 | 217 | if (!np) { 218 | break; 219 | } 220 | } 221 | 222 | if (np->left || np->right) { 223 | last_np = np; 224 | last_s = s + 1; 225 | } 226 | } 227 | 228 | if (*s != 0) { 229 | return; 230 | } 231 | 232 | if (!np->data) { 233 | return; 234 | } else { 235 | delete np->data; 236 | np->data = NULL; 237 | } 238 | 239 | if (np->center) { 240 | return; 241 | } 242 | 243 | if (last_np) { 244 | if (last_np->left && last_np->left->c == *last_s) { 245 | Clean(last_np->left); 246 | last_np->left = NULL; 247 | } else if (last_np->right && last_np->right->c == *last_s) { 248 | Clean(last_np->right); 249 | last_np->right = NULL; 250 | } else if(last_np->center && last_np->center->c == *last_s) { 251 | Clean(last_np->center); 252 | last_np->center = NULL; 253 | } 254 | } else { 255 | Clean(first_np->center); 256 | first_np->center = NULL; 257 | } 258 | } 259 | 260 | void TernarySearchTree::Clean(Node *np) { 261 | if (!np) { 262 | return; 263 | } 264 | 265 | Clean(np->left); 266 | Clean(np->center); 267 | Clean(np->right); 268 | 269 | if (np->data) { 270 | delete np->data; 271 | } 272 | 273 | delete np; 274 | } 275 | 276 | void TernarySearchTree::FreeNode(Node *tnp) { 277 | if (tnp->data) { 278 | delete tnp->data; 279 | } 280 | 281 | delete tnp; 282 | } 283 | 284 | void TernarySearchTree::FreeResult(Result *rp) { 285 | ResultNode *list = rp->list; 286 | ResultNode *node; 287 | 288 | while (list) { 289 | node = list; 290 | list = list->next; 291 | delete node->data; 292 | delete node; 293 | } 294 | 295 | delete rp; 296 | } 297 | 298 | } // namespace tst 299 | } // namespace mevent -------------------------------------------------------------------------------- /ternary_search_tree.h: -------------------------------------------------------------------------------- 1 | #ifndef _TST_H 2 | #define _TST_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace mevent { 13 | 14 | class Connection; 15 | 16 | typedef std::function HTTPHandleFunc; 17 | 18 | namespace tst { 19 | 20 | struct NodeData { 21 | std::string str; 22 | HTTPHandleFunc func; 23 | }; 24 | 25 | struct Node { 26 | Node *left; 27 | Node *center; 28 | Node *right; 29 | NodeData *data; 30 | char c; 31 | }; 32 | 33 | struct ResultNode { 34 | ResultNode *next; 35 | ResultNode *prev; 36 | NodeData *data; 37 | }; 38 | 39 | struct Result { 40 | ResultNode *list; 41 | ResultNode *tail; 42 | int count; 43 | int limit; 44 | }; 45 | 46 | class TernarySearchTree { 47 | public: 48 | TernarySearchTree(); 49 | ~TernarySearchTree(); 50 | 51 | Node *Insert(const NodeData *data, Node **npp); 52 | 53 | void Traverse(Result *rp); 54 | 55 | Result *Search(const char *str, int limit); 56 | 57 | NodeData SearchOne(const char *str); 58 | 59 | void FreeResult(Result *rp); 60 | 61 | void Erase(const char *str); 62 | 63 | void Traverse(Node *np, Result *rp); 64 | 65 | private: 66 | Node *Insert(Node *np, const char *pos, const NodeData *data, Node **npp); 67 | 68 | void Search(Node *np, const char *pos, Result *rp); 69 | void Search(Node *np, const char *pos, NodeData *ndp); 70 | 71 | void Clean(Node *np); 72 | 73 | void FreeNode(Node *np); 74 | 75 | Result *ResultInit(); 76 | 77 | void ResultAdd(Result *rp, const NodeData *data); 78 | 79 | Node *root_; 80 | }; 81 | 82 | } // namespace tst 83 | } // namespace mevent 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef __APPLE__ 15 | #include 16 | #endif 17 | 18 | #include 19 | 20 | namespace mevent { 21 | namespace util { 22 | 23 | static const unsigned char hexchars[] = "0123456789ABCDEF"; 24 | 25 | pthread_mutex_t log_mtx = PTHREAD_MUTEX_INITIALIZER; 26 | 27 | std::string GetGMTimeStr() { 28 | char buf[128]; 29 | time_t t = time(NULL); 30 | struct tm stm; 31 | gmtime_r(&t, &stm); 32 | strftime(buf, 128, "%a, %d %b %Y %H:%M:%S GMT", &stm); 33 | return std::string(buf); 34 | } 35 | 36 | void LogDebug(const char *file, int line, int opt, const char *fmt, ...) { 37 | if (pthread_mutex_lock(&log_mtx) != 0) { 38 | fprintf(stderr, "DEBUG file:%s line:%d %s\n", __FILE__, __LINE__, strerror(errno)); 39 | } 40 | 41 | do { 42 | time_t now = time(NULL); 43 | struct tm t; 44 | localtime_r(&now, &t); 45 | 46 | char buf[32]; 47 | if (strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &t) == 0) { 48 | fprintf(stderr, "DEBUG file:%s line:%d %s\n", file, __LINE__, strerror(errno)); 49 | break; 50 | } 51 | 52 | if (fmt) { 53 | fprintf(stderr, "%s DEBUG file:%s line:%d ", buf, file, line); 54 | 55 | va_list argptr; 56 | va_start(argptr, fmt); 57 | vfprintf(stderr, fmt, argptr); 58 | va_end(argptr); 59 | 60 | fprintf(stderr, "\n"); 61 | } else { 62 | fprintf(stderr, "%s DEBUG file:%s line:%d %s\n", buf, file, line, strerror(errno)); 63 | } 64 | fflush(stderr); 65 | } while (0); 66 | 67 | if (pthread_mutex_unlock(&log_mtx) != 0) { 68 | fprintf(stderr, "DEBUG file:%s line:%d %s\n", __FILE__, __LINE__, strerror(errno)); 69 | } 70 | 71 | if (opt) { 72 | exit(opt); 73 | } 74 | } 75 | 76 | int GetAddrFromCIDR(int cidr, struct in_addr *addr) { 77 | int ocets; 78 | 79 | if (cidr < 0 || cidr > 32) { 80 | errno = EINVAL; 81 | return -1; 82 | } 83 | ocets = (cidr + 7) / 8; 84 | 85 | addr->s_addr = 0; 86 | if (ocets > 0) { 87 | memset(&addr->s_addr, 255, (size_t)ocets - 1); 88 | memset((unsigned char *)&addr->s_addr + (ocets - 1), (256 - (1 << (32 - cidr) % 8)), 1); 89 | } 90 | 91 | return 0; 92 | } 93 | 94 | bool IsInSubNet(const struct in_addr *addr, const struct in_addr *netaddr, const struct in_addr *netmask) { 95 | if ((addr->s_addr & netmask->s_addr) == (netaddr->s_addr & netmask->s_addr)) { 96 | return true; 97 | } 98 | return false; 99 | } 100 | 101 | void RC4(const std::string &key, const std::string &src, std::string &dest) { 102 | int x = 0, y = 0; 103 | uint8_t tmp; 104 | std::size_t key_len = key.length(); 105 | std::size_t src_len = src.length(); 106 | 107 | std::vector buf; 108 | buf.insert(buf.end(), src.begin(), src.end()); 109 | 110 | unsigned char s[256] = { 111 | 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 112 | 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, 113 | 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, 114 | 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, 115 | 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, 116 | 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, 117 | 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, 118 | 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, 119 | 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 120 | 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, 121 | 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, 122 | 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, 123 | 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, 124 | 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, 125 | 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, 126 | 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 127 | }; 128 | 129 | for (int i = 0; i < 256; i++) { 130 | y = (key[x] + s[i] + y) % 256; 131 | 132 | tmp = s[i]; 133 | s[i] = s[y]; 134 | s[y] = tmp; 135 | 136 | x = (x + 1) % key_len; 137 | } 138 | 139 | x = 0; 140 | y = 0; 141 | 142 | for (std::size_t i = 0; i < src_len; i++) { 143 | x = (x + 1) % 256; 144 | y = (s[x] + y) % 256; 145 | 146 | tmp = s[x]; 147 | s[x] = s[y]; 148 | s[y] = tmp; 149 | 150 | buf[i] ^= s[(s[x] + s[y]) % 256]; 151 | } 152 | 153 | dest.insert(dest.end(), buf.begin(), buf.end()); 154 | } 155 | 156 | 157 | void Daemonize(const std::string &working_dir) { 158 | pid_t pid; 159 | // struct rlimit rl; 160 | // int i; 161 | // int fd0, fd1, fd2; 162 | 163 | umask(0); 164 | 165 | if ((pid = fork()) < 0) { 166 | fprintf(stderr, "fork error!"); 167 | exit(-1); 168 | } else if (pid > 0) { 169 | exit(0); 170 | } 171 | 172 | setsid(); 173 | 174 | signal(SIGHUP, SIG_IGN); 175 | 176 | if ((pid = fork()) < 0) { 177 | exit(-1); 178 | } else if (pid > 0) { 179 | exit(0); 180 | } 181 | 182 | // if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { 183 | // fprintf(stderr, "can't get file limit."); 184 | // } 185 | // 186 | // if (rl.rlim_max == RLIM_INFINITY) { 187 | // rl.rlim_max = 1024; 188 | // } 189 | // 190 | // for (i = 0; i < (int)rl.rlim_max; i++) { 191 | // close(i); 192 | // } 193 | // 194 | // fd0 = open(log_file, O_RDWR); 195 | // fd1 = dup(0); 196 | // fd2 = dup(0); 197 | 198 | if (working_dir.empty()) { 199 | char cwd[1024]; 200 | if (getcwd(cwd, sizeof(cwd)) != NULL) { 201 | chdir(cwd); 202 | } else { 203 | chdir("/"); 204 | } 205 | } else { 206 | chdir(working_dir.c_str()); 207 | } 208 | } 209 | 210 | int GetSysUid(char const *name) { 211 | if (!name) { 212 | return -1; 213 | } 214 | long const buflen = sysconf(_SC_GETPW_R_SIZE_MAX); 215 | if (-1 == buflen) { 216 | return -1; 217 | } 218 | // requires c99 219 | char buf[buflen]; 220 | struct passwd pwbuf, *pwbufp; 221 | if (0 != getpwnam_r(name, &pwbuf, buf, buflen, &pwbufp) || !pwbufp) { 222 | return -1; 223 | } 224 | return pwbufp->pw_uid; 225 | } 226 | 227 | int GetSysGid(char const *name) { 228 | if (!name) { 229 | return -1; 230 | } 231 | long const buflen = sysconf(_SC_GETGR_R_SIZE_MAX); 232 | if (-1 == buflen) { 233 | return -1; 234 | } 235 | // requires c99 236 | char buf[buflen]; 237 | struct group grbuf, *grbufp; 238 | if (0 != getgrnam_r(name, &grbuf, buf, buflen, &grbufp) || !grbufp) { 239 | return -1; 240 | } 241 | return grbufp->gr_gid; 242 | } 243 | 244 | static int Htoi(const char *s) { 245 | int value; 246 | int c; 247 | 248 | c = ((unsigned char *)s)[0]; 249 | if (isupper(c)) 250 | c = tolower(c); 251 | value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16; 252 | 253 | c = ((unsigned char *)s)[1]; 254 | if (isupper(c)) 255 | c = tolower(c); 256 | value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10; 257 | 258 | return value; 259 | } 260 | 261 | std::string URLDecode(const std::string &str) { 262 | std::string dest; 263 | 264 | const char *data = str.c_str(); 265 | 266 | std::size_t len = str.length(); 267 | while (len--) { 268 | if (*data == '+') { 269 | dest.push_back(' '); 270 | } else if (*data == '%' 271 | && len >= 2 272 | && isxdigit(*(data + 1)) 273 | && isxdigit(*(data + 2))) { 274 | dest.push_back(Htoi(data + 1)); 275 | data += 2; 276 | len -= 2; 277 | } else { 278 | dest.push_back(*data); 279 | } 280 | data++; 281 | } 282 | 283 | return dest; 284 | } 285 | 286 | std::string URLEncode(const std::string &str) { 287 | std::string dest; 288 | 289 | unsigned char c; 290 | 291 | std::size_t len = str.length(); 292 | for (std::size_t i = 0; i < len; i++) { 293 | c = str[i]; 294 | 295 | if (c == ' ') { 296 | dest.push_back('+'); 297 | } else if ((c < '0' && c != '-' && c != '.') 298 | || (c < 'A' && c > '9') 299 | || (c > 'Z' && c < 'a' && c != '_') 300 | || (c > 'z')) { 301 | dest.push_back('%'); 302 | dest.push_back(hexchars[toascii(c) << 4]); 303 | dest.push_back(hexchars[toascii(c) & 0xf]); 304 | } else { 305 | dest.push_back(c); 306 | } 307 | } 308 | 309 | return dest; 310 | } 311 | 312 | std::string ExecutablePath() { 313 | char buf[1024] = {0}; 314 | #ifdef __linux__ 315 | readlink("/proc/self/exe", buf, sizeof(buf)); 316 | #endif 317 | 318 | #ifdef __FreeBSD__ 319 | readlink("/proc/curproc/file", buf, sizeof(buf)); 320 | #endif 321 | 322 | #ifdef __APPLE__ 323 | proc_pidpath(getpid(), buf, sizeof(buf)); 324 | #endif 325 | 326 | char *p = strrchr(buf, '/'); 327 | if (NULL == p) { 328 | return ""; 329 | } 330 | *(p + 1) = '\0'; 331 | 332 | return std::string(buf); 333 | } 334 | 335 | }//namespace util 336 | }//namespace mevent 337 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTIL_H 2 | #define _UTIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #define MEVENT_LOG_DEBUG_EXIT(...) mevent::util::LogDebug(__FILE__, __LINE__, -1, __VA_ARGS__) 13 | #define MEVENT_LOG_DEBUG(...) mevent::util::LogDebug(__FILE__, __LINE__, 0, __VA_ARGS__) 14 | 15 | namespace mevent { 16 | namespace util { 17 | std::string GetGMTimeStr(); 18 | 19 | void LogDebug(const char *file, int line, int opt, const char *fmt, ...); 20 | 21 | int GetAddrFromCIDR(int cidr, struct in_addr *addr); 22 | bool IsInSubNet(const struct in_addr *addr, const struct in_addr *netaddr, const struct in_addr *netmask); 23 | 24 | void RC4(const std::string &key, const std::string &src, std::string &dest); 25 | 26 | void Daemonize(const std::string &working_dir); 27 | 28 | int GetSysUid(const char *name); 29 | int GetSysGid(const char *name); 30 | 31 | std::string URLDecode(const std::string &str); 32 | std::string URLEncode(const std::string &str); 33 | 34 | std::string ExecutablePath(); 35 | }//namespace util 36 | }//namespace mevent 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /websocket.cpp: -------------------------------------------------------------------------------- 1 | #include "websocket.h" 2 | #include "mevent.h" 3 | #include "base64.h" 4 | #include "util.h" 5 | #include "connection.h" 6 | #include "lock_guard.h" 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace mevent { 13 | 14 | // http://tools.ietf.org/html/rfc6455#section-5.2 Base Framing Protocol 15 | // 16 | // 0 1 2 3 17 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 18 | // +-+-+-+-+-------+-+-------------+-------------------------------+ 19 | // |F|R|R|R| opcode|M| Payload len | Extended payload length | 20 | // |I|S|S|S| (4) |A| (7) | (16/64) | 21 | // |N|V|V|V| |S| | (if payload len==126/127) | 22 | // | |1|2|3| |K| | | 23 | // +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + 24 | // | Extended payload length continued, if payload len == 127 | 25 | // + - - - - - - - - - - - - - - - +-------------------------------+ 26 | // | |Masking-key, if MASK set to 1 | 27 | // +-------------------------------+-------------------------------+ 28 | // | Masking-key (continued) | Payload Data | 29 | // +-------------------------------- - - - - - - - - - - - - - - - + 30 | // : Payload Data continued ... : 31 | // + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 32 | // | Payload Data continued ... | 33 | // +---------------------------------------------------------------+ 34 | 35 | //Reference: https://github.com/dhbaird/easywsclient 36 | 37 | #define READ_BUFFER_SIZE 2048 38 | 39 | void WebSocket::Reset() { 40 | rbuf_.clear(); 41 | std::vector().swap(rbuf_); 42 | 43 | cache_str_.clear(); 44 | std::string().swap(cache_str_); 45 | 46 | on_close_func_ = nullptr; 47 | ping_func_ = nullptr; 48 | pong_func_ = nullptr; 49 | on_message_func_ = nullptr; 50 | 51 | max_buffer_size_ = 8192; 52 | } 53 | 54 | void WebSocket::MakeFrame(std::vector &frame_data, const std::string &message, uint8_t opcode) { 55 | std::size_t message_len = message.length(); 56 | 57 | std::vector header; 58 | 59 | if (message_len < 126) { 60 | header.assign(2, 0); 61 | } else if (message_len < 65536) { 62 | header.assign(4, 0); 63 | } else { 64 | header.assign(10, 0); 65 | } 66 | 67 | header[0] = 0x80 | opcode; 68 | 69 | if (message_len < 126) { 70 | header[1] = (message_len & 0xff); 71 | } else if (message_len < 65536) { 72 | header[1] = 126; 73 | header[2] = (message_len >> 8) & 0xff; 74 | header[3] = (message_len >> 0) & 0xff; 75 | } else { 76 | header[1] = 127; 77 | header[2] = (message_len >> 56) & 0xff; 78 | header[3] = (message_len >> 48) & 0xff; 79 | header[4] = (message_len >> 40) & 0xff; 80 | header[5] = (message_len >> 32) & 0xff; 81 | header[6] = (message_len >> 24) & 0xff; 82 | header[7] = (message_len >> 16) & 0xff; 83 | header[8] = (message_len >> 8) & 0xff; 84 | header[9] = (message_len >> 0) & 0xff; 85 | } 86 | 87 | frame_data.insert(frame_data.end(), header.begin(), header.end()); 88 | if (!message.empty()) { 89 | frame_data.insert(frame_data.end(), message.begin(), message.end()); 90 | } 91 | } 92 | 93 | void WebSocket::Handshake(std::string &data, const std::string &sec_websocket_key) { 94 | std::string sec_str = sec_websocket_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 95 | 96 | unsigned char md[SHA_DIGEST_LENGTH]; 97 | 98 | SHA_CTX ctx; 99 | SHA1_Init(&ctx); 100 | SHA1_Update(&ctx, sec_str.c_str(), sec_str.length()); 101 | SHA1_Final(md, &ctx); 102 | 103 | std::string sec_str_b64 = Base64Encode(static_cast(md), SHA_DIGEST_LENGTH); 104 | 105 | data = "HTTP/1.1 101 Switching Protocols" CRLF; 106 | data += "Server: " SERVER CRLF; 107 | data += "Date: " + util::GetGMTimeStr() + CRLF; 108 | data += "Upgrade: websocket" CRLF; 109 | data += "Connection: Upgrade" CRLF; 110 | data += "Sec-WebSocket-Accept: " + sec_str_b64 + CRLF; 111 | data += CRLF; 112 | } 113 | 114 | bool WebSocket::Upgrade() 115 | { 116 | if (conn_->Req()->sec_websocket_key_.empty()) { 117 | return false; 118 | } 119 | 120 | std::string sec_websocket_key = conn_->Req()->sec_websocket_key_; 121 | 122 | // conn_->Req()->Reset(); 123 | // conn_->Resp()->Reset(); 124 | 125 | conn_->Req()->status_ = RequestStatus::UPGRADE; 126 | 127 | std::string response_header; 128 | 129 | Handshake(response_header, sec_websocket_key); 130 | 131 | conn_->WriteString(response_header); 132 | 133 | return true; 134 | } 135 | 136 | ConnStatus WebSocket::ReadData() { 137 | char buf[READ_BUFFER_SIZE]; 138 | ssize_t n = 0; 139 | 140 | do { 141 | n = conn_->Readn(buf, READ_BUFFER_SIZE); 142 | if (n > 0) { 143 | rbuf_.insert(rbuf_.end(), buf, buf + n); 144 | if (!Parse()) { 145 | return ConnStatus::ERROR; 146 | } 147 | } else if (n < 0) { 148 | return ConnStatus::CLOSE; 149 | } else { 150 | break; 151 | } 152 | } while (n == READ_BUFFER_SIZE); 153 | 154 | return ConnStatus::AGAIN; 155 | } 156 | 157 | bool WebSocket::Parse() { 158 | while (true) { 159 | WebSocketHeader wsh; 160 | 161 | if (rbuf_.size() < 2) { 162 | return true; 163 | } 164 | 165 | const uint8_t *data = static_cast(&rbuf_[0]); 166 | 167 | wsh.len = 0; 168 | wsh.fin = (data[0] & 0x80) == 0x80; 169 | wsh.opcode = static_cast(data[0] & 0x0f); 170 | wsh.mask = (data[1] & 0x80) == 0x80; 171 | wsh.len0 = data[1] & 0x7f; 172 | wsh.header_size = 2; 173 | if (wsh.len0 == 126) { 174 | wsh.header_size += 2; 175 | } else if (wsh.len0 == 127) { 176 | wsh.header_size += 8; 177 | } 178 | 179 | if (wsh.mask) { 180 | wsh.header_size += sizeof(wsh.masking_key); 181 | } 182 | 183 | if (rbuf_.size() < wsh.header_size) { 184 | return true; 185 | } 186 | 187 | if (wsh.len0 < 126) { 188 | wsh.len = wsh.len0; 189 | } else if (wsh.len0 == 126) { 190 | wsh.len |= ((uint64_t) data[2]) << 8; 191 | wsh.len |= ((uint64_t) data[3]) << 0; 192 | } else if (wsh.len0 == 127) { 193 | wsh.len |= ((uint64_t) data[2]) << 56; 194 | wsh.len |= ((uint64_t) data[3]) << 48; 195 | wsh.len |= ((uint64_t) data[4]) << 40; 196 | wsh.len |= ((uint64_t) data[5]) << 32; 197 | wsh.len |= ((uint64_t) data[6]) << 24; 198 | wsh.len |= ((uint64_t) data[7]) << 16; 199 | wsh.len |= ((uint64_t) data[8]) << 8; 200 | wsh.len |= ((uint64_t) data[9]) << 0; 201 | } 202 | 203 | if (wsh.mask) { 204 | wsh.masking_key[0] = data[wsh.header_size - 4]; 205 | wsh.masking_key[1] = data[wsh.header_size - 3]; 206 | wsh.masking_key[2] = data[wsh.header_size - 2]; 207 | wsh.masking_key[3] = data[wsh.header_size - 1]; 208 | } 209 | 210 | if (rbuf_.size() < wsh.header_size + wsh.len) { 211 | return true; 212 | } 213 | 214 | if (cache_str_.length() + wsh.len > max_buffer_size_) { 215 | return false; 216 | } 217 | 218 | if (wsh.mask && wsh.len > 0) { 219 | for (uint64_t i = 0; i != wsh.len; i++) { 220 | rbuf_[i + wsh.header_size] ^= wsh.masking_key[i & 0x3]; 221 | } 222 | } 223 | 224 | cache_str_.insert(cache_str_.end(), rbuf_.begin() + wsh.header_size, rbuf_.begin() + wsh.header_size + wsh.len); 225 | 226 | if (wsh.opcode == WebSocketOpcodeType::BINARY_FRAME 227 | || wsh.opcode == WebSocketOpcodeType::TEXT_FRAME 228 | || wsh.opcode == WebSocketOpcodeType::CONTINUATION) { 229 | if (wsh.fin) { 230 | conn_->WebSocketTaskPush(wsh.opcode, cache_str_); 231 | 232 | cache_str_.clear(); 233 | std::string().swap(cache_str_); 234 | } 235 | } else if (wsh.opcode == WebSocketOpcodeType::PING) { 236 | conn_->WebSocketTaskPush(wsh.opcode, cache_str_); 237 | cache_str_.clear(); 238 | std::string().swap(cache_str_); 239 | } else if (wsh.opcode == WebSocketOpcodeType::PONG) { 240 | conn_->WebSocketTaskPush(wsh.opcode, cache_str_); 241 | } else if (wsh.opcode == WebSocketOpcodeType::CLOSE) { 242 | conn_->WebSocketTaskPush(wsh.opcode, cache_str_); 243 | } else { 244 | conn_->WebSocketTaskPush(WebSocketOpcodeType::CLOSE, cache_str_); 245 | } 246 | 247 | rbuf_.erase(rbuf_.begin(), rbuf_.begin() + wsh.header_size + wsh.len); 248 | } 249 | 250 | return true; 251 | } 252 | 253 | void WebSocket::SendPong(const std::string &str) { 254 | std::vector frame_data; 255 | MakeFrame(frame_data, str, WebSocketOpcodeType::PONG); 256 | conn_->WriteData(frame_data); 257 | } 258 | 259 | void WebSocket::SendPing(const std::string &str) { 260 | std::vector frame_data; 261 | MakeFrame(frame_data, str, WebSocketOpcodeType::PING); 262 | conn_->WriteData(frame_data); 263 | } 264 | 265 | void WebSocket::WriteString(const std::string &str) { 266 | std::vector frame_data; 267 | MakeFrame(frame_data, str, WebSocketOpcodeType::TEXT_FRAME); 268 | conn_->WriteData(frame_data); 269 | } 270 | 271 | bool WebSocket::SendPongSafe(const std::string &str) { 272 | LockGuard lock_guard(conn_->mtx_); 273 | 274 | std::vector frame_data; 275 | MakeFrame(frame_data, str, WebSocketOpcodeType::PONG); 276 | conn_->WriteData(frame_data); 277 | 278 | ConnStatus status = conn_->Flush(); 279 | if (status == ConnStatus::ERROR && status == ConnStatus::CLOSE) { 280 | return false; 281 | } 282 | 283 | return true; 284 | } 285 | 286 | bool WebSocket::SendPingSafe(const std::string &str) { 287 | LockGuard lock_guard(conn_->mtx_); 288 | 289 | std::vector frame_data; 290 | MakeFrame(frame_data, str, WebSocketOpcodeType::PING); 291 | conn_->WriteData(frame_data); 292 | 293 | ConnStatus status = conn_->Flush(); 294 | if (status == ConnStatus::ERROR && status == ConnStatus::CLOSE) { 295 | return false; 296 | } 297 | 298 | return true; 299 | } 300 | 301 | bool WebSocket::WriteStringSafe(const std::string &str) { 302 | LockGuard lock_guard(conn_->mtx_); 303 | 304 | std::vector frame_data; 305 | MakeFrame(frame_data, str, WebSocketOpcodeType::TEXT_FRAME); 306 | conn_->WriteData(frame_data); 307 | 308 | ConnStatus status = conn_->Flush(); 309 | if (status == ConnStatus::ERROR && status == ConnStatus::CLOSE) { 310 | return false; 311 | } 312 | 313 | return true; 314 | } 315 | 316 | bool WebSocket::WriteRawDataSafe(const std::vector &data) { 317 | LockGuard lock_guard(conn_->mtx_); 318 | 319 | conn_->WriteData(data); 320 | 321 | ConnStatus status = conn_->Flush(); 322 | if (status == ConnStatus::ERROR && status == ConnStatus::CLOSE) { 323 | return false; 324 | } 325 | 326 | return true; 327 | } 328 | 329 | Connection *WebSocket::Conn() { 330 | return conn_; 331 | } 332 | 333 | void WebSocket::SetOnMessageHandler(WebSocketHandlerFunc func) { 334 | on_message_func_ = func; 335 | } 336 | 337 | void WebSocket::SetPingHandler(WebSocketHandlerFunc func) { 338 | ping_func_ = func; 339 | } 340 | 341 | void WebSocket::SetPongHandler(WebSocketHandlerFunc func) { 342 | pong_func_ = func; 343 | } 344 | 345 | void WebSocket::SetOnCloseHandler(WebSocketCloseHandlerFunc func) { 346 | on_close_func_ = func; 347 | } 348 | 349 | void WebSocket::SetMaxBufferSize(std::size_t size) { 350 | max_buffer_size_ = size; 351 | } 352 | 353 | }//namespace mevent 354 | -------------------------------------------------------------------------------- /websocket.h: -------------------------------------------------------------------------------- 1 | #ifndef _WEBSOCKET_H 2 | #define _WEBSOCKET_H 3 | 4 | #include "request.h" 5 | #include "conn_status.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace mevent { 14 | 15 | class Connection; 16 | class WebSocket; 17 | 18 | enum class WebsocketParseStatus : uint8_t { 19 | AGAIN, 20 | FINISHED, 21 | ERROR 22 | }; 23 | 24 | enum WebSocketOpcodeType { 25 | CONTINUATION = 0x0, 26 | TEXT_FRAME = 0x1, 27 | BINARY_FRAME = 0x2, 28 | CLOSE = 0x8, 29 | PING = 0x9, 30 | PONG = 0xa, 31 | }; 32 | 33 | struct WebSocketHeader { 34 | uint8_t header_size; 35 | bool fin; 36 | bool mask; 37 | WebSocketOpcodeType opcode; 38 | uint8_t len0; 39 | uint64_t len; 40 | uint8_t masking_key[4]; 41 | }; 42 | 43 | struct WebSocketTaskItem { 44 | WebSocketTaskItem(WebSocket *w, 45 | WebSocketOpcodeType op, 46 | const std::string &m) 47 | : ws(w), opcode(op), msg(m) {}; 48 | 49 | WebSocket *ws; 50 | WebSocketOpcodeType opcode; 51 | std::string msg; 52 | }; 53 | 54 | typedef std::function WebSocketHandlerFunc; 55 | typedef std::function WebSocketCloseHandlerFunc; 56 | 57 | class WebSocket 58 | { 59 | public: 60 | WebSocket(Connection *conn) : conn_(conn) { Reset(); }; 61 | ~WebSocket() {}; 62 | 63 | static void MakeFrame(std::vector &frame_data, const std::string &message, uint8_t opcode); 64 | 65 | bool Upgrade(); 66 | 67 | void SendPong(const std::string &str); 68 | void SendPing(const std::string &str); 69 | 70 | void WriteString(const std::string &str); 71 | 72 | Connection *Conn(); 73 | 74 | void SetOnMessageHandler(WebSocketHandlerFunc func); 75 | void SetPingHandler(WebSocketHandlerFunc func); 76 | void SetPongHandler(WebSocketHandlerFunc func); 77 | void SetOnCloseHandler(WebSocketCloseHandlerFunc func); 78 | 79 | void SetMaxBufferSize(std::size_t size); 80 | 81 | //Thread Safe 82 | bool SendPongSafe(const std::string &str); 83 | bool SendPingSafe(const std::string &str); 84 | bool WriteStringSafe(const std::string &str); 85 | bool WriteRawDataSafe(const std::vector &data); 86 | 87 | private: 88 | friend class EventLoop; 89 | friend class Connection; 90 | 91 | void Reset(); 92 | 93 | void Handshake(std::string &data, const std::string &sec_websocket_key); 94 | 95 | bool Parse(); 96 | 97 | ConnStatus ReadData(); 98 | 99 | Connection *conn_; 100 | 101 | std::size_t max_buffer_size_; 102 | 103 | std::vector rbuf_; 104 | std::string cache_str_; 105 | 106 | WebSocketHandlerFunc on_message_func_; 107 | WebSocketHandlerFunc ping_func_; 108 | WebSocketHandlerFunc pong_func_; 109 | WebSocketCloseHandlerFunc on_close_func_; 110 | }; 111 | 112 | }//namespace mevent 113 | 114 | #endif 115 | --------------------------------------------------------------------------------