├── LICENSE ├── Makefile ├── README.md ├── include ├── Acceptor.h ├── BlockingQueue.h ├── Buffer.h ├── Channel.h ├── Common.h ├── Condition.h ├── CurrentThread.h ├── Epoller.h ├── EventLoop.h ├── EventLoopThread.h ├── EventLoopThreadPool.h ├── HttpParser.h ├── HttpServer.h ├── InetAddr.h ├── MutexLocker.h ├── MutexLockerGuard.h ├── Poller.h ├── Socket.h ├── TcpConnection.h ├── TcpServer.h ├── Thread.h ├── TimeStamp.h ├── Timer.h ├── TimerQueue.h └── noncopyable.h └── src ├── Acceptor.cc ├── Buffer.cc ├── Channel.cc ├── CurrentThread.cc ├── Epoller.cc ├── EventLoop.cc ├── EventLoopThread.cc ├── EventLoopThreadPool.cc ├── HttpParser.cc ├── HttpServer.cc ├── InetAddr.cc ├── Poller.cc ├── Socket.cc ├── TcpConnection.cc ├── TcpServer.cc ├── Thread.cc ├── TimeStamp.cc ├── Timer.cc ├── TimerQueue.cc └── main.cc /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 xggggg 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 | CC := g++ 2 | C_FLAGS := -std=c++17 -Wall -Wextra -pthread -g 3 | 4 | BIN := bin 5 | SRC := src 6 | INCLUDE := include 7 | LIB := lib 8 | 9 | LIBRARIES := 10 | 11 | ifeq ($(OS),Windows_NT) 12 | EXECUTABLE := main.exe 13 | else 14 | EXECUTABLE := main 15 | endif 16 | 17 | all: $(BIN)/$(EXECUTABLE) 18 | 19 | clean: 20 | $(RM) $(BIN)/$(EXECUTABLE) 21 | 22 | run: all 23 | ./$(BIN)/$(EXECUTABLE) 24 | 25 | $(BIN)/$(EXECUTABLE): $(SRC)/* 26 | $(CC) $(C_FLAGS) -I$(INCLUDE) -L$(LIB) $^ -o $@ $(LIBRARIES) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # miniwebserver 2 | 尝试实现一个迷你的web服务器,用来学习C++语法、多线程编程、网络编程、Linux环境下编程 3 | -------------------------------------------------------------------------------- /include/Acceptor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Acceptor.h 3 | * 4 | * Created on: Feb 1, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | 9 | #pragma once 10 | #include "noncopyable.h" 11 | #include "EventLoop.h" 12 | #include "Socket.h" 13 | #include "Channel.h" 14 | #include "InetAddr.h" 15 | 16 | #include 17 | 18 | namespace miniws { 19 | 20 | class Acceptor : noncopyable { 21 | public: 22 | typedef std::function NewConnCallback; 23 | 24 | Acceptor(EventLoop *eventLoop, const InetAddr &listenAddr); 25 | void setNewConnCallback(const NewConnCallback &cb); 26 | void listen(); 27 | 28 | private: 29 | void handleRead(); 30 | 31 | EventLoop *m_eventLoop; 32 | Socket m_listenSocket; 33 | Channel m_listenChannel; 34 | NewConnCallback m_newConnCb; 35 | }; 36 | 37 | } -------------------------------------------------------------------------------- /include/BlockingQueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BlockingQueue.h 3 | * 4 | * Created on: Dec 16, 2018 5 | * Author: xiagai 6 | */ 7 | 8 | #ifndef BLOCKINGQUEUE_H_ 9 | #define BLOCKINGQUEUE_H_ 10 | 11 | #include "Condition.h" 12 | 13 | #include 14 | #include 15 | 16 | namespace miniws { 17 | 18 | template 19 | class BlockingQueue : noncopyable { 20 | BlockingQueue() 21 | : m_locker(), 22 | m_cond(m_locker), 23 | m_queue() {} 24 | 25 | void push(const T &x) { 26 | MutexLockerGuard lock(m_locker); 27 | m_queue.push(x); 28 | m_cond.notify(); 29 | } 30 | 31 | T &pop() { 32 | MutexLockerGuard lock(m_locker); 33 | while (m_queue.isEmpty()) { 34 | m_cond.wait(); 35 | } 36 | assert(!m_queue.isEmpty()); 37 | T x(m_queue.front()); 38 | m_queue.pop(); 39 | return x; 40 | } 41 | 42 | size_t size() const { 43 | MutexLockerGuard lock(m_locker); 44 | return m_queue.size(); 45 | } 46 | 47 | private: 48 | MutexLocker m_locker; 49 | Condition m_cond; 50 | std::queue m_queue; 51 | }; 52 | 53 | } 54 | 55 | 56 | #endif /* BLOCKINGQUEUE_H_ */ 57 | -------------------------------------------------------------------------------- /include/Buffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Buffer.h 3 | * 4 | * Created on: Feb 18, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | 13 | namespace miniws { 14 | 15 | class Buffer { 16 | public: 17 | Buffer(size_t capacity); 18 | ~Buffer(); 19 | 20 | bool isEmpty() const; 21 | bool isFull() const; 22 | size_t remainingSize() const; 23 | size_t size() const; 24 | bool putIn(char *buf, size_t len); 25 | bool takeOut(size_t n); 26 | /// if i > m_size, return value is undefined; 27 | char &operator[](size_t i); 28 | size_t findCRLF(size_t pos) const; 29 | size_t findSpace(size_t l, size_t r) const; 30 | size_t skipSpace(size_t pos) const; 31 | std::string getStringPiece(size_t pos, size_t len) const; 32 | 33 | private: 34 | const size_t m_capacity; 35 | size_t m_size; 36 | size_t m_start; 37 | size_t m_end; 38 | std::vector m_buf; 39 | }; 40 | 41 | } -------------------------------------------------------------------------------- /include/Channel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Channel.h 3 | * 4 | * Created on: Jan 15, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #ifndef CHANNEL_H_ 9 | #define CHANNEL_H_ 10 | 11 | #include "noncopyable.h" 12 | 13 | #include 14 | #include 15 | 16 | namespace miniws { 17 | 18 | class EventLoop; 19 | 20 | class Channel : noncopyable { 21 | public: 22 | typedef std::function EventCallback; 23 | 24 | Channel(EventLoop *loop, int fd, bool readET = false, bool writeET = true); 25 | ~Channel(); 26 | 27 | void handleEvent(); 28 | void setReadCallback(const EventCallback& cb); 29 | void setWriteCallback(const EventCallback& cb); 30 | void setErrorCallback(const EventCallback& cb); 31 | void setCloseCallback(const EventCallback& cb); 32 | 33 | int fd() const; 34 | int events() const; 35 | void setRevents(int revt); 36 | bool isNoneEvent() const; 37 | 38 | void enableReading(); 39 | void enableWriting(); 40 | void disableWriting(); 41 | void disableAll(); 42 | void remove(); 43 | 44 | // for ET mode 45 | bool isReadETMode() const; 46 | bool isWriteETMode() const; 47 | 48 | // for Poller 49 | int index(); 50 | void setIndex(int idx); 51 | 52 | EventLoop *ownerLoop(); 53 | 54 | private: 55 | void update(); 56 | 57 | private: 58 | static const int kNoneEvent = 0; 59 | static const int kReadEvent = POLLIN | POLLPRI; 60 | static const int kWriteEvent = POLLOUT; 61 | 62 | EventLoop *m_ownerLoop; 63 | const int m_fd; 64 | bool m_readET; 65 | bool m_writeET; 66 | uint32_t m_events; 67 | uint32_t m_revents; 68 | int m_index; 69 | bool m_eventHandling; 70 | 71 | EventCallback m_readCallback; 72 | EventCallback m_writeCallback; 73 | EventCallback m_errorCallback; 74 | EventCallback m_closeCallback; 75 | }; 76 | 77 | } 78 | 79 | #endif /* CHANNEL_H_ */ 80 | -------------------------------------------------------------------------------- /include/Common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Common.h 3 | * 4 | * Created on: Feb 3, 2019 5 | * Author: xiagai 6 | */ 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace miniws { 13 | 14 | class TcpConnection; 15 | class Buffer; 16 | 17 | typedef std::shared_ptr TcpConnectionPtr; 18 | typedef uint64_t TimerId; 19 | 20 | typedef std::function ConnectionCallback; 21 | typedef std::function MessageCallback; 22 | typedef std::function CloseCallback; 23 | 24 | static const int MAX_EVENT_NUM = 65536; 25 | 26 | } -------------------------------------------------------------------------------- /include/Condition.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Condition.h 3 | * 4 | * Created on: Dec 16, 2018 5 | * Author: xiagai 6 | */ 7 | 8 | #ifndef CONDITION_H_ 9 | #define CONDITION_H_ 10 | 11 | #include "MutexLockerGuard.h" 12 | 13 | namespace miniws { 14 | 15 | class Condition : noncopyable { 16 | public: 17 | explicit Condition(MutexLocker &locker) 18 | : m_locker(locker) { 19 | pthread_cond_init(&m_cond, NULL); 20 | } 21 | ~Condition (){ 22 | pthread_cond_destroy(&m_cond); 23 | } 24 | void wait() { 25 | pthread_cond_wait(&m_cond, m_locker.getPthreadMutex()); 26 | } 27 | void notify() { 28 | pthread_cond_signal(&m_cond); 29 | } 30 | void notifyAll() { 31 | pthread_cond_broadcast(&m_cond); 32 | } 33 | private: 34 | MutexLocker& m_locker; 35 | pthread_cond_t m_cond; 36 | }; 37 | 38 | } 39 | 40 | 41 | 42 | #endif /* CONDITION_H_ */ 43 | -------------------------------------------------------------------------------- /include/CurrentThread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CurrentThread.h 3 | * 4 | * Created on: Jan 9, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #ifndef CURRENTTHREAD_H_ 9 | #define CURRENTTHREAD_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace miniws { 16 | 17 | class CurrentThread { 18 | public: 19 | static pid_t tid(); 20 | }; 21 | 22 | } 23 | 24 | #endif /* CURRENTTHREAD_H_ */ 25 | -------------------------------------------------------------------------------- /include/Epoller.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Epoller.h 3 | * 4 | * Created on: Feb 19, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #pragma once 9 | 10 | #include "noncopyable.h" 11 | #include "TimeStamp.h" 12 | 13 | #include 14 | #include 15 | 16 | struct epoll_event; 17 | 18 | namespace miniws { 19 | 20 | class EventLoop; 21 | class Channel; 22 | 23 | class Epoller : noncopyable { 24 | public: 25 | Epoller(EventLoop *ownerLoop); 26 | ~Epoller(); 27 | 28 | /// EPolls the I/O events 29 | /// Must be called in the loop thread 30 | TimeStamp epoll(int timeoutMs, std::vector &activeChannels); 31 | 32 | /// Changes the interested I/O events; 33 | /// Must be called in the loop thread 34 | void updateChannel(Channel *channel); 35 | 36 | /// Remove the channel, when it destructs. 37 | /// Must be called in the loop thread. 38 | void removeChannel(Channel *channel); 39 | 40 | private: 41 | void fillActiveChannels(int numEvents, epoll_event *revents, std::vector &activeChannels) const; 42 | 43 | private: 44 | EventLoop *m_ownerLoop; 45 | int m_epollfd; 46 | std::map m_channels; 47 | 48 | }; 49 | 50 | } -------------------------------------------------------------------------------- /include/EventLoop.h: -------------------------------------------------------------------------------- 1 | /* 2 | * EventLoop.h 3 | * 4 | * Created on: Jan 9, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #pragma once 9 | 10 | #include "noncopyable.h" 11 | #include "pthread.h" 12 | #include "MutexLocker.h" 13 | #include "TimerQueue.h" 14 | #include "TimeStamp.h" 15 | #include "Timer.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | namespace miniws { 22 | 23 | class Channel; 24 | class Epoller; 25 | 26 | class EventLoop : noncopyable { 27 | public: 28 | typedef std::function Functor; 29 | EventLoop(); 30 | ~EventLoop(); 31 | 32 | void loop(); 33 | void quit(); 34 | void assertInLoopThread(); 35 | bool isInLoopThread() const; 36 | // Must be called in the loop thread 37 | void updateChannel(Channel* channel); 38 | // Must be called in the loop thread 39 | void removeChannel(Channel* channel); 40 | void runInLoop(const Functor &cb); 41 | static EventLoop *getEventLoopOfCurrentThread(); 42 | TimerId runAt(const TimeStamp ×tamp, const Timer::TimerCallback &cb); 43 | TimerId runAfter(double delay, const Timer::TimerCallback &cb); 44 | TimerId runEvery(double interval, const Timer::TimerCallback &cb); 45 | void cancelRun(TimerId timerId); 46 | 47 | private: 48 | void handleWakeUp(); 49 | void wakeup(); 50 | void doPendingFunctors(); 51 | void queueInLoop(const Functor &cb); 52 | 53 | private: 54 | bool m_looping; 55 | bool m_quit; 56 | const pid_t m_threadId; 57 | std::unique_ptr m_poller; 58 | std::unique_ptr m_timerQueue; 59 | std::vector m_activeChannels; 60 | 61 | bool m_eventHandling; 62 | Channel *m_currentChannel; 63 | 64 | bool m_callingPendingFunctors; 65 | int m_wakeupfd; 66 | std::unique_ptr m_wakeupChannel; 67 | MutexLocker m_mutex; 68 | std::vector m_pendingFunctors; 69 | }; 70 | 71 | } 72 | 73 | -------------------------------------------------------------------------------- /include/EventLoopThread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * EventLoopThread.h 3 | * 4 | * Created on: Feb 1, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #pragma once 9 | #include "noncopyable.h" 10 | #include "Thread.h" 11 | #include "MutexLocker.h" 12 | #include "MutexLockerGuard.h" 13 | #include "Condition.h" 14 | #include "EventLoop.h" 15 | 16 | namespace miniws { 17 | 18 | class EventLoopThread : noncopyable{ 19 | public: 20 | EventLoopThread(); 21 | ~EventLoopThread(); 22 | void threadFunc(); 23 | EventLoop *startLoop(); 24 | private: 25 | EventLoop *m_eventLoop; 26 | MutexLocker m_mutex; 27 | Condition m_cond; 28 | Thread m_thread; 29 | }; 30 | 31 | } -------------------------------------------------------------------------------- /include/EventLoopThreadPool.h: -------------------------------------------------------------------------------- 1 | /* 2 | * EventLoopThreadPool.h 3 | * 4 | * Created on: Feb 15, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #pragma once 9 | 10 | #include "noncopyable.h" 11 | #include "EventLoop.h" 12 | #include "EventLoopThread.h" 13 | 14 | #include 15 | #include 16 | 17 | namespace miniws { 18 | 19 | class EventLoopThreadPool : noncopyable { 20 | public: 21 | EventLoopThreadPool(EventLoop *baseLoop, int numThreads); 22 | ~EventLoopThreadPool(); 23 | void start(); 24 | EventLoop *getNextLoop(); 25 | private: 26 | EventLoop *m_baseLoop; 27 | bool m_started; 28 | int m_numThreads; 29 | int m_next; 30 | std::vector> m_threads; 31 | std::vector m_loops; 32 | }; 33 | 34 | } -------------------------------------------------------------------------------- /include/HttpParser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * HttpParser.h 3 | * 4 | * Created on: Feb 20, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #pragma once 9 | 10 | #include "noncopyable.h" 11 | #include "Buffer.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace miniws { 18 | 19 | struct httpret { 20 | struct iovec *iov = nullptr; 21 | size_t iovlen = 0; 22 | bool keepAlive = false; 23 | }; 24 | 25 | class HttpParser : noncopyable { 26 | public: 27 | enum METHOD { 28 | GET, 29 | POST, 30 | HEAD, 31 | PUT, 32 | DELETE, 33 | TRACE, 34 | OPTIONS, 35 | CONNECT, 36 | PATCH 37 | }; 38 | 39 | enum CHECK_STATE { 40 | CHECK_STATE_REQUESTLINE, //正在分析请求行 41 | CHECK_STATE_HEADER, //正在分析头部字段 42 | CHECK_STATE_CONTENT //正在分析请求体 43 | }; 44 | 45 | enum HTTP_CODE { 46 | NO_REQUEST, //请求不完整,需要继续读取客户数据 47 | GET_REQUEST, //获得了一个完整的客户请求 48 | BAD_REQUEST, //客户请求有语法错误 49 | NO_RESOURCE, //资源不存在 50 | FORBIDDEN_REQUEST, //客户对资源没有足够的访问权限 51 | FILE_REQUEST, //文件请求 52 | INTERNAL_ERROR, //服务器内部错误 53 | CLOSED_CONNECTION //客户端已经关闭连接 54 | }; 55 | 56 | enum LINE_STATUS { 57 | LINE_OK, //读到一个完整的行 58 | LINE_BAD, //行出错 59 | LINE_OPEN //行数据尚且不完整 60 | }; 61 | 62 | public: 63 | HttpParser(char *homeDir, Buffer &readBuf); 64 | ~HttpParser(); 65 | 66 | struct httpret process(); 67 | HTTP_CODE processRead(); 68 | bool processWrite(HTTP_CODE); 69 | 70 | private: 71 | // used by read 72 | LINE_STATUS parseLine(); 73 | HTTP_CODE parseRequestLine(); 74 | HTTP_CODE parseHeaders(); 75 | HTTP_CODE parseContent(); 76 | HTTP_CODE doRequest(); 77 | 78 | // used by write 79 | bool addLine(const char *format, ...); 80 | bool addStatusLine(); 81 | bool addHeaders(); 82 | void addResponse(); 83 | void addContent(); 84 | 85 | private: 86 | static const int FILENAME_LEN = 200; 87 | static const int WRITE_BUFFER_SIZE = 1024; 88 | static const int FORM_MAX_SIZE = 256; 89 | static const int ok_200 = 200; 90 | static const int error_400 = 400; 91 | static const int error_403 = 403; 92 | static const int error_404 = 404; 93 | static const int error_500 = 500; 94 | static const char* ok_200_title; 95 | static const char* error_400_title; 96 | static const char* error_400_form; 97 | static const char* error_403_title; 98 | static const char* error_403_form; 99 | static const char* error_404_title; 100 | static const char* error_404_form; 101 | static const char* error_500_title; 102 | static const char* error_500_form; 103 | 104 | CHECK_STATE m_checkState; 105 | METHOD m_method; 106 | char *m_homeDir; 107 | 108 | //read buffer 109 | Buffer &m_readBuf; 110 | size_t m_readIdx; 111 | size_t m_lineStart; 112 | size_t m_lineEnd; 113 | 114 | //request arguments 115 | std::string m_url; 116 | std::string m_version; 117 | std::string m_host; 118 | int m_contentLen; 119 | bool m_linger; 120 | 121 | //file arguments 122 | char *m_fileAddress; 123 | char m_readFile[FILENAME_LEN]; 124 | struct stat m_fileStat; 125 | 126 | //response arguments 127 | char m_response[WRITE_BUFFER_SIZE]; 128 | size_t m_writeIdx; 129 | struct iovec m_iov[2]; 130 | int m_status; 131 | const char *m_title; 132 | char m_form[FORM_MAX_SIZE]; 133 | }; 134 | 135 | } -------------------------------------------------------------------------------- /include/HttpServer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * HttpServer.h 3 | * 4 | * Created on: Feb 25, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #pragma once 9 | 10 | #include "noncopyable.h" 11 | #include "InetAddr.h" 12 | #include "TcpServer.h" 13 | #include "Buffer.h" 14 | #include "Common.h" 15 | 16 | #include 17 | 18 | namespace miniws { 19 | 20 | class HttpServer : noncopyable { 21 | public: 22 | HttpServer(std::string &serverName, InetAddr &localAddr, std::string &homeDir, int numThreads, double delayCloseSec); 23 | ~HttpServer(); 24 | void start(); 25 | 26 | private: 27 | void onConnection(const TcpConnectionPtr conn); 28 | void onMessage(const TcpConnectionPtr conn, Buffer &buf); 29 | 30 | private: 31 | static const int MAX_HOME_SIZE = 100; 32 | std::string m_name; 33 | InetAddr m_localAddr; 34 | char m_homeDir[MAX_HOME_SIZE]; 35 | int m_numThreads; 36 | double m_delayCloseSec; 37 | }; 38 | 39 | } -------------------------------------------------------------------------------- /include/InetAddr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * InetAddr.h 3 | * 4 | * Created on: Feb 2, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | 13 | namespace miniws { 14 | 15 | class InetAddr { 16 | public: 17 | InetAddr(); 18 | InetAddr(std::string ip, uint16_t port); 19 | explicit InetAddr(sockaddr_in &addr); 20 | ~InetAddr(); 21 | 22 | std::string getIP () const; 23 | std::string getIPPort() const; 24 | u_int16_t getPort() const; 25 | const struct sockaddr_in *getAddr() const; 26 | void setAddr(sockaddr_in &); 27 | 28 | public: 29 | const static int kPORTSTRLEN = 10; 30 | 31 | private: 32 | struct sockaddr_in m_addr; 33 | }; 34 | 35 | } -------------------------------------------------------------------------------- /include/MutexLocker.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MutexLocker.h 3 | * 4 | * Created on: Dec 16, 2018 5 | * Author: xiagai 6 | */ 7 | 8 | #ifndef MUTEXLOCKER_H_ 9 | #define MUTEXLOCKER_H_ 10 | 11 | #include "noncopyable.h" 12 | 13 | #include 14 | 15 | namespace miniws { 16 | 17 | class MutexLocker : noncopyable { 18 | public: 19 | MutexLocker() { 20 | pthread_mutex_init(&m_mutex, NULL); 21 | } 22 | ~MutexLocker() { 23 | pthread_mutex_destroy(&m_mutex); 24 | } 25 | void lock() { 26 | pthread_mutex_lock(&m_mutex); 27 | } 28 | void unlock() { 29 | pthread_mutex_unlock(&m_mutex); 30 | } 31 | pthread_mutex_t *getPthreadMutex() { 32 | return &m_mutex; 33 | } 34 | 35 | private: 36 | pthread_mutex_t m_mutex; 37 | }; 38 | 39 | } 40 | 41 | 42 | 43 | #endif /* MUTEXLOCKER_H_ */ 44 | -------------------------------------------------------------------------------- /include/MutexLockerGuard.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MutexLockerGuard.h 3 | * 4 | * Created on: Dec 16, 2018 5 | * Author: xiagai 6 | */ 7 | 8 | #ifndef MUTEXLOCKERGUARD_H_ 9 | #define MUTEXLOCKERGUARD_H_ 10 | 11 | #include "MutexLocker.h" 12 | 13 | namespace miniws { 14 | 15 | class MutexLockerGuard : noncopyable { 16 | public: 17 | explicit MutexLockerGuard(MutexLocker &locker) 18 | : m_locker(locker) { 19 | m_locker.lock(); 20 | } 21 | ~MutexLockerGuard() { 22 | m_locker.unlock(); 23 | } 24 | 25 | private: 26 | MutexLocker &m_locker; 27 | }; 28 | 29 | } 30 | 31 | #define MutexLockerGuard(x) error "Missing guard object name" 32 | 33 | #endif /* MUTEXLOCKERGUARD_H_ */ 34 | -------------------------------------------------------------------------------- /include/Poller.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Poller.h 3 | * 4 | * Created on: Jan 15, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #ifndef POLLER_H_ 9 | #define POLLER_H_ 10 | 11 | #include "noncopyable.h" 12 | #include "TimeStamp.h" 13 | 14 | #include 15 | #include 16 | 17 | struct pollfd; 18 | 19 | namespace miniws { 20 | 21 | class EventLoop; 22 | class Channel; 23 | 24 | class Poller : noncopyable { 25 | public: 26 | Poller(EventLoop *loop); 27 | ~Poller(); 28 | 29 | /// Polls the I/O events 30 | /// Must be called in the loop thread 31 | TimeStamp poll(int timeoutMs, std::vector &activeChannels); 32 | 33 | /// Changes the interested I/O events; 34 | /// Must be called in the loop thread 35 | void updateChannel(Channel *channel); 36 | 37 | /// Remove the channel, when it destructs. 38 | /// Must be called in the loop thread. 39 | void removeChannel(Channel *channel); 40 | 41 | void assertInLoopThread(); 42 | 43 | private: 44 | void fillActiveChannels(int numEvents, std::vector &activeChannels) const; 45 | 46 | private: 47 | EventLoop *m_ownerLoop; 48 | std::vector m_pollfds; 49 | std::map m_channels; 50 | }; 51 | 52 | } 53 | 54 | 55 | #endif /* POLLER_H_ */ 56 | -------------------------------------------------------------------------------- /include/Socket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Socket.h 3 | * 4 | * Created on: Feb 1, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #pragma once 9 | #include "noncopyable.h" 10 | #include "InetAddr.h" 11 | 12 | namespace miniws { 13 | 14 | class Socket : noncopyable { 15 | public: 16 | Socket(); 17 | Socket(int sockfd); 18 | ~Socket(); 19 | 20 | int getSocketfd() const; 21 | int getSocketError(); 22 | void setTcpNoDelay(bool on); 23 | void setTcpKeepAlive(bool on); 24 | void bindAddr(const InetAddr &addr); 25 | void listenConn(); 26 | int acceptConn(InetAddr &peerAddr); 27 | 28 | private: 29 | const int m_socketfd; 30 | }; 31 | 32 | } -------------------------------------------------------------------------------- /include/TcpConnection.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TcpConnection.h 3 | * 4 | * Created on: Feb 6, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #pragma once 9 | 10 | #include "noncopyable.h" 11 | #include "EventLoop.h" 12 | #include "Socket.h" 13 | #include "Channel.h" 14 | #include "InetAddr.h" 15 | #include "Common.h" 16 | #include "Buffer.h" 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | namespace miniws { 23 | 24 | class TcpConnection : noncopyable, 25 | public std::enable_shared_from_this { 26 | public: 27 | TcpConnection(EventLoop *loop, 28 | const std::string &name, 29 | int sockfd, 30 | const InetAddr &localAddr, 31 | const InetAddr &peerAddr, 32 | double delayCloseSec); 33 | ~TcpConnection(); 34 | std::string getName(); 35 | EventLoop *getLoop(); 36 | 37 | void setConnectionCallback(const ConnectionCallback &cb); 38 | void setMessageCallback(const MessageCallback &cb); 39 | void setCloseCallback(const CloseCallback &cb); 40 | void setTcpNoDelay(bool on); 41 | void setTcpKeepAlive(bool on); 42 | void connectEstablished(); 43 | void connectDestroyed(); 44 | void sendv(struct iovec* iov, size_t iovlen); 45 | void setDelayClose(bool delayClose); 46 | 47 | private: 48 | enum StateE { kConnecting, kConnected, kDisconnected, }; 49 | static const size_t READ_BUFFER_SIZE = 512; //FIXME 修改为用户可自定义缓存大小 50 | private: 51 | void setState(StateE s); 52 | void handleRead(); 53 | void handleWrite(); 54 | void handleClose(); 55 | void handleError(); 56 | void shutdown(); 57 | 58 | private: 59 | EventLoop *m_loop; 60 | std::string m_name; 61 | StateE m_state; 62 | Socket m_socket; 63 | Channel m_channel; 64 | InetAddr m_localAddr; 65 | InetAddr m_peerAddr; 66 | Buffer m_readBuf; 67 | bool m_delayClose; 68 | double m_delayCloseSec; 69 | TimerId m_delayTimerId; 70 | ConnectionCallback m_connectionCb; 71 | MessageCallback m_messageCb; 72 | CloseCallback m_closeCb; 73 | }; 74 | 75 | } -------------------------------------------------------------------------------- /include/TcpServer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TcpServer.h 3 | * 4 | * Created on: Feb 6, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #pragma once 9 | 10 | #include "noncopyable.h" 11 | #include "InetAddr.h" 12 | #include "EventLoop.h" 13 | #include "Acceptor.h" 14 | #include "Common.h" 15 | #include "EventLoopThreadPool.h" 16 | 17 | #include 18 | #include 19 | 20 | namespace miniws { 21 | 22 | class TcpServer : noncopyable { 23 | public: 24 | TcpServer(EventLoop *loop, const std::string name, int numThreads, const InetAddr &localAddr, double delayCloseSec); 25 | ~TcpServer(); 26 | 27 | void start(); 28 | 29 | /// Not Thread safe 30 | void setConnectionCallback(const ConnectionCallback &cb); 31 | /// Not Thread safe 32 | void setMessageCallback(const MessageCallback &cb); 33 | 34 | private: 35 | /// Not thread safe, but in loop 36 | void newConnection(int sockfd, const InetAddr &peerAddr); 37 | void removeConnection(const TcpConnectionPtr conn); 38 | void removeConnectionInLoop(const TcpConnectionPtr conn); 39 | 40 | private: 41 | typedef std::map ConnectionMap; 42 | 43 | EventLoop *m_loop; // the acceptor loop 44 | const std::string m_name; 45 | EventLoopThreadPool m_eventLoopThreadPool; 46 | InetAddr m_localAddr; 47 | Acceptor m_acceptor; 48 | ConnectionCallback m_connectionCb; 49 | MessageCallback m_messageCb; 50 | bool m_started; 51 | int m_nextConnId; 52 | double m_delayCloseSec; 53 | ConnectionMap m_connections; 54 | }; 55 | 56 | } -------------------------------------------------------------------------------- /include/Thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Thread.h 3 | * 4 | * Created on: Dec 17, 2018 5 | * Author: xiagai 6 | */ 7 | 8 | #ifndef THREAD_H_ 9 | #define THREAD_H_ 10 | 11 | #include "noncopyable.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace miniws { 20 | 21 | class Thread : noncopyable { 22 | public: 23 | typedef std::function ThreadFunc; 24 | //typedef void *(*ThreadFunc)(void *); 25 | Thread(ThreadFunc func); 26 | ~Thread(); 27 | void start(); 28 | int join(); 29 | 30 | private: 31 | static void *startThread(void *); 32 | 33 | private: 34 | pthread_t m_thread; 35 | pid_t m_tid; 36 | ThreadFunc m_func; 37 | }; 38 | 39 | } 40 | 41 | 42 | 43 | #endif /* THREAD_H_ */ 44 | -------------------------------------------------------------------------------- /include/TimeStamp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TImeStamp.h 3 | * 4 | * Created on: Jan 16, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #ifndef TIMESTAMP_H_ 9 | #define TIMESTAMP_H_ 10 | 11 | #include 12 | 13 | namespace miniws { 14 | 15 | class TimeStamp { 16 | public: 17 | TimeStamp(); 18 | explicit TimeStamp(int64_t microseconds); 19 | int64_t getMicroSeconds() const; 20 | inline bool operator<(const TimeStamp &rhs) const { 21 | return this->getMicroSeconds() < rhs.getMicroSeconds(); 22 | } 23 | inline bool operator==(const TimeStamp &rhs) const { 24 | return this->getMicroSeconds() == rhs.getMicroSeconds(); 25 | } 26 | inline bool operator>(const TimeStamp &rhs) const { 27 | return this->getMicroSeconds() > rhs.getMicroSeconds(); 28 | } 29 | inline bool operator<=(const TimeStamp &rhs) const { 30 | return !(*this > rhs); 31 | } 32 | inline bool operator>=(const TimeStamp &rhs) const { 33 | return !(*this < rhs); 34 | } 35 | bool isValid(); 36 | 37 | static TimeStamp now(); 38 | static TimeStamp invalid(); 39 | static TimeStamp addTime(TimeStamp, double); 40 | 41 | public: 42 | const static int kMicroSecPerSec = 1000 * 1000; 43 | 44 | private: 45 | int64_t m_mircoseconds; 46 | }; 47 | 48 | } 49 | 50 | #endif /* TIMESTAMP_H_ */ 51 | -------------------------------------------------------------------------------- /include/Timer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Time.h 3 | * 4 | * Created on: Jan 17, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #pragma once 9 | 10 | #include "noncopyable.h" 11 | #include "TimeStamp.h" 12 | #include "Common.h" 13 | 14 | #include 15 | 16 | namespace miniws { 17 | 18 | class Timer : noncopyable { 19 | public: 20 | typedef std::function TimerCallback; 21 | 22 | Timer(TimerId id, TimerCallback cb, TimeStamp expiration, double interval); 23 | 24 | void cb(); 25 | TimerId getTimerId() const; 26 | TimeStamp getExpiration(); 27 | double getInterval(); 28 | bool getRepeat(); 29 | void restart(TimeStamp now); 30 | bool toCancel() const; 31 | void setToCancel(bool on); 32 | 33 | private: 34 | // set only once 35 | const TimerId m_timerId; 36 | const TimerCallback m_cb; 37 | TimeStamp m_expiration; 38 | const double m_interval; 39 | const bool m_repeat; 40 | 41 | bool m_toCancel; 42 | 43 | }; 44 | 45 | } -------------------------------------------------------------------------------- /include/TimerQueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TimeQueue.h 3 | * 4 | * Created on: Jan 29, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #pragma once 9 | 10 | #include "noncopyable.h" 11 | #include "Channel.h" 12 | #include "TimeStamp.h" 13 | #include "Timer.h" 14 | #include "Common.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace miniws { 22 | 23 | class EventLoop; 24 | 25 | class TimerQueue : noncopyable { 26 | public: 27 | 28 | explicit TimerQueue(EventLoop *loop); 29 | ~TimerQueue(); 30 | 31 | //thread safe 32 | TimerId addTimer(const Timer::TimerCallback &cb, TimeStamp when, double interval); 33 | void cancelTimer(TimerId); 34 | 35 | private: 36 | typedef std::map> TimersQueue; 37 | typedef std::unordered_map> TimersMap; 38 | 39 | void addTimerInLoop(TimerId timerId, std::shared_ptr timer); 40 | void cancelTimerInLoop(TimerId); 41 | 42 | // called when timerfd alarms 43 | void handleRead(); 44 | // move out all expired timers 45 | std::vector> getExpired(TimeStamp now); 46 | void reset(std::vector> &expired, TimeStamp now); 47 | 48 | // return whether the earliest item in the map is changed 49 | bool insert(std::shared_ptr &timer); 50 | 51 | private: 52 | static std::atomic_uint64_t s_sequence; 53 | 54 | EventLoop *m_ownerLoop; 55 | const int m_timerfd; 56 | Channel m_timerChannel; 57 | TimersMap m_timersMap; 58 | TimersQueue m_timersQueue; 59 | 60 | }; 61 | 62 | } -------------------------------------------------------------------------------- /include/noncopyable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * noncopybale.h 3 | * 4 | * Created on: Dec 16, 2018 5 | * Author: xiagai 6 | */ 7 | 8 | #ifndef NONCOPYABLE_H_ 9 | #define NONCOPYABLE_H_ 10 | 11 | namespace miniws { 12 | 13 | class noncopyable { 14 | public: 15 | noncopyable(const noncopyable &) = delete; 16 | void operator=(const noncopyable &) = delete; 17 | 18 | protected: 19 | noncopyable() = default; 20 | ~noncopyable() = default; 21 | }; 22 | 23 | } 24 | 25 | #endif /* NONCOPYABLE_H_ */ 26 | -------------------------------------------------------------------------------- /src/Acceptor.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Acceptor.cc 3 | * 4 | * Created on: Feb 1, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #include "Acceptor.h" 9 | 10 | #include 11 | 12 | namespace miniws { 13 | 14 | Acceptor::Acceptor(EventLoop *eventLoop, const InetAddr &listenAddr) 15 | : m_eventLoop(eventLoop), 16 | m_listenSocket(), 17 | m_listenChannel(eventLoop, m_listenSocket.getSocketfd()) { 18 | m_listenSocket.bindAddr(listenAddr); 19 | m_listenChannel.setReadCallback(std::bind(&Acceptor::handleRead, this)); 20 | } 21 | 22 | void Acceptor::setNewConnCallback(const NewConnCallback &cb) { 23 | m_newConnCb = cb; 24 | } 25 | 26 | void Acceptor::listen() { 27 | m_eventLoop->assertInLoopThread(); 28 | m_listenSocket.listenConn(); 29 | m_listenChannel.enableReading(); 30 | } 31 | 32 | void Acceptor::handleRead() { 33 | m_eventLoop->assertInLoopThread(); 34 | InetAddr peerAddr; 35 | //FIX ME file descriptor mo more 36 | int connfd = m_listenSocket.acceptConn(peerAddr); 37 | if (connfd >= 0) { 38 | if (m_newConnCb) { 39 | m_newConnCb(connfd, peerAddr); 40 | } 41 | else { 42 | close(connfd); 43 | } 44 | } 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /src/Buffer.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Buffer.cc 3 | * 4 | * Created on: Feb 18, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #include "Buffer.h" 9 | 10 | #include 11 | 12 | namespace miniws { 13 | 14 | Buffer::Buffer(size_t capacity) 15 | : m_capacity(capacity), 16 | m_size(0), 17 | m_start(0), 18 | m_end(0), 19 | m_buf(capacity, 0) {} 20 | 21 | Buffer::~Buffer() {}; 22 | 23 | bool Buffer::isEmpty() const { 24 | return m_size == 0; 25 | } 26 | 27 | bool Buffer::isFull() const { 28 | return m_size == m_capacity; 29 | } 30 | 31 | size_t Buffer::remainingSize() const { 32 | return m_capacity - m_size; 33 | } 34 | 35 | size_t Buffer::size() const { 36 | return m_size; 37 | } 38 | 39 | bool Buffer::putIn(char *buf, size_t n) { 40 | if (n > remainingSize()) { 41 | return false; 42 | } 43 | for (size_t i = 0; i < n; ++i) { 44 | m_buf[m_end % m_capacity] = buf[i]; 45 | m_end = (m_end + 1) % m_capacity; 46 | ++m_size; 47 | } 48 | return true; 49 | } 50 | 51 | bool Buffer::takeOut(size_t n) { 52 | if (n > m_size) { 53 | return false; 54 | } 55 | m_start = (m_start + n) % m_capacity; 56 | m_size -= n; 57 | return true; 58 | } 59 | 60 | char &Buffer::operator[](size_t i) { 61 | return m_buf[(m_start + i) % m_capacity]; 62 | } 63 | 64 | size_t Buffer::findCRLF(size_t pos) const { 65 | for (; pos + 1 < m_size; ++pos) { 66 | if (m_buf[(m_start + pos) % m_capacity] == '\r' && m_buf[(m_start + pos + 1) % m_capacity] == '\n') { 67 | return pos; 68 | } 69 | } 70 | return m_size; 71 | } 72 | 73 | size_t Buffer::findSpace(size_t l, size_t r) const { 74 | if (l >= m_size || r >= m_size) { 75 | return m_size; 76 | } 77 | for (; l < r; ++l) { 78 | if (m_buf[(m_start + l) % m_capacity] == ' ' || m_buf[(m_start + l) % m_capacity] == '\t') { 79 | return l; 80 | } 81 | } 82 | return m_size; 83 | } 84 | 85 | size_t Buffer::skipSpace(size_t pos) const { 86 | if (pos >= m_size) { 87 | return m_size; 88 | } 89 | while (pos < m_size) { 90 | if (!(m_buf[(m_start + pos) % m_capacity] == ' ' || m_buf[(m_start + pos) % m_capacity] == '\t')) { 91 | return pos; 92 | } 93 | pos++; 94 | } 95 | return m_size; 96 | } 97 | 98 | std::string Buffer::getStringPiece(size_t pos, size_t len) const { 99 | if (pos + len > m_size) { 100 | return ""; 101 | } 102 | if (((m_start + pos) % m_capacity + len) <= m_capacity) { 103 | return std::string(m_buf.begin() + (m_start + pos) % m_capacity, m_buf.begin() + (m_start + pos) % m_capacity + len); 104 | } 105 | else { 106 | std::string str1(m_buf.begin() + (m_start + pos) % m_capacity, m_buf.end()); 107 | std::string str2(m_buf.begin(), m_buf.begin() + (m_start + pos + len) % m_capacity); 108 | return str1 + str2; 109 | } 110 | } 111 | 112 | } -------------------------------------------------------------------------------- /src/Channel.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Channel.cc 3 | * 4 | * Created on: Jan 15, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | 9 | #include "Channel.h" 10 | #include "EventLoop.h" 11 | 12 | #include 13 | #include 14 | 15 | namespace miniws { 16 | 17 | Channel::Channel(EventLoop *loop, int fd, bool readET, bool writeET) 18 | : m_ownerLoop(loop), 19 | m_fd(fd), 20 | m_readET(readET), 21 | m_writeET(writeET), 22 | m_events(0), 23 | m_revents(0), 24 | m_index(-1), 25 | m_eventHandling(false) { 26 | 27 | } 28 | Channel::~Channel() { 29 | assert(!m_eventHandling); 30 | } 31 | void Channel::handleEvent() { 32 | m_eventHandling = true; 33 | if (m_revents & POLLNVAL) { 34 | printf("LOG_WARN Channel::handleEvent() POLLNVAL\n"); 35 | } 36 | if ((m_revents & POLLHUP) && !(m_revents & POLLIN)) { 37 | printf("LOG_WARN Channel::handleEvent() POLLHUP\n"); 38 | if (m_closeCallback) { 39 | m_closeCallback(); 40 | } 41 | } 42 | if (m_revents & (POLLERR | POLLNVAL)) { 43 | if (m_errorCallback) { 44 | m_errorCallback(); 45 | } 46 | } 47 | if (m_revents & (POLLIN | POLLPRI | POLLRDHUP)) { 48 | if (m_readCallback) { 49 | m_readCallback(); 50 | } 51 | } 52 | if (m_revents & (POLLOUT)) { 53 | if (m_writeCallback) { 54 | m_writeCallback(); 55 | } 56 | } 57 | m_eventHandling = false; 58 | } 59 | void Channel::setReadCallback(const EventCallback& cb) { 60 | m_readCallback = cb; 61 | } 62 | void Channel::setWriteCallback(const EventCallback& cb) { 63 | m_writeCallback = cb; 64 | } 65 | void Channel::setErrorCallback(const EventCallback& cb) { 66 | m_errorCallback = cb; 67 | } 68 | void Channel::setCloseCallback(const EventCallback& cb) { 69 | m_closeCallback = cb; 70 | } 71 | int Channel::fd() const { 72 | return m_fd; 73 | } 74 | int Channel::events() const { 75 | return m_events; 76 | } 77 | void Channel::setRevents(int revt) { 78 | m_revents = revt; 79 | } 80 | bool Channel::isNoneEvent() const { 81 | return m_events == kNoneEvent; 82 | } 83 | void Channel::enableReading() { 84 | m_events |= kReadEvent; 85 | if (isReadETMode()) { 86 | m_events |= EPOLLET; 87 | } 88 | update(); 89 | } 90 | void Channel::enableWriting() { 91 | m_events |= kWriteEvent; 92 | if (isWriteETMode()) { 93 | m_events |= EPOLLET; 94 | } 95 | update(); 96 | } 97 | void Channel::disableWriting() { 98 | m_events &= ~kWriteEvent; 99 | update(); 100 | } 101 | void Channel::disableAll() { 102 | m_events = kNoneEvent; 103 | update(); 104 | } 105 | bool Channel::isReadETMode() const { 106 | return m_readET; 107 | } 108 | bool Channel::isWriteETMode() const { 109 | return m_writeET; 110 | } 111 | int Channel::index() { 112 | return m_index; 113 | } 114 | void Channel::setIndex(int idx) { 115 | m_index = idx; 116 | } 117 | 118 | EventLoop *Channel::ownerLoop() { 119 | return m_ownerLoop; 120 | } 121 | 122 | void Channel::update() { 123 | m_ownerLoop->updateChannel(this); 124 | } 125 | 126 | void Channel::remove() { 127 | m_ownerLoop->removeChannel(this); 128 | } 129 | 130 | 131 | } 132 | 133 | 134 | -------------------------------------------------------------------------------- /src/CurrentThread.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * CurrentThread.cc 3 | * 4 | * Created on: Jan 9, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | 9 | #include "CurrentThread.h" 10 | 11 | namespace miniws { 12 | 13 | pid_t CurrentThread::tid() { 14 | return syscall(SYS_gettid); 15 | } 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/Epoller.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Epoller.cc 3 | * 4 | * Created on: Feb 19, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #include "Epoller.h" 9 | #include "EventLoop.h" 10 | #include "Channel.h" 11 | #include "Common.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace miniws { 19 | 20 | Epoller::Epoller(EventLoop *ownerLoop) 21 | : m_ownerLoop(ownerLoop), 22 | m_epollfd(::epoll_create1(EPOLL_CLOEXEC)) {} 23 | 24 | Epoller::~Epoller() { 25 | ::close(m_epollfd); 26 | } 27 | 28 | TimeStamp Epoller::epoll(int timeoutMs, std::vector &activeChannels) { 29 | m_ownerLoop->assertInLoopThread(); 30 | epoll_event revents[MAX_EVENT_NUM]; 31 | int numEvents = ::epoll_wait(m_epollfd, revents, MAX_EVENT_NUM, timeoutMs); 32 | TimeStamp now(TimeStamp::now()); 33 | if (numEvents > 0) { 34 | printf("LOG_TRACE %p %d events happened\n", m_ownerLoop, numEvents); 35 | fillActiveChannels(numEvents, revents, activeChannels); 36 | } else if (numEvents == 0) { 37 | printf("LOG_TRACE %p nothing happened\n", m_ownerLoop); 38 | } else { 39 | char strerr[64]; 40 | printf("LOG_ERR EPoller::epoll() %s\n", strerror_r(errno, strerr, sizeof strerr)); 41 | } 42 | return now; 43 | } 44 | 45 | void Epoller::updateChannel(Channel *channel) { 46 | m_ownerLoop->assertInLoopThread(); 47 | if (channel->isNoneEvent()) { 48 | removeChannel(channel); 49 | return; 50 | } 51 | printf("LOG_TRACE Epoller::updateChannel %p fd = %d events = %u\n", m_ownerLoop, channel->fd(), channel->events()); 52 | epoll_event event; 53 | event.data.fd = channel->fd(); 54 | event.events = channel->events(); 55 | int ret; 56 | if (m_channels.find(channel->fd()) != m_channels.end()) { 57 | ret = epoll_ctl(m_epollfd, EPOLL_CTL_MOD, channel->fd(), &event); 58 | } 59 | else { 60 | ret = epoll_ctl(m_epollfd, EPOLL_CTL_ADD, channel->fd(), &event); 61 | m_channels[channel->fd()] = channel; 62 | assert(m_channels.find(channel->fd()) != m_channels.end()); 63 | } 64 | if (ret < 0) { 65 | int err = errno; 66 | assert(err != EEXIST || err != ENOENT); 67 | char strerr[64]; 68 | printf("LOG_ERR Epoller::updateChannel %s\n", strerror_r(errno, strerr, sizeof strerr)); 69 | } 70 | } 71 | 72 | void Epoller::removeChannel(Channel *channel) { 73 | m_ownerLoop->assertInLoopThread(); 74 | if (m_channels.find(channel->fd()) != m_channels.end()) { 75 | printf("LOG_TRACE Epoller::removeChannel %p fd = %d events = %d\n", m_ownerLoop, channel->fd(), channel->events()); 76 | //Since Linux 2.6.9 event can be NULL 77 | int ret = epoll_ctl(m_epollfd, EPOLL_CTL_DEL, channel->fd(), NULL); 78 | size_t n = m_channels.erase(channel->fd()); 79 | assert(n == 1); 80 | if (ret < 0) { 81 | int err = errno; 82 | assert(err != ENOENT); 83 | char strerr[64]; 84 | printf("LOG_ERR Epoller::removeChannel %s\n", strerror_r(errno, strerr, sizeof strerr)); 85 | } 86 | } 87 | } 88 | 89 | void Epoller::fillActiveChannels(int numEvents, epoll_event *revents, std::vector &activeChannels) const { 90 | for (int i = 0; i < numEvents; ++i) { 91 | auto chIt = m_channels.find(revents[i].data.fd); 92 | assert(chIt != m_channels.end()); 93 | Channel *channel = chIt->second; 94 | assert(channel->fd() == revents[i].data.fd); 95 | channel->setRevents(revents[i].events); 96 | activeChannels.push_back(channel); 97 | } 98 | } 99 | 100 | } -------------------------------------------------------------------------------- /src/EventLoop.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * EventLoop.cc 3 | * 4 | * Created on: Jan 9, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | 9 | #include "EventLoop.h" 10 | #include "CurrentThread.h" 11 | #include "Epoller.h" 12 | #include "Channel.h" 13 | #include "MutexLockerGuard.h" 14 | 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace miniws { 23 | 24 | namespace { 25 | 26 | __thread EventLoop *t_loopInThisThread = nullptr; 27 | const int kPollTimeMs = 10000; 28 | 29 | } 30 | 31 | namespace detail { 32 | 33 | int createEventFd() { 34 | int fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); 35 | return fd; 36 | } 37 | 38 | } 39 | 40 | EventLoop::EventLoop() 41 | : m_looping(false), 42 | m_quit(false), 43 | m_threadId(CurrentThread::tid()), 44 | m_poller(std::make_unique(this)), 45 | m_timerQueue(std::make_unique(this)), 46 | m_eventHandling(false), 47 | m_currentChannel(nullptr), 48 | m_callingPendingFunctors(false), 49 | m_wakeupfd(detail::createEventFd()), 50 | m_wakeupChannel(std::make_unique(this, m_wakeupfd)), 51 | m_mutex() { 52 | printf("EventLoop created %p in thread %d\n", this, m_threadId); 53 | if (t_loopInThisThread) { 54 | printf("Another EventLoop %p exists in this thread %d\n", t_loopInThisThread, m_threadId); 55 | } 56 | else { 57 | t_loopInThisThread = this; 58 | } 59 | m_wakeupChannel->setReadCallback(std::bind(&EventLoop::handleWakeUp, this)); 60 | m_wakeupChannel->enableReading(); 61 | } 62 | 63 | EventLoop::~EventLoop() { 64 | assert(!m_looping); 65 | m_wakeupChannel->disableAll(); 66 | m_wakeupChannel->remove(); 67 | close(m_wakeupfd); 68 | t_loopInThisThread = nullptr; 69 | } 70 | 71 | void EventLoop::loop() { 72 | assert(!m_looping); 73 | assertInLoopThread(); 74 | m_looping = true; 75 | m_quit = false; 76 | 77 | while (!m_quit) { 78 | m_activeChannels.clear(); 79 | m_poller->epoll(kPollTimeMs, m_activeChannels); 80 | m_eventHandling = true; 81 | for (std::vector::iterator it = m_activeChannels.begin(); 82 | it != m_activeChannels.end(); ++it) { 83 | m_currentChannel = (*it); 84 | m_currentChannel->handleEvent(); 85 | } 86 | m_eventHandling = false; 87 | doPendingFunctors(); 88 | } 89 | printf("LOG_TRACE EventLoop %p stop looping\n", this); 90 | m_looping = false; 91 | } 92 | 93 | void EventLoop::quit() { 94 | m_quit = true; 95 | if (!isInLoopThread()) { 96 | wakeup(); 97 | } 98 | } 99 | 100 | bool EventLoop::isInLoopThread() const { 101 | return m_threadId == CurrentThread::tid(); 102 | } 103 | 104 | void EventLoop::updateChannel(Channel *channel) { 105 | assert(channel->ownerLoop() == this); 106 | assertInLoopThread(); 107 | m_poller->updateChannel(channel); 108 | } 109 | 110 | void EventLoop::removeChannel(Channel *channel) { 111 | assert(channel->ownerLoop() == this); 112 | assertInLoopThread(); 113 | //??? 114 | if (m_eventHandling) { 115 | assert(m_currentChannel == channel || std::find(m_activeChannels.begin(), m_activeChannels.end(), channel) != m_activeChannels.end()); 116 | } 117 | m_poller->removeChannel(channel); 118 | } 119 | 120 | void EventLoop::runInLoop(const Functor &cb) { 121 | if (isInLoopThread()) { 122 | cb(); 123 | } 124 | else { 125 | queueInLoop(cb); 126 | } 127 | } 128 | 129 | void EventLoop::wakeup() { 130 | uint64_t one = 1; 131 | ssize_t n = write(m_wakeupfd, &one, sizeof(one)); 132 | if (n != sizeof(one)) { 133 | printf("LOG_ERROR EventLoop::wakeup() writes %ld bytes instead of 8\n", n); 134 | } 135 | } 136 | 137 | void EventLoop::handleWakeUp() { 138 | uint64_t ret; 139 | ssize_t n = read(m_wakeupfd, &ret, sizeof(ret)); 140 | if (n != sizeof(ret)) { 141 | printf("LOG_ERROR EventLoop::handleWakeUp() reads %ld bytes instead of 8\n", n); 142 | } 143 | } 144 | 145 | void EventLoop::assertInLoopThread() { 146 | assert(isInLoopThread()); 147 | } 148 | 149 | EventLoop *EventLoop::getEventLoopOfCurrentThread() { 150 | return t_loopInThisThread; 151 | } 152 | 153 | TimerId EventLoop::runAt(const TimeStamp ×tamp, const Timer::TimerCallback &cb) { 154 | return m_timerQueue->addTimer(cb, timestamp, 0.0); 155 | } 156 | 157 | TimerId EventLoop::runAfter(double delay, const Timer::TimerCallback &cb) { 158 | TimeStamp timestamp = TimeStamp::addTime(TimeStamp::now(), delay); 159 | return runAt(timestamp, cb); 160 | } 161 | 162 | TimerId EventLoop::runEvery(double interval, const Timer::TimerCallback &cb) { 163 | TimeStamp timestamp = TimeStamp::addTime(TimeStamp::now(), interval); 164 | return m_timerQueue->addTimer(cb, timestamp, interval); 165 | } 166 | 167 | void EventLoop::cancelRun(TimerId timerId) { 168 | m_timerQueue->cancelTimer(timerId); 169 | } 170 | 171 | void EventLoop::doPendingFunctors() { 172 | std::vector functors; 173 | m_callingPendingFunctors = true; 174 | { 175 | MutexLockerGuard guard(m_mutex); 176 | functors.swap(m_pendingFunctors); 177 | } 178 | for (auto functor : functors) { 179 | functor(); 180 | } 181 | m_callingPendingFunctors = false; 182 | } 183 | 184 | void EventLoop::queueInLoop(const Functor &cb) { 185 | { 186 | MutexLockerGuard guard(m_mutex); 187 | m_pendingFunctors.push_back(cb); 188 | } 189 | if (!isInLoopThread() || m_callingPendingFunctors) { 190 | wakeup(); 191 | } 192 | } 193 | 194 | } -------------------------------------------------------------------------------- /src/EventLoopThread.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * EventLoopThread.cc 3 | * 4 | * Created on: Feb 1, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #include "EventLoopThread.h" 9 | 10 | namespace miniws { 11 | 12 | EventLoopThread::EventLoopThread() 13 | : m_eventLoop(nullptr), 14 | m_mutex(), 15 | m_cond(m_mutex), 16 | m_thread(std::bind(&EventLoopThread::threadFunc, this)) { 17 | 18 | } 19 | 20 | EventLoopThread::~EventLoopThread() { 21 | if (m_eventLoop != nullptr) { 22 | m_eventLoop->quit(); 23 | m_thread.join(); 24 | } 25 | } 26 | 27 | EventLoop *EventLoopThread::startLoop() { 28 | m_thread.start(); 29 | { 30 | MutexLockerGuard lock(m_mutex); 31 | while (m_eventLoop == nullptr) { 32 | m_cond.wait(); 33 | } 34 | } 35 | return m_eventLoop; 36 | } 37 | 38 | void EventLoopThread::threadFunc() { 39 | EventLoop loop; 40 | { 41 | MutexLockerGuard lock(m_mutex); 42 | m_eventLoop = &loop; 43 | m_cond.notify(); 44 | } 45 | loop.loop(); 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /src/EventLoopThreadPool.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * EventLoopThreadPool.cc 3 | * 4 | * Created on: Feb 15, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #include "EventLoopThreadPool.h" 9 | 10 | #include 11 | 12 | namespace miniws { 13 | 14 | EventLoopThreadPool::EventLoopThreadPool(EventLoop *baseLoop, int numThreads) 15 | : m_baseLoop(baseLoop), 16 | m_started(false), 17 | m_numThreads(numThreads), 18 | m_next(0) { 19 | assert(numThreads > 0); 20 | } 21 | 22 | EventLoopThreadPool::~EventLoopThreadPool() {} 23 | 24 | void EventLoopThreadPool::start() { 25 | m_baseLoop->assertInLoopThread(); 26 | m_started = true; 27 | for (int i = 0; i < m_numThreads; ++i) { 28 | std::shared_ptr t = std::make_shared(); 29 | m_threads.push_back(t); 30 | m_loops.push_back(t->startLoop()); 31 | } 32 | } 33 | 34 | EventLoop *EventLoopThreadPool::getNextLoop() { 35 | m_baseLoop->assertInLoopThread(); 36 | assert(m_started); 37 | EventLoop *loop = m_baseLoop; 38 | if (!m_loops.empty()) { 39 | loop = m_loops[m_next]; 40 | m_next = (m_next + 1) % m_numThreads; 41 | } 42 | return loop; 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /src/HttpParser.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * HttpParser.cc 3 | * 4 | * Created on: Feb 20, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #include "HttpParser.h" 9 | #include "Common.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace miniws { 19 | 20 | const char* HttpParser::ok_200_title = "OK"; 21 | const char* HttpParser::error_400_title = "Bad Request"; 22 | const char* HttpParser::error_400_form = "Your requests has bad syntax or is inherently impossible to satisfy.\n"; 23 | const char* HttpParser::error_403_title = "Forbidden"; 24 | const char* HttpParser::error_403_form = "You do not have permission to get file from this server.\n"; 25 | const char* HttpParser::error_404_title = "Not Found"; 26 | const char* HttpParser::error_404_form = "The requested file was not found on this server.\n"; 27 | const char* HttpParser::error_500_title = "Internal Error"; 28 | const char* HttpParser::error_500_form = "There was an unusual problem serving the requested file.\n"; 29 | 30 | HttpParser::HttpParser(char *homeDir, Buffer &readBuf) 31 | : m_homeDir(homeDir), 32 | m_checkState(CHECK_STATE_REQUESTLINE), 33 | m_method(GET), 34 | m_readBuf(readBuf), 35 | m_readIdx(readBuf.size()), 36 | m_lineStart(0), 37 | m_lineEnd(0), 38 | m_contentLen(0), 39 | m_linger(false), 40 | m_fileAddress(nullptr), 41 | m_writeIdx(0), 42 | m_status(0), 43 | m_title(nullptr) { 44 | 45 | } 46 | 47 | HttpParser::~HttpParser() { 48 | if (m_fileAddress) { 49 | munmap(m_fileAddress, m_fileStat.st_size); 50 | m_fileAddress = nullptr; 51 | } 52 | } 53 | 54 | struct httpret HttpParser::process() { 55 | HTTP_CODE res = processRead(); 56 | httpret ret; 57 | if (processWrite(res)) { 58 | ret.iov = m_iov; 59 | ret.iovlen = 2; 60 | ret.keepAlive = m_linger; 61 | return ret; 62 | } 63 | else { 64 | printf("SYS_DEBUG HttpParser::process\n"); 65 | } 66 | } 67 | 68 | HttpParser::HTTP_CODE HttpParser::processRead() { 69 | LINE_STATUS lineStatus = LINE_OK; 70 | HTTP_CODE ret = NO_REQUEST; 71 | while (m_checkState == CHECK_STATE_CONTENT || (lineStatus = parseLine()) == LINE_OK) { 72 | switch (m_checkState) { 73 | case CHECK_STATE_REQUESTLINE: 74 | ret = parseRequestLine(); 75 | if (ret == BAD_REQUEST) { 76 | return BAD_REQUEST; 77 | } 78 | break; 79 | case CHECK_STATE_HEADER: 80 | ret = parseHeaders(); 81 | if (ret == BAD_REQUEST) { 82 | return BAD_REQUEST; 83 | } 84 | else if (ret == GET_REQUEST) { 85 | return doRequest(); 86 | } 87 | break; 88 | case CHECK_STATE_CONTENT: 89 | ret = parseContent(); 90 | if (ret == GET_REQUEST) { 91 | return doRequest(); 92 | } 93 | lineStatus = LINE_OPEN; 94 | break; 95 | default: 96 | return INTERNAL_ERROR; 97 | } 98 | } 99 | return NO_REQUEST; 100 | } 101 | 102 | bool HttpParser::processWrite(HTTP_CODE httpCode) { 103 | switch(httpCode) { 104 | case FILE_REQUEST: 105 | m_status = ok_200; 106 | m_title = ok_200_title; 107 | break; 108 | case BAD_REQUEST: 109 | m_status = error_400; 110 | m_title = error_400_title; 111 | strncpy(m_form, error_400_form, strlen(error_400_form)); 112 | case FORBIDDEN_REQUEST: 113 | m_status = error_403; 114 | m_title = error_403_title; 115 | strncpy(m_form, error_403_form, strlen(error_403_form)); 116 | case NO_RESOURCE: 117 | m_status = error_404; 118 | m_title = error_404_title; 119 | strncpy(m_form, error_404_form, strlen(error_404_form)); 120 | case INTERNAL_ERROR: 121 | m_status = error_500; 122 | m_title = error_500_title; 123 | strncpy(m_form, error_500_form, strlen(error_500_form)); 124 | default: 125 | printf("SYS_DEBUG HttpParser::processWrite\n"); 126 | } 127 | bool ret = addStatusLine() && addHeaders(); 128 | addResponse(); 129 | addContent(); 130 | return ret; 131 | } 132 | 133 | HttpParser::LINE_STATUS HttpParser::parseLine() { 134 | size_t crlf = m_readBuf.findCRLF(m_lineStart); 135 | if (crlf == m_readBuf.size()) { 136 | return LINE_OPEN; 137 | } 138 | else { 139 | m_lineEnd = crlf; 140 | return LINE_OK; 141 | } 142 | } 143 | 144 | HttpParser::HTTP_CODE HttpParser::parseRequestLine() { 145 | //解析method 146 | size_t begin = m_readBuf.skipSpace(m_lineStart); 147 | size_t space = m_readBuf.findSpace(begin, m_lineEnd); 148 | if (space == m_readBuf.size()) { 149 | return BAD_REQUEST; 150 | } 151 | std::string method = m_readBuf.getStringPiece(begin, space - begin); 152 | if (method == "GET") { 153 | m_method = GET; 154 | } 155 | else { 156 | printf("only support get method now.\n"); 157 | return BAD_REQUEST; 158 | } 159 | 160 | //解析url 161 | begin = m_readBuf.skipSpace(space); 162 | space = m_readBuf.findSpace(begin, m_lineEnd); 163 | if (space == m_readBuf.size()) { 164 | return BAD_REQUEST; 165 | } 166 | m_url = m_readBuf.getStringPiece(begin, space - begin); 167 | 168 | //解析version 169 | begin = m_readBuf.skipSpace(space); 170 | m_version = m_readBuf.getStringPiece(begin, m_lineEnd - begin); 171 | if (m_version.compare("HTTP/1.1") != 0) { 172 | printf("only support http 1.1 now.\n"); 173 | return BAD_REQUEST; 174 | } 175 | m_checkState = CHECK_STATE_HEADER; 176 | //结尾需跳过/r/n两个字符的位置才是下一行的开始 177 | m_lineStart = m_lineEnd + 2; 178 | return NO_REQUEST; 179 | } 180 | 181 | HttpParser::HTTP_CODE HttpParser::parseHeaders() { 182 | //headers部分结束,根据content的长度判断有没有content部分 183 | if (m_lineStart == m_lineEnd) { 184 | m_lineStart = m_lineEnd + 2; 185 | if (m_contentLen != 0) { 186 | m_contentLen = CHECK_STATE_CONTENT; 187 | return NO_REQUEST; 188 | } 189 | else { 190 | return GET_REQUEST; 191 | } 192 | } 193 | //解析header 194 | else { 195 | std::string header = m_readBuf.getStringPiece(m_lineStart, m_lineEnd - m_lineStart); 196 | if (header.compare(0, 11, "Connection:") == 0) { 197 | size_t space = m_readBuf.skipSpace(m_lineStart + 11); 198 | if (header.compare(space - m_lineStart, header.length() - (space - m_lineStart), "keep-alive") == 0) { 199 | m_linger = true; 200 | } 201 | } 202 | else if (header.compare(0, 15, "Content-Length:") == 0) { 203 | size_t space = m_readBuf.skipSpace(m_lineStart + 15); 204 | m_contentLen = std::stoi(header.substr(space - m_lineStart, header.length() - (space - m_lineStart))); 205 | } 206 | else if (header.compare(0, 5, "Host:") == 0) { 207 | size_t space = m_readBuf.skipSpace(m_lineStart + 5); 208 | m_host = header.substr(space - m_lineStart, header.length() - (space - m_lineStart)); 209 | } 210 | else { 211 | printf("only support connection, content-length, host headers now, and unknow header %s\n", header.c_str()); 212 | } 213 | m_lineStart = m_lineEnd + 2; 214 | return NO_REQUEST; 215 | } 216 | } 217 | 218 | HttpParser::HTTP_CODE HttpParser::parseContent() { 219 | //目前没对content做任何处理 220 | if (m_readIdx >= (m_lineStart + m_contentLen)) { 221 | m_lineStart += m_contentLen; 222 | return GET_REQUEST; 223 | } 224 | return NO_REQUEST; 225 | } 226 | 227 | HttpParser::HTTP_CODE HttpParser::doRequest() { 228 | //将已解析的http报文移出缓冲区 229 | m_readBuf.takeOut(m_lineStart); 230 | strncpy(m_readFile, m_homeDir, FILENAME_LEN); 231 | int len = strlen(m_homeDir); 232 | strncpy(m_readFile + len, m_url.c_str(), FILENAME_LEN - len - 1); 233 | if (stat(m_readFile, &m_fileStat) < 0) { 234 | char strerr[64]; 235 | printf("SYS_INFO HttpParser::deRequest %s\n", strerror_r(errno, strerr, sizeof strerr)); 236 | return NO_RESOURCE; 237 | } 238 | if (!(m_fileStat.st_mode & S_IROTH)) { 239 | return FORBIDDEN_REQUEST; 240 | } 241 | if (!S_ISREG(m_fileStat.st_mode)) { 242 | printf("SYS_INFO HttpParser::doRequest not regular file\n"); 243 | return BAD_REQUEST; 244 | } 245 | int fd = ::open(m_readFile, O_RDONLY); 246 | m_fileAddress = (char *)mmap(0, m_fileStat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 247 | close(fd); 248 | return FILE_REQUEST; 249 | } 250 | 251 | bool HttpParser::addLine(const char *format, ...) { 252 | if (m_writeIdx >= WRITE_BUFFER_SIZE) { 253 | return false; 254 | } 255 | va_list arg_list; 256 | va_start(arg_list, format); 257 | int len = vsnprintf(m_response + m_writeIdx, WRITE_BUFFER_SIZE - 1 - m_writeIdx, format, arg_list); 258 | if (len >= (WRITE_BUFFER_SIZE - 1 - m_writeIdx)) { 259 | return false; 260 | } 261 | m_writeIdx += len; 262 | va_end(arg_list); 263 | return true; 264 | } 265 | 266 | bool HttpParser::addStatusLine() { 267 | return addLine("%s %d %s\r\n", "HTTP/1.1", m_status, m_title); 268 | } 269 | 270 | bool HttpParser::addHeaders() { 271 | bool ret = true; 272 | if (m_status == ok_200) { 273 | ret = addLine("Content-Length: %d\r\n", m_fileStat.st_size); 274 | } 275 | else { 276 | ret = addLine("Content-Length: %d\r\n", strlen(m_form)); 277 | } 278 | if (!ret) { 279 | return ret; 280 | } 281 | ret = addLine("Connection: %s\r\n", (m_linger ? "keep-alive" : "close")); 282 | if (!ret) { 283 | return ret; 284 | } 285 | ret = addLine("%s", "\r\n"); 286 | if (!ret) { 287 | return ret; 288 | } 289 | return ret; 290 | } 291 | 292 | void HttpParser::addResponse() { 293 | m_iov[0].iov_base = m_response; 294 | m_iov[0].iov_len = m_writeIdx; 295 | } 296 | 297 | void HttpParser::addContent() { 298 | if (m_status == ok_200) { 299 | //FIXME file size == 0 300 | m_iov[1].iov_base = m_fileAddress; 301 | m_iov[1].iov_len = m_fileStat.st_size; 302 | } 303 | else { 304 | m_iov[1].iov_base = m_form; 305 | m_iov[1].iov_len = strlen(m_form); 306 | } 307 | } 308 | 309 | } -------------------------------------------------------------------------------- /src/HttpServer.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * HttpServer.cc 3 | * 4 | * Created on: Feb 25, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #include "HttpServer.h" 9 | #include "HttpParser.h" 10 | #include "TcpConnection.h" 11 | 12 | #include 13 | 14 | namespace miniws { 15 | 16 | HttpServer::HttpServer(std::string &serverName, InetAddr &localAddr, std::string &homeDir, int numThreads, double delayCLoseSec) 17 | : m_name(serverName), 18 | m_localAddr(localAddr), 19 | m_numThreads(numThreads), 20 | m_delayCloseSec(delayCLoseSec) { 21 | if (homeDir.size() > MAX_HOME_SIZE) { 22 | printf("The home directory length should be less than 100 characters. And ./ directory will be used.\n"); 23 | strncpy(m_homeDir, "./", MAX_HOME_SIZE); 24 | } 25 | else { 26 | strncpy(m_homeDir, homeDir.c_str(), MAX_HOME_SIZE); 27 | } 28 | } 29 | 30 | HttpServer::~HttpServer() {} 31 | 32 | void HttpServer::start() { 33 | EventLoop baseLoop; 34 | TcpServer tcpServer(&baseLoop, m_name, 4, m_localAddr, m_delayCloseSec); 35 | tcpServer.setConnectionCallback(std::bind(&HttpServer::onConnection, this, std::placeholders::_1)); 36 | tcpServer.setMessageCallback(std::bind(&HttpServer::onMessage, this, std::placeholders::_1, std::placeholders::_2)); 37 | tcpServer.start(); 38 | baseLoop.loop(); 39 | } 40 | 41 | void HttpServer::onConnection(const TcpConnectionPtr conn) { 42 | printf("connCb\n"); 43 | } 44 | 45 | void HttpServer::onMessage(const TcpConnectionPtr conn, Buffer &buf) { 46 | printf("messageCb\n"); 47 | HttpParser httpParser(m_homeDir, buf); 48 | httpret data = httpParser.process(); 49 | if (data.iovlen > 0) { 50 | conn->sendv(data.iov, data.iovlen); 51 | } 52 | if (data.keepAlive) { 53 | conn->setDelayClose(true); 54 | } 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /src/InetAddr.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * InetAddr.cc 3 | * 4 | * Created on: Feb 2, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #include "InetAddr.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace miniws { 16 | 17 | InetAddr::InetAddr() { 18 | memset(&m_addr, 0, sizeof(m_addr)); 19 | } 20 | 21 | InetAddr::InetAddr(std::string ip, uint16_t port) { 22 | memset(&m_addr, 0, sizeof(m_addr)); 23 | m_addr.sin_family = AF_INET; 24 | int ret = inet_pton(m_addr.sin_family, ip.c_str(), &(m_addr.sin_addr)); 25 | if (ret <= 0) { 26 | printf("LOG_SYSERR InetAddr::InetAddr\n"); 27 | } 28 | m_addr.sin_port = htobe16(port); 29 | } 30 | 31 | InetAddr::InetAddr(sockaddr_in &addr) 32 | : m_addr(addr) { 33 | memset(&m_addr, 0, sizeof(m_addr)); 34 | m_addr = addr; 35 | } 36 | 37 | InetAddr::~InetAddr() {} 38 | 39 | const struct sockaddr_in *InetAddr::getAddr() const{ 40 | return &m_addr; 41 | } 42 | 43 | std::string InetAddr::getIP() const { 44 | char ip[INET_ADDRSTRLEN] = ""; 45 | const char *dst = inet_ntop(m_addr.sin_family, &(m_addr.sin_addr), ip, INET_ADDRSTRLEN); 46 | assert(dst != nullptr); 47 | return ip; 48 | } 49 | 50 | std::string InetAddr::getIPPort() const { 51 | std::string ipport; 52 | ipport = getIP(); 53 | ipport.append(":" + std::to_string(getPort())); 54 | return ipport; 55 | } 56 | 57 | uint16_t InetAddr::getPort() const { 58 | return be16toh(m_addr.sin_port); 59 | } 60 | 61 | void InetAddr::setAddr(sockaddr_in &addr) { 62 | m_addr = addr; 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /src/Poller.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Poller.cc 3 | * 4 | * Created on: Jan 15, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #include "Poller.h" 9 | #include "EventLoop.h" 10 | #include "Channel.h" 11 | 12 | #include 13 | #include 14 | 15 | namespace miniws { 16 | 17 | Poller::Poller(EventLoop *loop) 18 | : m_ownerLoop(loop) {} 19 | Poller::~Poller() {} 20 | 21 | TimeStamp Poller::poll(int timeoutMs, std::vector &activeChannels) { 22 | int numEvents = ::poll(&(*m_pollfds.begin()), m_pollfds.size(), timeoutMs); 23 | TimeStamp now(TimeStamp::now()); 24 | if (numEvents > 0) { 25 | printf("LOG_TRACE %p %d events happened\n", m_ownerLoop, numEvents); 26 | fillActiveChannels(numEvents, activeChannels); 27 | } else if (numEvents == 0) { 28 | printf("LOG_TRACE %p nothing happened\n", m_ownerLoop); 29 | } else { 30 | printf("LOG_SYSERR Poller::poll()\n"); 31 | } 32 | return now; 33 | } 34 | 35 | void Poller::updateChannel(Channel *channel) { 36 | assertInLoopThread(); 37 | printf("LOG_TRACE %p fd = %d events = %d\n", m_ownerLoop, channel->fd(), channel->events()); 38 | if (channel->index() < 0) { 39 | // a new channel, add to m_pollfds 40 | assert(m_channels.find(channel->fd()) == m_channels.end()); 41 | pollfd pfd; 42 | pfd.fd = channel->fd(); 43 | pfd.events = static_cast(channel->events()); 44 | pfd.revents = 0; 45 | m_pollfds.push_back(pfd); 46 | int idx = static_cast(m_pollfds.size()) - 1; 47 | assert(idx >= 0); 48 | channel->setIndex(idx); 49 | m_channels[pfd.fd] = channel; 50 | } else { 51 | // update existing channel 52 | assert(m_channels.find(channel->fd()) != m_channels.end()); 53 | assert(m_channels[channel->fd()] == channel); 54 | int idx = channel->index(); 55 | assert(0 <= idx && idx < static_cast(m_pollfds.size())); 56 | pollfd &pfd = m_pollfds[idx]; 57 | assert(pfd.fd == channel->fd() || pfd.fd == -1); 58 | pfd.events = static_cast(channel->events()); 59 | pfd.revents = 0; 60 | if (channel->isNoneEvent()) { 61 | pfd.fd = -1; 62 | } 63 | } 64 | } 65 | 66 | void Poller::removeChannel(Channel *channel) { 67 | assertInLoopThread(); 68 | if (channel->index() < 0) { 69 | // a new channel, don't need to remove 70 | } else { 71 | // remove existing channel 72 | assert(m_channels.find(channel->fd()) != m_channels.end()); 73 | assert(m_channels[channel->fd()] == channel); 74 | int idx = channel->index(); 75 | assert(0 <= idx && idx < static_cast(m_pollfds.size())); 76 | pollfd &pfd = m_pollfds[idx]; 77 | assert(pfd.fd == channel->fd() || pfd.fd == -1); 78 | size_t n = m_channels.erase(channel->fd()); 79 | assert(n == 1); 80 | m_pollfds.erase(m_pollfds.begin() + idx); 81 | } 82 | } 83 | 84 | void Poller::assertInLoopThread() { 85 | m_ownerLoop->assertInLoopThread(); 86 | } 87 | 88 | void Poller::fillActiveChannels(int numEvents, std::vector &activeChannels) const { 89 | for (std::vector::const_iterator it = m_pollfds.cbegin(); it != m_pollfds.cend() && numEvents > 0; ++it) { 90 | if (it->revents > 0) { 91 | --numEvents; 92 | std::map::const_iterator chIt = m_channels.find(it->fd); 93 | assert(chIt != m_channels.end()); 94 | Channel* channel = chIt->second; 95 | assert(channel->fd() == it->fd); 96 | channel->setRevents(it->revents); 97 | activeChannels.push_back(channel); 98 | } 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/Socket.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Socket.cc 3 | * 4 | * Created on: Feb 1, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #include "Socket.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace miniws { 20 | 21 | namespace detail { 22 | 23 | int createSocketfd() { 24 | int fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP); 25 | if (fd < 0) { 26 | printf("LOG_SYSFATAL createSocketfd\n"); 27 | } 28 | return fd; 29 | } 30 | 31 | } 32 | 33 | Socket::Socket() 34 | : m_socketfd(detail::createSocketfd()) { 35 | } 36 | 37 | Socket::Socket(int sockfd) 38 | : m_socketfd(sockfd) { 39 | } 40 | 41 | Socket::~Socket() { 42 | close(m_socketfd); 43 | } 44 | 45 | int Socket::getSocketfd() const { 46 | return m_socketfd; 47 | } 48 | 49 | int Socket::getSocketError() { 50 | int optval; 51 | socklen_t optlen = static_cast(sizeof(sockaddr)); 52 | 53 | if (getsockopt(m_socketfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) { 54 | return errno; 55 | } 56 | else { 57 | return optval; 58 | } 59 | } 60 | 61 | void Socket::setTcpNoDelay(bool on) { 62 | int optval = on ? 1 : 0; 63 | ::setsockopt(m_socketfd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof optval); 64 | } 65 | 66 | void Socket::setTcpKeepAlive(bool on) { 67 | int optval = on ? 1 : 0; 68 | ::setsockopt(m_socketfd, IPPROTO_TCP, SO_KEEPALIVE, &optval, sizeof optval); 69 | } 70 | 71 | void Socket::bindAddr(const InetAddr &addr) { 72 | socklen_t addrlen = static_cast(sizeof(sockaddr)); 73 | int ret = bind(m_socketfd, (sockaddr *)addr.getAddr(), addrlen); 74 | if (ret < 0) { 75 | printf("LOG_SYSFATAL bindAddr\n"); 76 | } 77 | } 78 | 79 | void Socket::listenConn() { 80 | int ret = listen(m_socketfd, SOMAXCONN); 81 | if (ret < 0) { 82 | printf("LOG_SYSFATAL listenConn\n"); 83 | } 84 | } 85 | 86 | int Socket::acceptConn(InetAddr &peerAddr) { 87 | socklen_t addrlen = static_cast(sizeof(sockaddr)); 88 | sockaddr_in addr; 89 | memset(&addr, 0, sizeof(sockaddr_in)); 90 | int connfd = accept4(m_socketfd, (sockaddr *)&addr, &addrlen, SOCK_CLOEXEC | SOCK_NONBLOCK); 91 | if (connfd < 0) { 92 | printf("LOG_SYSFATAL acceptConn\n"); 93 | } 94 | peerAddr.setAddr(addr); 95 | //FIXME errno switch 96 | return connfd; 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /src/TcpConnection.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * TcpConnection.cc 3 | * 4 | * Created on: Feb 6, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #include "TcpConnection.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace miniws { 15 | 16 | TcpConnection::TcpConnection(EventLoop *loop, const std::string &name, int sockfd, const InetAddr &localAddr, 17 | const InetAddr &peerAddr, double delayCloseSec) 18 | : m_loop(loop), 19 | m_name(name), 20 | m_state(kConnecting), 21 | m_socket(sockfd), 22 | m_channel(loop, sockfd, true), 23 | m_localAddr(localAddr), 24 | m_peerAddr(peerAddr), 25 | m_readBuf(READ_BUFFER_SIZE), 26 | m_delayClose(false), 27 | m_delayCloseSec(delayCloseSec), 28 | m_delayTimerId(0) { 29 | m_channel.setReadCallback(std::bind(&TcpConnection::handleRead, this)); 30 | m_channel.setWriteCallback(std::bind(&TcpConnection::handleWrite, this)); 31 | m_channel.setCloseCallback(std::bind(&TcpConnection::handleClose, this)); 32 | m_channel.setErrorCallback(std::bind(&TcpConnection::handleError, this)); 33 | printf("LOG_DEBUG TcpConnection::ctor at fd= %d\n", sockfd); 34 | } 35 | 36 | TcpConnection::~TcpConnection() { 37 | printf("LOG_DEBUG TcpConnection::dtor at fd= %d\n", m_socket.getSocketfd()); 38 | assert(m_state == kDisconnected); 39 | } 40 | 41 | std::string TcpConnection::getName() { 42 | return m_name; 43 | } 44 | 45 | EventLoop *TcpConnection::getLoop() { 46 | return m_loop; 47 | } 48 | 49 | void TcpConnection::setConnectionCallback(const ConnectionCallback &cb) { 50 | m_connectionCb = cb; 51 | } 52 | 53 | void TcpConnection::setMessageCallback(const MessageCallback &cb) { 54 | m_messageCb = cb; 55 | } 56 | 57 | void TcpConnection::setCloseCallback(const CloseCallback &cb) { 58 | m_closeCb = cb; 59 | } 60 | 61 | void TcpConnection::setTcpNoDelay(bool on) { 62 | m_socket.setTcpNoDelay(on); 63 | } 64 | 65 | void TcpConnection::setTcpKeepAlive(bool on) { 66 | m_socket.setTcpKeepAlive(on); 67 | } 68 | 69 | void TcpConnection::connectEstablished() { 70 | m_loop->assertInLoopThread(); 71 | assert(m_state == kConnecting); 72 | setState(kConnected); 73 | m_channel.enableReading(); 74 | m_connectionCb(shared_from_this()); 75 | } 76 | 77 | void TcpConnection::connectDestroyed() { 78 | m_loop->assertInLoopThread(); 79 | assert(m_state == kConnected); 80 | setState(kDisconnected); 81 | m_channel.disableAll(); 82 | m_connectionCb(shared_from_this()); 83 | m_channel.remove(); 84 | } 85 | 86 | void TcpConnection::sendv(struct iovec* iov, size_t iovlen) { 87 | m_loop->assertInLoopThread(); 88 | struct msghdr msg = {}; 89 | msg.msg_iov = iov; 90 | msg.msg_iovlen = iovlen; 91 | ssize_t n = ::sendmsg(m_socket.getSocketfd(), &msg, MSG_NOSIGNAL); 92 | if (n < 0) { 93 | char strerr[64]; 94 | printf("LOG_ERR TcpConnection::sendv %s\n", strerror_r(errno, strerr, sizeof strerr)); 95 | } 96 | } 97 | 98 | void TcpConnection::setState(StateE s) { 99 | m_state = s; 100 | } 101 | 102 | void TcpConnection::handleRead() { 103 | m_loop->assertInLoopThread(); 104 | if (m_channel.isReadETMode()) { 105 | while (true) { 106 | //FIXME 有没有其他优雅的方式实现 107 | char *buf = new char[m_readBuf.remainingSize()]; 108 | ssize_t n = ::recv(m_channel.fd(), buf, m_readBuf.remainingSize(), 0); 109 | if (n > 0) { 110 | m_readBuf.putIn(buf, static_cast(n)); 111 | if (m_readBuf.isFull()) { 112 | //handle read buf 113 | m_messageCb(shared_from_this(), m_readBuf); 114 | } 115 | } 116 | else if (n == 0 || (n < 0 && errno == EAGAIN)) { 117 | //handle read buf 118 | m_messageCb(shared_from_this(), m_readBuf); 119 | delete[] buf; 120 | if (m_delayClose) { 121 | m_loop->cancelRun(m_delayTimerId); 122 | m_delayTimerId = m_loop->runAfter(m_delayCloseSec, std::bind(&TcpConnection::handleClose, shared_from_this())); 123 | } 124 | else { 125 | handleClose(); 126 | } 127 | break; 128 | } 129 | else { 130 | delete[] buf; 131 | handleError(); 132 | break; 133 | } 134 | delete[] buf; 135 | } 136 | } 137 | } 138 | 139 | void TcpConnection::handleWrite() {} 140 | 141 | void TcpConnection::shutdown() { 142 | if (m_delayTimerId != 0) { 143 | m_loop->cancelRun(m_delayTimerId); 144 | } 145 | handleClose(); 146 | } 147 | 148 | void TcpConnection::handleClose() { 149 | m_loop->assertInLoopThread(); 150 | printf("LOG_TRACE TcpConnection::handleClose state = %d\n", m_state); 151 | assert(m_state == kConnected); 152 | m_channel.disableAll(); 153 | m_closeCb(shared_from_this()); 154 | } 155 | 156 | void TcpConnection::handleError() { 157 | m_loop->assertInLoopThread(); 158 | int err = m_socket.getSocketError(); 159 | printf("LOG_ERROR TcpConnection::handleError() %d\n", err); 160 | } 161 | 162 | void TcpConnection::setDelayClose(bool on) { 163 | m_delayClose = on; 164 | } 165 | 166 | } -------------------------------------------------------------------------------- /src/TcpServer.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * TcpServer.cc 3 | * 4 | * Created on: Feb 6, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #include "TcpServer.h" 9 | #include "TcpConnection.h" 10 | 11 | #include 12 | 13 | namespace miniws { 14 | 15 | TcpServer::TcpServer(EventLoop *loop, const std::string name, int numThreads, const InetAddr &localAddr, double delayCloseSec) 16 | : m_loop(loop), 17 | m_name(name), 18 | m_eventLoopThreadPool(loop, numThreads), 19 | m_localAddr(localAddr), 20 | m_acceptor(loop, localAddr), 21 | m_started(false), 22 | m_nextConnId(0), 23 | m_delayCloseSec(delayCloseSec) { 24 | m_acceptor.setNewConnCallback(std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2)); 25 | } 26 | 27 | TcpServer::~TcpServer() { 28 | m_loop->assertInLoopThread(); 29 | for (auto &item : m_connections) { 30 | TcpConnectionPtr conn(item.second); 31 | item.second.reset(); 32 | conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn)); 33 | } 34 | } 35 | 36 | void TcpServer::start() { 37 | if (!m_started) { 38 | m_eventLoopThreadPool.start(); 39 | m_loop->runInLoop(std::bind(&Acceptor::listen, &m_acceptor)); 40 | } 41 | } 42 | 43 | void TcpServer::setConnectionCallback(const ConnectionCallback &cb) { 44 | m_connectionCb = cb; 45 | } 46 | 47 | void TcpServer::setMessageCallback(const MessageCallback &cb) { 48 | m_messageCb = cb; 49 | } 50 | 51 | void TcpServer::newConnection(int sockfd, const InetAddr &peerAddr) { 52 | m_loop->assertInLoopThread(); 53 | EventLoop *ioLoop = m_eventLoopThreadPool.getNextLoop(); 54 | char buf[32]; 55 | snprintf(buf, sizeof buf, "#%d", m_nextConnId); 56 | ++m_nextConnId; 57 | std::string connName = m_name + buf; 58 | 59 | printf("LOG_INFO TcpServer::newConnection [%s] - new connection [%s] from %s\n", 60 | m_name.c_str(), connName.c_str(), peerAddr.getIPPort().c_str()); 61 | TcpConnectionPtr conn = std::make_shared(ioLoop, connName, sockfd, m_localAddr, peerAddr, m_delayCloseSec); 62 | m_connections[connName] = conn; 63 | conn->setConnectionCallback(m_connectionCb); 64 | conn->setMessageCallback(m_messageCb); 65 | conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1)); 66 | ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn)); 67 | } 68 | 69 | void TcpServer::removeConnection(const TcpConnectionPtr conn) { 70 | m_loop->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn)); 71 | } 72 | 73 | void TcpServer::removeConnectionInLoop(const TcpConnectionPtr conn) { 74 | m_loop->assertInLoopThread(); 75 | printf("LOG_INFO TcpServer::removeConnection\n"); 76 | size_t n = m_connections.erase(conn->getName()); 77 | assert(n == 1); 78 | EventLoop *ioLoop = conn->getLoop(); 79 | ioLoop->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn)); 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /src/Thread.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Thread.cc 3 | * 4 | * Created on: Dec 17, 2018 5 | * Author: xiagai 6 | */ 7 | 8 | #include "Thread.h" 9 | 10 | namespace miniws { 11 | 12 | Thread::Thread(ThreadFunc func) 13 | : m_thread(0), 14 | m_tid(0), 15 | m_func(func) {} 16 | 17 | Thread::~Thread() {} 18 | 19 | void Thread::start() { 20 | if (pthread_create(&m_thread, NULL, startThread, this)) { 21 | printf("thread creation fails\n"); 22 | } 23 | } 24 | 25 | int Thread::join() { 26 | int ret = pthread_join(m_thread, NULL); 27 | return ret; 28 | } 29 | 30 | void *Thread::startThread(void *args) { 31 | Thread *thread = static_cast(args); 32 | thread->m_tid = syscall(SYS_gettid); 33 | thread->m_func(); 34 | return thread; 35 | } 36 | 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/TimeStamp.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * TimeStamp.cc 3 | * 4 | * Created on: Jan 16, 2019 5 | * Author: xiagai 6 | */ 7 | 8 | #include "TimeStamp.h" 9 | 10 | #include 11 | 12 | namespace miniws { 13 | 14 | TimeStamp::TimeStamp() 15 | : m_mircoseconds(0) {} 16 | 17 | TimeStamp::TimeStamp(int64_t microseconds) { 18 | m_mircoseconds = microseconds; 19 | } 20 | 21 | int64_t TimeStamp::getMicroSeconds() const { 22 | return m_mircoseconds; 23 | } 24 | 25 | bool TimeStamp::isValid() { 26 | return m_mircoseconds > 0; 27 | } 28 | 29 | TimeStamp TimeStamp::now() { 30 | struct timeval tv; 31 | gettimeofday(&tv, nullptr); 32 | return TimeStamp(tv.tv_sec * kMicroSecPerSec + tv.tv_usec); 33 | } 34 | 35 | TimeStamp TimeStamp::invalid() { 36 | return TimeStamp(); 37 | } 38 | 39 | TimeStamp TimeStamp::addTime(TimeStamp timestamp, double seconds) { 40 | int64_t delta = static_cast(seconds * kMicroSecPerSec); 41 | return TimeStamp(timestamp.getMicroSeconds() + delta); 42 | } 43 | 44 | } 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/Timer.cc: -------------------------------------------------------------------------------- 1 | #include "Timer.h" 2 | 3 | namespace miniws { 4 | 5 | Timer::Timer(TimerId id, TimerCallback cb, TimeStamp expiration, double interval) 6 | : m_timerId(id), 7 | m_cb(cb), 8 | m_expiration(expiration), 9 | m_interval(interval), 10 | m_repeat(interval > 0.0), 11 | m_toCancel(false) {} 12 | 13 | void Timer::cb() { 14 | m_cb(); 15 | } 16 | 17 | TimerId Timer::getTimerId() const { 18 | return m_timerId; 19 | } 20 | 21 | TimeStamp Timer::getExpiration() { 22 | return m_expiration; 23 | } 24 | 25 | double Timer::getInterval() { 26 | return m_interval; 27 | } 28 | 29 | bool Timer::getRepeat() { 30 | return m_repeat; 31 | } 32 | 33 | void Timer::restart(TimeStamp now) { 34 | if (m_repeat) { 35 | m_expiration = TimeStamp::addTime(now, m_interval); 36 | } 37 | else { 38 | m_expiration = TimeStamp::invalid(); 39 | } 40 | } 41 | 42 | bool Timer::toCancel() const { 43 | return m_toCancel; 44 | } 45 | 46 | void Timer::setToCancel(bool on) { 47 | m_toCancel = on; 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /src/TimerQueue.cc: -------------------------------------------------------------------------------- 1 | #include "TimerQueue.h" 2 | #include "EventLoop.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace miniws { 11 | 12 | namespace detail { 13 | 14 | int createTimerfd() { 15 | int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); 16 | if (timerfd < 0) { 17 | printf("LOG_SYSFATAL Failed in timerfd_create\n"); 18 | } 19 | return timerfd; 20 | } 21 | 22 | timespec howMuchTimeFromNow(TimeStamp when) { 23 | TimeStamp now = TimeStamp::now(); 24 | int64_t microDiff = when.getMicroSeconds() - now.getMicroSeconds(); 25 | timespec diff; 26 | diff.tv_sec = static_cast(microDiff / TimeStamp::kMicroSecPerSec); 27 | diff.tv_nsec = static_cast((microDiff % TimeStamp::kMicroSecPerSec) * 1000); 28 | return diff; 29 | } 30 | 31 | void resetTimerfd(int timerfd, TimeStamp expiration) { 32 | itimerspec newValue; 33 | itimerspec oldValue; 34 | //memset(&newValue, 0, sizeof(newValue)); 35 | //memset(&oldValue, 0, izeof(oldValue)); 36 | newValue.it_interval.tv_nsec = 0; 37 | newValue.it_interval.tv_sec = 0; 38 | newValue.it_value = howMuchTimeFromNow(expiration); 39 | int ret = timerfd_settime(timerfd, 0, &newValue, &oldValue); 40 | if (ret) { 41 | printf("LOG_SYSERR timerfd_settime()\n"); 42 | } 43 | } 44 | 45 | void readTimerfd(int timerfd, TimeStamp now) { 46 | uint64_t howmany; 47 | ssize_t n = read(timerfd, &howmany, sizeof(howmany)); 48 | printf("LOG_TRACE TimerQueue::handleRead() %ld at %ld\n", howmany, now.getMicroSeconds()); 49 | if (n != sizeof(howmany)) { 50 | printf("LOG_ERROR TimerQueue::handleRead() reads %ld bytes instead of 8\n", n); 51 | } 52 | } 53 | 54 | } 55 | 56 | std::atomic_uint64_t TimerQueue::s_sequence(1); 57 | 58 | TimerQueue::TimerQueue(EventLoop *eventloop) 59 | : m_ownerLoop(eventloop), 60 | m_timerfd(detail::createTimerfd()), 61 | m_timerChannel(eventloop, m_timerfd) { 62 | m_timerChannel.setReadCallback(std::bind(&TimerQueue::handleRead, this)); 63 | m_timerChannel.enableReading(); 64 | } 65 | 66 | TimerQueue::~TimerQueue() { 67 | m_timerChannel.disableAll(); 68 | m_timerChannel.remove(); 69 | close(m_timerfd); 70 | } 71 | 72 | TimerId TimerQueue::addTimer(const Timer::TimerCallback &cb, TimeStamp when, double interval) { 73 | TimerId timerId = s_sequence++; 74 | std::shared_ptr timer = std::make_shared(timerId, cb, when, interval); 75 | 76 | m_ownerLoop->runInLoop(EventLoop::Functor([&]() { 77 | addTimerInLoop(timerId, timer); 78 | })); 79 | return timerId; 80 | } 81 | 82 | void TimerQueue::cancelTimer(TimerId timerId) { 83 | m_ownerLoop->runInLoop(EventLoop::Functor([&]() { 84 | cancelTimerInLoop(timerId); 85 | })); 86 | } 87 | 88 | void TimerQueue::addTimerInLoop(TimerId timerId, std::shared_ptr timer) { 89 | m_ownerLoop->assertInLoopThread(); 90 | m_timersMap.insert(std::pair>(timerId, timer)); 91 | bool earliestChanged = insert(timer); 92 | if (earliestChanged) { 93 | detail::resetTimerfd(m_timerfd, m_timersQueue.cbegin()->second->getExpiration()); 94 | } 95 | } 96 | 97 | void TimerQueue::cancelTimerInLoop(TimerId timerId) { 98 | m_ownerLoop->assertInLoopThread(); 99 | if (m_timersMap.find(timerId) != m_timersMap.end()) { 100 | m_timersMap[timerId]->setToCancel(true); 101 | } 102 | } 103 | 104 | void TimerQueue::handleRead() { 105 | m_ownerLoop->assertInLoopThread(); 106 | TimeStamp now = TimeStamp::now(); 107 | detail::readTimerfd(m_timerfd, now); 108 | 109 | std::vector> expired = getExpired(now); 110 | 111 | for (auto &each : expired) { 112 | if (!each->toCancel()) { 113 | each->cb(); 114 | } 115 | } 116 | 117 | reset(expired, now); 118 | } 119 | 120 | std::vector> TimerQueue::getExpired(TimeStamp now) { 121 | std::vector> expired; 122 | TimersQueue::const_iterator end = m_timersQueue.lower_bound(now); 123 | assert(end == m_timersQueue.end() || now < end->first); 124 | for (auto it = m_timersQueue.begin(); it != end; ++it) { 125 | expired.push_back(std::move(it->second)); 126 | } 127 | m_timersQueue.erase(m_timersQueue.begin(), end); 128 | return expired; 129 | } 130 | 131 | void TimerQueue::reset(std::vector> &expired, TimeStamp now) { 132 | for (std::shared_ptr &each : expired) { 133 | if (!each->toCancel() && each->getRepeat()) { 134 | each->restart(now); 135 | insert(each); 136 | } 137 | else { 138 | m_timersMap.erase(each->getTimerId()); 139 | } 140 | } 141 | TimeStamp nextExpire; 142 | if (!m_timersQueue.empty()) { 143 | nextExpire = m_timersQueue.begin()->second->getExpiration(); 144 | } 145 | if (nextExpire.isValid()) { 146 | detail::resetTimerfd(m_timerfd, nextExpire); 147 | } 148 | } 149 | 150 | bool TimerQueue::insert(std::shared_ptr &timer) { 151 | m_ownerLoop->assertInLoopThread(); 152 | bool earlistChanged = false; 153 | TimeStamp when = timer->getExpiration(); 154 | TimersQueue::const_iterator it = m_timersQueue.cbegin(); 155 | if (it == m_timersQueue.cend() || when < it->first) { 156 | earlistChanged = true; 157 | } 158 | m_timersQueue.insert(std::pair>(when, std::move(timer))); 159 | return earlistChanged; 160 | } 161 | 162 | } -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * main.cc 3 | * 4 | * Created on: Dec 16, 2018 5 | * Author: xiagai 6 | */ 7 | 8 | #include "EventLoop.h" 9 | #include "CurrentThread.h" 10 | #include "Thread.h" 11 | #include "Channel.h" 12 | #include "InetAddr.h" 13 | #include "Acceptor.h" 14 | #include "TcpServer.h" 15 | #include "Common.h" 16 | #include "HttpParser.h" 17 | #include "TcpConnection.h" 18 | #include "HttpServer.h" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | //void threadFunc1() { 25 | // printf("threadFunc(): pid = %d, tid = %d\n", getpid(), miniws::CurrentThread::tid()); 26 | // miniws::EventLoop loop; 27 | // loop.loop(); 28 | //} 29 | //void test1() { 30 | // printf("main(): pid = %d, tid = %d\n", getpid(), miniws::CurrentThread::tid()); 31 | // miniws::EventLoop loop; 32 | // 33 | // miniws::Thread thread(threadFunc1); 34 | // thread.start(); 35 | // 36 | // loop.loop(); 37 | // pthread_exit(nullptr); 38 | //} 39 | // 40 | //miniws::EventLoop *g_loop; 41 | //void threadFunc2() { 42 | // printf("threadFunc(): pid = %d, tid = %d\n", getpid(), miniws::CurrentThread::tid()); 43 | // g_loop->loop(); 44 | // printf("tf2 end\n"); 45 | //} 46 | //void test2() { 47 | // printf("main(): pid = %d, tid = %d\n", getpid(), miniws::CurrentThread::tid()); 48 | // miniws::EventLoop loop; 49 | // g_loop = &loop; 50 | // miniws::Thread t(threadFunc2); 51 | // t.start(); 52 | // printf("main2 end\n"); 53 | // t.join(); 54 | // 55 | //} 56 | 57 | // miniws::EventLoop *g_loop; 58 | // void timeout() { 59 | // printf("Timeout!\n"); 60 | // g_loop->quit(); 61 | // } 62 | 63 | // void test3() { 64 | // miniws::EventLoop loop; 65 | // g_loop = &loop; 66 | 67 | // int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK |TFD_CLOEXEC); 68 | // miniws::Channel channel(&loop, timerfd); 69 | // channel.setReadCallback(timeout); 70 | // channel.enableReading(); 71 | 72 | // itimerspec howlong; 73 | // bzero(&howlong, sizeof howlong); 74 | // howlong.it_value.tv_sec = 5; 75 | // ::timerfd_settime(timerfd, 0, &howlong, NULL); 76 | 77 | // loop.loop(); 78 | // ::close(timerfd); 79 | // } 80 | 81 | // void f1() { 82 | // printf("f1\n"); 83 | // } 84 | 85 | // void test4() { 86 | // miniws::EventLoop loop; 87 | 88 | // loop.runAfter(60, f1); 89 | // //loop.loop(); 90 | // } 91 | // void f5() { 92 | // printf("f5\n"); 93 | // } 94 | 95 | // miniws::EventLoop *thread_loop = nullptr; 96 | // void threadFunc5() { 97 | // printf("threadFunc(): pid = %d, tid = %d\n", getpid(), miniws::CurrentThread::tid()); 98 | // miniws::EventLoop loop; 99 | // thread_loop = &loop; 100 | // loop.loop(); 101 | // printf("tf2 end\n"); 102 | // } 103 | // void test5() { 104 | // printf("main(): pid = %d, tid = %d\n", getpid(), miniws::CurrentThread::tid()); 105 | // miniws::Thread thread(threadFunc5); 106 | // thread.start(); 107 | // while (thread_loop == nullptr) { 108 | // } 109 | // thread_loop->runEvery(3, f5); 110 | // while (1) {} 111 | // } 112 | 113 | // void newConnection(int sockfd, const miniws::InetAddr &peerAddr) { 114 | // printf("newConnection(): accepted a new connection from %s\n", peerAddr.getIPPort().c_str()); 115 | // write(sockfd, "How are you?\n", 13); 116 | // close(sockfd); 117 | // } 118 | 119 | // void test6() { 120 | // printf("main(): pid = %d, tid = %d\n", getpid(), miniws::CurrentThread::tid()); 121 | 122 | // miniws::InetAddr serverAddr("127.0.0.1", 9981); 123 | // miniws::EventLoop loop; 124 | 125 | // miniws::Acceptor acceptor(&loop, serverAddr); 126 | // acceptor.setNewConnCallback(newConnection); 127 | // acceptor.listen(); 128 | // loop.loop(); 129 | // } 130 | 131 | void test7() { 132 | miniws::InetAddr localAddr("127.0.0.1", 9983); 133 | std::string serverName = "testServer"; 134 | std::string homeDir = "./home"; 135 | miniws::HttpServer httpServer(serverName, localAddr, homeDir, 4); 136 | httpServer.start(); 137 | } 138 | 139 | int main() { 140 | test7(); 141 | } 142 | 143 | 144 | --------------------------------------------------------------------------------