├── .gitignore ├── .travis.yml ├── Dockerfile.massconnect ├── Dockerfile.test ├── FUNDING.yml ├── LICENSE ├── Makefile ├── README.md ├── conf ├── autobahn.debug.json ├── autobahn.json ├── doxyfile.conf ├── fuzzingclient.json └── wss.json ├── extensions ├── extension.h └── permessage-deflate │ ├── Makefile │ ├── permessage-deflate.c │ ├── permessage-deflate.h │ ├── predict.h │ └── uthash.h ├── include ├── alloc.h ├── b64.h ├── config.h ├── error.h ├── event.h ├── extensions.h ├── frame.h ├── header.h ├── http.h ├── httpstatuscodes.h ├── json.h ├── log.h ├── message.h ├── pool.h ├── predict.h ├── ringbuf.h ├── ringbuf_utils.h ├── rpmalloc.h ├── server.h ├── session.h ├── sha1.h ├── socket.h ├── ssl.h ├── str.h ├── subprotocols.h ├── utf8.h ├── uthash.h └── worker.h ├── resources ├── dhparam.pem ├── favicon.ico ├── logo.doxygen.png ├── logo.png ├── rootCA.crt ├── test-ext-no-close.so ├── test-ext-no-destroy.so ├── test-ext-no-in-frame.so ├── test-ext-no-in-frames.so ├── test-ext-no-init.so ├── test-ext-no-open.so ├── test-ext-no-out-frame.so ├── test-ext-no-out-frames.so ├── test-ext-no-set-allocators.so ├── test-sub-no-close.so ├── test-sub-no-connect.so ├── test-sub-no-destroy.so ├── test-sub-no-init.so ├── test-sub-no-message.so ├── test-sub-no-set-allocators.so ├── test-sub-no-write.so ├── test.txt ├── test_invalid_extension2_wss.json ├── test_invalid_extension_wss.json ├── test_invalid_host_wss.json ├── test_invalid_origin_wss.json ├── test_invalid_path_wss.json ├── test_invalid_query_wss.json ├── test_invalid_subprotocol2_wss.json ├── test_invalid_subprotocol_wss.json ├── test_invalid_wss.json ├── test_no_port_wss.json ├── test_no_starting_object_wss.json ├── test_subprotocol_and_extension_with_invalid_config_wss.json ├── test_wss.json ├── test_wss_empty_arrays.json ├── wsserver.crt └── wsserver.key ├── scripts ├── bump.sh └── travis.sh ├── src ├── alloc.c ├── b64.c ├── config.c ├── event.c ├── extensions.c ├── frame.c ├── header.c ├── http.c ├── json.c ├── log.c ├── main.c ├── message.c ├── pool.c ├── ringbuf.c ├── rpmalloc.c ├── server.c ├── session.c ├── sha1.c ├── socket.c ├── ssl.c ├── str.c ├── subprotocols.c ├── utf8.c └── worker.c ├── subprotocols ├── broadcast │ ├── Makefile │ ├── broadcast.c │ ├── broadcast.h │ ├── predict.h │ └── uthash.h ├── echo │ ├── Makefile │ ├── echo.c │ └── echo.h └── subprotocol.h └── test ├── test_alloc.c ├── test_config.c ├── test_extensions.c ├── test_frame.c ├── test_header.c ├── test_socket.c ├── test_str.c └── test_subprotocols.c /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | build/ 3 | log/ 4 | doc/ 5 | html/ 6 | generated/ 7 | conf/*.json 8 | !conf/fuzzingclient.json 9 | !conf/autobahn.debug.json 10 | !conf/autobahn.json 11 | !conf/wss.json 12 | *.bak 13 | scripts/* 14 | !scripts/travis.sh 15 | !scripts/bump.sh 16 | 17 | # Created by https://www.gitignore.io/api/c,vim,linux,macos 18 | # Edit at https://www.gitignore.io/?templates=c,vim,linux,macos 19 | 20 | ### C ### 21 | # Prerequisites 22 | *.d 23 | 24 | # Object files 25 | *.o 26 | *.ko 27 | *.obj 28 | *.elf 29 | 30 | # Linker output 31 | *.ilk 32 | *.map 33 | *.exp 34 | 35 | # Precompiled Headers 36 | *.gch 37 | *.pch 38 | 39 | # Libraries 40 | *.lib 41 | *.a 42 | *.la 43 | *.lo 44 | 45 | # Shared objects (inc. Windows DLLs) 46 | *.dll 47 | *.so 48 | !resources/*.so 49 | *.so.* 50 | *.dylib 51 | 52 | # Executables 53 | *.exe 54 | *.out 55 | *.app 56 | *.i*86 57 | *.x86_64 58 | *.hex 59 | 60 | # Debug files 61 | *.dSYM/ 62 | *.su 63 | *.idb 64 | *.pdb 65 | 66 | # Kernel Module Compile Results 67 | *.mod* 68 | *.cmd 69 | .tmp_versions/ 70 | modules.order 71 | Module.symvers 72 | Mkfile.old 73 | dkms.conf 74 | 75 | ### Linux ### 76 | *~ 77 | 78 | # temporary files which can be created if a process still has a handle open of a deleted file 79 | .fuse_hidden* 80 | 81 | # KDE directory preferences 82 | .directory 83 | 84 | # Linux trash folder which might appear on any partition or disk 85 | .Trash-* 86 | 87 | # .nfs files are created when an open file is removed but is still being accessed 88 | .nfs* 89 | 90 | ### macOS ### 91 | # General 92 | .DS_Store 93 | .AppleDouble 94 | .LSOverride 95 | 96 | # Icon must end with two \r 97 | Icon 98 | 99 | # Thumbnails 100 | ._* 101 | 102 | # Files that might appear in the root of a volume 103 | .DocumentRevisions-V100 104 | .fseventsd 105 | .Spotlight-V100 106 | .TemporaryItems 107 | .Trashes 108 | .VolumeIcon.icns 109 | .com.apple.timemachine.donotpresent 110 | 111 | # Directories potentially created on remote AFP share 112 | .AppleDB 113 | .AppleDesktop 114 | Network Trash Folder 115 | Temporary Items 116 | .apdisk 117 | 118 | ### Vim ### 119 | # Swap 120 | [._]*.s[a-v][a-z] 121 | [._]*.sw[a-p] 122 | [._]s[a-rt-v][a-z] 123 | [._]ss[a-gi-z] 124 | [._]sw[a-p] 125 | 126 | # Session 127 | Session.vim 128 | Sessionx.vim 129 | 130 | # Temporary 131 | .netrwhist 132 | 133 | # Auto-generated tag files 134 | tags 135 | 136 | # Persistent undo 137 | [._]*.un~ 138 | 139 | # Coc configuration directory 140 | .vim 141 | 142 | # YCM 143 | .ycm_extra_conf.py 144 | 145 | # End of https://www.gitignore.io/api/c,vim,linux,macos 146 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | os: linux 3 | dist: focal 4 | branches: 5 | only: 6 | - master 7 | services: 8 | - docker 9 | compiler: 10 | - gcc 11 | cache: 12 | directories: 13 | - docker_images 14 | before_install: 15 | - echo "deb http://ppa.launchpad.net/snaipewastaken/ppa/ubuntu cosmic main" | sudo tee -a ${TRAVIS_ROOT}/etc/apt/sources.list >/dev/null 16 | - sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com CE6500E9003E6E24 17 | - sudo apt-get -q update 18 | - sudo apt-get -y install pkg-config doxygen openssl zlib1g-dev build-essential criterion-dev gcovr graphviz cppcheck 19 | - docker load -i docker_images/images.tar || true 20 | before_cache: 21 | - docker save -o docker_images/images.tar $(docker images -a -q) 22 | stages: 23 | - cppcheck 24 | - docs 25 | - criterion 26 | - autobahn 27 | jobs: 28 | include: 29 | - stage: cppcheck 30 | script: 31 | - cppcheck --language=c -f -q --enable=warning,performance,portability --std=c11 --error-exitcode=1 -itest -Iinclude -Iextensions -Isubprotocols . >build.log 2>&1 || (cat build.log && rm build.log && exit 1) 32 | 33 | - stage: docs 34 | script: 35 | - ./scripts/travis.sh 36 | - make docs 37 | deploy: 38 | strategy: git 39 | provider: pages 40 | cleanup: false 41 | skip_cleanup: true 42 | local_dir: generated 43 | github_token: $GH_REPO_TOKEN 44 | keep_history: true 45 | project_name: WSServer 46 | on: 47 | branch: master 48 | 49 | - stage: criterion 50 | script: 51 | - ./scripts/travis.sh 52 | - make test 53 | deploy: 54 | strategy: git 55 | provider: pages 56 | cleanup: false 57 | skip_cleanup: true 58 | local_dir: generated 59 | github_token: $GH_REPO_TOKEN 60 | keep_history: true 61 | project_name: WSServer 62 | on: 63 | branch: master 64 | 65 | - stage: autobahn 66 | script: 67 | - ./scripts/travis.sh 68 | - make autobahn 69 | deploy: 70 | strategy: git 71 | provider: pages 72 | cleanup: false 73 | skip_cleanup: true 74 | local_dir: generated 75 | github_token: $GH_REPO_TOKEN 76 | keep_history: true 77 | project_name: WSServer 78 | on: 79 | branch: master 80 | -------------------------------------------------------------------------------- /Dockerfile.massconnect: -------------------------------------------------------------------------------- 1 | FROM pypy:2 2 | 3 | MAINTAINER WSServer Project 4 | 5 | # Application home 6 | ENV HOME /app 7 | ENV DEBIAN_FRONTEND noninteractive 8 | ENV NODE_PATH /usr/local/lib/node_modules/ 9 | 10 | # make "pypy" available as "python" 11 | RUN ln -s /usr/local/bin/pypy /usr/local/bin/python 12 | 13 | # install Autobahn|Testsuite 14 | RUN pip install -U pip && pip install autobahntestsuite 15 | 16 | # make volumes for input configuration and output generated 17 | VOLUME /config 18 | VOLUME /generated 19 | 20 | WORKDIR / 21 | EXPOSE 9001 9001 22 | 23 | # run wstest by default in massconnect mode 24 | CMD ["wstest", "--mode", "massconnect", "--spec", "/config/massconnect.json"] 25 | -------------------------------------------------------------------------------- /Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM pypy:2 2 | 3 | MAINTAINER WSServer Project 4 | 5 | # Application home 6 | ENV HOME /app 7 | ENV DEBIAN_FRONTEND noninteractive 8 | ENV NODE_PATH /usr/local/lib/node_modules/ 9 | 10 | # make "pypy" available as "python" 11 | RUN ln -s /usr/local/bin/pypy /usr/local/bin/python 12 | 13 | # install Autobahn|Testsuite 14 | RUN pip install -U pip && pip install autobahntestsuite 15 | 16 | # make volumes for input configuration and output generated 17 | VOLUME /config 18 | VOLUME /generated 19 | 20 | WORKDIR / 21 | EXPOSE 9001 9001 22 | 23 | # run wstest by default in fuzzingclient mode 24 | CMD ["wstest", "--mode", "fuzzingclient", "--spec", "/config/fuzzingclient.json"] 25 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: websocket 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Morten Houmøller Nygaard - www.mortz.dk - admin@mortz.dk 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /conf/autobahn.debug.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts" : [ 3 | "localhost", 4 | "127.0.0.1" 5 | ], 6 | "paths" : [ 7 | ], 8 | "queries" : [ 9 | ], 10 | "origins" : [ 11 | ], 12 | "setup" : { 13 | "subprotocols" : [ 14 | { 15 | "file" : "subprotocols/echo/echo.so", 16 | "config" : "" 17 | } 18 | ], 19 | "extensions" : [ 20 | { 21 | "file" : "extensions/permessage-deflate/permessage-deflate.so", 22 | "config" : "server_max_window_bits=15;client_max_window_bits=15;memory_level=8" 23 | } 24 | ], 25 | "log_level": 7, 26 | "favicon" : "resources/favicon.ico", 27 | "timeouts" : { 28 | "poll" : -1, 29 | "read" : -1, 30 | "write" : -1, 31 | "client" : -1, 32 | "pings" : 0 33 | }, 34 | "port" : { 35 | "http" : 9010, 36 | "https" : 9011 37 | }, 38 | "size" : { 39 | "payload" : 16777215, 40 | "header" : 8192, 41 | "uri" : 8192, 42 | "buffer" : 1048576, 43 | "thread" : 4194304, 44 | "ringbuffer" : 1024, 45 | "frame" : 16777216, 46 | "fragmented" : 1048576 47 | }, 48 | "pool" : { 49 | "workers" : 8, 50 | "retries" : 5 51 | }, 52 | "ssl" : { 53 | "key" : "resources/wsserver.key", 54 | "cert" : "resources/wsserver.crt", 55 | "ca_file" : "resources/rootCA.crt", 56 | "ca_path" : null, 57 | "dhparam" : "resources/dhparam.pem", 58 | "cipher_list" : "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256", 59 | "cipher_suites": "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256", 60 | "compression" : false, 61 | "peer_cert" : false 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /conf/autobahn.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts" : [ 3 | "localhost", 4 | "127.0.0.1" 5 | ], 6 | "paths" : [ 7 | ], 8 | "queries" : [ 9 | ], 10 | "origins" : [ 11 | ], 12 | "setup" : { 13 | "subprotocols" : [ 14 | { 15 | "file" : "subprotocols/echo/echo.so", 16 | "config" : "" 17 | } 18 | ], 19 | "extensions" : [ 20 | { 21 | "file" : "extensions/permessage-deflate/permessage-deflate.so", 22 | "config" : "server_max_window_bits=15;client_max_window_bits=15;memory_level=8" 23 | } 24 | ], 25 | "log_level": 3, 26 | "favicon" : "resources/favicon.ico", 27 | "timeouts" : { 28 | "poll" : -1, 29 | "read" : -1, 30 | "write" : -1, 31 | "client" : -1, 32 | "pings" : 0 33 | }, 34 | "port" : { 35 | "http" : 9010, 36 | "https" : 9011 37 | }, 38 | "size" : { 39 | "payload" : 16777215, 40 | "header" : 8192, 41 | "uri" : 8192, 42 | "buffer" : 1048576, 43 | "thread" : 4194304, 44 | "ringbuffer" : 1024, 45 | "frame" : 16777216, 46 | "fragmented" : 1048576 47 | }, 48 | "pool" : { 49 | "workers" : 8, 50 | "retries" : 5 51 | }, 52 | "ssl" : { 53 | "key" : "resources/wsserver.key", 54 | "cert" : "resources/wsserver.crt", 55 | "ca_file" : "resources/rootCA.crt", 56 | "ca_path" : null, 57 | "dhparam" : "resources/dhparam.pem", 58 | "cipher_list" : "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256", 59 | "cipher_suites": "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256", 60 | "compression" : false, 61 | "peer_cert" : false 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /conf/fuzzingclient.json: -------------------------------------------------------------------------------- 1 | { 2 | "outdir": "./generated/autobahn", 3 | "servers": [ 4 | { 5 | "agent": "WSServer (HTTP)", 6 | "url": "ws://127.0.0.1:9010", 7 | "desc" : "WSServer through the HTTP protocol", 8 | "options": {"version": 18} 9 | }, 10 | { 11 | "agent": "WSServer (HTTPS)", 12 | "url": "wss://127.0.0.1:9011", 13 | "desc" : "WSServer through the HTTPS protocol", 14 | "options": {"version": 18} 15 | } 16 | ], 17 | "cases": [ 18 | "*" 19 | ], 20 | "exclude-cases": [], 21 | "exclude-agent-cases": {} 22 | } 23 | -------------------------------------------------------------------------------- /conf/wss.json: -------------------------------------------------------------------------------- 1 | { 2 | // Allowed Hosts 3 | "hosts" : [ 4 | "localhost", 5 | "127.0.0.1" 6 | ], 7 | // Allowed paths 8 | "paths" : [ 9 | ], 10 | // Allowed queries 11 | "queries" : [ 12 | ], 13 | // Allowed origins 14 | "origins" : [ 15 | "http://localhost", 16 | "http://127.0.0.1" 17 | ], 18 | "setup" : { 19 | "log_level": 3, 20 | // Subprotocols to load with the server 21 | "subprotocols" : [ 22 | { 23 | "file" : "subprotocols/echo/echo.so", 24 | "config" : "" 25 | }, 26 | { 27 | "file" : "subprotocols/broadcast/broadcast.so", 28 | "config" : "" 29 | } 30 | ], 31 | // Extensions to load with the server 32 | "extensions" : [ 33 | { 34 | "file" : "extensions/permessage-deflate/permessage-deflate.so", 35 | "config" : "server_max_window_bits=15;client_max_window_bits=15;memory_level=4" 36 | } 37 | ], 38 | // A favicon to present to webserver request 39 | "favicon" : "resources/favicon.ico", 40 | // Timeouts 41 | "timeouts" : { 42 | // How long the eventpoll should wait for a timeout. -1 is infinitely 43 | "poll" : -1, 44 | // How long, before timeouting reading from a client. -1 is infinitely 45 | "read" : 1000, 46 | // How long, before timeouting writing to a client. -1 is infinitely 47 | "write" : 1000, 48 | // How long, since a client last was active before timing out. -1 is infinitely 49 | "client" : 3600000, 50 | // How many pings should be sent during the client timeout period 51 | "pings" : 2 52 | }, 53 | // Ports to assign the ws and wss to 54 | "port" : { 55 | "http" : 9010, 56 | "https" : 9011 57 | }, 58 | // Sizes of different aspects of the server 59 | "size" : { 60 | // How large the payload from a client can be 61 | "payload" : 16777215, 62 | // How large the HTTP header from a client can be 63 | "header" : 8192, 64 | // How large the URI of a HTTP request from a client can be 65 | "uri" : 8192, 66 | // The buffer size used to read and write 67 | "buffer" : 32768, 68 | // How large the stack of a thread should be 69 | "thread" : 2097152, 70 | // How many messages the ringbuffer is able to contain at once 71 | "ringbuffer" : 1024, 72 | // Max size of a single frames payload 73 | "frame" : 1048576, 74 | // Maximum amount of frames in fragmented message 75 | "fragmented" : 1048576 76 | }, 77 | // Configurations regarding the thread poll 78 | "pool" : { 79 | // How many worker threads to use 80 | "workers" : 8 81 | }, 82 | // Configurations regarding SSL 83 | "ssl" : { 84 | // The private key of the server 85 | "key" : null, 86 | // The certificate of the server 87 | "cert" : null, 88 | // The root CA certiface 89 | "ca_file" : null, 90 | // Path to folder where root CA certificates are placed 91 | "ca_path" : "/usr/lib/ssl/certs/", 92 | // Where the dhparam file is located 93 | "dhparam" : null, 94 | // Allowed TLS 2.0 and below ciphers 95 | "cipher_list" : "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256", 96 | // Allowed TLS 3.0 cipher suites 97 | "cipher_suites": "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256", 98 | // Whether to use compression 99 | "compression" : false, 100 | // Whether a peer certificate is required from the connection clients 101 | "peer_cert" : false 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /extensions/extension.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_extension_h 2 | #define wss_extension_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | /** 13 | * 0 1 2 3 14 | * 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 15 | * +-+-+-+-+-------+-+-------------+-------------------------------+ 16 | * |F|R|R|R| opcode|M| Payload len | Extended payload length | 17 | * |I|S|S|S| (4) |A| (7) | (16/64) | 18 | * |N|V|V|V| |S| | (if payload len==126/127) | 19 | * | |1|2|3| |K| | | 20 | * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + 21 | * | Extended payload length continued, if payload len == 127 | 22 | * + - - - - - - - - - - - - - - - +-------------------------------+ 23 | * | |Masking-key, if MASK set to 1 | 24 | * +-------------------------------+-------------------------------+ 25 | * | Masking-key (continued) | Payload Data | 26 | * +-------------------------------- - - - - - - - - - - - - - - - + 27 | * : Payload Data continued ... : 28 | * + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 29 | * | Payload Data continued ... | 30 | * +---------------------------------------------------------------+ 31 | */ 32 | 33 | typedef struct { 34 | bool fin; 35 | bool rsv1; 36 | bool rsv2; 37 | bool rsv3; 38 | uint8_t opcode; 39 | bool mask; 40 | uint64_t payloadLength; 41 | char maskingKey[4]; 42 | char *payload; 43 | uint64_t extensionDataLength; 44 | uint64_t applicationDataLength; 45 | } wss_frame_t; 46 | 47 | typedef void *(*WSS_malloc_t)(size_t size); 48 | typedef void *(*WSS_realloc_t)(void *ptr, size_t size); 49 | typedef void (*WSS_free_t)(void *ptr); 50 | 51 | /** 52 | * The extension API calls 53 | */ 54 | typedef void (*extAlloc)(WSS_malloc_t extmalloc, WSS_realloc_t extrealloc, WSS_free_t extfree); 55 | typedef void (*extInit)(char *config); 56 | typedef void (*extOpen)(int fd, char *param, char **accepted, bool *valid); 57 | typedef void (*extInFrame)(int fd, wss_frame_t *frame); 58 | typedef void (*extInFrames)(int fd, wss_frame_t **frames, size_t len); 59 | typedef void (*extOutFrame)(int fd, wss_frame_t *frame); 60 | typedef void (*extOutFrames)(int fd, wss_frame_t **frames, size_t len); 61 | typedef void (*extClose)(int fd); 62 | typedef void (*extDestroy)(); 63 | 64 | #ifdef __cplusplus 65 | } 66 | #endif 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /extensions/permessage-deflate/Makefile: -------------------------------------------------------------------------------- 1 | #Shell 2 | SHELL = /bin/bash 3 | 4 | #Executeable name 5 | NAME = permessage-deflate 6 | 7 | #Compiler 8 | CC = gcc 9 | 10 | PROFILE = -Og -g -DNDEBUG 11 | DEBUG = -Og -g 12 | RELEASE = -O3 -funroll-loops -DNDEBUG 13 | SPACE = -Os -DNDEBUG 14 | EXEC = $(RELEASE) 15 | 16 | #Compiler options 17 | CFLAGS = $(EXEC) \ 18 | -fno-exceptions \ 19 | -fPIC \ 20 | -fstack-protector \ 21 | -funroll-loops \ 22 | -fvisibility=hidden \ 23 | -march=native \ 24 | -MMD \ 25 | -pedantic \ 26 | -pedantic-errors \ 27 | -pipe \ 28 | -W \ 29 | -Wall \ 30 | -Werror \ 31 | -Wformat \ 32 | -Wformat-security \ 33 | -Wformat-nonliteral \ 34 | -Winit-self \ 35 | -Winline \ 36 | -Wl,-z,relro \ 37 | -Wl,-z,now \ 38 | -Wmultichar \ 39 | -Wno-unused-parameter \ 40 | -Wno-unused-function \ 41 | -Wno-unused-label \ 42 | -Wno-deprecated \ 43 | -Wno-strict-aliasing \ 44 | -Wpointer-arith \ 45 | -Wreturn-type \ 46 | -Wsign-compare \ 47 | -Wuninitialized \ 48 | -D_DEFAULT_SOURCE 49 | 50 | CVER = -std=c11 51 | 52 | # Flags 53 | FLAGS_EXTRA = -lz 54 | 55 | # Folders 56 | FOLDER = $(shell pwd) 57 | EXTENSIONS_FOLDER = $(FOLDER)/../ 58 | 59 | # Includes 60 | INCLUDES = -I$(EXTENSIONS_FOLDER) 61 | 62 | # Files 63 | SRC = $(shell find $(FOLDER) -name '*.c' -type f;) 64 | SRC_OBJ = $(patsubst %.c, %.o, $(SRC)) 65 | DEPS = $(SRC_OBJ:%.o=%.d) 66 | 67 | .PHONY: clean release debug profiling space 68 | 69 | #what we are trying to build 70 | all: $(NAME) 71 | 72 | release_mode: 73 | $(eval EXEC = $(RELEASE)) 74 | 75 | debug_mode: 76 | $(eval EXEC = $(DEBUG)) 77 | 78 | profiling_mode: 79 | $(eval EXEC = $(PROFILE)) 80 | 81 | space_mode: 82 | $(eval EXEC = $(SPACE)) 83 | 84 | # Recompile when headers change 85 | -include $(DEPS) 86 | 87 | #linkage 88 | $(NAME): $(SRC_OBJ) 89 | @echo 90 | @echo ================ [Creating Shared Object] ================ 91 | @echo 92 | $(CC) -shared $(CFLAGS) $(CVER) -o $(NAME).so $(SRC_OBJ) $(FLAGS_EXTRA) $(INCLUDES) 93 | @echo 94 | @echo ================ [$(NAME).so compiled succesfully] ================ 95 | @echo 96 | 97 | # compile every source file 98 | %.o: %.c 99 | @echo 100 | @echo ================ [Building Object] ================ 101 | @echo 102 | $(CC) $(CFLAGS) $(CVER) -c $< -o $@ $(FLAGS_EXTRA) $(INCLUDES) 103 | @echo 104 | @echo OK [$<] - [$@] 105 | @echo 106 | 107 | #make clean 108 | clean: 109 | @echo 110 | @echo ================ [Cleaning $(NAME)] ================ 111 | @echo 112 | rm -f *.d 113 | rm -f *.o 114 | rm -f *.so 115 | 116 | #make release 117 | release: clean release_mode all 118 | 119 | #make debug 120 | debug: clean debug_mode all 121 | 122 | #make profiling 123 | profiling: clean profiling_mode all 124 | 125 | #make space 126 | space: clean space_mode all 127 | -------------------------------------------------------------------------------- /extensions/permessage-deflate/permessage-deflate.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_extension_permessage_deflate_h 2 | #define wss_extension_permessage_deflate_h 3 | 4 | #define WSS_PERMESSAGE_DEFLATE_VERSION_MAJOR 1 5 | #define WSS_PERMESSAGE_DEFLATE_VERSION_MINOR 0 6 | #define WSS_PERMESSAGE_DEFLATE_VERSION ((WSS_PERMESSAGE_DEFLATE_VERSION_MAJOR << 16) | WSS_PERMESSAGE_DEFLATE_VERSION_MINOR) 7 | 8 | #include 9 | #include 10 | #include "extension.h" 11 | 12 | /** 13 | * Event called when extension is initialized. 14 | * 15 | * @param config [char *] "The configuration of the extension" 16 | * @return [void] 17 | */ 18 | void __attribute__((visibility("default"))) onInit(char *config); 19 | 20 | /** 21 | * Sets the allocators to use instead of the default ones 22 | * 23 | * @param extmalloc [WSS_malloc_t] "The malloc function" 24 | * @param extrealloc [WSS_realloc_t] "The realloc function" 25 | * @param extfree [WSS_free_t] "The free function" 26 | * @return [void] 27 | */ 28 | void __attribute__((visibility("default"))) setAllocators(WSS_malloc_t extmalloc, WSS_realloc_t extrealloc, WSS_free_t extfree); 29 | 30 | /** 31 | * Event called when parameters are available for the pcme i.e. when the 32 | * connection is opened. 33 | * 34 | * @param fd [int] "The filedescriptor of the session" 35 | * @param param [char *] "The parameters to the PCME" 36 | * @param accepted [char *] "The accepted parameters to the PCME" 37 | * @param valid [bool *] "A pointer to a boolean, that should state whether the parameters are accepted" 38 | * @return [void] 39 | */ 40 | void __attribute__((visibility("default"))) onOpen(int fd, char *param, char **accepted, bool *valid); 41 | 42 | /** 43 | * Event called when a frame_t of data is received. 44 | * 45 | * @param fd [int] "The filedescriptor of the session" 46 | * @param frame [wss_frame_t *] "A websocket frame" 47 | * @return [void] 48 | */ 49 | void __attribute__((visibility("default"))) inFrame(int fd, wss_frame_t *frame); 50 | 51 | /** 52 | * Event called when a full set of frames are received. 53 | * 54 | * @param fd [int] "The filedescriptor of the session" 55 | * @param frames [wss_frame_t **] "The websocket frames received" 56 | * @param len [size_t] "The amount of frames" 57 | * @return [void] 58 | */ 59 | void __attribute__((visibility("default"))) inFrames(int fd, wss_frame_t **frames, size_t len); 60 | 61 | /** 62 | * Event called when a frame_t of data is about to be sent. 63 | * 64 | * @param fd [int] "The filedescriptor of the session" 65 | * @param frame [wss_frame_t *] "A websocket frame" 66 | * @return [void] 67 | */ 68 | void __attribute__((visibility("default"))) outFrame(int fd, wss_frame_t *frame); 69 | 70 | /** 71 | * Event called when a full set of frames are about to be sent. 72 | * 73 | * @param fd [int] "The filedescriptor of the session" 74 | * @param frames [wss_frame_t **] "The websocket frames received" 75 | * @param len [size_t] "The amount of frames" 76 | * @return [void] 77 | */ 78 | void __attribute__((visibility("default"))) outFrames(int fd, wss_frame_t **frames, size_t len); 79 | 80 | /** 81 | * Event called when a session disconnects from the WSS server. 82 | * 83 | * @param fd [int] "A filedescriptor of the disconnecting session" 84 | * @return [void] 85 | */ 86 | void __attribute__((visibility("default"))) onClose(int fd); 87 | 88 | /** 89 | * Event called when the subprotocol should be destroyed. 90 | * 91 | * @return [void] 92 | */ 93 | void __attribute__((visibility("default"))) onDestroy(); 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /extensions/permessage-deflate/predict.h: -------------------------------------------------------------------------------- 1 | ../../include/predict.h -------------------------------------------------------------------------------- /extensions/permessage-deflate/uthash.h: -------------------------------------------------------------------------------- 1 | ../../include/uthash.h -------------------------------------------------------------------------------- /include/alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_alloc_h 2 | #define wss_alloc_h 3 | 4 | #include 5 | #include 6 | 7 | #include "rpmalloc.h" 8 | 9 | /** 10 | * Function that allocates memory. 11 | * @param ptr [void *] "The memory that needs to be copies" 12 | * @param void [size_t] "The size of the memory that should be copied" 13 | * @return [void *] "Returns pointer to allocated memory if successful, otherwise NULL" 14 | */ 15 | void *WSS_copy(void *ptr, size_t size); 16 | 17 | /** 18 | * Function that allocates memory. 19 | * @param size [size_t] "The size of the memory that should be allocated" 20 | * @return [void *] "Returns pointer to allocated memory if successful, otherwise NULL" 21 | */ 22 | void *WSS_malloc(size_t size); 23 | 24 | /** 25 | * Function that allocates an array of memory. 26 | * 27 | * @param nmemb [size_t] "The size of the array i.e. indexes" 28 | * @param size [size_t] "The size of the memory that should be allocated" 29 | * @return [void *] "Returns pointer to allocated memory if successful, otherwise NULL" 30 | */ 31 | void *WSS_calloc(size_t memb, size_t size); 32 | 33 | /** 34 | * Function that re-allocates some memory. 35 | * 36 | * @param ptr [void **] "The memory that needs to be rearranged" 37 | * @param oldSize [size_t] "The size of the memory that is already allocated" 38 | * @param newSize [size_t] "The size of the memory that should be allocated" 39 | * @return [void *] "Returns pointer to allocated memory if successful, otherwise NULL" 40 | */ 41 | void *WSS_realloc(void **ptr, size_t oldSize, size_t newSize); 42 | 43 | /** 44 | * Function that re-allocates some memory. The interface is of this function is 45 | * similar to the realloc(3) function. 46 | * 47 | * @param ptr [void **] "The memory that needs to be rearranged" 48 | * @param newSize [size_t] "The size of the memory that should be allocated" 49 | * @return [void *] "Returns pointer to allocated memory if successful, otherwise NULL" 50 | */ 51 | void *WSS_realloc_normal(void *ptr, size_t newSize); 52 | 53 | /** 54 | * Function that frees some memory. 55 | * 56 | * @param ptr [void **] "The memory that needs to be freed" 57 | * @return [void] 58 | */ 59 | #ifndef NDEBUG 60 | void WSS_free(void **ptr); 61 | #else 62 | #ifdef USE_RPMALLOC 63 | #define WSS_free(p) do { void ** __p = (p); rpfree(*(__p)); *(__p) = NULL; } while (0) 64 | #else 65 | #define WSS_free(p) do { void ** __p = (p); free(*(__p)); *(__p) = NULL; } while (0) 66 | #endif 67 | #endif 68 | 69 | /** 70 | * Function that frees some memory. The interface is of this function is 71 | * similar to the free(3) function. 72 | * 73 | * @param ptr [void *] "The memory that needs to be freed" 74 | * @return [void] 75 | */ 76 | void WSS_free_normal(void *ptr); 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /include/b64.h: -------------------------------------------------------------------------------- 1 | /** 2 | * `b64.h' - b64 3 | * 4 | * copyright (c) 2014 joseph werle 5 | */ 6 | 7 | #ifndef B64_H 8 | #define B64_H 1 9 | 10 | #include 11 | 12 | #include "alloc.h" 13 | 14 | /** 15 | * Memory allocation functions to use. You can define b64_malloc and 16 | * b64_realloc to custom functions if you want. 17 | */ 18 | 19 | #ifndef b64_malloc 20 | # define b64_malloc(size) WSS_malloc(size) 21 | #endif 22 | #ifndef b64_realloc 23 | # define b64_realloc(ptr, size) WSS_realloc((void **)&ptr, size, size) 24 | #endif 25 | 26 | /** 27 | * Base64 index table. 28 | */ 29 | 30 | static const char b64_table[] = { 31 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 32 | 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 33 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 34 | 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 35 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 36 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 37 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', 38 | '4', '5', '6', '7', '8', '9', '+', '/' 39 | }; 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | /** 46 | * Encode `unsigned char *' source with `size_t' size. 47 | * Returns a `char *' base64 encoded string. 48 | */ 49 | 50 | char * 51 | b64_encode (const unsigned char *, size_t); 52 | 53 | /** 54 | * Decode `char *' source with `size_t' size. 55 | * Returns a `unsigned char *' base64 decoded string + size of decoded string. 56 | */ 57 | unsigned char *b64_decode_ex (const char *src, size_t len, size_t *decsize); 58 | 59 | #ifdef __cplusplus 60 | } 61 | #endif 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /include/config.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_config_h 2 | #define wss_config_h 3 | 4 | #include 5 | 6 | #include "json.h" 7 | #include "error.h" 8 | 9 | typedef struct { 10 | char *string; 11 | size_t length; 12 | char **hosts; 13 | char **paths; 14 | char **queries; 15 | char **origins; 16 | int hosts_length; 17 | int paths_length; 18 | int queries_length; 19 | int origins_length; 20 | json_value *data; 21 | unsigned int port_http; 22 | unsigned int port_https; 23 | unsigned int log; 24 | unsigned int size_uri; 25 | unsigned int size_payload; 26 | unsigned int size_header; 27 | unsigned int size_thread; 28 | unsigned int size_buffer; 29 | unsigned int size_ringbuffer; 30 | unsigned int size_frame; 31 | unsigned int max_frames; 32 | unsigned int pool_workers; 33 | unsigned int pool_retries; 34 | unsigned int timeout_pings; 35 | int timeout_poll; 36 | int timeout_read; 37 | int timeout_write; 38 | long int timeout_client; 39 | char *ssl_key; 40 | char *ssl_cert; 41 | char *ssl_ca_file; 42 | char *ssl_ca_path; 43 | char *ssl_cipher_list; 44 | char *ssl_cipher_suites; 45 | char *ssl_dhparam; 46 | bool ssl_compression; 47 | bool ssl_peer_cert; 48 | char *favicon; 49 | char **subprotocols; 50 | unsigned int subprotocols_length; 51 | char **subprotocols_config; 52 | char **extensions; 53 | unsigned int extensions_length; 54 | char **extensions_config; 55 | } wss_config_t; 56 | 57 | /** 58 | * Loads configuration from JSON file 59 | * 60 | * @param config [wss_config_t *] "The configuration structure to fill" 61 | * @param path [char *] "The path to the JSON file" 62 | * @return [wss_error_t] "The error status" 63 | */ 64 | wss_error_t WSS_config_load(wss_config_t *config, char *path); 65 | 66 | /** 67 | * Frees allocated memory from configuration 68 | * 69 | * @param config [wss_config_t *] "The configuration structure to free" 70 | * @return [wss_error_t] "The error status" 71 | */ 72 | wss_error_t WSS_config_free(wss_config_t *config); 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /include/error.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_error_h 2 | #define wss_error_h 3 | 4 | typedef enum { 5 | /************************************************************************** 6 | * SUCCESS * 7 | **************************************************************************/ 8 | WSS_SUCCESS = 0, 9 | 10 | /************************************************************************** 11 | * ERRORS * 12 | **************************************************************************/ 13 | 14 | // Unable to load configuration file 15 | WSS_CONFIG_LOAD_ERROR = -1, 16 | 17 | // Unable to parse configurati on file as valid JSON [RFC7160] 18 | WSS_CONFIG_JSON_PARSE_ERROR = -2, 19 | 20 | // JSON configuration must be an object at the root level 21 | WSS_CONFIG_JSON_ROOT_ERROR = -3, 22 | 23 | // JSON configuration must have an active port 24 | WSS_CONFIG_NO_PORT_ERROR = -4, 25 | 26 | // JSON configuration host must be a string 27 | WSS_CONFIG_INVALID_HOST = -5, 28 | 29 | // JSON configuration origin must be a string 30 | WSS_CONFIG_INVALID_ORIGIN = -6, 31 | 32 | // JSON configuration path must be a string 33 | WSS_CONFIG_INVALID_PATH = -7, 34 | 35 | // JSON configuration query must be a string 36 | WSS_CONFIG_INVALID_QUERY = -8, 37 | 38 | // JSON configuration subprotocol must be a string 39 | WSS_CONFIG_INVALID_SUBPROTOCOL = -9, 40 | 41 | // JSON configuration extensions must be a string 42 | WSS_CONFIG_INVALID_EXTENSION = -10, 43 | 44 | // Unable to allocate memory (Out of memory) 45 | WSS_MEMORY_ERROR = -11, 46 | 47 | // If one of the printf functions fail 48 | WSS_PRINTF_ERROR = -12, 49 | 50 | // Unable to spawn a new thread 51 | WSS_THREAD_CREATE_ERROR = -13, 52 | 53 | // Error occured in joined thread 54 | WSS_THREAD_JOIN_ERROR = -14, 55 | 56 | // Unable to create threadpool 57 | WSS_THREADPOOL_CREATE_ERROR = -15, 58 | 59 | // Threadpool is full (All threads are currently working) 60 | WSS_THREADPOOL_FULL_ERROR = -16, 61 | 62 | // Threadpool lock failed 63 | WSS_THREADPOOL_LOCK_ERROR = -26, 64 | 65 | // Threadpool was served with invalid data 66 | WSS_THREADPOOL_INVALID_ERROR = -27, 67 | 68 | // Threadpool is shutting down 69 | WSS_THREADPOOL_SHUTDOWN_ERROR = -28, 70 | 71 | // Threadpool thread returned with an error 72 | WSS_THREADPOOL_THREAD_ERROR = -29, 73 | 74 | // Unknown threadpool error 75 | WSS_THREADPOOL_ERROR = -30, 76 | 77 | // Unable to initialize poll 78 | WSS_POLL_CREATE_ERROR = -31, 79 | 80 | // Unable to add filedescriptor to poll eventslist 81 | WSS_POLL_SET_ERROR = -32, 82 | 83 | // Unable to remove filedescriptor from poll eventslist 84 | WSS_POLL_REMOVE_ERROR = -33, 85 | 86 | // Unknown error occured while waiting for events 87 | WSS_POLL_WAIT_ERROR = -34, 88 | 89 | // Unknown error occured while waiting for events 90 | WSS_POLL_PIPE_ERROR = -35, 91 | 92 | // Unable to create socket 93 | WSS_SOCKET_CREATE_ERROR = -36, 94 | 95 | // Unable to set reuse on socket 96 | WSS_SOCKET_REUSE_ERROR = -37, 97 | 98 | // Unable to bind socket to address and port 99 | WSS_SOCKET_BIND_ERROR = -38, 100 | 101 | // Unable to set socket to nonblocking 102 | WSS_SOCKET_NONBLOCKED_ERROR = -39, 103 | 104 | // Unable to start listening on socket 105 | WSS_SOCKET_LISTEN_ERROR = -40, 106 | 107 | // Unable to shutdown socket 108 | WSS_SOCKET_SHUTDOWN_ERROR = -41, 109 | 110 | // Unable to shutdown socket 111 | WSS_SOCKET_CLOSE_ERROR = -42, 112 | 113 | // Unable to set signal 114 | WSS_SIGNAL_ERROR = -43, 115 | 116 | // Unable to create session lock 117 | WSS_SESSION_LOCK_CREATE_ERROR = -44, 118 | 119 | // Unable to destroy session lock 120 | WSS_SESSION_LOCK_DESTROY_ERROR = -45, 121 | 122 | // Unable to use session lock 123 | WSS_SESSION_LOCK_ERROR = -46, 124 | 125 | // Unable to create SSL context 126 | WSS_SSL_CTX_ERROR = -47, 127 | 128 | // Unable to load CA certificates 129 | WSS_SSL_CA_ERROR = -48, 130 | 131 | // Unable to load server certificate 132 | WSS_SSL_CERT_ERROR = -49, 133 | 134 | // Unable to load or verify server private key 135 | WSS_SSL_KEY_ERROR = -50, 136 | 137 | // Unable to run ssl shutdown 138 | WSS_SSL_SHUTDOWN_ERROR = -51, 139 | 140 | // Requires further reading to shutdown correctly 141 | WSS_SSL_SHUTDOWN_READ_ERROR = -52, 142 | 143 | // Requires further writing to shutdown correctly 144 | WSS_SSL_SHUTDOWN_WRITE_ERROR = -53, 145 | 146 | // Unable to read kernel filedescriptor limits 147 | WSS_RLIMIT_ERROR = -54, 148 | 149 | // Unable to read kernel filedescriptor limits 150 | WSS_RINGBUFFER_ERROR = -55, 151 | 152 | // Cleanup thread failed 153 | WSS_CLEANUP_ERROR = -56, 154 | 155 | // Regex creation failed 156 | WSS_REGEX_ERROR = -57, 157 | } wss_error_t; 158 | 159 | #endif 160 | -------------------------------------------------------------------------------- /include/event.h: -------------------------------------------------------------------------------- 1 | #ifndef WSS_EVENT_H 2 | #define WSS_EVENT_H 3 | 4 | #if defined(__linux__) && !defined(USE_POLL) 5 | #define WSS_EPOLL 1 6 | #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ 7 | defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) 8 | #define WSS_KQUEUE 1 9 | #else 10 | #define WSS_POLL 1 11 | #endif 12 | 13 | #include "server.h" 14 | #include "session.h" 15 | #include "error.h" 16 | 17 | /** 18 | * Structure used to send from event loop to threadpool 19 | */ 20 | typedef struct { 21 | int fd; 22 | wss_server_t *server; 23 | wss_session_state_t state; 24 | } wss_thread_args_t; 25 | 26 | /** 27 | * Function that creates poll instance and adding the filedescriptor of the 28 | * servers socket to it. 29 | * 30 | * @param server [wss_server_t *] "A pointer to a server structure" 31 | * @return [wss_error_t] "The error status" 32 | */ 33 | wss_error_t WSS_poll_init(wss_server_t *server); 34 | 35 | /** 36 | * Function that rearms the poll instance for write events with the clients 37 | * filedescriptor 38 | * 39 | * @param server [wss_server_t *] "A pointer to a server structure" 40 | * @param fd [int] "The clients file descriptor" 41 | * @return [wss_error_t] "The error status" 42 | */ 43 | wss_error_t WSS_poll_set_write(wss_server_t *server, int fd); 44 | 45 | /** 46 | * Function that rearms the poll instance for read events with the clients 47 | * filedescriptor 48 | * 49 | * @param server [wss_server_t *] "A pointer to a server structure" 50 | * @param fd [int] "The clients file descriptor" 51 | * @return [wss_error_t] "The error status" 52 | */ 53 | wss_error_t WSS_poll_set_read(wss_server_t *server, int fd); 54 | 55 | /** 56 | * Function removes the client filedescriptor from the poll instance 57 | * 58 | * @param server [wss_server_t *] "A pointer to a server structure" 59 | * @param fd [int] "The clients file descriptor" 60 | * @return [wss_error_t] "The error status" 61 | */ 62 | wss_error_t WSS_poll_remove(wss_server_t *server, int fd); 63 | 64 | /** 65 | * Function that listens for new events on the servers file descriptor 66 | * 67 | * @param server [wss_server_t *] "A pointer to a server structure" 68 | * @return [wss_error_t] "The error status" 69 | */ 70 | wss_error_t WSS_poll_delegate(wss_server_t *server); 71 | 72 | /** 73 | * Function that cleanup poll function when closing 74 | * 75 | * @param server [wss_server_t *] "A pointer to a server structure" 76 | * @return [wss_error_t] "The error status" 77 | */ 78 | wss_error_t WSS_poll_close(wss_server_t *server); 79 | 80 | extern int close_pipefd[2]; 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /include/extensions.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_extensions_h 2 | #define wss_extensions_h 3 | 4 | #if !defined(uthash_malloc) || !defined(uthash_free) 5 | #include "alloc.h" 6 | 7 | #ifndef uthash_malloc 8 | #define uthash_malloc(sz) WSS_malloc(sz) /* malloc fcn */ 9 | #endif 10 | 11 | #ifndef uthash_free 12 | #define uthash_free(ptr,sz) WSS_free((void **)&ptr) /* free fcn */ 13 | #endif 14 | #endif 15 | 16 | #include "extension.h" 17 | #include "uthash.h" 18 | #include "config.h" 19 | 20 | #include 21 | 22 | typedef void (*pyInit)(void); 23 | 24 | typedef struct { 25 | int *handle; 26 | char *name; 27 | extAlloc alloc; 28 | extInit init; 29 | extOpen open; 30 | extInFrame inframe; 31 | extInFrames inframes; 32 | extOutFrame outframe; 33 | extOutFrames outframes; 34 | extClose close; 35 | extDestroy destroy; 36 | pyInit pyinit; 37 | UT_hash_handle hh; 38 | } wss_extension_t; 39 | 40 | typedef struct { 41 | wss_extension_t *ext; 42 | char *name; 43 | char *accepted; 44 | } wss_ext_t; 45 | 46 | /** 47 | * Function that loads extension implementations into memory, by loading 48 | * shared objects from the server configuration. 49 | * 50 | * E.g. 51 | * 52 | * extensions/permessage-deflate/permessage-deflate.so 53 | * 54 | * @param config [wss_config_t *] "The configuration of the server" 55 | * @return [void] 56 | */ 57 | void WSS_load_extensions(wss_config_t *config); 58 | 59 | /** 60 | * Function that looks for a extension implementation of the name given. 61 | * 62 | * @param name [char *] "The name of the extension" 63 | * @return [wss_extension_t *] "The extension or NULL" 64 | */ 65 | wss_extension_t *WSS_find_extension(char *name); 66 | 67 | /** 68 | * Destroys all memory used to load and store the extensions 69 | * 70 | * @return [void] 71 | */ 72 | void WSS_destroy_extensions(); 73 | 74 | /** 75 | * Global hashtable of extensions 76 | */ 77 | extern wss_extension_t *extensions; 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /include/frame.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_frame_h 2 | #define wss_frame_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "extension.h" 9 | #include "subprotocol.h" 10 | #include "config.h" 11 | 12 | #ifndef MIN 13 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) 14 | #endif 15 | #ifndef MAX 16 | #define MAX(x, y) (((x) > (y)) ? (x) : (y)) 17 | #endif 18 | 19 | typedef enum { 20 | CLOSE_NORMAL = 1000, /* The connection */ 21 | CLOSE_SHUTDOWN = 1001, /* Server is shutting down */ 22 | CLOSE_PROTOCOL = 1002, /* Some error in the protocol has happened */ 23 | CLOSE_TYPE = 1003, /* The type (text, binary) was not supported */ 24 | CLOSE_NO_STATUS_CODE = 1005, /* No status code available */ 25 | CLOSE_ABNORMAL = 1006, /* Abnormal close */ 26 | CLOSE_UTF8 = 1007, /* The message wasn't in UTF8 */ 27 | CLOSE_POLICY = 1008, /* The policy of the server has been broken */ 28 | CLOSE_BIG = 1009, /* The messages received is too big */ 29 | CLOSE_EXTENSION = 1010, /* Mandatory extension missing */ 30 | CLOSE_UNEXPECTED = 1011, /* Unexpected happened */ 31 | CLOSE_RESTARTING = 1012, /* Service Restart */ 32 | CLOSE_TRY_AGAIN = 1013, /* Try Again Later */ 33 | CLOSE_INVALID_PROXY_RESPONSE = 1014, /* Server acted as a gateway or proxy and received an invalid response from the upstream server. */ 34 | CLOSE_FAILED_TLS_HANDSHAKE = 1015 /* Unexpected TLS handshake failed */ 35 | } wss_close_t; 36 | 37 | /** 38 | * Parses a payload of data into a websocket frame. Returns the frame and 39 | * corrects the offset pointer in order for multiple frames to be processed 40 | * from the same payload. 41 | * 42 | * @param payload [char *] "The payload to be processed" 43 | * @param payload_length [size_t] "The length of the payload" 44 | * @param offset [size_t *] "A pointer to an offset" 45 | * @return [wss_frame_t *] "A websocket frame" 46 | */ 47 | wss_frame_t *WSS_parse_frame(char *payload, size_t payload_length, uint64_t *offset); 48 | 49 | /** 50 | * Converts a single frame into a char array. 51 | * 52 | * @param frame [wss_frame_t *] "The frame" 53 | * @param message [char **] "A pointer to a char array which should be filled with the frame data" 54 | * @return [size_t] "The size of the frame data" 55 | */ 56 | size_t WSS_stringify_frame(wss_frame_t *frame, char **message); 57 | 58 | /** 59 | * Converts an array of frames into a char array that can be written to others. 60 | * 61 | * @param frames [wss_frame_t **] "The frames to be converted" 62 | * @param size [size_t] "The amount of frames" 63 | * @param message [char **] "A pointer to a char array which should be filled with the frame data" 64 | * @return [size_t] "The size of the frame data" 65 | */ 66 | size_t WSS_stringify_frames(wss_frame_t **frames, size_t size, char **message); 67 | 68 | /** 69 | * Creates a series of frames from a message. 70 | * 71 | * @param config [wss_config_t *] "The server configuration" 72 | * @param opcode [wss_opcode_t] "The opcode that the frames should be" 73 | * @param message [char *] "The message to be converted into frames" 74 | * @param message_length [size_t] "The length of the message" 75 | * @param frames [wss_frame_t ***] "The frames created from the message" 76 | * @return [size_t] "The amount of frames created" 77 | */ 78 | size_t WSS_create_frames(wss_config_t *config, wss_opcode_t opcode, char *message, size_t message_length, wss_frame_t ***frames); 79 | 80 | /** 81 | * Creates a closing frame given a reason for the closure. 82 | * 83 | * @param reason [wss_close_t] "The reason for the closure" 84 | * @return [wss_frame_t *] "A websocket frame" 85 | */ 86 | wss_frame_t *WSS_closing_frame(wss_close_t reason, char *message); 87 | 88 | /** 89 | * Creates a ping frame. 90 | * 91 | * @return [wss_frame_t *] "A websocket frame" 92 | */ 93 | wss_frame_t *WSS_ping_frame(); 94 | 95 | /** 96 | * Creates a pong frame from a received ping frame. 97 | * 98 | * @param ping [wss_frame_t *] "A ping frame" 99 | * @return [wss_frame_t *] "A websocket frame" 100 | */ 101 | wss_frame_t *WSS_pong_frame(wss_frame_t *ping); 102 | 103 | /** 104 | * Releases memory used by a frame. 105 | * 106 | * @param ping [wss_frame_t *] "The frame that should be freed" 107 | * @return [void] 108 | */ 109 | void WSS_free_frame(wss_frame_t *frame); 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /include/header.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_header_h 2 | #define wss_header_h 3 | 4 | #include 5 | #include /* regex_t, regcomp, regexec */ 6 | 7 | #include "config.h" 8 | #include "httpstatuscodes.h" 9 | #include "subprotocols.h" 10 | #include "extensions.h" 11 | 12 | #define WEBSOCKET_STRING "websocket" 13 | #define WEBSOCKET_UPPERCASE_STRING "WebSocket" 14 | #define WEBSOCKET_PROTOCOL_STRING "WebSocket-Protocol" 15 | #define UPGRADE_STRING "Upgrade" 16 | #define ORIGIN_STRING "Origin" 17 | #define CONNECTION_STRING "Connection" 18 | #define COOKIE_STRING "Cookie" 19 | #define HOST_STRING "Host" 20 | #define SEC_WEBSOCKET_KEY_LENGTH 16 21 | #define SEC_WEBSOCKET_VERSION "Sec-WebSocket-Version" 22 | #define SEC_WEBSOCKET_PROTOCOL "Sec-WebSocket-Protocol" 23 | #define SEC_WEBSOCKET_KEY "Sec-WebSocket-Key" 24 | #define SEC_WEBSOCKET_KEY1 "Sec-WebSocket-Key1" 25 | #define SEC_WEBSOCKET_KEY2 "Sec-WebSocket-Key2" 26 | #define SEC_WEBSOCKET_ORIGIN "Sec-WebSocket-Origin" 27 | #define SEC_WEBSOCKET_EXTENSIONS "Sec-WebSocket-Extensions" 28 | 29 | typedef enum { 30 | UNKNOWN = 0, 31 | HIXIE75 = 1, 32 | HIXIE76 = 2, 33 | HYBI04 = 4, 34 | HYBI05 = 5, 35 | HYBI06 = 6, 36 | HYBI07 = 7, 37 | HYBI10 = 8, 38 | RFC6455 = 13 39 | } wss_type_t; 40 | 41 | typedef struct { 42 | unsigned int length; 43 | char *content; 44 | char *method; 45 | char *version; 46 | char *path; 47 | char *host; 48 | char *payload; 49 | char *cookies; 50 | int ws_version; 51 | wss_type_t ws_type; 52 | wss_subprotocol_t *ws_protocol; 53 | char *ws_upgrade; 54 | char *ws_connection; 55 | wss_ext_t **ws_extensions; 56 | unsigned int ws_extensions_count; 57 | char *ws_origin; 58 | char *ws_key; 59 | char *ws_key1; 60 | char *ws_key2; 61 | char *ws_key3; 62 | } wss_header_t; 63 | 64 | /** 65 | * Parses a HTTP header into a header structure and returns the status code 66 | * appropriate. 67 | * 68 | * @param fd [int] "The filedescriptor" 69 | * @param header [wss_header_t *] "The header structure to fill" 70 | * @param config [wss_config_t *] "The configuration of the server" 71 | * @return [enum HttpStatus_Code] "The status code to return to the client" 72 | */ 73 | enum HttpStatus_Code WSS_parse_header(int fd, wss_header_t *header, wss_config_t *config); 74 | 75 | /** 76 | * Upgrades a HTTP header, that is returns switching protocols response if 77 | * the header contains the required options. 78 | * 79 | * @param header [wss_header_t *] "The header structure to fill" 80 | * @param config [wss_config_t *] "The configuration of the server" 81 | * @param re [regex_t *] "The regex to validate path" 82 | * @return [enum HttpStatus_Code] "The status code to return to the client" 83 | */ 84 | enum HttpStatus_Code WSS_upgrade_header(wss_header_t *header, wss_config_t *config, regex_t *re); 85 | 86 | /** 87 | * Frees a HTTP header structure 88 | * 89 | * @param header [wss_header_t *] "The HTTP header to free" 90 | * @return [void] 91 | */ 92 | void WSS_free_header(wss_header_t *header); 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /include/http.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_http_h 2 | #define wss_http_h 3 | 4 | #define REQUEST_URI "^(ws%s://(%s)(:%d)?)?/(%s)?(\\?(%s)(&(%s))*)?$" 5 | #define MAX(x, y) (((x) > (y)) ? (x) : (y)) 6 | 7 | #include "server.h" 8 | 9 | /** 10 | * Function that initializes a regex for the server instance that can be used 11 | * to validate the connecting path of the client. 12 | * 13 | * @param server [wss_server_t *] "The server instance" 14 | * @return [wss_error_t] "The error status" 15 | */ 16 | wss_error_t WSS_http_regex_init(wss_server_t *server); 17 | 18 | /** 19 | * Function that initializes a http server instance and creating thread where 20 | * the instance is being run. 21 | * 22 | * @param server [wss_server_t *] "The server instance" 23 | * @return [wss_error_t] "The error status" 24 | */ 25 | wss_error_t WSS_http_server(wss_server_t *server); 26 | 27 | /** 28 | * Function that free op space allocated for the http server and closes the 29 | * filedescriptors in use.. 30 | * 31 | * @param server [wss_server_t *] "The http server" 32 | * @return [wss_error_t] "The error status" 33 | */ 34 | wss_error_t WSS_http_server_free(wss_server_t *server); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/json.h: -------------------------------------------------------------------------------- 1 | 2 | /* vim: set et ts=3 sw=3 sts=3 ft=c: 3 | * 4 | * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. 5 | * https://github.com/udp/json-parser 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _JSON_H 32 | #define _JSON_H 33 | 34 | #ifndef json_char 35 | #define json_char char 36 | #endif 37 | 38 | #ifndef json_int_t 39 | #ifndef _MSC_VER 40 | #include 41 | #define json_int_t int64_t 42 | #else 43 | #define json_int_t __int64 44 | #endif 45 | #endif 46 | 47 | #include 48 | 49 | #ifdef __cplusplus 50 | 51 | #include 52 | 53 | extern "C" 54 | { 55 | 56 | #endif 57 | 58 | typedef struct 59 | { 60 | unsigned long max_memory; 61 | int settings; 62 | 63 | /* Custom allocator support (leave null to use malloc/free) 64 | */ 65 | 66 | void * (* mem_alloc) (size_t, int zero, void * user_data); 67 | void (* mem_free) (void *, void * user_data); 68 | 69 | void * user_data; /* will be passed to mem_alloc and mem_free */ 70 | 71 | size_t value_extra; /* how much extra space to allocate for values? */ 72 | 73 | } json_settings; 74 | 75 | #define json_enable_comments 0x01 76 | 77 | typedef enum 78 | { 79 | json_none, 80 | json_object, 81 | json_array, 82 | json_integer, 83 | json_double, 84 | json_string, 85 | json_boolean, 86 | json_null 87 | 88 | } json_type; 89 | 90 | extern const struct _json_value json_value_none; 91 | 92 | typedef struct _json_object_entry 93 | { 94 | json_char * name; 95 | unsigned int name_length; 96 | 97 | struct _json_value * value; 98 | 99 | } json_object_entry; 100 | 101 | typedef struct _json_value 102 | { 103 | struct _json_value * parent; 104 | 105 | json_type type; 106 | 107 | union 108 | { 109 | int boolean; 110 | json_int_t integer; 111 | double dbl; 112 | 113 | struct 114 | { 115 | unsigned int length; 116 | json_char * ptr; /* null terminated */ 117 | 118 | } string; 119 | 120 | struct 121 | { 122 | unsigned int length; 123 | 124 | json_object_entry * values; 125 | 126 | #if defined(__cplusplus) && __cplusplus >= 201103L 127 | decltype(values) begin () const 128 | { return values; 129 | } 130 | decltype(values) end () const 131 | { return values + length; 132 | } 133 | #endif 134 | 135 | } object; 136 | 137 | struct 138 | { 139 | unsigned int length; 140 | struct _json_value ** values; 141 | 142 | #if defined(__cplusplus) && __cplusplus >= 201103L 143 | decltype(values) begin () const 144 | { return values; 145 | } 146 | decltype(values) end () const 147 | { return values + length; 148 | } 149 | #endif 150 | 151 | } array; 152 | 153 | } u; 154 | 155 | union 156 | { 157 | struct _json_value * next_alloc; 158 | void * object_mem; 159 | 160 | } _reserved; 161 | 162 | #ifdef JSON_TRACK_SOURCE 163 | 164 | /* Location of the value in the source JSON 165 | */ 166 | unsigned int line, col; 167 | 168 | #endif 169 | 170 | 171 | /* Some C++ operator sugar */ 172 | 173 | #ifdef __cplusplus 174 | 175 | public: 176 | 177 | inline _json_value () 178 | { memset (this, 0, sizeof (_json_value)); 179 | } 180 | 181 | inline const struct _json_value &operator [] (int index) const 182 | { 183 | if (type != json_array || index < 0 184 | || ((unsigned int) index) >= u.array.length) 185 | { 186 | return json_value_none; 187 | } 188 | 189 | return *u.array.values [index]; 190 | } 191 | 192 | inline const struct _json_value &operator [] (const char * index) const 193 | { 194 | if (type != json_object) 195 | return json_value_none; 196 | 197 | for (unsigned int i = 0; i < u.object.length; ++ i) 198 | if (!strcmp (u.object.values [i].name, index)) 199 | return *u.object.values [i].value; 200 | 201 | return json_value_none; 202 | } 203 | 204 | inline operator const char * () const 205 | { 206 | switch (type) 207 | { 208 | case json_string: 209 | return u.string.ptr; 210 | 211 | default: 212 | return ""; 213 | }; 214 | } 215 | 216 | inline operator json_int_t () const 217 | { 218 | switch (type) 219 | { 220 | case json_integer: 221 | return u.integer; 222 | 223 | case json_double: 224 | return (json_int_t) u.dbl; 225 | 226 | default: 227 | return 0; 228 | }; 229 | } 230 | 231 | inline operator bool () const 232 | { 233 | if (type != json_boolean) 234 | return false; 235 | 236 | return u.boolean != 0; 237 | } 238 | 239 | inline operator double () const 240 | { 241 | switch (type) 242 | { 243 | case json_integer: 244 | return (double) u.integer; 245 | 246 | case json_double: 247 | return u.dbl; 248 | 249 | default: 250 | return 0; 251 | }; 252 | } 253 | 254 | #endif 255 | 256 | } json_value; 257 | 258 | json_value * json_parse (const json_char * json, 259 | size_t length); 260 | 261 | #define json_error_max 128 262 | json_value * json_parse_ex (json_settings * settings, 263 | const json_char * json, 264 | size_t length, 265 | char * error); 266 | 267 | void json_value_free (json_value *); 268 | 269 | 270 | /* Not usually necessary, unless you used a custom mem_alloc and now want to 271 | * use a custom mem_free. 272 | */ 273 | void json_value_free_ex (json_settings * settings, 274 | json_value *); 275 | 276 | json_value * json_value_find(json_value * parent, char * name); 277 | 278 | #ifdef __cplusplus 279 | } /* extern "C" */ 280 | #endif 281 | 282 | #endif 283 | 284 | 285 | -------------------------------------------------------------------------------- /include/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See `log.c` for details. 6 | */ 7 | 8 | #ifndef LOG_H 9 | #define LOG_H 10 | 11 | #include 12 | #include 13 | 14 | #define LOG_VERSION "0.1.0" 15 | 16 | typedef void (*log_LockFn)(void *udata, int lock); 17 | 18 | enum { 19 | WSS_LOG_FATAL, 20 | WSS_LOG_ERROR, 21 | WSS_LOG_WARN, 22 | WSS_LOG_INFO, 23 | WSS_LOG_DEBUG, 24 | WSS_LOG_TRACE 25 | }; 26 | 27 | #define WSS_log_trace(...) log_log(WSS_LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) 28 | #define WSS_log_debug(...) log_log(WSS_LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) 29 | #define WSS_log_info(...) log_log(WSS_LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) 30 | #define WSS_log_warn(...) log_log(WSS_LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) 31 | #define WSS_log_error(...) log_log(WSS_LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) 32 | #define WSS_log_fatal(...) log_log(WSS_LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) 33 | 34 | void log_set_udata(void *udata); 35 | void log_set_lock(log_LockFn fn); 36 | void log_set_fp(FILE *fp); 37 | void log_set_level(int level); 38 | void log_set_quiet(int enable); 39 | 40 | void log_log(int level, const char *file, int line, const char *fmt, ...); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /include/message.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_message_h 2 | #define wss_message_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "subprotocol.h" 9 | #include "extension.h" 10 | 11 | /** 12 | * Structure containing a message that should be sent to a client 13 | */ 14 | typedef struct { 15 | size_t length; 16 | char *msg; 17 | bool framed; 18 | } wss_message_t; 19 | 20 | void WSS_message_send_frames(void *server, void *session, wss_frame_t **frames, size_t frames_count); 21 | 22 | void WSS_message_send(int fd, wss_opcode_t opcode, char *message, uint64_t message_length); 23 | 24 | void WSS_message_free(wss_message_t *msg); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /include/pool.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Mathias Brossard . 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef _THREADPOOL_H_ 30 | #define _THREADPOOL_H_ 31 | 32 | /** 33 | * @file threadpool.h 34 | * @brief Threadpool Header File 35 | */ 36 | 37 | typedef struct threadpool_t threadpool_t; 38 | 39 | typedef enum { 40 | threadpool_invalid = -1, 41 | threadpool_lock_failure = -2, 42 | threadpool_queue_full = -3, 43 | threadpool_shutdown = -4, 44 | threadpool_thread_failure = -5 45 | } threadpool_error_t; 46 | 47 | typedef enum { 48 | threadpool_graceful = 1 49 | } threadpool_destroy_flags_t; 50 | 51 | /** 52 | * @function threadpool_create 53 | * @brief Creates a threadpool_t object. 54 | * @param thread_count Number of worker threads. 55 | * @param queue_size Size of the queue. 56 | * @param thread_size Size of a thread. 57 | * @param flags Unused parameter. 58 | * @return a newly created thread pool or NULL 59 | */ 60 | threadpool_t *threadpool_create(int thread_count, int queue_size, int thread_size, int flags); 61 | 62 | char *threadpool_strerror(int err); 63 | 64 | /** 65 | * @function threadpool_add 66 | * @brief add a new task in the queue of a thread pool 67 | * @param pool Thread pool to which add the task. 68 | * @param function Pointer to the function that will perform the task. 69 | * @param argument Argument to be passed to the function. 70 | * @param flags Unused parameter. 71 | * @return 0 if all goes well, negative values in case of error (@see 72 | * threadpool_error_t for codes). 73 | */ 74 | int threadpool_add(threadpool_t *pool, void (*routine)(void *), void *arg, int flags); 75 | 76 | /** 77 | * @function threadpool_destroy 78 | * @brief Stops and destroys a thread pool. 79 | * @param pool Thread pool to destroy. 80 | * @param flags Flags for shutdown 81 | * 82 | * Known values for flags are 0 (default) and threadpool_graceful in 83 | * which case the thread pool doesn't accept any new tasks but 84 | * processes all pending tasks before shutdown. 85 | */ 86 | int threadpool_destroy(threadpool_t *pool, int flags); 87 | 88 | #endif /* _THREADPOOL_H_ */ 89 | -------------------------------------------------------------------------------- /include/predict.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_predict_h 2 | #define wss_predict_h 3 | 4 | #if defined(__GNUC__ ) || defined(__INTEL_COMPILER) 5 | #define likely(x) __builtin_expect(!!(x), 1) 6 | #define unlikely(x) __builtin_expect(!!(x), 0) 7 | #else 8 | #define likely(x) (x) 9 | #define unlikely(x) (x) 10 | #endif 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /include/ringbuf.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2016-2017 Mindaugas Rasiukevicius 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _RINGBUF_H_ 28 | #define _RINGBUF_H_ 29 | 30 | #include 31 | 32 | #undef __BEGIN_DECLS 33 | #undef __END_DECLS 34 | #ifdef __cplusplus 35 | # define __BEGIN_DECLS extern "C" { 36 | # define __END_DECLS } 37 | #else 38 | # define __BEGIN_DECLS /* empty */ 39 | # define __END_DECLS /* empty */ 40 | #endif 41 | 42 | __BEGIN_DECLS 43 | 44 | typedef struct ringbuf ringbuf_t; 45 | typedef struct ringbuf_worker ringbuf_worker_t; 46 | 47 | int ringbuf_setup(ringbuf_t *, unsigned, unsigned, size_t); 48 | void ringbuf_get_sizes(unsigned, unsigned, size_t *, size_t *); 49 | 50 | ringbuf_worker_t *ringbuf_register(ringbuf_t *, unsigned); 51 | void ringbuf_unregister(ringbuf_t *, ringbuf_worker_t *); 52 | 53 | ssize_t ringbuf_acquire(ringbuf_t *, ringbuf_worker_t **, size_t); 54 | void ringbuf_produce(ringbuf_t *, ringbuf_worker_t **); 55 | size_t ringbuf_consume(ringbuf_t *, size_t *); 56 | void ringbuf_release(ringbuf_t *, size_t); 57 | 58 | __END_DECLS 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /include/ringbuf_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1991, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * This code is derived from software contributed to Berkeley by 6 | * Berkeley Software Design, Inc. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. Neither the name of the University nor the names of its contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | * 32 | * @(#)cdefs.h 8.8 (Berkeley) 1/9/95 33 | */ 34 | 35 | #ifndef _UTILS_H_ 36 | #define _UTILS_H_ 37 | 38 | #include 39 | 40 | /* 41 | * A regular assert (debug/diagnostic only). 42 | */ 43 | #if defined(DEBUG) 44 | #define ASSERT assert 45 | #else 46 | #define ASSERT(x) 47 | #endif 48 | 49 | /* 50 | * Minimum, maximum and rounding macros. 51 | */ 52 | 53 | #ifndef MIN 54 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) 55 | #endif 56 | 57 | #ifndef MAX 58 | #define MAX(x, y) ((x) > (y) ? (x) : (y)) 59 | #endif 60 | 61 | /* 62 | * Branch prediction macros. 63 | */ 64 | #ifndef __predict_true 65 | #define __predict_true(x) __builtin_expect((x) != 0, 1) 66 | #define __predict_false(x) __builtin_expect((x) != 0, 0) 67 | #endif 68 | 69 | /* 70 | * Atomic operations and memory barriers. If C11 API is not available, 71 | * then wrap the GCC builtin routines. 72 | * 73 | * Note: This atomic_compare_exchange_weak does not do the C11 thing of 74 | * filling *(expected) with the actual value, because we don't need 75 | * that here. 76 | */ 77 | #ifndef atomic_compare_exchange_weak 78 | #define atomic_compare_exchange_weak(ptr, expected, desired) \ 79 | __sync_bool_compare_and_swap(ptr, *(expected), desired) 80 | #endif 81 | 82 | #ifndef atomic_thread_fence 83 | #define memory_order_relaxed __ATOMIC_RELAXED 84 | #define memory_order_acquire __ATOMIC_ACQUIRE 85 | #define memory_order_release __ATOMIC_RELEASE 86 | #define memory_order_seq_cst __ATOMIC_SEQ_CST 87 | #define atomic_thread_fence(m) __atomic_thread_fence(m) 88 | #endif 89 | #ifndef atomic_store_explicit 90 | #define atomic_store_explicit __atomic_store_n 91 | #endif 92 | #ifndef atomic_load_explicit 93 | #define atomic_load_explicit __atomic_load_n 94 | #endif 95 | 96 | /* 97 | * Exponential back-off for the spinning paths. 98 | */ 99 | #define SPINLOCK_BACKOFF_MIN 4 100 | #define SPINLOCK_BACKOFF_MAX 128 101 | #if defined(__x86_64__) || defined(__i386__) 102 | #define SPINLOCK_BACKOFF_HOOK __asm volatile("pause" ::: "memory") 103 | #else 104 | #define SPINLOCK_BACKOFF_HOOK 105 | #endif 106 | #define SPINLOCK_BACKOFF(count) \ 107 | do { \ 108 | for (int __i = (count); __i != 0; __i--) { \ 109 | SPINLOCK_BACKOFF_HOOK; \ 110 | } \ 111 | if ((count) < SPINLOCK_BACKOFF_MAX) \ 112 | (count) += (count); \ 113 | } while (/* CONSTCOND */ 0); 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /include/server.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_server_h 2 | #define wss_server_h 3 | 4 | #include 5 | #include /* socket, setsockopt, inet_ntoa, accept, shutdown */ 6 | #include /* sockaddr_in, inet_ntoa */ 7 | #include /* regex_t, regcomp, regexec */ 8 | #include /* pthread_create, pthread_t, pthread_attr_t 9 | pthread_mutex_init */ 10 | #include "pool.h" 11 | #include "config.h" 12 | 13 | #if !defined(uthash_malloc) || !defined(uthash_free) 14 | #include "alloc.h" 15 | 16 | #ifndef uthash_malloc 17 | #define uthash_malloc(sz) WSS_malloc(sz) /* malloc fcn */ 18 | #endif 19 | 20 | #ifndef uthash_free 21 | #define uthash_free(ptr,sz) WSS_free((void **)&ptr) /* free fcn */ 22 | #endif 23 | #endif 24 | 25 | #ifndef WSS_SERVER_VERSION 26 | #define WSS_SERVER_VERSION "v2.0.0" 27 | #endif 28 | 29 | #include "uthash.h" 30 | 31 | typedef enum { 32 | STARTING, 33 | RUNNING, 34 | HALTING, 35 | HALT_ERROR 36 | } wss_state_t; 37 | 38 | typedef struct { 39 | int port; 40 | int fd; 41 | int poll_fd; 42 | int max_fd; 43 | wss_config_t *config; 44 | void *ssl_ctx; 45 | threadpool_t *pool; 46 | void *events; 47 | struct sockaddr_in6 info; 48 | pthread_t thread_id; 49 | pthread_t cleanup_thread_id; 50 | pthread_mutex_t lock; 51 | int rearm_pipefd[2]; 52 | regex_t *re; 53 | } wss_server_t; 54 | 55 | typedef struct { 56 | wss_state_t state; 57 | pthread_mutex_t lock; 58 | } wss_server_state_t; 59 | 60 | typedef struct { 61 | wss_server_t *http; 62 | wss_server_t *https; 63 | } wss_servers_t; 64 | 65 | /** 66 | * Function that updates the state of the server. 67 | * 68 | * @param s [wss_state_t *] "A state_t value describing the servers state" 69 | * @return [void] 70 | */ 71 | void WSS_server_set_state(wss_state_t state); 72 | 73 | void WSS_server_set_max_fd(wss_server_t *server, int fd); 74 | 75 | /** 76 | * Function that loops over poll events and distribute the events to different 77 | * threadpools. 78 | * 79 | * @param arg [void *] "Is in fact a server_t instance" 80 | * @return pthread_exit [void *] "0 if successfull and otherwise <0" 81 | */ 82 | void *WSS_server_run(void *arg); 83 | 84 | /** 85 | * Starts the websocket server. 86 | * 87 | * @param config [wss_config_t *] "The configuration of the server" 88 | * @return [int] "EXIT_SUCCESS if successfull or EXIT_FAILURE on error" 89 | */ 90 | int WSS_server_start(wss_config_t *config); 91 | 92 | extern wss_server_state_t state; 93 | 94 | extern wss_servers_t servers; 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /include/session.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_session_h 2 | #define wss_session_h 3 | 4 | #if !defined(uthash_malloc) || !defined(uthash_free) 5 | #include "alloc.h" 6 | 7 | #ifndef uthash_malloc 8 | #define uthash_malloc(sz) WSS_malloc(sz) /* malloc fcn */ 9 | #endif 10 | 11 | #ifndef uthash_free 12 | #define uthash_free(ptr,sz) WSS_free((void **)&ptr) /* free fcn */ 13 | #endif 14 | #endif 15 | 16 | #include 17 | #include 18 | #include /* pthread_create, pthread_t, pthread_attr_t 19 | pthread_mutex_init */ 20 | #include "uthash.h" 21 | #include "header.h" 22 | #include "ringbuf.h" 23 | #include "frame.h" 24 | #include "message.h" 25 | #include "error.h" 26 | 27 | typedef enum { 28 | NONE, 29 | READ, 30 | WRITE, 31 | } wss_session_event_t; 32 | 33 | typedef enum { 34 | IDLE, 35 | READING, 36 | WRITING, 37 | CLOSING, 38 | CONNECTING 39 | } wss_session_state_t; 40 | 41 | typedef struct { 42 | // The file descriptor of the session 43 | int fd; 44 | // The port of the session 45 | int port; 46 | // The IP of the session 47 | char *ip; 48 | // Whether session has been WSS handshaked 49 | bool handshaked; 50 | // The ssl object used to communicate with session 51 | void *ssl; 52 | // Whether session has been SSL handshaked 53 | bool ssl_connected; 54 | // Whether the session is closing 55 | bool closing; 56 | // Whether the session has begun disconnecting 57 | bool disconnecting; 58 | // Lock that ensures disconnecting check is done atomically 59 | pthread_mutex_t lock_disconnecting; 60 | // Lock that ensures only one thread can perform IO at a time 61 | pthread_mutex_t lock; 62 | // Attributes for the lock 63 | pthread_mutexattr_t lock_attr; 64 | // Jobs to be performed 65 | int jobs; 66 | // Lock that ensures jobs are incremented/decremented atomically 67 | pthread_mutex_t lock_jobs; 68 | // Conditional variable that ensures that close call is only performed when no other IO is waiting 69 | pthread_cond_t cond_jobs; 70 | // Which state the session is currently in 71 | wss_session_state_t state; 72 | // Which event the session should continue listening for 73 | wss_session_event_t event; 74 | // The HTTP header of the session 75 | wss_header_t *header; 76 | // A ringbuffer containing references to the messages that the session shall receive 77 | ringbuf_t *ringbuf; 78 | // The actual messages 79 | wss_message_t **messages; 80 | // The size the messages/ringbuffer 81 | int messages_count; 82 | // Store the lastest activity of the session 83 | struct timespec alive; 84 | // Store pong application data if a ping was sent to the session 85 | char *pong; 86 | // Length of the pong application data 87 | unsigned int pong_length; 88 | // If not all data was read, store the payload temporarily 89 | char *payload; 90 | // The size of the temporarily payload 91 | size_t payload_length; 92 | // The offset into the payload where the next frame should be read from 93 | size_t offset; 94 | // If not all frames was read, store the frames temporarily 95 | wss_frame_t **frames; 96 | // The size of the temporarily frames 97 | size_t frames_length; 98 | // If not all data was written, store many bytes currently written 99 | unsigned int written; 100 | // Used for session hash table 101 | UT_hash_handle hh; 102 | } wss_session_t; 103 | 104 | /** 105 | * Function that initialize a mutex lock. 106 | * 107 | * @return [wss_error_t] "The error status" 108 | */ 109 | wss_error_t WSS_session_init_lock(); 110 | 111 | /** 112 | * Function that destroy a mutex lock. 113 | * 114 | * @return [wss_error_t] "The error status" 115 | */ 116 | wss_error_t WSS_session_destroy_lock(); 117 | 118 | /** 119 | * Function that allocates and creates a new session. 120 | * 121 | * @param fd [int] "The filedescriptor associated to the session" 122 | * @param ip [char *] "The ip-address of the session" 123 | * @param port [int] "The port" 124 | * @return [wss_session_t *] "Returns session if successful, otherwise NULL" 125 | */ 126 | wss_session_t *WSS_session_add(int fd, char* ip, int port); 127 | 128 | /** 129 | * Function that frees the allocated memory and removes the session from the 130 | * hashtable but without locking the hashtable. This should only be used in 131 | * conjunction with the WSS_session_all call. 132 | * 133 | * @param session [wss_session_t *] "The session to be deleted" 134 | * @return [wss_error_t] "The error status" 135 | */ 136 | wss_error_t WSS_session_delete_no_lock(wss_session_t *session); 137 | 138 | /** 139 | * Function that frees the allocated memory and removes the session from the 140 | * hashtable. 141 | * 142 | * @param session [wss_session_t *] "The session to be deleted" 143 | * @return [wss_error_t] "The error status" 144 | */ 145 | wss_error_t WSS_session_delete(wss_session_t *session); 146 | 147 | /** 148 | * Function that frees the allocated memory and deletes all sessions. 149 | * 150 | * @return [wss_error_t] "The error status" 151 | */ 152 | wss_error_t WSS_session_delete_all(); 153 | 154 | /** 155 | * Function that finds all sessions and calls callback for each of them. 156 | * 157 | * @param callback [void (*callback)(wss_session_t *, void *)] "A callback to be called for each session" 158 | * @return [wss_error_t] "The error status" 159 | */ 160 | wss_error_t WSS_session_all(void (*callback)(wss_session_t *)); 161 | 162 | /** 163 | * Function that finds a session using the filedescriptor of the session. 164 | * 165 | * @param fd [int] "The filedescriptor associated to some session" 166 | * @return [wss_session_t *] "Returns session if successful, otherwise NULL" 167 | */ 168 | wss_session_t *WSS_session_find(int fd); 169 | 170 | /** 171 | * Function that increments the job counter atomically. 172 | * 173 | * @param session [wss_session_t *] "The session" 174 | * @return [wss_error_t] "The error status" 175 | */ 176 | wss_error_t WSS_session_jobs_inc(wss_session_t *session); 177 | 178 | /** 179 | * Function that decrements the job counter atomically and signals the 180 | * condition if no jobs are left. 181 | * 182 | * @param session [wss_session_t *] "The session" 183 | * @return [wss_error_t] "The error status" 184 | */ 185 | wss_error_t WSS_session_jobs_dec(wss_session_t *session); 186 | 187 | /** 188 | * Function that waits for all jobs to be done. 189 | * 190 | * @param session [wss_session_t *] "The session" 191 | * @return [wss_error_t] "The error status" 192 | */ 193 | wss_error_t WSS_session_jobs_wait(wss_session_t *session); 194 | 195 | /** 196 | * Function that determines atomically if session is already disconnecting. 197 | * 198 | * @param session [wss_session_t *] "The session" 199 | * @param dc [bool *] "Whether session is already disconnecting or not" 200 | * 201 | * @return [wss_error_t] "The error status" 202 | */ 203 | wss_error_t WSS_session_is_disconnecting(wss_session_t *session, bool *dc); 204 | 205 | #endif 206 | -------------------------------------------------------------------------------- /include/sha1.h: -------------------------------------------------------------------------------- 1 | /* 2 | * sha1.h 3 | * 4 | * Copyright (C) 1998, 2009 5 | * Paul E. Jones 6 | * All Rights Reserved 7 | * 8 | * Freeware Public License (FPL) 9 | * 10 | * This software is licensed as "freeware." Permission to distribute 11 | * this software in source and binary forms, including incorporation 12 | * into other products, is hereby granted without a fee. THIS SOFTWARE 13 | * IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED OR IMPLIED WARRANTIES, 14 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 15 | * AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHOR SHALL NOT BE HELD 16 | * LIABLE FOR ANY DAMAGES RESULTING FROM THE USE OF THIS SOFTWARE, EITHER 17 | * DIRECTLY OR INDIRECTLY, INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA 18 | * OR DATA BEING RENDERED INACCURATE. 19 | * 20 | ***************************************************************************** 21 | * $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $ 22 | ***************************************************************************** 23 | * 24 | * Description: 25 | * This class implements the Secure Hashing Standard as defined 26 | * in FIPS PUB 180-1 published April 17, 1995. 27 | * 28 | * Many of the variable names in the SHA1Context, especially the 29 | * single character names, were used because those were the names 30 | * used in the publication. 31 | * 32 | * Please read the file sha1.c for more information. 33 | * 34 | */ 35 | 36 | #ifndef _SHA1_H_ 37 | #define _SHA1_H_ 38 | 39 | /* 40 | * This structure will hold context information for the hashing 41 | * operation 42 | */ 43 | typedef struct SHA1Context 44 | { 45 | unsigned Message_Digest[5]; /* Message Digest (output) */ 46 | 47 | unsigned Length_Low; /* Message length in bits */ 48 | unsigned Length_High; /* Message length in bits */ 49 | 50 | unsigned char Message_Block[64]; /* 512-bit message blocks */ 51 | int Message_Block_Index; /* Index into message block array */ 52 | 53 | int Computed; /* Is the digest computed? */ 54 | int Corrupted; /* Is the message digest corruped? */ 55 | } SHA1Context; 56 | 57 | /* 58 | * Function Prototypes 59 | */ 60 | void SHA1Reset(SHA1Context *); 61 | int SHA1Result(SHA1Context *); 62 | void SHA1Input( SHA1Context *, 63 | const unsigned char *, 64 | unsigned); 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /include/socket.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_socket_h 2 | #define wss_socket_h 3 | 4 | #include "server.h" 5 | #include "error.h" 6 | 7 | /** 8 | * Function that initializes a socket and store the filedescriptor. 9 | * 10 | * @param server [wss_server_t *] "The server instance" 11 | * @return [wss_error_t] "The error status" 12 | */ 13 | wss_error_t WSS_socket_create(wss_server_t *server); 14 | 15 | /** 16 | * Function that enables reuse of the port if the server shuts down. 17 | * 18 | * @param fd [int] "The filedescriptor associated to some user" 19 | * @return [wss_error_t] "The error status" 20 | */ 21 | wss_error_t WSS_socket_reuse(int fd); 22 | 23 | /** 24 | * Function that binds the socket to a specific port and chooses IPV4. 25 | * 26 | * @param server [wss_server_t *] "The server instance" 27 | * @return [wss_error_t] "The error status" 28 | */ 29 | wss_error_t WSS_socket_bind(wss_server_t *server); 30 | 31 | /** 32 | * Function that makes the socket non-blocking for both reads and writes. 33 | * 34 | * @param fd [int] "The filedescriptor associated to some user" 35 | * @return [wss_error_t] "The error status" 36 | */ 37 | wss_error_t WSS_socket_non_blocking(int fd); 38 | 39 | /** 40 | * Function that makes the server start listening on the socket. 41 | * 42 | * @param fd [int] "The filedescriptor associated to some user" 43 | * @return [wss_error_t] "The error status" 44 | */ 45 | wss_error_t WSS_socket_listen(int fd); 46 | 47 | /** 48 | * Function that creates a threadpool which can be used handle traffic. 49 | * 50 | * @param server [wss_server_t *] "The server instance" 51 | * @return [wss_error_t] "The error status" 52 | */ 53 | wss_error_t WSS_socket_threadpool(wss_server_t *server); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/ssl.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_ssl_h 2 | #define wss_ssl_h 3 | 4 | #if defined(USE_OPENSSL) | defined(USE_BORINGSSL) | defined(USE_LIBRESSL) 5 | 6 | #include 7 | 8 | #elif defined(USE_WOLFSSL) 9 | 10 | #pragma GCC diagnostic push 11 | #pragma GCC diagnostic ignored "-Wcpp" 12 | #include 13 | #pragma GCC diagnostic pop 14 | 15 | #define SHA_DIGEST_LENGTH 20 16 | 17 | #else 18 | 19 | #define SHA_DIGEST_LENGTH 20 20 | 21 | #endif 22 | 23 | #include 24 | #include /* pthread_create, pthread_t, pthread_attr_t 25 | pthread_rwlock_init */ 26 | 27 | #include "error.h" 28 | #include "server.h" 29 | #include "session.h" 30 | 31 | /** 32 | * Function initializes SSL context that can be used to serve over https. 33 | * 34 | * @param server [wss_server_t *] "The server instance" 35 | * @return [wss_error_t] "The error status" 36 | */ 37 | wss_error_t WSS_http_ssl(wss_server_t *server); 38 | 39 | /** 40 | * Function frees SSL context that was used to serve over https. 41 | * 42 | * @param server [wss_server_t *] "The server instance" 43 | * @return [void] 44 | */ 45 | void WSS_http_ssl_free(wss_server_t *server); 46 | 47 | /** 48 | * Function that performs a ssl handshake with the connecting client. 49 | * 50 | * @param server [wss_server_t *] "The server implementation" 51 | * @param session [wss_session_t *] "The connecting client session" 52 | * @return [void] 53 | */ 54 | void WSS_ssl_handshake(wss_server_t *server, wss_session_t *session); 55 | 56 | /** 57 | * Function that performs a ssl read from the connecting client. 58 | * 59 | * @param server [wss_server_t *] "The server implementation" 60 | * @param session [wss_session_t *] "The connecting client session" 61 | * @param buffer [char *] "The buffer to use" 62 | * @return [int] 63 | */ 64 | int WSS_ssl_read(wss_server_t *server, wss_session_t *session, char *buffer); 65 | 66 | /** 67 | * Function that performs a ssl write to the connecting client. 68 | * 69 | * @param session [wss_session_t *] "The connecting client session" 70 | * @param message_index [unsigned int] "The message index" 71 | * @param message [wss_message_t *] "The message" 72 | * @param bytes_sent [unsigned int *] "Pointer to the amount of bytes currently sent" 73 | * @return [bool] 74 | */ 75 | bool WSS_ssl_write_partial(wss_session_t *session, unsigned int message_index, wss_message_t *message, unsigned int* bytes_sent); 76 | 77 | /** 78 | * Function that performs a ssl write to the connecting client. 79 | * 80 | * @param session [wss_session_t *] "The connecting client session" 81 | * @param message [char *] "The message" 82 | * @param message_length [size_t] "The message length" 83 | * @return [void] 84 | */ 85 | void WSS_ssl_write(wss_session_t *session, char *message, unsigned int message_length); 86 | 87 | /** 88 | * Function initializes SSL session instance that can be used to serve over https. 89 | * 90 | * @param server [wss_server_t *] "The server instance" 91 | * @param session [wss_session_t *] "The session instance" 92 | * @return [bool] "Whether function was successful" 93 | */ 94 | bool WSS_session_ssl(wss_server_t *server, wss_session_t *session); 95 | 96 | /** 97 | * Function frees SSL session instance that was used to serve over https. 98 | * 99 | * @param session [wss_session_t *] "The session instance" 100 | * @param lock [pthread_rwlock_t *] "The read/write session lock" 101 | * @return [wss_error_t] "An error or success" 102 | */ 103 | wss_error_t WSS_session_ssl_free(wss_session_t *session, pthread_rwlock_t *lock); 104 | 105 | /** 106 | * Function creates a sha1 hash of the key. 107 | * 108 | * @param key [char *] "The key to be hashed" 109 | * @param key_length [pthread_rwlock_t *] "The length of the key" 110 | * @param hash [char **] "A pointer to where the hash should be stored" 111 | * @return [size_t] "The length of the hash" 112 | */ 113 | size_t WSS_sha1(char *key, size_t key_length, char **hash); 114 | 115 | /** 116 | * Function creates a sha1 hash of the key and base64 encodes it. 117 | * 118 | * @param key [char *] "The key to be hashed" 119 | * @param key_length [pthread_rwlock_t *] "The length of the key" 120 | * @param accept_key [char **] "A pointer to where the hash should be stored" 121 | * @return [size_t] "Length of accept_key" 122 | */ 123 | size_t WSS_base64_encode_sha1(char *key, size_t key_length, char **accept_key); 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /include/str.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_string_h 2 | #define wss_string_h 3 | 4 | #include 5 | 6 | /** 7 | * Function that converts a binary representation into a hexidecimal one. 8 | * 9 | * @param bin [const unsigned char *] "The binary value" 10 | * @param len [size_t] "The length of the binary value" 11 | * @return [char *] "The hexidecimal representation of the binary value in a new memory block" 12 | */ 13 | char *bin2hex(const unsigned char *bin, size_t len); 14 | 15 | /** 16 | * Function that looks for a value in a char * array. 17 | * 18 | * A value is said to be found if the prefix of the needle is contained in the 19 | * whole array value. 20 | * 21 | * E.g 22 | * 23 | * haystack[0] = "testing" 24 | * needle = "testing123" 25 | * 26 | * will return 0, since all of the string in haystack[0] is present in needle. 27 | * 28 | * @param needle [const char *] "The value to look for" 29 | * @param haystack [const char **] "The array to look in" 30 | * @param size [size_t] "The amount of values in the array" 31 | * @return [int] "Will return 0 if present and -1 if not" 32 | */ 33 | int strinarray(const char *needle, const char *haystack[], size_t size); 34 | 35 | /** 36 | * Function that loads the content of a file into memory. 37 | * 38 | * @param path [char *] "The path to the subprotocols folder" 39 | * @param str [char **] "Will be filled with the content of the file" 40 | * @return [size_t] "The length of the string in memory" 41 | */ 42 | size_t strload(char *path, char **str); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /include/subprotocols.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_subprotocols_h 2 | #define wss_subprotocols_h 3 | 4 | #if !defined(uthash_malloc) || !defined(uthash_free) 5 | #include "alloc.h" 6 | 7 | #ifndef uthash_malloc 8 | #define uthash_malloc(sz) WSS_malloc(sz) /* malloc fcn */ 9 | #endif 10 | 11 | #ifndef uthash_free 12 | #define uthash_free(ptr,sz) WSS_free((void **)&ptr) /* free fcn */ 13 | #endif 14 | #endif 15 | 16 | #include "uthash.h" 17 | #include "subprotocol.h" 18 | #include "message.h" 19 | #include "config.h" 20 | 21 | typedef void (*pyInit)(void); 22 | 23 | typedef struct { 24 | int *handle; 25 | char *name; 26 | subAlloc alloc; 27 | subInit init; 28 | subConnect connect; 29 | subMessage message; 30 | subWrite write; 31 | subClose close; 32 | subDestroy destroy; 33 | pyInit pyinit; 34 | UT_hash_handle hh; 35 | } wss_subprotocol_t; 36 | 37 | /** 38 | * Function that loads subprotocol implementations into memory, by loading the 39 | * shared objects defined in the configuration. 40 | * 41 | * E.g. 42 | * 43 | * subprotocols/echo/echo.so 44 | * 45 | * @param config [wss_config_t *config] "The configuration of the server" 46 | * @return [void] 47 | */ 48 | void WSS_load_subprotocols(wss_config_t *config); 49 | 50 | /** 51 | * Function that looks for a subprotocol implementation of the name given. 52 | * 53 | * @param name [char *] "The name of the subprotocol" 54 | * @return [wss_subprotocol_t *] "The subprotocol or NULL" 55 | */ 56 | wss_subprotocol_t *WSS_find_subprotocol(char *name); 57 | 58 | /** 59 | * Destroys all memory used to load and store the subprotocols 60 | * 61 | * @return [void] 62 | */ 63 | void WSS_destroy_subprotocols(); 64 | 65 | /** 66 | * Global hashtable of subprotocols 67 | */ 68 | extern wss_subprotocol_t *subprotocols; 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /include/utf8.h: -------------------------------------------------------------------------------- 1 | #ifndef WSS_UTF8_H 2 | #define WSS_UTF8_H 3 | 4 | #include 5 | #include 6 | 7 | bool utf8_check(const char *src, size_t len); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /include/worker.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_comm_h 2 | #define wss_comm_h 3 | 4 | #if !defined(uthash_malloc) || !defined(uthash_free) 5 | #include "alloc.h" 6 | 7 | #ifndef uthash_malloc 8 | #define uthash_malloc(sz) WSS_malloc(sz) /* malloc fcn */ 9 | #endif 10 | 11 | #ifndef uthash_free 12 | #define uthash_free(ptr,sz) WSS_free((void **)&ptr) /* free fcn */ 13 | #endif 14 | #endif 15 | 16 | #include "server.h" 17 | #include "session.h" 18 | #include "uthash.h" 19 | 20 | #define MAGIC_WEBSOCKET_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 21 | #define GMT_FORMAT_LENGTH 30 22 | #define HTTP_STATUS_LINE "%s %d %s\r\n" 23 | #define HTTP_ICO_HEADERS "Content-Type: image/x-icon\r\n"\ 24 | "Content-Length: %d\r\n"\ 25 | "Connection: close\r\n"\ 26 | "Date: %s\r\n"\ 27 | "Server: WSServer/%s\r\n"\ 28 | "Etag: %s\r\n"\ 29 | "Accept-Ranges: bytes\r\n"\ 30 | "Last-Modified: %s\r\n"\ 31 | "\r\n" 32 | #define HTTP_HTML_HEADERS "Content-Type: text/html; charset=utf-8\r\n"\ 33 | "Content-Length: %d\r\n"\ 34 | "Connection: Closed\r\n"\ 35 | "Date: %s\r\n"\ 36 | "Server: WSServer/%s\r\n"\ 37 | "\r\n" 38 | #define HTTP_WS_VERSION_HEADER "Sec-WebSocket-Version: %d, %d, %d\r\n" 39 | #define HTTP_UPGRADE_HEADERS "Content-Type: text/plain\r\n"\ 40 | "Content-Length: %d\r\n"\ 41 | "Connection: Upgrade\r\n"\ 42 | "Upgrade: websocket\r\n"\ 43 | "Sec-WebSocket-Version: 13\r\n"\ 44 | "Server: WSServer/%s\r\n"\ 45 | "\r\n" 46 | #define HTTP_HANDSHAKE_EXTENSIONS "Sec-Websocket-Extensions: " 47 | #define HTTP_HANDSHAKE_SUBPROTOCOL "Sec-Websocket-Protocol: " 48 | #define HTTP_HANDSHAKE_ACCEPT "Sec-Websocket-Accept: " 49 | #define HTTP_HANDSHAKE_HEADERS "Upgrade: websocket\r\n"\ 50 | "Connection: Upgrade\r\n"\ 51 | "Server: WSServer/%s\r\n"\ 52 | "\r\n" 53 | #define HTTP_BODY ""\ 54 | ""\ 55 | ""\ 56 | ""\ 57 | ""\ 58 | "%d %s"\ 59 | ""\ 60 | ""\ 61 | "

%s

"\ 62 | "

%s

"\ 63 | ""\ 64 | "" 65 | 66 | /** 67 | * Structure used to find receivers 68 | */ 69 | typedef struct { 70 | int fd; 71 | UT_hash_handle hh; 72 | } wss_receiver_t; 73 | 74 | /** 75 | * Function that disconnects a session and freeing any allocated memory used by 76 | * the session. 77 | * 78 | * @param server [wss_server_t *] "The server structure" 79 | * @param session [wss_session_t *] "The session structure" 80 | * @return [void] 81 | */ 82 | void WSS_disconnect(wss_server_t *server, wss_session_t *session); 83 | 84 | /** 85 | * Function that handles new connections. This function creates a new session and 86 | * associates the sessions filedescriptor to the epoll instance such that we can 87 | * start communicating with the session. 88 | * 89 | * @param server [wss_server_t *] "The server structure" 90 | * @param session [wss_session_t *] "The session structure" 91 | * @return [void] 92 | */ 93 | //void WSS_connect(void *arg); 94 | 95 | /** 96 | * Function that reads information from a session. 97 | * 98 | * @param server [wss_server_t *] "The server structure" 99 | * @param session [wss_session_t *] "The session structure" 100 | * @return [void] 101 | */ 102 | //void WSS_read(void *args); 103 | 104 | /** 105 | * Function that writes information to a session 106 | * 107 | * @param server [wss_server_t *] "The server structure" 108 | * @param session [wss_session_t *] "The session structure" 109 | * @return [void] 110 | */ 111 | void WSS_write(wss_server_t *server, wss_session_t *session); 112 | 113 | /** 114 | * Function that performs and distributes the IO work. 115 | * 116 | * @param args [void *] "Is a args_t structure holding server_t, filedescriptor, and the state" 117 | * @return [void] 118 | */ 119 | void WSS_work(void *args); 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /resources/dhparam.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DH PARAMETERS----- 2 | MIIBCAKCAQEA2Z/vD597VJSoAUxBiXlVhOyhG6grtIOMRxBDes00tgtHn/QdIbLM 3 | hpTPX08K6xL5BuFLvjVpmJMvMva5DtGdkDHMDedrf1eRm9H+33jUtdj4RTNNNIaG 4 | aJcrSY/igyRpVO0qmFaKOum2BwKniMl/OLYgx5mwp58HXSk17fVDmsvjc191Qudf 5 | iN3g5xOUp920Iu578cZTbYqTHUy8MtXqSdcQG7CGvprdbwKxCX8MIXlAlTzJuE9P 6 | JuhlFUtoKErLbkFSIVLiiZAXS1Yf3AeTAw6dxaC8v7N0MmgRLgwtQwlSKEQ2gKE4 7 | kmScHly47zmL4WJ7gi2ZK4YVKDZVMMlVUwIBAg== 8 | -----END DH PARAMETERS----- 9 | -------------------------------------------------------------------------------- /resources/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/favicon.ico -------------------------------------------------------------------------------- /resources/logo.doxygen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/logo.doxygen.png -------------------------------------------------------------------------------- /resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/logo.png -------------------------------------------------------------------------------- /resources/rootCA.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFQTCCAymgAwIBAgIUUtMNtqv8eNR27/VA7b4XyiwkhzowDQYJKoZIhvcNAQEL 3 | BQAwMDELMAkGA1UEBhMCREsxDjAMBgNVBAgMBVNhYnJvMREwDwYDVQQKDAhXU1Nl 4 | cnZlcjAeFw0yMDA5MjEwNjIyNTBaFw0yMzA3MTIwNjIyNTBaMDAxCzAJBgNVBAYT 5 | AkRLMQ4wDAYDVQQIDAVTYWJybzERMA8GA1UECgwIV1NTZXJ2ZXIwggIiMA0GCSqG 6 | SIb3DQEBAQUAA4ICDwAwggIKAoICAQC2HIkqCZBkOGgPkKNgNIV5wdI74v7+DUhz 7 | m/p+Hmrj3axapAczefZug9HqygyZfF1ULxQAesgCMtsZ+Hc0JlnbTO4q6xBD92De 8 | G/zUXe/LpIHgkUo/x4KlfmASCbRzrfaoLC4v/DFBxhGzqktwR5aQH8jggAMcyX4o 9 | wuq+kCL0bCgQFmUnd180Bxt+TnH8SdhHSiCz2iZa63zRfOIxGOPBgyPAODW9ttpv 10 | FLf55B/ba4n+8O0RzoJ32RV79nxiCRW4JDXK2wP9vtK1xyMYdwvomO69OKEew8ns 11 | ONbgc4jbgTz5I3Zx9+2+4GrA53Gu7xcGLOZgnGrI2ub1tGKLQW4jFc1NM/Y+3zVO 12 | TPuWJmrpKAjFJc0OJY8wgDli54Fr+BFqYIBtHzRAa7+AAq03SkSPcttul8y1I5V8 13 | bNfnrE9YwZQZ6aWs/3GjJm8FbQ0sAzNUvAYNNK/y7/QK9T+zEA1OZ3d06z5/hl4S 14 | /4qgXBigMIAXS3r98mhaInhif+tvClNQMzesj0upp9aVw5xzdpgwANaqj9r6njlx 15 | HCU91jdfWO0BYyToZ4S3PbCZikGld+zrOSVypj6xEEuXowFjaheLkQOApy4MPnTs 16 | 1TQIbGiUJTWs9du8r+eTpI9K2wiTsfQ6E8keD2q5zj4WfYJcTqykuZ5HLSmwclET 17 | xwwyx5zBMwIDAQABo1MwUTAdBgNVHQ4EFgQUkpm2ZSjW8ptMuj4/qqn+/1mCWGQw 18 | HwYDVR0jBBgwFoAUkpm2ZSjW8ptMuj4/qqn+/1mCWGQwDwYDVR0TAQH/BAUwAwEB 19 | /zANBgkqhkiG9w0BAQsFAAOCAgEAH2zb7049R+hoaS+mHs2KD4y0Gc+ygsKfWvjN 20 | /C6oG3FkZ+CApAgiIc1VkHyX5SfztO936WuusvL8FBmP45bX7jTLM51VEKmUXYSE 21 | trr5UwQdjQCxdAu7h5+WYJrNzFYoXAVdqDppLo8cMWBdOnMLwau31ebixipf5PqJ 22 | x2EbSEOv/K6PcIS5jX3tLIrgpSIISqJk/tmGVqj6oXIUqKMD+xKA9kr1kGYWZWqP 23 | vrFs3+tdrOskhxrOm1sWULYmz+WtRhoBqfMPKFQSbo8xHTzBkXJfYzhPztJzt+Mi 24 | WGG8yPjfHf3TGpvWoWFJ7iu7T3LS9Q1o9Z57c0zbo/Eg+v/seykATnW73Md54k4M 25 | 8EweEVc/myMTzsqXsl+aTkrhLtv18iPzNDSv8xuYdARIU+Sypkvgi20ZVkq7QpxL 26 | QQFTVMKr1zUS/W+6vWKnxtJ5IfLIsrGlK5wIwP0LInkL5+5vTDTXv/x/8LDuZsh3 27 | l+6Xpmnv7nfZfjJzke1ICWWfz/KgVKq1/eiZpVDRY2tI1vz/3TU+GYt7JUDrPO3k 28 | 6hT0FcVettAsVItc9lGBzMmmrPQ8A9c5NFxrdDlWQC2oDPOAgh4mQK2P9jmXd9eg 29 | Gyja78WT6FY5Vdtja9f9+mSmNlnRJtr5KYkbHH+ryL+ZKt0+P5ywJtVo/STV6pMT 30 | eUbcyOI= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /resources/test-ext-no-close.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-ext-no-close.so -------------------------------------------------------------------------------- /resources/test-ext-no-destroy.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-ext-no-destroy.so -------------------------------------------------------------------------------- /resources/test-ext-no-in-frame.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-ext-no-in-frame.so -------------------------------------------------------------------------------- /resources/test-ext-no-in-frames.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-ext-no-in-frames.so -------------------------------------------------------------------------------- /resources/test-ext-no-init.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-ext-no-init.so -------------------------------------------------------------------------------- /resources/test-ext-no-open.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-ext-no-open.so -------------------------------------------------------------------------------- /resources/test-ext-no-out-frame.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-ext-no-out-frame.so -------------------------------------------------------------------------------- /resources/test-ext-no-out-frames.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-ext-no-out-frames.so -------------------------------------------------------------------------------- /resources/test-ext-no-set-allocators.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-ext-no-set-allocators.so -------------------------------------------------------------------------------- /resources/test-sub-no-close.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-sub-no-close.so -------------------------------------------------------------------------------- /resources/test-sub-no-connect.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-sub-no-connect.so -------------------------------------------------------------------------------- /resources/test-sub-no-destroy.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-sub-no-destroy.so -------------------------------------------------------------------------------- /resources/test-sub-no-init.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-sub-no-init.so -------------------------------------------------------------------------------- /resources/test-sub-no-message.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-sub-no-message.so -------------------------------------------------------------------------------- /resources/test-sub-no-set-allocators.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-sub-no-set-allocators.so -------------------------------------------------------------------------------- /resources/test-sub-no-write.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortzdk/Websocket/de767149899e9be366951f2260a69411f1f45841/resources/test-sub-no-write.so -------------------------------------------------------------------------------- /resources/test.txt: -------------------------------------------------------------------------------- 1 | This is a file, used for testing. 2 | -------------------------------------------------------------------------------- /resources/test_invalid_extension2_wss.json: -------------------------------------------------------------------------------- 1 | { 2 | "setup" : { 3 | "extensions" : [ 4 | 1 5 | ] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /resources/test_invalid_extension_wss.json: -------------------------------------------------------------------------------- 1 | { 2 | "setup" : { 3 | "extensions" : [ 4 | { 5 | "file" : 1 6 | } 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /resources/test_invalid_host_wss.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts" : [ 3 | 1 4 | ] 5 | } 6 | 7 | -------------------------------------------------------------------------------- /resources/test_invalid_origin_wss.json: -------------------------------------------------------------------------------- 1 | { 2 | "origins" : [ 3 | 1 4 | ] 5 | } 6 | 7 | -------------------------------------------------------------------------------- /resources/test_invalid_path_wss.json: -------------------------------------------------------------------------------- 1 | { 2 | "paths" : [ 3 | 1 4 | ] 5 | } 6 | 7 | -------------------------------------------------------------------------------- /resources/test_invalid_query_wss.json: -------------------------------------------------------------------------------- 1 | { 2 | "queries" : [ 3 | 1 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /resources/test_invalid_subprotocol2_wss.json: -------------------------------------------------------------------------------- 1 | { 2 | "setup" : { 3 | "subprotocols" : [ 4 | 1 5 | ] 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /resources/test_invalid_subprotocol_wss.json: -------------------------------------------------------------------------------- 1 | { 2 | "setup" : { 3 | "subprotocols" : [ 4 | { 5 | "file" : 1, 6 | "config" : "" 7 | } 8 | ] 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /resources/test_invalid_wss.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts" : [ 3 | "localhost", 4 | "127.0.0.1" 5 | ], 6 | "origins" : [ 7 | "localhost", 8 | "127.0.0.1" 9 | ], 10 | "setup" : { 11 | "favicon" : "favicon.ico", 12 | "timeout" : 1000, 13 | "port" : { 14 | "http" : 9010, 15 | "https" : 9011 16 | }, 17 | "size" : { 18 | "payload" : 16777215, 19 | "header" : 8192, 20 | "uri" : 8192, 21 | "buffer" : 25600, 22 | "thread" : 524288, 23 | "queue" : 1024, 24 | "pipe" : 128 25 | }, 26 | "pool" : { 27 | "size" : 8192, 28 | "queues" : 32, 29 | "workers" : 4 30 | }, 31 | "ssl" : { 32 | "key" : "key.pem", 33 | "cert" : "cert.pem", 34 | "ca" : "root.pem" 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /resources/test_no_port_wss.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts" : [ 3 | "localhost", 4 | "127.0.0.1" 5 | ], 6 | "origins" : [ 7 | "localhost", 8 | "127.0.0.1" 9 | ], 10 | "paths" : [ 11 | "test/path", 12 | "another/test/path" 13 | ], 14 | "queries" : [ 15 | "csrf_token=[^&]*", 16 | "access_token=[^&]*" 17 | ], 18 | "setup" : { 19 | "subprotocols" : [ 20 | { 21 | "file" : "subprotocols/echo/echo.so", 22 | "config" : "" 23 | }, 24 | { 25 | "file" : "subprotocols/broadcast/broadcast.so", 26 | "config" : "" 27 | } 28 | ], 29 | "extensions" : [ 30 | { 31 | "file" : "extensions/permessage-deflate/permessage-deflate.so", 32 | "config" : "server_max_window_bits=10;client_max_window_bits=10;memory_level=8" 33 | } 34 | ], 35 | "log_level": 7, 36 | "favicon" : "favicon.ico", 37 | "timeouts" : { 38 | "poll" : 10, 39 | "read" : 10, 40 | "write" : 10, 41 | "client" : 600, 42 | "pings" : 1 43 | }, 44 | "size" : { 45 | "payload" : 1024, 46 | "header" : 1024, 47 | "uri" : 128, 48 | "buffer" : 25600, 49 | "thread" : 524288, 50 | "ringbuffer" : 128, 51 | "frame" : 16777215, 52 | "fragmented" : 1048576 53 | }, 54 | "pool" : { 55 | "workers" : 4 56 | }, 57 | "ssl" : { 58 | "key" : "key.pem", 59 | "cert" : "cert.pem", 60 | "ca_file" : "root.pem", 61 | "ca_path" : "/usr/lib/ssl/certs/", 62 | "dhparam" : "dhparam.pem", 63 | "cipher_list" : "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256", 64 | "cipher_suites": "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256", 65 | "compression" : false, 66 | "peer_cert" : false 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /resources/test_no_starting_object_wss.json: -------------------------------------------------------------------------------- 1 | [1, 2, 3] 2 | -------------------------------------------------------------------------------- /resources/test_subprotocol_and_extension_with_invalid_config_wss.json: -------------------------------------------------------------------------------- 1 | { 2 | "setup" : { 3 | "subprotocols" : [ 4 | { 5 | "file" : "subprotocols/echo/echo.so", 6 | "config" : 1 7 | }, 8 | { 9 | "file" : "subprotocols/broadcast/broadcast.so", 10 | "config" : "" 11 | } 12 | ], 13 | "extensions" : [ 14 | { 15 | "file" : "extensions/permessage-deflate/permessage-deflate.so", 16 | "config" : 1 17 | } 18 | ], 19 | "port" : { 20 | "http" : 9010, 21 | "https" : 9011 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /resources/test_wss.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts" : [ 3 | "localhost", 4 | "127.0.0.1" 5 | ], 6 | "origins" : [ 7 | "localhost", 8 | "127.0.0.1" 9 | ], 10 | "paths" : [ 11 | "test/path", 12 | "another/test/path" 13 | ], 14 | "queries" : [ 15 | "csrf_token=[^&]*", 16 | "access_token=[^&]*" 17 | ], 18 | "setup" : { 19 | "subprotocols" : [ 20 | { 21 | "file" : "subprotocols/echo/echo.so", 22 | "config" : "" 23 | }, 24 | { 25 | "file" : "subprotocols/broadcast/broadcast.so", 26 | "config" : "" 27 | } 28 | ], 29 | "extensions" : [ 30 | { 31 | "file" : "extensions/permessage-deflate/permessage-deflate.so", 32 | "config" : "server_max_window_bits=10;client_max_window_bits=10;memory_level=8" 33 | } 34 | ], 35 | "log_level": 7, 36 | "favicon" : "favicon.ico", 37 | "timeouts" : { 38 | "poll" : 10, 39 | "read" : 10, 40 | "write" : 10, 41 | "client" : 600, 42 | "pings" : 1 43 | }, 44 | "port" : { 45 | "http" : 9010, 46 | "https" : 9011 47 | }, 48 | "size" : { 49 | "payload" : 1024, 50 | "header" : 1024, 51 | "uri" : 128, 52 | "buffer" : 25600, 53 | "thread" : 524288, 54 | "ringbuffer" : 128, 55 | "frame" : 128, 56 | "fragmented" : 1048576 57 | }, 58 | "pool" : { 59 | "workers" : 4, 60 | "retries" : 5 61 | }, 62 | "ssl" : { 63 | "key" : "key.pem", 64 | "cert" : "cert.pem", 65 | "ca_file" : "root.pem", 66 | "ca_path" : "/usr/lib/ssl/certs/", 67 | "dhparam" : "dhparam.pem", 68 | "cipher_list" : "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256", 69 | "cipher_suites": "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256", 70 | "compression" : false, 71 | "peer_cert" : false 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /resources/test_wss_empty_arrays.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts" : [ 3 | ], 4 | "origins" : [ 5 | ], 6 | "paths" : [ 7 | ], 8 | "queries" : [ 9 | ], 10 | "setup" : { 11 | "subprotocols" : [ 12 | ], 13 | "extensions" : [ 14 | ], 15 | "log_level": 7, 16 | "favicon" : "favicon.ico", 17 | "timeouts" : { 18 | "poll" : -5, 19 | "read" : -4, 20 | "write" : -3, 21 | "client" : -2, 22 | "pings" : 0 23 | }, 24 | "port" : { 25 | "http" : 9010, 26 | "https" : 9011 27 | }, 28 | "size" : { 29 | "payload" : 1024, 30 | "header" : 1024, 31 | "uri" : 128, 32 | "buffer" : 25600, 33 | "thread" : 524288, 34 | "ringbuffer" : 128, 35 | "frame" : 16777215, 36 | "fragmented" : 1048576 37 | }, 38 | "pool" : { 39 | "workers" : 4, 40 | "retries" : 5 41 | }, 42 | "ssl" : { 43 | "key" : "key.pem", 44 | "cert" : "cert.pem", 45 | "ca_file" : "root.pem", 46 | "ca_path" : "/usr/lib/ssl/certs/", 47 | "dhparam" : "dhparam.pem", 48 | "cipher_list" : "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256", 49 | "cipher_suites": "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256", 50 | "compression" : false, 51 | "peer_cert" : false 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /resources/wsserver.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEJjCCAg6gAwIBAgIUN9qL8s8akUy6TxJFST6nNwMjyDIwDQYJKoZIhvcNAQEL 3 | BQAwMDELMAkGA1UEBhMCREsxDjAMBgNVBAgMBVNhYnJvMREwDwYDVQQKDAhXU1Nl 4 | cnZlcjAeFw0yMDA5MjEwNjI0MTNaFw0yMjAyMDMwNjI0MTNaMEMxCzAJBgNVBAYT 5 | AkRLMQ4wDAYDVQQIDAVTYWJybzERMA8GA1UECgwIV1NTZXJ2ZXIxETAPBgNVBAMM 6 | CHdzc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuWWiWpiM 7 | SFnstNY9JfCLCLYWzKSNN/tU4EHP9dU0i9CZLXUwMJ5ycRat0GAfPDVndOi9zQJq 8 | wJ6KR8cDmpwmJ/zsnXjB1z7wy7ombckMLwjvRBuvEaF8Fc0J/IIwCWE694Jc1Kvv 9 | Eze+cDDVvy4Clpa4qNC7nrYyvpx/qS6Hmwt06/548zna3b2mguQGHGAcFYN+td5C 10 | EQMEvDXGl0RJkVkq4qFabhPle5x592EAE+KUyfex+ZWQ7q4WK/fBbm/GMsomCPye 11 | TyAjeEk46dwl9hEUaPcnpPzcuLL5AfWHtR38lwp89pu6zw5VDpfU2YhS6D8/SyOO 12 | gWV5g8HZBrqZ0QIDAQABoyUwIzAhBgNVHREEGjAYggh3c3NlcnZlcoIMd3d3Lndz 13 | c2VydmVyMA0GCSqGSIb3DQEBCwUAA4ICAQBBQWCKte2rhkUSgruwcsLPR6wUHMAH 14 | THdvUvD0GmzY7SuUX2lSMSQf+yUx5LDE6Uycya/m9uJ6lC0HVM8DJCTaYzRAD+vn 15 | Fps1I6AjytB1C9W+/gM5eInOt3FpisCwjvMGebV4UbQAVnGsJbdN/TCeWLN2gga+ 16 | s3g2RtqcJn4cOe4moyk6Yj16oQRStGMExClnfnKql05wpQrvcyPpqxjXUVqyzfuU 17 | n2dBbh+VmYXyEy10Axf+VY0UbSSQnxNwYyKYZEd7nS4iOx33befnUifTj7NV1jy8 18 | 9q1g/xqe8YoJAIdd5SskARKJUBJgElWjNb1Kxu/+3ymC9Tm1elipzMrlBKgsc0ya 19 | tYD1wzb7+amzUP4tepE8U8r9RmXTZ9PxDVTeFzyxu+98caCB16Df1gNFBBuTGKKl 20 | j72vcyvI+Feh6sRmr8gI41nUoezwMjVqdSzfuik+CbL3ayU1dEmWVXn9Lz8z5+bR 21 | lFvze4ZQ8CbvWNZ1njDojQQEuzLWMJPly936RqvVjt0gAD1r++ZIGnLOv6wUf1Ql 22 | fYo3IyzXe0c8x1YS3QPGx2s8VfEjDOub0QNkIRvRDMAlRE/aXKR8N1t/FGdisV+/ 23 | TxIesPXSlGoXv79dw6oY23WIe5plnWgVIJhlD7+cUFXfWzttUnvSbK9dgmeLX/v6 24 | mGnbNJovqphFLg== 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /resources/wsserver.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAuWWiWpiMSFnstNY9JfCLCLYWzKSNN/tU4EHP9dU0i9CZLXUw 3 | MJ5ycRat0GAfPDVndOi9zQJqwJ6KR8cDmpwmJ/zsnXjB1z7wy7ombckMLwjvRBuv 4 | EaF8Fc0J/IIwCWE694Jc1KvvEze+cDDVvy4Clpa4qNC7nrYyvpx/qS6Hmwt06/54 5 | 8zna3b2mguQGHGAcFYN+td5CEQMEvDXGl0RJkVkq4qFabhPle5x592EAE+KUyfex 6 | +ZWQ7q4WK/fBbm/GMsomCPyeTyAjeEk46dwl9hEUaPcnpPzcuLL5AfWHtR38lwp8 7 | 9pu6zw5VDpfU2YhS6D8/SyOOgWV5g8HZBrqZ0QIDAQABAoIBABFQFGExbagz/Y7F 8 | kuBAHjuqBWcG1skrhzmJ68mhJ08HI8bC8sUXhshYvdJ/p2sP8aFROqgyWZWzOGaL 9 | hyKhAk8pp07tP+zBy+q6b2xBIgoiY3WQwEB9PAEtLImP/rxhgAgs/E4/4AuDYR1I 10 | 8izwNv2pBYV+OIcRrJtwMduypKctueATBL3ROt+YkdCiOt+7lWDmbp5Qk6HbSOmn 11 | PDSRkodbIwd+Jk7IfGJOtn5gQfunVhAXCdm6YsvmFFK1ygesob9tLLiuN1GCkY8R 12 | 2jdXu83wuuH5J+s7SKfjeHIR8xDt62iJZdFvMqW0IHpG7MoKkkdsyR1ApG9ywhIt 13 | tszX3YECgYEA7QfknJHq7v9mEQbDZCPu1Sf3dWkCOdB8d0Aecpx+FzVFLzk4BZ/r 14 | hTxyKFjrL7kUcQfbTfmx3DxpkQUGzzky2k9JR0mhwsJoMbeIUDTdBj5ToK19ZWEG 15 | c69TCxXzrNkjy8clGOYRhxd4ZnHFLiBF+ccmdzCCspbHseC6FpPxbl0CgYEAyDvo 16 | IfNL4XXKTn2hqD8dvN5UK+d/QV+xRVSY7GqjGONe177HdQ8g4aLCNkzcMuj45uCv 17 | N3XXIoe7TuhGanMz05/zenHuGXg5/btBsRtQpQCAg1jsIHNY93X11ZiU4cQtNY/H 18 | V5B/UqI4Tw5KfdtrVAazB0QM4Jj/bmp8LGwmGgUCgYA7B2kueMRaKq5/dQC58pzq 19 | 5p4g1jrgrQQiKoyqnEBmhTSqkwRZmj7sGmaPl71SUDRABeXeoF1j7IOZwve0KZl5 20 | dp9YKoUnXITYRM2ks9WLVYA8FpiQ473Yl4QF0byygg0BFaIudjVNQFISqSii2RS0 21 | JJ12t1UFJrmqmb2eG8/XXQKBgEExkCENsBUWXuozaWD4O9x0nv80fnBwka07IBdJ 22 | mRTPYt/n09U/Obt9XLKFT+Sbif7SBPqPuZ4ah1fntJWQML2khIfHdHiCdo8wWvEp 23 | LZarlSwu1aKzK7JWi9YhucfQMdAQohzWhT4IZ0EGLCbSPB29mKiy9WZjKjdvIELu 24 | a3FpAoGAC1Ii3HdavzOJ4/m+1OKboGjfMDmXnwy/VftvKeg+0hgfVrQGIX8319El 25 | wIzSSxyO1CsLF+sqi3yD48TtkzJ3MrCojvksmLcBrgBtfYk+tWxLWenMaf0SGKpz 26 | bc4RWa9TxlYYDYitmM5XxzKdH4UPcn1wxlM7dmzIzIpATUeRToo= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /scripts/bump.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # bump.sh 4 | # Copyright (C) 2019 Morten Houmøller Nygaard 5 | # 6 | # Distributed under terms of the MIT license. 7 | # 8 | 9 | # Function that checks whether command is available and installed 10 | function is_installed() 11 | { 12 | if ! [ -x "$(command -v $1)" ]; then 13 | echo "ERROR: '$1' is not installed." >&2 14 | exit 1 15 | fi 16 | } 17 | 18 | is_installed "git"; 19 | is_installed "sed"; 20 | is_installed "find"; 21 | 22 | # Accepts a version string and prints it incremented by one. 23 | # Usage: increment_version [] [] 24 | increment_version() { 25 | local usage=" USAGE: $FUNCNAME [-l] [-t] [] [] 26 | -l : remove leading zeros 27 | -t : drop trailing zeros 28 | : The version string. 29 | : Optional. The position (starting with one) of the number 30 | within to increment. If the position does not 31 | exist, it will be created. Defaults to last position. 32 | : The leftmost position that can be incremented. If does not 33 | exist, position will be created. This right-padding will 34 | occur even to right of , unless passed the -t flag." 35 | 36 | # Get flags. 37 | local flag_remove_leading_zeros=0 38 | local flag_drop_trailing_zeros=0 39 | while [ "${1:0:1}" == "-" ]; do 40 | if [ "$1" == "--" ]; then shift; break 41 | elif [ "$1" == "-l" ]; then flag_remove_leading_zeros=1 42 | elif [ "$1" == "-t" ]; then flag_drop_trailing_zeros=1 43 | else echo -e "Invalid flag: ${1}\n$usage"; return 1; fi 44 | shift; done 45 | 46 | # Get arguments. 47 | if [ ${#@} -lt 1 ]; then echo "$usage"; return 1; fi 48 | local v="${1}" # version string 49 | local targetPos=${2-last} # target position 50 | local minPos=${3-${2-0}} # minimum position 51 | 52 | # Split version string into array using its periods. 53 | local IFSbak; IFSbak=IFS; IFS='.' # IFS restored at end of func to 54 | read -ra v <<< "$v" # avoid breaking other scripts. 55 | 56 | # Determine target position. 57 | if [ "${targetPos}" == "last" ]; then 58 | if [ "${minPos}" == "last" ]; then minPos=0; fi 59 | targetPos=$((${#v[@]}>${minPos}?${#v[@]}:$minPos)); fi 60 | if [[ ! ${targetPos} -gt 0 ]]; then 61 | echo -e "Invalid position: '$targetPos'\n$usage"; return 1; fi 62 | (( targetPos-- )) || true # offset to match array index 63 | 64 | # Make sure minPosition exists. 65 | while [ ${#v[@]} -lt ${minPos} ]; do v+=("0"); done; 66 | 67 | # Increment target position. 68 | v[$targetPos]=`printf %0${#v[$targetPos]}d $((10#${v[$targetPos]}+1))`; 69 | 70 | # Remove leading zeros, if -l flag passed. 71 | if [ $flag_remove_leading_zeros == 1 ]; then 72 | for (( pos=0; $pos<${#v[@]}; pos++ )); do 73 | v[$pos]=$((${v[$pos]}*1)); done; fi 74 | 75 | # If targetPosition was not at end of array, reset following positions to 76 | # zero (or remove them if -t flag was passed). 77 | if [[ ${flag_drop_trailing_zeros} -eq "1" ]]; then 78 | for (( p=$((${#v[@]}-1)); $p>$targetPos; p-- )); do unset v[$p]; done 79 | else for (( p=$((${#v[@]}-1)); $p>$targetPos; p-- )); do v[$p]=0; done; fi 80 | 81 | echo "${v[*]}" 82 | IFS=IFSbak 83 | return 0 84 | } 85 | 86 | POSITIONAL=() 87 | while [[ $# -gt 0 ]] 88 | do 89 | key="$1" 90 | 91 | case $key in 92 | --major) 93 | position=1 94 | shift # past argument 95 | shift # past value 96 | ;; 97 | --minor) 98 | position=2 99 | shift # past argument 100 | shift # past value 101 | ;; 102 | --patch) 103 | position=3 104 | shift # past argument 105 | shift # past value 106 | ;; 107 | --default) 108 | position=3 109 | shift # past argument 110 | ;; 111 | *) # unknown option 112 | POSITIONAL+=("$1") # save it in an array for later 113 | shift # past argument 114 | ;; 115 | esac 116 | done 117 | set -- "${POSITIONAL[@]}" # restore positional parameters 118 | 119 | last_version=`git describe --abbrev=0 --tags` 120 | new_version=`increment_version ${last_version#?} $position` 121 | new_version="v${new_version}" 122 | 123 | sed -i "/Current Version: /{s/\*\*$last_version\*\*/\*\*$new_version\*\*/g}" README.md 124 | sed -i "/PROJECT_NUMBER\s*=/{s/$last_version/$new_version/g}" conf/doxyfile.conf 125 | 126 | echo "$new_version" 127 | -------------------------------------------------------------------------------- /scripts/travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e # Exit with nonzero exit code if anything fails 3 | REPO=`git config remote.origin.url` 4 | git clone -b gh-pages $REPO generated 5 | -------------------------------------------------------------------------------- /src/alloc.c: -------------------------------------------------------------------------------- 1 | #include /* atoi, malloc, free, realloc */ 2 | #include /* strerror, memset, strncpy, memcpy, strlen */ 3 | 4 | #include "alloc.h" 5 | #include "predict.h" 6 | 7 | /** 8 | * Function that allocates memory. 9 | * @param ptr [void *] "The memory that needs to be copies" 10 | * @param void [size_t] "The size of the memory that should be copied" 11 | * @return [void *] "Returns pointer to allocated memory if successful, otherwise NULL" 12 | */ 13 | void *WSS_copy(void *ptr, size_t size) { 14 | void *buffer; 15 | 16 | if ( unlikely(ptr == NULL) ) { 17 | return NULL; 18 | } 19 | 20 | if ( unlikely(NULL == (buffer = WSS_malloc(size))) ) { 21 | return NULL; 22 | } 23 | 24 | return memcpy(buffer, ptr, size); 25 | } 26 | 27 | /** 28 | * Function that allocates memory. * 29 | * @param size [size_t] "The size of the memory that should be allocated" 30 | * @return [void *] "Returns pointer to allocated memory if successful, otherwise NULL" 31 | */ 32 | void *WSS_malloc(size_t size) { 33 | void *buffer; 34 | 35 | if ( unlikely(size == 0) ) { 36 | return NULL; 37 | } 38 | 39 | #ifdef USE_RPMALLOC 40 | if ( unlikely(NULL == (buffer = rpmalloc( size ))) ) { 41 | return NULL; 42 | } 43 | #else 44 | if ( unlikely(NULL == (buffer = malloc( size ))) ) { 45 | return NULL; 46 | } 47 | #endif 48 | 49 | 50 | memset(buffer, '\0', size); 51 | 52 | return buffer; 53 | } 54 | 55 | /** 56 | * Function that allocates an array of memory. 57 | * 58 | * @param nmemb [size_t] "The size of the array i.e. indexes" 59 | * @param size [size_t] "The size of the memory that should be allocated" 60 | * @return [void *] "Returns pointer to allocated memory if successful, otherwise NULL" 61 | */ 62 | void *WSS_calloc(size_t memb, size_t size) { 63 | void *buffer; 64 | 65 | if ( unlikely(size == 0 || memb == 0) ) { 66 | return NULL; 67 | } 68 | 69 | #ifdef USE_RPMALLOC 70 | if ( unlikely(NULL == (buffer = rpcalloc(memb, size))) ) { 71 | return NULL; 72 | } 73 | #else 74 | if ( unlikely(NULL == (buffer = calloc(memb, size))) ) { 75 | return NULL; 76 | } 77 | #endif 78 | 79 | 80 | memset(buffer, '\0', size*memb); 81 | 82 | return buffer; 83 | } 84 | 85 | /** 86 | * Function that re-allocates some memory. 87 | * 88 | * @param ptr [void **] "The memory that needs to be rearranged" 89 | * @param oldSize [size_t] "The size of the memory that is already allocated" 90 | * @param newSize [size_t] "The size of the memory that should be allocated" 91 | * @return [void *] "Returns pointer to allocated memory if successful, otherwise NULL" 92 | */ 93 | void *WSS_realloc(void **ptr, size_t oldSize, size_t newSize) { 94 | void *buffer; 95 | 96 | if ( unlikely(newSize == 0) ) { 97 | WSS_free(ptr); 98 | return NULL; 99 | } 100 | 101 | if ( unlikely(ptr == NULL) ) { 102 | #ifdef USE_RPMALLOC 103 | if ( unlikely(NULL == (buffer = rprealloc(NULL, newSize))) ) { 104 | return NULL; 105 | } 106 | #else 107 | if ( unlikely(NULL == (buffer = realloc(NULL, newSize))) ) { 108 | return NULL; 109 | } 110 | #endif 111 | } else { 112 | #ifdef USE_RPMALLOC 113 | if ( unlikely(NULL == (buffer = rprealloc(*ptr, newSize))) ) { 114 | WSS_free(ptr); 115 | return NULL; 116 | } 117 | #else 118 | if ( unlikely(NULL == (buffer = realloc(*ptr, newSize))) ) { 119 | WSS_free(ptr); 120 | return NULL; 121 | } 122 | #endif 123 | } 124 | 125 | 126 | if ( likely(oldSize < newSize) ) { 127 | memset(((char *) buffer)+oldSize, '\0', (newSize-oldSize)); 128 | } 129 | 130 | return buffer; 131 | } 132 | 133 | /** 134 | * Function that re-allocates some memory. The interface is of this function is 135 | * similar to the realloc(3) function. 136 | * 137 | * @param ptr [void *] "The memory that needs to be rearranged" 138 | * @param newSize [size_t] "The size of the memory that should be allocated" 139 | * @return [void *] "Returns pointer to allocated memory if successful, otherwise NULL" 140 | */ 141 | void *WSS_realloc_normal(void *ptr, size_t newSize) { 142 | return WSS_realloc(&ptr, newSize, newSize); 143 | } 144 | 145 | #ifndef NDEBUG 146 | /** 147 | * Function that re-allocates some memory. 148 | * 149 | * @param ptr [void **] "The memory that needs to be freed" 150 | * @return [void] 151 | */ 152 | void WSS_free(void **ptr) { 153 | if ( likely(NULL != *ptr) ) { 154 | #ifdef USE_RPMALLOC 155 | rpfree(*ptr); 156 | #else 157 | free(*ptr); 158 | #endif 159 | *ptr = NULL; 160 | } 161 | } 162 | #endif 163 | 164 | /** 165 | * Function that re-allocates some memory. The interface is of this function is 166 | * similar to the free(3) function. 167 | * 168 | * @param ptr [void *] "The memory that needs to be freed" 169 | * @return [void] 170 | */ 171 | void WSS_free_normal(void *ptr) { 172 | WSS_free(&ptr); 173 | } 174 | -------------------------------------------------------------------------------- /src/b64.c: -------------------------------------------------------------------------------- 1 | /** 2 | * `encode.c' - b64 3 | * 4 | * copyright (c) 2014 joseph werle 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "b64.h" 11 | 12 | char * 13 | b64_encode (const unsigned char *src, size_t len) { 14 | int i = 0; 15 | int j = 0; 16 | char *enc = NULL; 17 | size_t size = 0; 18 | unsigned char buf[4]; 19 | unsigned char tmp[3]; 20 | 21 | // alloc 22 | enc = (char *) b64_malloc(1); 23 | if (NULL == enc) { return NULL; } 24 | 25 | // parse until end of source 26 | while (len--) { 27 | // read up to 3 bytes at a time into `tmp' 28 | tmp[i++] = *(src++); 29 | 30 | // if 3 bytes read then encode into `buf' 31 | if (3 == i) { 32 | buf[0] = (tmp[0] & 0xfc) >> 2; 33 | buf[1] = ((tmp[0] & 0x03) << 4) + ((tmp[1] & 0xf0) >> 4); 34 | buf[2] = ((tmp[1] & 0x0f) << 2) + ((tmp[2] & 0xc0) >> 6); 35 | buf[3] = tmp[2] & 0x3f; 36 | 37 | // allocate 4 new byts for `enc` and 38 | // then translate each encoded buffer 39 | // part by index from the base 64 index table 40 | // into `enc' unsigned char array 41 | enc = (char *) b64_realloc(enc, size + 4); 42 | for (i = 0; i < 4; ++i) { 43 | enc[size++] = b64_table[buf[i]]; 44 | } 45 | 46 | // reset index 47 | i = 0; 48 | } 49 | } 50 | 51 | // remainder 52 | if (i > 0) { 53 | // fill `tmp' with `\0' at most 3 times 54 | for (j = i; j < 3; ++j) { 55 | tmp[j] = '\0'; 56 | } 57 | 58 | // perform same codec as above 59 | buf[0] = (tmp[0] & 0xfc) >> 2; 60 | buf[1] = ((tmp[0] & 0x03) << 4) + ((tmp[1] & 0xf0) >> 4); 61 | buf[2] = ((tmp[1] & 0x0f) << 2) + ((tmp[2] & 0xc0) >> 6); 62 | buf[3] = tmp[2] & 0x3f; 63 | 64 | // perform same write to `enc` with new allocation 65 | for (j = 0; (j < i + 1); ++j) { 66 | enc = (char *) b64_realloc(enc, size + 1); 67 | enc[size++] = b64_table[buf[j]]; 68 | } 69 | 70 | // while there is still a remainder 71 | // append `=' to `enc' 72 | while ((i++ < 3)) { 73 | enc = (char *) b64_realloc(enc, size + 1); 74 | enc[size++] = '='; 75 | } 76 | } 77 | 78 | // Make sure we have enough space to add '\0' character at end. 79 | enc = (char *) b64_realloc(enc, size + 1); 80 | enc[size] = '\0'; 81 | 82 | return enc; 83 | } 84 | 85 | unsigned char *b64_decode_ex (const char *src, size_t len, size_t *decsize) { 86 | int i = 0; 87 | int j = 0; 88 | int l = 0; 89 | size_t size = 0; 90 | unsigned char *dec = NULL; 91 | unsigned char buf[3]; 92 | unsigned char tmp[4]; 93 | 94 | // alloc 95 | dec = (unsigned char *) b64_malloc(1); 96 | if (NULL == dec) { return NULL; } 97 | 98 | // parse until end of source 99 | while (len--) { 100 | // break if char is `=' or not base64 char 101 | if ('=' == src[j]) { break; } 102 | if (!(isalnum(src[j]) || '+' == src[j] || '/' == src[j])) { break; } 103 | 104 | // read up to 4 bytes at a time into `tmp' 105 | tmp[i++] = src[j++]; 106 | 107 | // if 4 bytes read then decode into `buf' 108 | if (4 == i) { 109 | // translate values in `tmp' from table 110 | for (i = 0; i < 4; ++i) { 111 | // find translation char in `b64_table' 112 | for (l = 0; l < 64; ++l) { 113 | if (tmp[i] == b64_table[l]) { 114 | tmp[i] = l; 115 | break; 116 | } 117 | } 118 | } 119 | 120 | // decode 121 | buf[0] = (tmp[0] << 2) + ((tmp[1] & 0x30) >> 4); 122 | buf[1] = ((tmp[1] & 0xf) << 4) + ((tmp[2] & 0x3c) >> 2); 123 | buf[2] = ((tmp[2] & 0x3) << 6) + tmp[3]; 124 | 125 | // write decoded buffer to `dec' 126 | dec = (unsigned char *) b64_realloc(dec, size + 3); 127 | if (dec != NULL){ 128 | for (i = 0; i < 3; ++i) { 129 | dec[size++] = buf[i]; 130 | } 131 | } else { 132 | return NULL; 133 | } 134 | 135 | // reset 136 | i = 0; 137 | } 138 | } 139 | 140 | // remainder 141 | if (i > 0) { 142 | // fill `tmp' with `\0' at most 4 times 143 | for (j = i; j < 4; ++j) { 144 | tmp[j] = '\0'; 145 | } 146 | 147 | // translate remainder 148 | for (j = 0; j < 4; ++j) { 149 | // find translation char in `b64_table' 150 | for (l = 0; l < 64; ++l) { 151 | if (tmp[j] == b64_table[l]) { 152 | tmp[j] = l; 153 | break; 154 | } 155 | } 156 | } 157 | 158 | // decode remainder 159 | buf[0] = (tmp[0] << 2) + ((tmp[1] & 0x30) >> 4); 160 | buf[1] = ((tmp[1] & 0xf) << 4) + ((tmp[2] & 0x3c) >> 2); 161 | buf[2] = ((tmp[2] & 0x3) << 6) + tmp[3]; 162 | 163 | // write remainer decoded buffer to `dec' 164 | dec = (unsigned char *) b64_realloc(dec, size + (i - 1)); 165 | if (dec != NULL){ 166 | for (j = 0; (j < i - 1); ++j) { 167 | dec[size++] = buf[j]; 168 | } 169 | } else { 170 | return NULL; 171 | } 172 | } 173 | 174 | // Make sure we have enough space to add '\0' character at end. 175 | dec = (unsigned char *) b64_realloc(dec, size + 1); 176 | if (dec != NULL){ 177 | dec[size] = '\0'; 178 | } else { 179 | return NULL; 180 | } 181 | 182 | // Return back the size of decoded string if demanded. 183 | if (decsize != NULL) { 184 | *decsize = size; 185 | } 186 | 187 | return dec; 188 | } 189 | -------------------------------------------------------------------------------- /src/extensions.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "extensions.h" 6 | #include "uthash.h" 7 | #include "alloc.h" 8 | #include "log.h" 9 | #include "predict.h" 10 | 11 | /** 12 | * Global hashtable of extensions 13 | */ 14 | wss_extension_t *extensions = NULL; 15 | 16 | /** 17 | * Function that loads extension implementations into memory, by loading 18 | * shared objects from the server configuration. 19 | * 20 | * E.g. 21 | * 22 | * extensions/permessage-deflate/permessage-deflate.so 23 | * 24 | * @param config [config_t *] "The configuration of the server" 25 | * @return [void] 26 | */ 27 | void WSS_load_extensions(wss_config_t *config) 28 | { 29 | size_t i, j; 30 | char *name; 31 | char *pyname; 32 | int *handle; 33 | wss_extension_t* proto; 34 | int name_length; 35 | 36 | if ( unlikely(NULL == config) ) { 37 | return; 38 | } 39 | 40 | WSS_log_trace("Loading extensions"); 41 | 42 | for (i = 0; i < config->extensions_length; i++) { 43 | name_length = 0; 44 | 45 | WSS_log_trace("Loading extension %s", config->extensions[i]); 46 | 47 | if ( unlikely(NULL == (handle = dlopen(config->extensions[i], RTLD_LAZY))) ) { 48 | WSS_log_error("Failed to load shared object: %s", dlerror()); 49 | continue; 50 | } 51 | 52 | if ( unlikely(NULL == (proto = WSS_malloc(sizeof(wss_extension_t)))) ) { 53 | WSS_log_error("Unable to allocate extension structure"); 54 | dlclose(handle); 55 | return; 56 | } 57 | proto->handle = handle; 58 | 59 | if ( unlikely((*(void**)(&proto->alloc) = dlsym(proto->handle, "setAllocators")) == NULL) ) { 60 | WSS_log_error("Failed to find 'setAllocators' function: %s", dlerror()); 61 | dlclose(proto->handle); 62 | WSS_free((void **) &proto); 63 | continue; 64 | } 65 | 66 | if ( unlikely((*(void**)(&proto->init) = dlsym(proto->handle, "onInit")) == NULL) ) { 67 | WSS_log_error("Failed to find 'onInit' function: %s", dlerror()); 68 | dlclose(proto->handle); 69 | WSS_free((void **) &proto); 70 | continue; 71 | } 72 | 73 | if ( unlikely((*(void**)(&proto->open) = dlsym(proto->handle, "onOpen")) == NULL) ) { 74 | WSS_log_error("Failed to find 'onOpen' function: %s", dlerror()); 75 | dlclose(proto->handle); 76 | WSS_free((void **) &proto); 77 | continue; 78 | } 79 | 80 | if ( unlikely((*(void**)(&proto->inframe) = dlsym(proto->handle, "inFrame")) == NULL) ) { 81 | WSS_log_error("Failed to find 'inFrame' function: %s", dlerror()); 82 | dlclose(proto->handle); 83 | WSS_free((void **) &proto); 84 | continue; 85 | } 86 | 87 | if ( unlikely((*(void**)(&proto->inframes) = dlsym(proto->handle, "inFrames")) == NULL) ) { 88 | WSS_log_error("Failed to find 'inFrames' function: %s", dlerror()); 89 | dlclose(proto->handle); 90 | WSS_free((void **) &proto); 91 | continue; 92 | } 93 | 94 | if ( unlikely((*(void**)(&proto->outframe) = dlsym(proto->handle, "outFrame")) == NULL) ) { 95 | WSS_log_error("Failed to find 'outFrame' function: %s", dlerror()); 96 | dlclose(proto->handle); 97 | WSS_free((void **) &proto); 98 | continue; 99 | } 100 | 101 | if ( unlikely((*(void**)(&proto->outframes) = dlsym(proto->handle, "outFrames")) == NULL) ) { 102 | WSS_log_error("Failed to find 'outFrames' function: %s", dlerror()); 103 | dlclose(proto->handle); 104 | WSS_free((void **) &proto); 105 | continue; 106 | } 107 | 108 | if ( unlikely((*(void**)(&proto->close) = dlsym(proto->handle, "onClose")) == NULL) ) { 109 | WSS_log_error("Failed to find 'onClose' function: %s", dlerror()); 110 | dlclose(proto->handle); 111 | WSS_free((void **) &proto); 112 | continue; 113 | } 114 | 115 | if ( unlikely((*(void**)(&proto->destroy) = dlsym(proto->handle, "onDestroy")) == NULL) ) { 116 | WSS_log_error("Failed to find 'onDestroy' function: %s", dlerror()); 117 | dlclose(proto->handle); 118 | WSS_free((void **) &proto); 119 | continue; 120 | } 121 | 122 | name = basename(config->extensions[i]); 123 | for (j = 0; name[j] != '.' && name[j] != '\0'; j++) { 124 | name_length++; 125 | } 126 | 127 | if ( unlikely(NULL == (proto->name = WSS_malloc(name_length+1))) ) { 128 | WSS_log_error("Unable to allocate name"); 129 | dlclose(proto->handle); 130 | WSS_free((void **) &proto); 131 | return; 132 | } 133 | 134 | // Call Cython init if available 135 | if ( unlikely(NULL == (pyname = WSS_malloc(7+name_length+1))) ) { 136 | WSS_log_error("Unable to allocate name"); 137 | dlclose(proto->handle); 138 | WSS_free((void **) &proto->name); 139 | WSS_free((void **) &proto); 140 | return; 141 | } 142 | 143 | sprintf(pyname, "PyInit_%s", name); 144 | if ( unlikely((*(void**)(&proto->pyinit) = dlsym(proto->handle, name)) != NULL) ) { 145 | proto->pyinit(); 146 | } 147 | WSS_free((void **) &pyname); 148 | 149 | memcpy(proto->name, name, name_length); 150 | 151 | HASH_ADD_KEYPTR(hh, extensions, proto->name, name_length, proto); 152 | 153 | WSS_log_trace("Setting custom allocators for extension %s", proto->name); 154 | 155 | proto->alloc(WSS_malloc, WSS_realloc_normal, WSS_free_normal); 156 | 157 | WSS_log_trace("Initializing extension %s", proto->name); 158 | 159 | proto->init(config->extensions_config[i]); 160 | 161 | WSS_log_info("Successfully loaded %s extension", proto->name); 162 | } 163 | } 164 | 165 | /** 166 | * Function that looks for a extension implementation of the name given. 167 | * 168 | * @param name [char *] "The name of the extension" 169 | * @return [wss_extension_t *] "The extension or NULL" 170 | */ 171 | wss_extension_t *WSS_find_extension(char *name) { 172 | wss_extension_t *proto; 173 | 174 | if ( unlikely(NULL == name) ) { 175 | return NULL; 176 | } 177 | 178 | HASH_FIND_STR(extensions, name, proto); 179 | 180 | return proto; 181 | } 182 | 183 | /** 184 | * Destroys all memory used to load and store the extensions 185 | * 186 | * @return [void] 187 | */ 188 | void WSS_destroy_extensions() { 189 | wss_extension_t *proto, *tmp; 190 | 191 | HASH_ITER(hh, extensions, proto, tmp) { 192 | HASH_DEL(extensions, proto); 193 | proto->destroy(); 194 | dlclose(proto->handle); 195 | WSS_free((void **) &proto->name); 196 | WSS_free((void **) &proto); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 rxi 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "log.h" 32 | #include "predict.h" 33 | 34 | static struct { 35 | void *udata; 36 | log_LockFn lock; 37 | FILE *fp; 38 | int level; 39 | int quiet; 40 | } L; 41 | 42 | static int year; 43 | static int month; 44 | static int day; 45 | static int hour; 46 | static int minutes; 47 | static int seconds; 48 | static int usec; 49 | 50 | static struct timeval tv; 51 | static struct tm *tm; 52 | 53 | static const char *level_names[] = { 54 | "FATAL", 55 | "ERROR", 56 | "WARN", 57 | "INFO", 58 | "DEBUG", 59 | "TRACE" 60 | }; 61 | 62 | static const char *level_colors[] = { 63 | "\x1b[35m", 64 | "\x1b[31m", 65 | "\x1b[33m", 66 | "\x1b[32m", 67 | "\x1b[36m", 68 | "\x1b[94m" 69 | }; 70 | 71 | static inline void time_to_str(char *buf) { 72 | gettimeofday(&tv, NULL); 73 | tm = localtime(&tv.tv_sec); 74 | /* Add 1900 to get the right year value read the manual page for localtime() */ 75 | year = tm->tm_year + 1900; 76 | /* Months are 0 indexed in struct tm */ 77 | month = tm->tm_mon + 1; 78 | day = tm->tm_mday; 79 | hour = tm->tm_hour; 80 | minutes = tm->tm_min; 81 | seconds = tm->tm_sec; 82 | usec = tv.tv_usec; 83 | // buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; 84 | int len = sprintf(buf, 85 | "%04d-%02d-%02d %02d:%02d:%02d.%06d ", 86 | year, 87 | month, 88 | day, 89 | hour, 90 | minutes, 91 | seconds, 92 | usec); 93 | buf[len] = '\0'; 94 | } 95 | 96 | static inline void lock(void) { 97 | if ( likely(L.lock) ) { 98 | L.lock(L.udata, 1); 99 | } 100 | } 101 | 102 | static inline void unlock(void) { 103 | if ( likely(L.lock) ) { 104 | L.lock(L.udata, 0); 105 | } 106 | } 107 | 108 | void log_set_udata(void *udata) { 109 | L.udata = udata; 110 | } 111 | 112 | void log_set_lock(log_LockFn fn) { 113 | L.lock = fn; 114 | } 115 | 116 | void log_set_fp(FILE *fp) { 117 | L.fp = fp; 118 | } 119 | 120 | void log_set_level(int level) { 121 | L.level = level; 122 | } 123 | 124 | void log_set_quiet(int enable) { 125 | L.quiet = enable ? 1 : 0; 126 | } 127 | 128 | void log_log(int level, const char *file, int line, const char *fmt, ...) { 129 | if ( likely(level > L.level) ) { 130 | return; 131 | } else if ( unlikely(L.quiet && !L.fp) ) { 132 | return; 133 | } 134 | 135 | char time_string[100]; 136 | 137 | /* Acquire lock */ 138 | lock(); 139 | 140 | time_to_str(time_string); 141 | 142 | /* Log to stderr */ 143 | if ( unlikely(!L.quiet) ) { 144 | va_list args; 145 | 146 | fprintf( 147 | stderr, "[%lu] %s %s %-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", 148 | pthread_self(), time_string, level_colors[level], level_names[level], file, line); 149 | va_start(args, fmt); 150 | vfprintf(stderr, fmt, args); 151 | va_end(args); 152 | fprintf(stderr, "\n"); 153 | fflush(stderr); 154 | } 155 | 156 | /* Log to file */ 157 | if ( likely(L.fp) ) { 158 | va_list args; 159 | fprintf(L.fp, "[%lu] %s %-5s %s:%d: ", pthread_self(), time_string, level_names[level], file, line); 160 | va_start(args, fmt); 161 | vfprintf(L.fp, fmt, args); 162 | va_end(args); 163 | fprintf(L.fp, "\n"); 164 | fflush(L.fp); 165 | } 166 | 167 | /* Release lock */ 168 | unlock(); 169 | } 170 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include /* fprintf */ 2 | #include /* NULL */ 3 | #include /* getopt_long, struct option */ 4 | #include /* strerror */ 5 | #include /* errno */ 6 | 7 | #include "server.h" /* server_start */ 8 | #include "config.h" /* config_t, WSS_config_load(), WSS_config_free() */ 9 | #include "error.h" /* wss_error_t */ 10 | #include "subprotocols.h" /* wss_subprotocol_t */ 11 | #include "alloc.h" /* WSS_malloc() */ 12 | #include "log.h" /* WSS_log_*() */ 13 | #include "predict.h" 14 | 15 | static void log_mutex(void *udata, int lock) { 16 | int err; 17 | pthread_mutex_t *l = (pthread_mutex_t *) udata; 18 | if (lock) { 19 | if ( unlikely((err = pthread_mutex_lock(l)) != 0) ) { 20 | WSS_log_error("Unable to lock log lock: %s", strerror(err)); 21 | } 22 | } else { 23 | if ( unlikely((err = pthread_mutex_unlock(l)) != 0) ) { 24 | WSS_log_error("Unable to unlock log lock: %s", strerror(err)); 25 | } 26 | } 27 | } 28 | 29 | static inline void wss_help(FILE *stream) { 30 | fprintf( 31 | stream, 32 | "Usage: ./WSServer [OPTIONS]\n\n" 33 | "OPTIONS:\n" 34 | "\t-h | --help show server arguments\n" 35 | "\t-c CONFIG_PATH | --config CONFIG_PATH provide JSON configuration file\n" 36 | ); 37 | } 38 | 39 | int main(int argc, char *argv[]) { 40 | int err, res; 41 | wss_config_t config; 42 | FILE *file; 43 | pthread_mutex_t log_lock; 44 | char *echo = "subprotocols/echo/echo.so", *broadcast = "subprotocols/broadcast/broadcast.so"; 45 | 46 | #ifdef USE_RPMALLOC 47 | rpmalloc_initialize(); 48 | #endif 49 | 50 | static struct option long_options[] = { 51 | {"help" , no_argument, 0, 'h'}, 52 | {"config" , required_argument, 0, 'c'}, 53 | {0 , 0 , 0, 0 } 54 | }; 55 | 56 | // Default values 57 | config.subprotocols = NULL; 58 | config.subprotocols_length = 0; 59 | config.extensions = NULL; 60 | config.extensions_length = 0; 61 | config.favicon = NULL; 62 | config.origins = NULL; 63 | config.origins_length = 0; 64 | config.hosts = NULL; 65 | config.hosts_length = 0; 66 | config.paths = NULL; 67 | config.paths_length = 0; 68 | config.queries = NULL; 69 | config.queries_length = 0; 70 | config.string = NULL; 71 | config.data = NULL; 72 | config.length = 0; 73 | config.port_http = 80; 74 | config.port_https = 443; 75 | #ifdef NDEBUG 76 | config.log = WSS_LOG_INFO; 77 | #else 78 | config.log = WSS_LOG_TRACE; 79 | #endif 80 | config.size_header = 8192; 81 | config.size_payload = 16777215; 82 | config.size_uri = 8192; 83 | config.size_ringbuffer = 128; 84 | config.size_buffer = 32768; 85 | config.size_thread = 2097152; 86 | config.size_frame = 1048576; 87 | config.max_frames = 1048576; 88 | config.pool_workers = 4; 89 | config.pool_retries = 5; 90 | config.timeout_pings = 1; // Times that a client will be pinged before timeout occurs 91 | config.timeout_poll = -1; // Infinite 92 | config.timeout_read = 1000; // 1 Second 93 | config.timeout_write = 1000; // 1 Second 94 | config.timeout_client = 60000; // 1 Hour 95 | config.ssl_key = NULL; 96 | config.ssl_cert = NULL; 97 | config.ssl_dhparam = NULL; 98 | config.ssl_ca_file = NULL; 99 | config.ssl_ca_path = NULL; 100 | config.ssl_cipher_list = NULL; 101 | config.ssl_cipher_suites = NULL; 102 | config.ssl_compression = false; 103 | config.ssl_peer_cert = false; 104 | 105 | if ( NULL == (file = fopen("log/WSServer.log", "a+")) ) { 106 | WSS_config_free(&config); 107 | fprintf(stderr, "%s\n", strerror(errno)); 108 | #ifdef USE_RPMALLOC 109 | rpmalloc_finalize(); 110 | #endif 111 | return EXIT_FAILURE; 112 | } 113 | 114 | // Set file to write log to 115 | log_set_fp(file); 116 | 117 | // Set log level to default value until configuration is loaded 118 | log_set_level(config.log); 119 | 120 | if ( unlikely((err = pthread_mutex_init(&log_lock, NULL)) != 0) ) { 121 | WSS_config_free(&config); 122 | WSS_log_error("Unable to initialize log lock: %s", strerror(err)); 123 | fclose(file); 124 | #ifdef USE_RPMALLOC 125 | rpmalloc_finalize(); 126 | #endif 127 | return EXIT_FAILURE; 128 | } 129 | 130 | // Set lock to use in interal lock function 131 | log_set_udata(&log_lock); 132 | 133 | // Set lock function for logging functions 134 | log_set_lock(log_mutex); 135 | 136 | while ( likely((err = getopt_long(argc, argv, "c:h", long_options, NULL)) != -1)) { 137 | switch (err) { 138 | case 'c': 139 | if ( unlikely(WSS_SUCCESS != WSS_config_load(&config, optarg)) ) { 140 | WSS_config_free(&config); 141 | 142 | if ( unlikely((err = pthread_mutex_destroy(&log_lock)) != 0) ) { 143 | WSS_log_error("Unable to initialize log lock: %s", strerror(err)); 144 | } 145 | 146 | fclose(file); 147 | #ifdef USE_RPMALLOC 148 | rpmalloc_finalize(); 149 | #endif 150 | return EXIT_FAILURE; 151 | } 152 | break; 153 | case 'h': 154 | wss_help(stdout); 155 | WSS_config_free(&config); 156 | if ( unlikely((err = pthread_mutex_destroy(&log_lock)) != 0) ) { 157 | WSS_log_error("Unable to initialize log lock: %s", strerror(err)); 158 | fclose(file); 159 | #ifdef USE_RPMALLOC 160 | rpmalloc_finalize(); 161 | #endif 162 | return EXIT_FAILURE; 163 | } 164 | fclose(file); 165 | #ifdef USE_RPMALLOC 166 | rpmalloc_finalize(); 167 | #endif 168 | return EXIT_SUCCESS; 169 | default: 170 | wss_help(stderr); 171 | WSS_config_free(&config); 172 | if ( unlikely((err = pthread_mutex_destroy(&log_lock)) != 0) ) { 173 | WSS_log_error("Unable to initialize log lock: %s", strerror(err)); 174 | } 175 | fclose(file); 176 | #ifdef USE_RPMALLOC 177 | rpmalloc_finalize(); 178 | #endif 179 | return EXIT_FAILURE; 180 | } 181 | } 182 | 183 | // If in production mode do not print to stdout 184 | #ifdef NDEBUG 185 | WSS_log_info("Log is in quiet mode"); 186 | log_set_quiet(1); 187 | #endif 188 | 189 | // Set log level to what what specified in the configuration 190 | log_set_level(config.log); 191 | 192 | // Setting echo and broadcast protocols as default if none was loaded 193 | if ( config.subprotocols_length == 0 && config.subprotocols == NULL) { 194 | if ( NULL != (config.subprotocols = WSS_calloc(2, sizeof(char *)))) { 195 | if ( NULL != (config.subprotocols_config = WSS_calloc(2, sizeof(char *)))) { 196 | config.subprotocols[0] = echo; 197 | config.subprotocols[1] = broadcast; 198 | config.subprotocols_config[0] = NULL; 199 | config.subprotocols_config[1] = NULL; 200 | config.subprotocols_length = 2; 201 | } 202 | } 203 | } 204 | 205 | res = WSS_server_start(&config); 206 | 207 | if ( unlikely((err = pthread_mutex_destroy(&log_lock)) != 0) ) { 208 | WSS_log_error("Unable to initialize log lock: %s", strerror(err)); 209 | res = EXIT_FAILURE; 210 | } 211 | 212 | if ( unlikely(WSS_SUCCESS != WSS_config_free(&config)) ) { 213 | res = EXIT_FAILURE; 214 | } 215 | 216 | fclose(file); 217 | 218 | #ifdef USE_RPMALLOC 219 | rpmalloc_finalize(); 220 | #endif 221 | 222 | return res; 223 | } 224 | -------------------------------------------------------------------------------- /src/message.c: -------------------------------------------------------------------------------- 1 | #include "message.h" 2 | #include "server.h" 3 | #include "worker.h" 4 | #include "ringbuf.h" 5 | #include "session.h" 6 | #include "event.h" 7 | #include "str.h" 8 | #include "log.h" 9 | #include "alloc.h" 10 | #include "error.h" 11 | #include "predict.h" 12 | 13 | #include 14 | 15 | void WSS_message_send_frames(void *serv, void *sess, wss_frame_t **frames, size_t frames_count) { 16 | size_t j, k; 17 | ssize_t off; 18 | char *out; 19 | uint64_t out_length; 20 | wss_message_t *m; 21 | ringbuf_worker_t *w = NULL; 22 | struct timespec tim; 23 | wss_server_t *server = (wss_server_t *)serv; 24 | wss_session_t *session = (wss_session_t *)sess; 25 | 26 | // Use extensions 27 | if ( NULL != session->header->ws_extensions ) { 28 | for (j = 0; likely(j < session->header->ws_extensions_count); j++) { 29 | session->header->ws_extensions[j]->ext->outframes( 30 | session->fd, 31 | frames, 32 | frames_count); 33 | 34 | for (k = 0; likely(k < frames_count); k++) { 35 | session->header->ws_extensions[j]->ext->outframe(session->fd, frames[k]); 36 | } 37 | } 38 | } 39 | 40 | if ( unlikely((out_length = WSS_stringify_frames(frames, frames_count, &out)) == 0) ) { 41 | WSS_log_error("Unable to convert frames to message"); 42 | 43 | return; 44 | } 45 | 46 | if ( unlikely(NULL == (m = WSS_malloc(sizeof(wss_message_t)))) ) { 47 | WSS_log_error("Unable to allocate message structure"); 48 | 49 | WSS_free((void **) &out); 50 | 51 | return; 52 | } 53 | m->msg = out; 54 | m->length = out_length; 55 | m->framed = true; 56 | 57 | WSS_log_trace("Putting message into ringbuffer"); 58 | 59 | if ( unlikely(-1 == (off = ringbuf_acquire(session->ringbuf, &w, 1))) ) { 60 | WSS_log_error("Failed to acquire space in ringbuffer"); 61 | 62 | WSS_message_free(m); 63 | 64 | return; 65 | } 66 | 67 | session->messages[off] = m; 68 | ringbuf_produce(session->ringbuf, &w); 69 | tim.tv_sec = 0; 70 | tim.tv_nsec = 100000000; 71 | 72 | do { 73 | pthread_mutex_lock(&session->lock); 74 | 75 | switch (session->state) { 76 | case READING: 77 | pthread_mutex_unlock(&session->lock); 78 | nanosleep(&tim, NULL); 79 | break; 80 | case CLOSING: 81 | WSS_session_jobs_dec(session); 82 | pthread_mutex_unlock(&session->lock); 83 | return; 84 | case CONNECTING: 85 | pthread_mutex_unlock(&session->lock); 86 | nanosleep(&tim, NULL); 87 | break; 88 | case WRITING: 89 | WSS_session_jobs_dec(session); 90 | pthread_mutex_unlock(&session->lock); 91 | return; 92 | case IDLE: 93 | session->state = WRITING; 94 | WSS_write(server, session); 95 | WSS_session_jobs_dec(session); 96 | pthread_mutex_unlock(&session->lock); 97 | return; 98 | } 99 | } while (1); 100 | } 101 | 102 | void WSS_message_send(int fd, wss_opcode_t opcode, char *message, uint64_t message_length) { 103 | size_t k; 104 | size_t frames_count; 105 | wss_session_t *session; 106 | wss_frame_t **frames; 107 | wss_server_t *server = servers.http; 108 | 109 | if ( unlikely(NULL == (session = WSS_session_find(fd))) ) { 110 | WSS_log_error("Unable to find session to send message to"); 111 | return; 112 | } 113 | 114 | WSS_session_jobs_inc(session); 115 | 116 | if (NULL != session->ssl && session->ssl_connected) { 117 | server = servers.https; 118 | } 119 | 120 | WSS_log_trace("Creating frames"); 121 | 122 | frames_count = WSS_create_frames(server->config, opcode, message, message_length, &frames); 123 | 124 | WSS_message_send_frames(server, session, frames, frames_count); 125 | 126 | for (k = 0; likely(k < frames_count); k++) { 127 | WSS_free_frame(frames[k]); 128 | } 129 | WSS_free((void **) &frames); 130 | } 131 | 132 | void WSS_message_free(wss_message_t *msg) { 133 | if (NULL != msg) { 134 | if (NULL != msg->msg) { 135 | WSS_free((void **)&msg->msg); 136 | } 137 | WSS_free((void **)&msg); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/socket.c: -------------------------------------------------------------------------------- 1 | #include /* errno */ 2 | #include /* strerror, memset, strncpy, memcpy */ 3 | #include /* close */ 4 | #include /* fcntl */ 5 | 6 | #include /* socket, setsockopt, accept, send, recv */ 7 | #include /* socket, setsockopt, inet_ntoa, accept */ 8 | #include /* sockaddr_in, inet_ntoa */ 9 | #include /* htonl, htons, inet_ntoa */ 10 | 11 | #include "socket.h" 12 | #include "log.h" 13 | #include "error.h" 14 | #include "pool.h" 15 | #include "alloc.h" 16 | #include "predict.h" 17 | 18 | /** 19 | * Function that initializes a socket and store the filedescriptor. 20 | * 21 | * @param server [server_t **] "The server instance" 22 | * @return [wss_error_t] "The error status" 23 | */ 24 | wss_error_t WSS_socket_create(wss_server_t *server) { 25 | if ( unlikely(NULL == server) ) { 26 | WSS_log_fatal("No server structure given"); 27 | return WSS_SOCKET_CREATE_ERROR; 28 | } 29 | 30 | if ( unlikely((server->fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) ) { 31 | WSS_log_fatal("Unable to create server filedescriptor: %s", strerror(errno)); 32 | return WSS_SOCKET_CREATE_ERROR; 33 | } 34 | 35 | return WSS_SUCCESS; 36 | } 37 | 38 | /** 39 | * Function that enables reuse of the port if the server shuts down. 40 | * 41 | * @param fd [int] "The filedescriptor associated to some user" 42 | * @return [wss_error_t] "The error status" 43 | */ 44 | wss_error_t WSS_socket_reuse(int fd) { 45 | int reuse = 1; 46 | if ( unlikely((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) < 0) ){ 47 | WSS_log_fatal("Unable to reuse port: %s", strerror(errno)); 48 | return WSS_SOCKET_REUSE_ERROR; 49 | } 50 | return WSS_SUCCESS; 51 | } 52 | 53 | /** 54 | * Function that binds the socket to a specific port and chooses IPV6. 55 | * 56 | * @param server [server_t *] "The server instance" 57 | * @return [wss_error_t] "The error status" 58 | */ 59 | wss_error_t WSS_socket_bind(wss_server_t *server) { 60 | if ( unlikely(NULL == server) ) { 61 | WSS_log_fatal("No server structure given"); 62 | return WSS_SOCKET_BIND_ERROR; 63 | } 64 | 65 | if ( unlikely(server->port < 0 || server->port > 65535) ) { 66 | WSS_log_fatal("Server port must be within range [0, 65535]"); 67 | server->fd = -1; 68 | return WSS_SOCKET_BIND_ERROR; 69 | } 70 | 71 | /** 72 | * Setting values of our server structure 73 | */ 74 | memset((char *) &server->info, '\0', sizeof(server->info)); 75 | server->info.sin6_family = AF_INET6; 76 | server->info.sin6_port = htons(server->port); 77 | server->info.sin6_addr = in6addr_any; 78 | 79 | /** 80 | * Binding address. 81 | */ 82 | if ( unlikely((bind(server->fd, (struct sockaddr *) &server->info, 83 | sizeof(server->info))) < 0) ) { 84 | WSS_log_fatal("Unable to bind socket to port: %s", strerror(errno)); 85 | server->fd = -1; 86 | return WSS_SOCKET_BIND_ERROR; 87 | } 88 | return WSS_SUCCESS; 89 | } 90 | 91 | /** 92 | * Function that makes the socket non-blocking for both reads and writes. 93 | * 94 | * @param fd [int] "The filedescriptor associated to some user" 95 | * @return [wss_error_t] "The error status" 96 | */ 97 | wss_error_t WSS_socket_non_blocking(int fd) { 98 | int flags; 99 | 100 | if ( unlikely((flags = fcntl(fd, F_GETFL, 0)) < 0)) { 101 | WSS_log_fatal("Unable to fetch current filedescriptor flags: %s", strerror(errno)); 102 | return WSS_SOCKET_NONBLOCKED_ERROR; 103 | } 104 | 105 | flags |= O_NONBLOCK; 106 | 107 | if ( unlikely((flags = fcntl(fd, F_SETFL, flags)) < 0) ) { 108 | WSS_log_fatal("Unable to set flags for filedescriptor: %s", strerror(errno)); 109 | return WSS_SOCKET_NONBLOCKED_ERROR; 110 | } 111 | 112 | return WSS_SUCCESS; 113 | } 114 | 115 | /** 116 | * Function that makes the server start listening on the socket. 117 | * 118 | * @param fd [int] "The filedescriptor associated to some user" 119 | * @return [wss_error_t] "The error status" 120 | */ 121 | wss_error_t WSS_socket_listen(int fd) { 122 | /** 123 | * Listen on the server socket for connections 124 | */ 125 | if ( unlikely((listen(fd, SOMAXCONN)) < 0) ) { 126 | WSS_log_fatal("Unable to listen on filedescriptor: %s", strerror(errno)); 127 | return WSS_SOCKET_LISTEN_ERROR; 128 | } 129 | return WSS_SUCCESS; 130 | } 131 | 132 | /** 133 | * Function that creates a threadpool which can be used handle traffic. 134 | * 135 | * @param server [wss_server_t *] "The server instance" 136 | * @return [wss_error_t] "The error status" 137 | */ 138 | wss_error_t WSS_socket_threadpool(wss_server_t *server) { 139 | if ( unlikely(NULL == server) ) { 140 | WSS_log_fatal("No server structure given"); 141 | return WSS_THREADPOOL_CREATE_ERROR; 142 | } 143 | 144 | if ( unlikely(NULL == server->config) ) { 145 | WSS_log_fatal("No server config structure"); 146 | return WSS_THREADPOOL_CREATE_ERROR; 147 | } 148 | 149 | /** 150 | * Creating threadpool 151 | */ 152 | if ( unlikely(NULL == (server->pool = threadpool_create(server->config->pool_workers, 153 | server->config->size_thread, server->config->size_thread, 0))) ) { 154 | WSS_log_fatal("The threadpool failed to initialize"); 155 | return WSS_THREADPOOL_CREATE_ERROR; 156 | } 157 | 158 | return WSS_SUCCESS; 159 | } 160 | -------------------------------------------------------------------------------- /src/str.c: -------------------------------------------------------------------------------- 1 | #include /* errno */ 2 | #include /* printf, fflush, fprintf, fopen, fclose */ 3 | #include /* atoi, malloc, free, realloc, calloc */ 4 | #include /* strerror, memset, strncpy, memcpy, strlen, 5 | strtok, strtok_r */ 6 | #include "str.h" 7 | #include "alloc.h" 8 | #include "log.h" 9 | #include "predict.h" 10 | 11 | /** 12 | * Function that converts a binary representation into a hexidecimal one. 13 | * 14 | * @param bin [const unsigned char *] "The binary value" 15 | * @param len [size_t] "The length of the binary value" 16 | * @return [char *] "The hexidecimal representation of the binary value in a new memory block" 17 | */ 18 | char *bin2hex(const unsigned char *bin, size_t len) 19 | { 20 | char *out; 21 | size_t i; 22 | 23 | if ( unlikely(NULL == (out = (char *) WSS_malloc(len*2+1))) ) { 24 | return NULL; 25 | } 26 | 27 | for (i = 0; likely(i < len); i++) { 28 | out[i*2] = "0123456789ABCDEF"[bin[i] >> 4]; 29 | out[i*2+1] = "0123456789ABCDEF"[bin[i] & 0x0F]; 30 | } 31 | 32 | out[len*2] = '\0'; 33 | 34 | return out; 35 | } 36 | 37 | /** 38 | * Function that looks for a value in a char * array. 39 | * 40 | * A value is said to be found if the prefix of the needle is contained in the 41 | * whole array value. 42 | * 43 | * E.g 44 | * 45 | * haystack[0] = "testing" 46 | * needle = "testing123" 47 | * 48 | * will return 0, since all of the string in haystack[0] is present in needle. 49 | * 50 | * @param needle [const char *] "The value to look for" 51 | * @param haystack [const char **] "The array to look in" 52 | * @param size [size_t] "The amount of values in the array" 53 | * @return [int] "Will return 0 if present and -1 if not" 54 | */ 55 | int strinarray(const char *needle, const char **haystack, size_t size) { 56 | if ( unlikely(NULL == needle) ) { 57 | return -1; 58 | } 59 | 60 | int i; 61 | unsigned long length = strlen(needle); 62 | 63 | for (i = 0; likely(i < (int) size); i++) { 64 | if (NULL != haystack[i] && length == strlen(haystack[i]) && strncmp(needle, haystack[i], length) == 0) { 65 | return 0; 66 | } 67 | } 68 | 69 | return -1; 70 | } 71 | 72 | /** 73 | * Function that loads the content of a file into memory. 74 | * 75 | * @param path [char *] "The path to the subprotocols folder" 76 | * @param str [char **] "Will be filled with the content of the file" 77 | * @return [size_t] "The length of the string in memory" 78 | */ 79 | size_t strload(char *path, char **str) { 80 | int n; 81 | long size; 82 | FILE *file; 83 | 84 | if ( unlikely(NULL == (file = fopen(path, "rb"))) ) { 85 | WSS_log_error("Unable to open file: %s", strerror(errno)); 86 | *str = NULL; 87 | return 0; 88 | } 89 | 90 | if ( unlikely(fseek(file, 0, SEEK_END) != 0) ) { 91 | WSS_log_error("Unable to seek to the end of file: %s", strerror(errno)); 92 | fclose(file); 93 | *str = NULL; 94 | return 0; 95 | } 96 | 97 | size = ftell(file); 98 | 99 | if ( unlikely(fseek(file, 0, SEEK_SET)) ) { 100 | WSS_log_error("Unable to seek back to the start of file: %s", strerror(errno)); 101 | fclose(file); 102 | *str = NULL; 103 | return 0; 104 | } 105 | 106 | 107 | if ( unlikely(NULL == (*str = (char *)WSS_malloc(size + 1))) ) { 108 | WSS_log_error("Unable to allocate for string"); 109 | fclose(file); 110 | *str = NULL; 111 | return 0; 112 | } 113 | 114 | if ( unlikely(fread(*str, sizeof(char), size, file) != (unsigned long)size) ) { 115 | WSS_log_error("Didn't read what was expected"); 116 | fclose(file); 117 | WSS_free((void **) &str); 118 | return 0; 119 | } 120 | 121 | n = fclose(file); 122 | if ( unlikely(n != 0) ) { 123 | WSS_log_error("Unable to close file: %s", strerror(errno)); 124 | WSS_free((void **) &str); 125 | return 0; 126 | } 127 | 128 | return size; 129 | } 130 | -------------------------------------------------------------------------------- /src/subprotocols.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "subprotocols.h" 7 | #include "subprotocol.h" 8 | #include "uthash.h" 9 | #include "message.h" 10 | #include "alloc.h" 11 | #include "log.h" 12 | #include "predict.h" 13 | 14 | /** 15 | * Global hashtable of subprotocols 16 | */ 17 | wss_subprotocol_t *subprotocols = NULL; 18 | 19 | /** 20 | * Function that loads subprotocol implementations into memory, by loading the 21 | * shared objects defined in the configuration. 22 | * 23 | * E.g. 24 | * 25 | * subprotocols/echo/echo.so 26 | * subprotocols/broadcast/broadcast.so 27 | * 28 | * @param config [config_t *] "The configuration of the server" 29 | * @return [void] 30 | */ 31 | void WSS_load_subprotocols(wss_config_t *config) 32 | { 33 | size_t i, j; 34 | char *name; 35 | char *pyname; 36 | int *handle; 37 | wss_subprotocol_t* proto; 38 | int name_length; 39 | 40 | if ( unlikely(NULL == config) ) { 41 | return; 42 | } 43 | 44 | WSS_log_trace("Loading %d subprotocols", config->subprotocols_length); 45 | 46 | for (i = 0; i < config->subprotocols_length; i++) { 47 | name_length = 0; 48 | 49 | WSS_log_trace("Loading subprotocol %s", config->subprotocols[i]); 50 | 51 | if ( unlikely(NULL == (handle = dlopen(config->subprotocols[i], RTLD_LAZY))) ) { 52 | WSS_log_error("Failed to load shared object: %s", dlerror()); 53 | continue; 54 | } 55 | 56 | if ( unlikely(NULL == (proto = WSS_malloc(sizeof(wss_subprotocol_t)))) ) { 57 | WSS_log_error("Unable to allocate subprotocol structure"); 58 | dlclose(handle); 59 | return; 60 | } 61 | proto->handle = handle; 62 | 63 | if ( unlikely((*(void**)(&proto->alloc) = dlsym(proto->handle, "setAllocators")) == NULL) ) { 64 | WSS_log_error("Failed to find 'setAllocators' function: %s", dlerror()); 65 | dlclose(proto->handle); 66 | WSS_free((void **) &proto); 67 | continue; 68 | } 69 | 70 | if ( unlikely((*(void**)(&proto->init) = dlsym(proto->handle, "onInit")) == NULL) ) { 71 | WSS_log_error("Failed to find 'onInit' function: %s", dlerror()); 72 | dlclose(proto->handle); 73 | WSS_free((void **) &proto); 74 | continue; 75 | } 76 | 77 | if ( unlikely((*(void**)(&proto->connect) = dlsym(proto->handle, "onConnect")) == NULL) ) { 78 | WSS_log_error("Failed to find 'onConnect' function: %s", dlerror()); 79 | dlclose(proto->handle); 80 | WSS_free((void **) &proto); 81 | continue; 82 | } 83 | 84 | if ( unlikely((*(void**)(&proto->message) = dlsym(proto->handle, "onMessage")) == NULL) ) { 85 | WSS_log_error("Failed to find 'onMessage' function: %s", dlerror()); 86 | dlclose(proto->handle); 87 | WSS_free((void **) &proto); 88 | continue; 89 | } 90 | 91 | if ( unlikely((*(void**)(&proto->write) = dlsym(proto->handle, "onWrite")) == NULL) ) { 92 | WSS_log_error("Failed to find 'onWrite' function: %s", dlerror()); 93 | dlclose(proto->handle); 94 | WSS_free((void **) &proto); 95 | continue; 96 | } 97 | 98 | if ( unlikely((*(void**)(&proto->close) = dlsym(proto->handle, "onClose")) == NULL) ) { 99 | WSS_log_error("Failed to find 'onClose' function: %s", dlerror()); 100 | dlclose(proto->handle); 101 | WSS_free((void **) &proto); 102 | continue; 103 | } 104 | 105 | if ( unlikely((*(void**)(&proto->destroy) = dlsym(proto->handle, "onDestroy")) == NULL) ) { 106 | WSS_log_error("Failed to find 'onDestroy' function: %s", dlerror()); 107 | dlclose(proto->handle); 108 | WSS_free((void **) &proto); 109 | continue; 110 | } 111 | 112 | name = basename(config->subprotocols[i]); 113 | for (j = 0; name[j] != '.' && name[j] != '\0'; j++) { 114 | name_length++; 115 | } 116 | 117 | if ( unlikely(NULL == (proto->name = WSS_malloc(name_length+1))) ) { 118 | WSS_log_error("Unable to allocate name"); 119 | dlclose(proto->handle); 120 | WSS_free((void **) &proto); 121 | return; 122 | } 123 | 124 | // Call Cython init if available 125 | if ( unlikely(NULL == (pyname = WSS_malloc(7+name_length+1))) ) { 126 | WSS_log_error("Unable to allocate name"); 127 | dlclose(proto->handle); 128 | WSS_free((void **) &proto->name); 129 | WSS_free((void **) &proto); 130 | return; 131 | } 132 | 133 | sprintf(pyname, "PyInit_%s", name); 134 | if ( unlikely((*(void**)(&proto->pyinit) = dlsym(proto->handle, name)) != NULL) ) { 135 | proto->pyinit(); 136 | } 137 | WSS_free((void **) &pyname); 138 | 139 | memcpy(proto->name, name, name_length); 140 | 141 | HASH_ADD_KEYPTR(hh, subprotocols, proto->name, name_length, proto); 142 | 143 | WSS_log_trace("Setting custom allocators for subprotocol %s", proto->name); 144 | 145 | // Set custom allocators 146 | proto->alloc(WSS_malloc, WSS_realloc_normal, WSS_free_normal); 147 | 148 | WSS_log_trace("Initializing subprotocol %s", proto->name); 149 | 150 | // Initialize subprotocol 151 | proto->init(config->subprotocols_config[i], WSS_message_send); 152 | 153 | WSS_log_info("Successfully loaded %s extension", proto->name); 154 | } 155 | } 156 | 157 | /** 158 | * Function that looks for a subprotocol implementation of the name given. 159 | * 160 | * @param name [char *] "The name of the subprotocol" 161 | * @return [wss_subprotocol_t *] "The subprotocol or NULL" 162 | */ 163 | wss_subprotocol_t *WSS_find_subprotocol(char *name) { 164 | wss_subprotocol_t *proto; 165 | 166 | if ( unlikely(NULL == name) ) { 167 | return NULL; 168 | } 169 | 170 | HASH_FIND_STR(subprotocols, name, proto); 171 | 172 | return proto; 173 | } 174 | 175 | /** 176 | * Destroys all memory used to load and store the subprotocols 177 | * 178 | * @return [void] 179 | */ 180 | void WSS_destroy_subprotocols() { 181 | wss_subprotocol_t *proto, *tmp; 182 | 183 | HASH_ITER(hh, subprotocols, proto, tmp) { 184 | HASH_DEL(subprotocols, proto); 185 | proto->destroy(); 186 | dlclose(proto->handle); 187 | WSS_free((void **) &proto->name); 188 | WSS_free((void **) &proto); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /subprotocols/broadcast/Makefile: -------------------------------------------------------------------------------- 1 | #Shell 2 | SHELL = /bin/bash 3 | 4 | #Executeable name 5 | NAME = broadcast 6 | 7 | #Compiler 8 | CC = gcc 9 | 10 | #Debug or Release 11 | PROFILE = -Og -g -DNDEBUG 12 | DEBUG = -Og -g 13 | RELEASE = -O3 -funroll-loops -DNDEBUG 14 | SPACE = -Os -DNDEBUG 15 | EXEC = $(RELEASE) 16 | 17 | #Compiler options 18 | CFLAGS = $(EXEC) \ 19 | -fno-exceptions \ 20 | -fPIC \ 21 | -fstack-protector \ 22 | -funroll-loops \ 23 | -fvisibility=hidden \ 24 | -march=native \ 25 | -MMD \ 26 | -pedantic \ 27 | -pedantic-errors \ 28 | -pipe \ 29 | -W \ 30 | -Wall \ 31 | -Werror \ 32 | -Wformat \ 33 | -Wformat-security \ 34 | -Wformat-nonliteral \ 35 | -Winit-self \ 36 | -Winline \ 37 | -Wl,-z,relro \ 38 | -Wl,-z,now \ 39 | -Wmultichar \ 40 | -Wno-unused-parameter \ 41 | -Wno-unused-function \ 42 | -Wno-unused-label \ 43 | -Wno-deprecated \ 44 | -Wno-strict-aliasing \ 45 | -Wpointer-arith \ 46 | -Wreturn-type \ 47 | -Wsign-compare \ 48 | -Wuninitialized \ 49 | -D_DEFAULT_SOURCE 50 | 51 | CVER = -std=c11 52 | 53 | # Folders 54 | FOLDER = $(shell pwd) 55 | SUBPROTOCOLS_FOLDER = $(FOLDER)/../ 56 | 57 | # Include folders 58 | INCLUDES = -I$(SUBPROTOCOLS_FOLDER) 59 | 60 | # Files 61 | SRC = $(shell find $(FOLRDER) -name '*.c' -type f;) 62 | SRC_OBJ = $(patsubst %.c, %.o, $(SRC)) 63 | DEPS = $(SRC_OBJ:%.o=%.d) 64 | 65 | .PHONY: clean release debug profiling space 66 | 67 | #what we are trying to build 68 | all: $(NAME) 69 | 70 | release_mode: 71 | $(eval EXEC = $(RELEASE)) 72 | 73 | debug_mode: 74 | $(eval EXEC = $(DEBUG)) 75 | 76 | profiling_mode: 77 | $(eval EXEC = $(PROFILE)) 78 | 79 | space_mode: 80 | $(eval EXEC = $(SPACE)) 81 | 82 | # Recompile when headers change 83 | -include $(DEPS) 84 | 85 | #linkage 86 | $(NAME): $(SRC_OBJ) 87 | @echo 88 | @echo ================ [Creating Shared Object] ================ 89 | @echo 90 | $(CC) -shared $(CFLAGS) $(CVER) -o $(NAME).so $(SRC_OBJ) $(INCLUDES) 91 | @echo 92 | @echo ================ [$(NAME).so compiled succesfully] ================ 93 | @echo 94 | 95 | # compile every source file 96 | %.o: %.c 97 | @echo 98 | @echo ================ [Building Object] ================ 99 | @echo 100 | $(CC) $(CFLAGS) $(CVER) -c $< -o $@ $(INCLUDES) 101 | @echo 102 | @echo OK [$<] - [$@] 103 | @echo 104 | 105 | #make clean 106 | clean: 107 | @echo 108 | @echo ================ [Cleaning $(NAME)] ================ 109 | @echo 110 | rm -f *.d 111 | rm -f *.o 112 | rm -f *.so 113 | 114 | #make release 115 | release: clean release_mode all 116 | 117 | #make debug 118 | debug: clean debug_mode all 119 | 120 | #make profiling 121 | profiling: clean profiling_mode all 122 | 123 | #make space 124 | space: clean space_mode all 125 | -------------------------------------------------------------------------------- /subprotocols/broadcast/broadcast.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "broadcast.h" 5 | #include "predict.h" 6 | 7 | /** 8 | * A hashtable containing all active sessions 9 | */ 10 | wss_client_t * volatile clients = NULL; 11 | 12 | /** 13 | * Structure containing allocators 14 | */ 15 | typedef struct { 16 | void *(*malloc)(size_t); 17 | void *(*realloc)(void *, size_t); 18 | void (*free)(void *); 19 | } allocators; 20 | 21 | /** 22 | * Global allocators 23 | */ 24 | allocators allocs = { 25 | malloc, 26 | realloc, 27 | free 28 | }; 29 | 30 | WSS_send send = NULL; 31 | 32 | /** 33 | * A lock that ensures the hash table is update atomically 34 | */ 35 | pthread_rwlock_t lock; 36 | 37 | /** 38 | * Event called when subprotocol is initialized. 39 | * 40 | * @param config [char *] "The configuration of the subprotocol" 41 | * @param s [WSS_send] "Function that send message to a single recipient" 42 | * @return [void] 43 | */ 44 | void onInit(char *config, WSS_send s) { 45 | send = s; 46 | return; 47 | } 48 | 49 | /** 50 | * Sets the allocators to use instead of the default ones 51 | * 52 | * @param submalloc [WSS_malloc_t] "The malloc function" 53 | * @param subrealloc [WSS_realloc_t] "The realloc function" 54 | * @param subfree [WSS_free_t] "The free function" 55 | * @return [void] 56 | */ 57 | void setAllocators(WSS_malloc_t submalloc, WSS_realloc_t subrealloc, WSS_free_t subfree) { 58 | allocs.malloc = submalloc; 59 | allocs.realloc = subrealloc; 60 | allocs.free = subfree; 61 | } 62 | 63 | /** 64 | * Function that finds a client using the filedescriptor of the client. 65 | * 66 | * @param fd [int] "The filedescriptor associated to some client" 67 | * @return [wss_session_t *] "Returns client if successful, otherwise NULL" 68 | */ 69 | inline static wss_client_t *WSS_client_find(int fd) { 70 | wss_client_t *client = NULL; 71 | 72 | if ( unlikely(pthread_rwlock_rdlock(&lock) != 0) ) { 73 | return NULL; 74 | } 75 | 76 | HASH_FIND_INT(clients, &fd, client); 77 | 78 | pthread_rwlock_unlock(&lock); 79 | 80 | return client; 81 | } 82 | 83 | /** 84 | * Event called when a new client has handshaked and hence connects to the WSS server. 85 | * 86 | * @param fd [int] "A filedescriptor of a connecting client" 87 | * @param ip [char *] "The ip address of the connecting session" 88 | * @param port [int] "The port of the connecting session" 89 | * @param path [char *] "The connection path. This can hold HTTP parameters such as access_token, csrf_token etc. that can be used to authentication" 90 | * @param cookies [char *] "The cookies received from the client. This can be used to do authentication." 91 | * @return [void] 92 | */ 93 | void onConnect(int fd, char *ip, int port, char *path, char *cookies) { 94 | wss_client_t *client; 95 | 96 | if ( unlikely(NULL != (client = WSS_client_find(fd))) ) { 97 | return; 98 | } 99 | 100 | if ( unlikely(pthread_rwlock_wrlock(&lock) != 0) ) { 101 | return; 102 | } 103 | 104 | if ( unlikely(NULL == (client = (wss_client_t *) allocs.malloc(sizeof(wss_client_t)))) ) { 105 | pthread_rwlock_unlock(&lock); 106 | return; 107 | } 108 | 109 | client->fd = fd; 110 | 111 | HASH_ADD_INT(clients, fd, client); 112 | 113 | pthread_rwlock_unlock(&lock); 114 | 115 | return; 116 | } 117 | 118 | /** 119 | * Event called when a client has received new data. 120 | * 121 | * @param fd [int] "A filedescriptor of the client receiving the data" 122 | * @param message [char *] "The message received" 123 | * @param message_length [size_t] "The length of the message" 124 | * @return [void] 125 | */ 126 | void onMessage(int fd, wss_opcode_t opcode, char *message, size_t message_length) { 127 | wss_client_t *client, *tmp; 128 | 129 | if ( unlikely(pthread_rwlock_rdlock(&lock) != 0) ) { 130 | return; 131 | } 132 | 133 | HASH_ITER(hh, clients, client, tmp) { 134 | if (client->fd != fd) { 135 | send(client->fd, opcode, message, message_length); 136 | } 137 | } 138 | 139 | pthread_rwlock_unlock(&lock); 140 | } 141 | 142 | /** 143 | * Event called when a client are about to perform a write. 144 | * 145 | * @param fd [int] "A filedescriptor the client about to receive the message" 146 | * @param message [char *] "The message that should be sent" 147 | * @param message_length [size_t] "The length of the message" 148 | * @return [void] 149 | */ 150 | void onWrite(int fd, char *message, size_t message_length) { 151 | return; 152 | } 153 | 154 | /** 155 | * Event called when a client disconnects from the WSS server. 156 | * 157 | * @param fd [int] "A filedescriptor of the disconnecting client" 158 | * @return [void] 159 | */ 160 | void onClose(int fd) { 161 | wss_client_t *client; 162 | 163 | if ( unlikely(NULL == (client = WSS_client_find(fd))) ) { 164 | return; 165 | } 166 | 167 | if ( unlikely(pthread_rwlock_wrlock(&lock) != 0) ) { 168 | return; 169 | } 170 | 171 | HASH_DEL(clients, client); 172 | 173 | allocs.free(client); 174 | 175 | pthread_rwlock_unlock(&lock); 176 | 177 | return; 178 | } 179 | 180 | /** 181 | * Event called when the subprotocol should be destroyed. 182 | * 183 | * @return [void] 184 | */ 185 | void onDestroy() { 186 | wss_client_t *client, *tmp; 187 | 188 | if ( unlikely(pthread_rwlock_wrlock(&lock) != 0) ) { 189 | return; 190 | } 191 | 192 | HASH_ITER(hh, clients, client, tmp) { 193 | if (client != NULL) { 194 | HASH_DEL(clients, client); 195 | 196 | allocs.free(client); 197 | } 198 | } 199 | 200 | pthread_rwlock_unlock(&lock); 201 | pthread_rwlock_destroy(&lock); 202 | } 203 | -------------------------------------------------------------------------------- /subprotocols/broadcast/broadcast.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_subprotocol_echo_h 2 | #define wss_subprotocol_echo_h 3 | 4 | #include 5 | 6 | #include "subprotocol.h" 7 | #include "uthash.h" 8 | 9 | typedef struct { 10 | // The file descriptor of the client 11 | int fd; 12 | // Used for client hash table 13 | UT_hash_handle hh; 14 | } wss_client_t; 15 | 16 | /** 17 | * Event called when subprotocol is initialized. 18 | * 19 | * @param config [char *] "The configuration of the subprotocol" 20 | * @param send [WSS_send] "Function that send message to a single recipient" 21 | * @param servers [void *] "Server structure used internally" 22 | * @return [void] 23 | */ 24 | void __attribute__((visibility("default"))) onInit(char *config, WSS_send send); 25 | 26 | /** 27 | * Sets the allocators to use instead of the default ones 28 | * 29 | * @param submalloc [WSS_malloc_t] "The malloc function" 30 | * @param subrealloc [WSS_realloc_t] "The realloc function" 31 | * @param subfree [WSS_free_t] "The free function" 32 | * @return [void] 33 | */ 34 | void __attribute__((visibility("default"))) setAllocators(WSS_malloc_t submalloc, WSS_realloc_t subrealloc, WSS_free_t subfree); 35 | 36 | /** 37 | * Event called when a new client has handshaked and hence connects to the WSS server. 38 | * 39 | * @param fd [int] "A filedescriptor of a connecting client" 40 | * @param ip [char *] "The ip address of the connecting session" 41 | * @param port [int] "The port of the connecting session" 42 | * @param path [char *] "The connection path. This can hold HTTP parameters such as access_token, csrf_token etc. that can be used to authentication" 43 | * @param cookies [char *] "The cookies received from the client. This can be used to do authentication." 44 | * @return [void] 45 | */ 46 | void __attribute__((visibility("default"))) onConnect(int fd, char *ip, int port, char *path, char *cookies); 47 | 48 | /** 49 | * Event called when a client has received new data. 50 | * 51 | * @param fd [int] "A filedescriptor of the client receiving the data" 52 | * @param message [char *] "The message received" 53 | * @param message_length [size_t] "The length of the message" 54 | * @return [void] 55 | */ 56 | void __attribute__((visibility("default"))) onMessage(int fd, wss_opcode_t opcode, char *message, size_t message_length); 57 | 58 | /** 59 | * Event called when a client are about to perform a write. 60 | * 61 | * @param fd [int] "A filedescriptor the client about to receive the message" 62 | * @param message [char *] "The message that should be sent" 63 | * @param message_length [size_t] "The length of the message" 64 | * @return [void] 65 | */ 66 | void __attribute__((visibility("default"))) onWrite(int fd, char *message, size_t message_length); 67 | 68 | /** 69 | * Event called when a client disconnects from the WSS server. 70 | * 71 | * @param fd [int] "A filedescriptor of the disconnecting client" 72 | * @return [void] 73 | */ 74 | void __attribute__((visibility("default"))) onClose(int fd); 75 | 76 | /** 77 | * Event called when the subprotocol should be destroyed. 78 | * 79 | * @return [void] 80 | */ 81 | void __attribute__((visibility("default"))) onDestroy(); 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /subprotocols/broadcast/predict.h: -------------------------------------------------------------------------------- 1 | ../../include/predict.h -------------------------------------------------------------------------------- /subprotocols/broadcast/uthash.h: -------------------------------------------------------------------------------- 1 | ../../include/uthash.h -------------------------------------------------------------------------------- /subprotocols/echo/Makefile: -------------------------------------------------------------------------------- 1 | #Shell 2 | SHELL = /bin/bash 3 | 4 | #Executeable name 5 | NAME = echo 6 | 7 | #Compiler 8 | CC = gcc 9 | 10 | #Debug or Release 11 | PROFILE = -Og -g -DNDEBUG 12 | DEBUG = -Og -g 13 | RELEASE = -O3 -funroll-loops -DNDEBUG 14 | SPACE = -Os -DNDEBUG 15 | EXEC = $(RELEASE) 16 | 17 | #Compiler options 18 | CFLAGS = $(EXEC) \ 19 | -fno-exceptions \ 20 | -fPIC \ 21 | -fstack-protector \ 22 | -funroll-loops \ 23 | -fvisibility=hidden \ 24 | -march=native \ 25 | -MMD \ 26 | -pedantic \ 27 | -pedantic-errors \ 28 | -pipe \ 29 | -W \ 30 | -Wall \ 31 | -Werror \ 32 | -Wformat \ 33 | -Wformat-security \ 34 | -Wformat-nonliteral \ 35 | -Winit-self \ 36 | -Winline \ 37 | -Wl,-z,relro \ 38 | -Wl,-z,now \ 39 | -Wmultichar \ 40 | -Wno-unused-parameter \ 41 | -Wno-unused-function \ 42 | -Wno-unused-label \ 43 | -Wno-deprecated \ 44 | -Wno-strict-aliasing \ 45 | -Wpointer-arith \ 46 | -Wreturn-type \ 47 | -Wsign-compare \ 48 | -Wuninitialized \ 49 | -D_DEFAULT_SOURCE 50 | 51 | CVER = -std=c11 52 | 53 | # Folders 54 | FOLDER = $(shell pwd) 55 | SUBPROTOCOLS_FOLDER = $(FOLDER)/../ 56 | 57 | # Include folders 58 | INCLUDES = -I$(SUBPROTOCOLS_FOLDER) 59 | 60 | # Files 61 | SRC = $(shell find $(FOLRDER) -name '*.c' -type f;) 62 | SRC_OBJ = $(patsubst %.c, %.o, $(SRC)) 63 | DEPS = $(SRC_OBJ:%.o=%.d) 64 | 65 | .PHONY: clean release debug profiling space 66 | 67 | #what we are trying to build 68 | all: $(NAME) 69 | 70 | release_mode: 71 | $(eval EXEC = $(RELEASE)) 72 | 73 | debug_mode: 74 | $(eval EXEC = $(DEBUG)) 75 | 76 | profiling_mode: 77 | $(eval EXEC = $(PROFILE)) 78 | 79 | space_mode: 80 | $(eval EXEC = $(SPACE)) 81 | 82 | # Recompile when headers change 83 | -include $(DEPS) 84 | 85 | #linkage 86 | $(NAME): $(SRC_OBJ) 87 | @echo 88 | @echo ================ [Creating Shared Object] ================ 89 | @echo 90 | $(CC) -shared $(CFLAGS) $(CVER) -o $(NAME).so $(SRC_OBJ) $(INCLUDES) 91 | @echo 92 | @echo ================ [$(NAME).so compiled succesfully] ================ 93 | @echo 94 | 95 | # compile every source file 96 | %.o: %.c 97 | @echo 98 | @echo ================ [Building Object] ================ 99 | @echo 100 | $(CC) $(CFLAGS) $(CVER) -c $< -o $@ $(INCLUDES) 101 | @echo 102 | @echo OK [$<] - [$@] 103 | @echo 104 | 105 | #make clean 106 | clean: 107 | @echo 108 | @echo ================ [Cleaning $(NAME)] ================ 109 | @echo 110 | rm -f *.d 111 | rm -f *.o 112 | rm -f *.so 113 | 114 | #make release 115 | release: clean release_mode all 116 | 117 | #make debug 118 | debug: clean debug_mode all 119 | 120 | #make profiling 121 | profiling: clean profiling_mode all 122 | 123 | #make space 124 | space: clean space_mode all 125 | -------------------------------------------------------------------------------- /subprotocols/echo/echo.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "echo.h" 4 | 5 | typedef struct { 6 | void *(*malloc)(size_t); 7 | void *(*realloc)(void *, size_t); 8 | void (*free)(void *); 9 | } allocators; 10 | 11 | allocators allocs = { 12 | malloc, 13 | realloc, 14 | free 15 | }; 16 | 17 | WSS_send send = NULL; 18 | 19 | /** 20 | * Event called when subprotocol is initialized. 21 | * 22 | * @param config [char *] "The configuration of the subprotocol" 23 | * @param s [WSS_send] "Function that send message to a single recipient" 24 | * @return [void] 25 | */ 26 | void onInit(char *config, WSS_send s) { 27 | send = s; 28 | return; 29 | } 30 | 31 | /** 32 | * Sets the allocators to use instead of the default ones 33 | * 34 | * @param submalloc [WSS_malloc_t] "The malloc function" 35 | * @param subrealloc [WSS_realloc_t] "The realloc function" 36 | * @param subfree [WSS_free_t] "The free function" 37 | * @return [void] 38 | */ 39 | void setAllocators(WSS_malloc_t submalloc, WSS_realloc_t subrealloc, WSS_free_t subfree) { 40 | allocs.malloc = submalloc; 41 | allocs.realloc = subrealloc; 42 | allocs.free = subfree; 43 | } 44 | 45 | /** 46 | * Event called when a new session has handshaked and hence connects to the WSS server. 47 | * 48 | * @param fd [int] "A filedescriptor of a connecting session" 49 | * @param ip [char *] "The ip address of the connecting session" 50 | * @param port [int] "The port of the connecting session" 51 | * @param path [char *] "The connection path. This can hold HTTP parameters such as access_token, csrf_token etc. that can be used to authentication" 52 | * @param cookies [char *] "The cookies received from the client. This can be used to do authentication." 53 | * @return [void] 54 | */ 55 | void onConnect(int fd, char *ip, int port, char *path, char *cookies) { 56 | return; 57 | } 58 | 59 | /** 60 | * Event called when a session has received new data. 61 | * 62 | * @param fd [int] "A filedescriptor of the session receiving the data" 63 | * @param message [char *] "The message received" 64 | * @param message_length [size_t] "The length of the message" 65 | * @return [void] 66 | */ 67 | void onMessage(int fd, wss_opcode_t opcode, char *message, size_t message_length) { 68 | send(fd, opcode, message, message_length); 69 | } 70 | 71 | /** 72 | * Event called when a session are about to perform a write. 73 | * 74 | * @param fd [int] "A filedescriptor the session about to receive the message" 75 | * @param message [char *] "The message that should be sent" 76 | * @param message_length [size_t] "The length of the message" 77 | * @return [void] 78 | */ 79 | void onWrite(int fd, char *message, size_t message_length) { 80 | return; 81 | } 82 | 83 | /** 84 | * Event called when a session disconnects from the WSS server. 85 | * 86 | * @param fd [int] "A filedescriptor of the disconnecting session" 87 | * @return [void] 88 | */ 89 | void onClose(int fd) { 90 | return; 91 | } 92 | 93 | /** 94 | * Event called when the subprotocol should be destroyed. 95 | * 96 | * @return [void] 97 | */ 98 | void onDestroy() { 99 | } 100 | -------------------------------------------------------------------------------- /subprotocols/echo/echo.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_subprotocol_echo_h 2 | #define wss_subprotocol_echo_h 3 | 4 | #include 5 | #include "subprotocol.h" 6 | 7 | /** 8 | * Event called when subprotocol is initialized. 9 | * 10 | * @param config [char *] "The configuration of the subprotocol" 11 | * @param send [WSS_send] "Function that send message to a single recipient" 12 | * @return [void] 13 | */ 14 | void __attribute__((visibility("default"))) onInit(char *config, WSS_send send); 15 | 16 | /** 17 | * Sets the allocators to use instead of the default ones 18 | * 19 | * @param submalloc [WSS_malloc_t] "The malloc function" 20 | * @param subrealloc [WSS_realloc_t] "The realloc function" 21 | * @param subfree [WSS_free_t] "The free function" 22 | * @return [void] 23 | */ 24 | void __attribute__((visibility("default"))) setAllocators(WSS_malloc_t submalloc, WSS_realloc_t subrealloc, WSS_free_t subfree); 25 | 26 | /** 27 | * Event called when a new session has handshaked and hence connects to the WSS server. 28 | * 29 | * @param fd [int] "A filedescriptor of a connecting session" 30 | * @param ip [char *] "The ip address of the connecting session" 31 | * @param port [int] "The port of the connecting session" 32 | * @param path [char *] "The connection path. This can hold HTTP parameters such as access_token, csrf_token etc. that can be used to authentication" 33 | * @param cookies [char *] "The cookies received from the client. This can be used to do authentication." 34 | * @return [void] 35 | */ 36 | void __attribute__((visibility("default"))) onConnect(int fd, char *ip, int port, char *path, char *cookies); 37 | 38 | /** 39 | * Event called when a session has received new data. 40 | * 41 | * @param fd [int] "A filedescriptor of the session receiving the data" 42 | * @param message [char *] "The message received" 43 | * @param message_length [size_t] "The length of the message" 44 | * @param receivers [int **] "A pointer which should be filled with the filedescriptors of those who should receive this message" 45 | * @param receiver_count [size_t] "The amount of receivers" 46 | * @return [void] 47 | */ 48 | void __attribute__((visibility("default"))) onMessage(int fd, wss_opcode_t opcode, char *message, size_t message_length); 49 | 50 | /** 51 | * Event called when a session are about to perform a write. 52 | * 53 | * @param fd [int] "A filedescriptor the session about to receive the message" 54 | * @param message [char *] "The message that should be sent" 55 | * @param message_length [size_t] "The length of the message" 56 | * @return [void] 57 | */ 58 | void __attribute__((visibility("default"))) onWrite(int fd, char *message, size_t message_length); 59 | 60 | /** 61 | * Event called when a session disconnects from the WSS server. 62 | * 63 | * @param fd [int] "A filedescriptor of the disconnecting session" 64 | * @return [void] 65 | */ 66 | void __attribute__((visibility("default"))) onClose(int fd); 67 | 68 | /** 69 | * Event called when the subprotocol should be destroyed. 70 | * 71 | * @return [void] 72 | */ 73 | void __attribute__((visibility("default"))) onDestroy(); 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /subprotocols/subprotocol.h: -------------------------------------------------------------------------------- 1 | #ifndef wss_subprotocol_h 2 | #define wss_subprotocol_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | typedef enum { 13 | CONTINUATION_FRAME = 0x0, 14 | TEXT_FRAME = 0x1, 15 | BINARY_FRAME = 0x2, 16 | CLOSE_FRAME = 0x8, 17 | PING_FRAME = 0x9, 18 | PONG_FRAME = 0xA, 19 | } wss_opcode_t; 20 | 21 | /** 22 | * A function that the subprotocol can use to send a message to a client of the 23 | * server. 24 | */ 25 | typedef void (*WSS_send)(int fd, wss_opcode_t opcode, char *message, uint64_t message_length); 26 | typedef void *(*WSS_malloc_t)(size_t size); 27 | typedef void *(*WSS_realloc_t)(void *ptr, size_t size); 28 | typedef void (*WSS_free_t)(void *ptr); 29 | 30 | /** 31 | * The subprotocol API calls 32 | */ 33 | typedef void (*subAlloc)(WSS_malloc_t submalloc, WSS_realloc_t subrealloc, WSS_free_t subfree); 34 | typedef void (*subInit)(char *config, WSS_send send); 35 | typedef void (*subConnect)(int fd, char *ip, int port, char *path, char *cookies); 36 | typedef void (*subMessage)(int fd, wss_opcode_t opcode, char *message, size_t message_length); 37 | typedef void (*subWrite)(int fd, char *message, size_t message_length); 38 | typedef void (*subClose)(int fd); 39 | typedef void (*subDestroy)(); 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /test/test_alloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "alloc.h" 6 | 7 | static void setup(void) { 8 | #ifdef USE_RPMALLOC 9 | rpmalloc_initialize(); 10 | #endif 11 | } 12 | 13 | static void teardown(void) { 14 | #ifdef USE_RPMALLOC 15 | rpmalloc_finalize(); 16 | #endif 17 | } 18 | 19 | 20 | TestSuite(WSS_malloc, .init = setup, .fini = teardown); 21 | 22 | Test(WSS_malloc, size_zero) { 23 | char *test = (char *)WSS_malloc(0); 24 | 25 | cr_assert(test == NULL); 26 | } 27 | 28 | Test(WSS_malloc, all_bytes_are_zero_bytes) { 29 | int i, n = 10; 30 | char *test = (char *)WSS_malloc(n*sizeof(char)); 31 | 32 | for (i = 0; i < n; i++) { 33 | cr_assert(test[i] == '\0'); 34 | } 35 | 36 | WSS_free((void **) &test); 37 | cr_assert(test == NULL); 38 | } 39 | 40 | TestSuite(WSS_copy, .init = setup, .fini = teardown); 41 | 42 | Test(WSS_copy, size_zero) { 43 | char *test = NULL; 44 | char *dup = WSS_copy(test, 0); 45 | 46 | cr_assert(NULL == dup); 47 | } 48 | 49 | Test(WSS_copy, all_bytes_are_zero_bytes) { 50 | char *test = "TESTING 123"; 51 | char *dup = WSS_copy(test, strlen(test)+1); 52 | 53 | cr_assert(strncmp(test, dup, strlen(test)) == 0); 54 | WSS_free((void **) &dup); 55 | } 56 | 57 | TestSuite(WSS_calloc, .init = setup, .fini = teardown); 58 | 59 | Test(WSS_calloc, memb_zero) { 60 | char *test = (char *)WSS_calloc(0, sizeof(int)); 61 | 62 | cr_assert(test == NULL); 63 | } 64 | 65 | Test(WSS_calloc, size_zero) { 66 | char *test = (char *)WSS_calloc(10, 0); 67 | 68 | cr_assert(test == NULL); 69 | } 70 | 71 | Test(WSS_calloc, can_use_as_integer_array) { 72 | int i, n = 10; 73 | int *test = (int *)WSS_calloc(10, sizeof(int)); 74 | 75 | for (i = 0; i < n; i++) { 76 | cr_assert(test[i] == 0); 77 | test[i] = i; 78 | } 79 | 80 | for (i = 0; i < n; i++) { 81 | cr_assert(test[i] == i); 82 | } 83 | 84 | WSS_free((void **) &test); 85 | 86 | cr_assert(test == NULL); 87 | } 88 | 89 | TestSuite(WSS_realloc, .init = setup, .fini = teardown); 90 | 91 | Test(WSS_realloc, size_zero) { 92 | char *test = NULL; 93 | test = (char *)WSS_realloc((void **)&test, 0, 0); 94 | 95 | cr_assert(test == NULL); 96 | } 97 | 98 | Test(WSS_realloc, realloc_as_malloc) { 99 | int i, n = 10; 100 | char *test = (char *) WSS_realloc(NULL, 0, n*sizeof(char)); 101 | 102 | for (i = 0; i < n; i++) { 103 | cr_assert(test[i] == '\0'); 104 | } 105 | 106 | WSS_free((void **) &test); 107 | 108 | cr_assert(test == NULL); 109 | } 110 | 111 | TestSuite(WSS_realloc_normal, .init = setup, .fini = teardown); 112 | 113 | Test(WSS_realloc_normal, realloc_as_malloc) { 114 | int i, n = 10; 115 | char *test = (char *) WSS_realloc_normal(NULL, n*sizeof(char)); 116 | 117 | for (i = 0; i < n; i++) { 118 | cr_assert(test[i] == '\0'); 119 | } 120 | 121 | WSS_free_normal(test); 122 | } 123 | 124 | Test(WSS_realloc, free_ptr_on_size_zero) { 125 | int i, n = 10; 126 | char *test = (char *)WSS_malloc(n); 127 | char *res; 128 | 129 | for (i = 0; i < n; i++) { 130 | cr_assert(test[i] == '\0'); 131 | } 132 | 133 | res = WSS_realloc((void **)&test, n, 0); 134 | 135 | cr_assert(res == NULL); 136 | cr_assert(test == NULL); 137 | } 138 | 139 | Test(WSS_realloc, all_bytes_are_zero_bytes) { 140 | int i, n = 10; 141 | char *test = (char *)WSS_malloc(n); 142 | 143 | for (i = 0; i < n; i++) { 144 | cr_assert(test[i] == '\0'); 145 | } 146 | 147 | test = WSS_realloc((void **)&test, n, n+5); 148 | 149 | for (i = 0; i < n+5; i++) { 150 | cr_assert(test[i] == '\0'); 151 | } 152 | 153 | WSS_free((void **) &test); 154 | 155 | cr_assert(test == NULL); 156 | } 157 | 158 | Test(WSS_realloc, shrinking) { 159 | int i, n = 10; 160 | char *test = (char *)WSS_malloc(n); 161 | char *res; 162 | 163 | for (i = 0; i < n; i++) { 164 | cr_assert(test[i] == '\0'); 165 | } 166 | 167 | res = WSS_realloc((void **)&test, n, n-(n/2)); 168 | 169 | cr_assert(test != NULL); 170 | cr_assert(res != NULL); 171 | 172 | WSS_free((void **) &res); 173 | 174 | cr_assert(res == NULL); 175 | } 176 | -------------------------------------------------------------------------------- /test/test_socket.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "alloc.h" 4 | #include "server.h" 5 | #include "http.h" 6 | #include "socket.h" 7 | #include "rpmalloc.h" 8 | 9 | static void setup(void) { 10 | #ifdef USE_RPMALLOC 11 | rpmalloc_initialize(); 12 | #endif 13 | } 14 | 15 | static void teardown(void) { 16 | #ifdef USE_RPMALLOC 17 | rpmalloc_finalize(); 18 | #endif 19 | } 20 | 21 | TestSuite(WSS_socket_create, .init = setup, .fini = teardown); 22 | 23 | Test(WSS_socket_create, null_server) { 24 | cr_assert(WSS_SOCKET_CREATE_ERROR == WSS_socket_create(NULL)); 25 | } 26 | 27 | Test(WSS_socket_create, creating_socket) { 28 | wss_server_t *server = (wss_server_t *) WSS_malloc(sizeof(wss_server_t)); 29 | 30 | cr_assert(WSS_SUCCESS == WSS_socket_create(server)); 31 | 32 | // Cleanup 33 | WSS_http_server_free(server); 34 | pthread_mutex_destroy(&server->lock); 35 | WSS_free((void **) &server); 36 | } 37 | 38 | TestSuite(WSS_socket_reuse, .init = setup, .fini = teardown); 39 | 40 | Test(WSS_socket_reuse, invalid_fd) { 41 | cr_assert(WSS_SOCKET_REUSE_ERROR == WSS_socket_reuse(-1)); 42 | } 43 | 44 | Test(WSS_socket_reuse, reuse_socket) { 45 | wss_server_t *server = (wss_server_t *) WSS_malloc(sizeof(wss_server_t)); 46 | 47 | cr_assert(WSS_SUCCESS == WSS_socket_create(server)); 48 | cr_assert(WSS_SUCCESS == WSS_socket_reuse(server->fd)); 49 | 50 | // Cleanup 51 | WSS_http_server_free(server); 52 | pthread_mutex_destroy(&server->lock); 53 | WSS_free((void **) &server); 54 | } 55 | 56 | TestSuite(WSS_socket_bind, .init = setup, .fini = teardown); 57 | 58 | Test(WSS_socket_bind, null_server) { 59 | cr_assert(WSS_SOCKET_BIND_ERROR == WSS_socket_bind(NULL)); 60 | } 61 | 62 | Test(WSS_socket_bind, invalid_server_port) { 63 | wss_server_t *server = (wss_server_t *) WSS_malloc(sizeof(wss_server_t)); 64 | server->port = -1; 65 | 66 | cr_assert(WSS_SOCKET_BIND_ERROR == WSS_socket_bind(server)); 67 | 68 | // Cleanup 69 | WSS_http_server_free(server); 70 | pthread_mutex_destroy(&server->lock); 71 | WSS_free((void **) &server); 72 | } 73 | 74 | Test(WSS_socket_bind, invalid_server_fd) { 75 | wss_server_t *server = (wss_server_t *) WSS_malloc(sizeof(wss_server_t)); 76 | server->port = 999; 77 | server->fd = -1; 78 | 79 | cr_assert(WSS_SOCKET_BIND_ERROR == WSS_socket_bind(server)); 80 | 81 | // Cleanup 82 | WSS_http_server_free(server); 83 | pthread_mutex_destroy(&server->lock); 84 | WSS_free((void **) &server); 85 | } 86 | 87 | Test(WSS_socket_bind, bind_socket) { 88 | wss_server_t *server = (wss_server_t *) WSS_malloc(sizeof(wss_server_t)); 89 | server->port = 4567; 90 | 91 | cr_assert(WSS_SUCCESS == WSS_socket_create(server)); 92 | cr_assert(WSS_SUCCESS == WSS_socket_reuse(server->fd)); 93 | cr_assert(WSS_SUCCESS == WSS_socket_bind(server)); 94 | 95 | // Cleanup 96 | WSS_http_server_free(server); 97 | pthread_mutex_destroy(&server->lock); 98 | WSS_free((void **) &server); 99 | } 100 | 101 | TestSuite(WSS_socket_non_blocking, .init = setup, .fini = teardown); 102 | 103 | Test(WSS_socket_non_blocking, invalid_fd) { 104 | cr_assert(WSS_SOCKET_NONBLOCKED_ERROR == WSS_socket_non_blocking(-1)); 105 | } 106 | 107 | Test(WSS_socket_non_blocking, non_blocking_socket) { 108 | wss_server_t *server = (wss_server_t *) WSS_malloc(sizeof(wss_server_t)); 109 | server->port = 4567; 110 | 111 | cr_assert(WSS_SUCCESS == WSS_socket_create(server)); 112 | cr_assert(WSS_SUCCESS == WSS_socket_reuse(server->fd)); 113 | cr_assert(WSS_SUCCESS == WSS_socket_bind(server)); 114 | cr_assert(WSS_SUCCESS == WSS_socket_non_blocking(server->fd)); 115 | 116 | // Cleanup 117 | WSS_http_server_free(server); 118 | pthread_mutex_destroy(&server->lock); 119 | WSS_free((void **) &server); 120 | } 121 | 122 | TestSuite(WSS_socket_listen, .init = setup, .fini = teardown); 123 | 124 | Test(WSS_socket_listen, invalid_fd) { 125 | cr_assert(WSS_SOCKET_LISTEN_ERROR == WSS_socket_listen(-1)); 126 | } 127 | 128 | Test(WSS_socket_listen, listen_socket) { 129 | wss_server_t *server = (wss_server_t *) WSS_malloc(sizeof(wss_server_t)); 130 | server->port = 4567; 131 | 132 | cr_assert(WSS_SUCCESS == WSS_socket_create(server)); 133 | cr_assert(WSS_SUCCESS == WSS_socket_reuse(server->fd)); 134 | cr_assert(WSS_SUCCESS == WSS_socket_bind(server)); 135 | cr_assert(WSS_SUCCESS == WSS_socket_non_blocking(server->fd)); 136 | cr_assert(WSS_SUCCESS == WSS_socket_listen(server->fd)); 137 | 138 | // Cleanup 139 | WSS_http_server_free(server); 140 | pthread_mutex_destroy(&server->lock); 141 | WSS_free((void **) &server); 142 | } 143 | 144 | TestSuite(WSS_socket_threadpool, .init = setup, .fini = teardown); 145 | 146 | Test(WSS_socket_threadpool, null_server) { 147 | cr_assert(WSS_THREADPOOL_CREATE_ERROR == WSS_socket_threadpool(NULL)); 148 | } 149 | 150 | Test(WSS_socket_threadpool, null_config) { 151 | wss_server_t *server = (wss_server_t *) WSS_malloc(sizeof(wss_server_t)); 152 | cr_assert(WSS_THREADPOOL_CREATE_ERROR == WSS_socket_threadpool(server)); 153 | } 154 | 155 | Test(WSS_socket_threadpool, invalid_create_threadpool_params) { 156 | wss_server_t *server = (wss_server_t *) WSS_malloc(sizeof(wss_server_t)); 157 | wss_config_t *conf = (wss_config_t *) WSS_malloc(sizeof(wss_config_t)); 158 | 159 | server->port = conf->port_http; 160 | server->config = conf; 161 | 162 | cr_assert(WSS_SUCCESS == WSS_socket_create(server)); 163 | cr_assert(WSS_SUCCESS == WSS_socket_reuse(server->fd)); 164 | cr_assert(WSS_SUCCESS == WSS_socket_bind(server)); 165 | cr_assert(WSS_SUCCESS == WSS_socket_non_blocking(server->fd)); 166 | cr_assert(WSS_SUCCESS == WSS_socket_listen(server->fd)); 167 | cr_assert(WSS_THREADPOOL_CREATE_ERROR == WSS_socket_threadpool(server)); 168 | 169 | // Cleanup 170 | WSS_http_server_free(server); 171 | pthread_mutex_destroy(&server->lock); 172 | WSS_free((void **) &server); 173 | WSS_free((void**) &conf); 174 | } 175 | 176 | Test(WSS_socket_threadpool, threadpool_socket) { 177 | wss_server_t *server = (wss_server_t *) WSS_malloc(sizeof(wss_server_t)); 178 | wss_config_t *conf = (wss_config_t *) WSS_malloc(sizeof(wss_config_t)); 179 | 180 | cr_assert(WSS_SUCCESS == WSS_config_load(conf, "resources/test_wss.json")); 181 | 182 | server->port = conf->port_http; 183 | server->config = conf; 184 | 185 | cr_assert(WSS_SUCCESS == WSS_socket_create(server)); 186 | cr_assert(WSS_SUCCESS == WSS_socket_reuse(server->fd)); 187 | cr_assert(WSS_SUCCESS == WSS_socket_bind(server)); 188 | cr_assert(WSS_SUCCESS == WSS_socket_non_blocking(server->fd)); 189 | cr_assert(WSS_SUCCESS == WSS_socket_listen(server->fd)); 190 | cr_assert(WSS_SUCCESS == WSS_socket_threadpool(server)); 191 | 192 | // Cleanup 193 | WSS_http_server_free(server); 194 | pthread_mutex_destroy(&server->lock); 195 | WSS_free((void **) &server); 196 | WSS_config_free(conf); 197 | WSS_free((void**) &conf); 198 | } 199 | -------------------------------------------------------------------------------- /test/test_str.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "str.h" 8 | #include "alloc.h" 9 | 10 | #if defined(USE_OPENSSL) | defined(USE_BORINGSSL) | defined(USE_LIBRESSL) 11 | 12 | #include 13 | 14 | #elif defined(USE_WOLFSSL) 15 | 16 | #pragma GCC diagnostic push 17 | #pragma GCC diagnostic ignored "-Wcpp" 18 | #include 19 | #pragma GCC diagnostic pop 20 | 21 | #define SHA_DIGEST_LENGTH 20 22 | 23 | #else 24 | 25 | #include "sha1.h" 26 | #define SHA_DIGEST_LENGTH 20 27 | 28 | #endif 29 | 30 | static void setup(void) { 31 | #ifdef USE_RPMALLOC 32 | rpmalloc_initialize(); 33 | #endif 34 | } 35 | 36 | static void teardown(void) { 37 | #ifdef USE_RPMALLOC 38 | rpmalloc_finalize(); 39 | #endif 40 | } 41 | 42 | TestSuite(bin2hex, .init = setup, .fini = teardown); 43 | 44 | Test(bin2hex, zero_length) { 45 | char *string = ""; 46 | char *str = bin2hex((const char unsigned *) string, 0); 47 | 48 | cr_assert(str != NULL); 49 | cr_assert(str[0] == '\0'); 50 | 51 | WSS_free((void**)&str); 52 | } 53 | 54 | Test(bin2hex, simple_string) { 55 | char *str; 56 | const char unsigned *string = (const char unsigned *)WSS_malloc(6); 57 | sprintf((char *)string, "%s", "12345"); 58 | 59 | str = bin2hex(string, strlen((char *)string)); 60 | 61 | cr_assert(str != NULL); 62 | cr_assert(strncmp(str, "3132333435", 10) == 0); 63 | 64 | WSS_free((void **)&str); 65 | WSS_free((void **) &string); 66 | } 67 | 68 | Test(bin2hex, sha1_string) { 69 | char *str; 70 | char sha1Key[SHA_DIGEST_LENGTH]; 71 | const char unsigned *string = (const char unsigned *)WSS_malloc(6); 72 | sprintf((char *)string, "%s", "12345"); 73 | 74 | #if defined(USE_OPENSSL) 75 | SHA1((const unsigned char *)string, strlen((char *)string), (unsigned char*) sha1Key); 76 | #elif defined(USE_WOLFSSL) 77 | Sha sha; 78 | wc_InitSha(&sha); 79 | wc_ShaUpdate(&sha, (const unsigned char *) string, strlen((char *)string)); 80 | wc_ShaFinal(&sha, (unsigned char *) sha1Key); 81 | #else 82 | SHA1Context sha; 83 | int i, b; 84 | memset(sha1Key, '\0', SHA_DIGEST_LENGTH); 85 | 86 | SHA1Reset(&sha); 87 | SHA1Input(&sha, (const unsigned char*) string, strlen((char *)string)); 88 | if ( SHA1Result(&sha) ) { 89 | for (i = 0; i < 5; i++) { 90 | b = htonl(sha.Message_Digest[i]); 91 | memcpy(sha1Key+(4*i), (unsigned char *) &b, 4); 92 | } 93 | } 94 | #endif 95 | str = bin2hex((const unsigned char *)sha1Key, SHA_DIGEST_LENGTH); 96 | 97 | cr_assert(str != NULL); 98 | cr_assert(strncmp(str, "8cb2237d0679ca88db6464eac60da96345513964", SHA_DIGEST_LENGTH)); 99 | 100 | WSS_free((void **) &str); 101 | WSS_free((void **) &string); 102 | } 103 | 104 | TestSuite(strinarray, .init = setup, .fini = teardown); 105 | 106 | Test(strinarray, test_zero_length) { 107 | int i; 108 | int length = 0; 109 | char **array = (char **) WSS_calloc(100, sizeof(char *)); 110 | 111 | for (i = 0; i < 100; i++) { 112 | array[i] = WSS_malloc(3); 113 | sprintf(array[i], "%d", i); 114 | } 115 | 116 | cr_assert(strinarray("87", (const char **)array, length) == -1); 117 | cr_assert(strinarray("34", (const char **)array, length) == -1); 118 | cr_assert(strinarray("0", (const char **)array, length) == -1); 119 | cr_assert(strinarray("99", (const char **)array, length) == -1); 120 | cr_assert(strinarray("37", (const char **)array, length) == -1); 121 | cr_assert(strinarray("100", (const char **)array, length) == -1); 122 | cr_assert(strinarray("testing123", (const char **)array, length) == -1); 123 | cr_assert(strinarray("lol", (const char **)array, length) == -1); 124 | 125 | for (i = 0; i < 100; i++) { 126 | WSS_free((void **)&array[i]); 127 | } 128 | WSS_free((void **)&array); 129 | } 130 | 131 | Test(strinarray, test_string_in_first_half_array) { 132 | int i; 133 | int length = 100; 134 | char **array = (char **) WSS_calloc(length, sizeof(char *)); 135 | 136 | for (i = 0; i < length; i++) { 137 | array[i] = WSS_malloc(3); 138 | sprintf(array[i], "%d", i); 139 | } 140 | 141 | cr_assert(strinarray("87", (const char **)array, length/2) == -1); 142 | cr_assert(strinarray("34", (const char **)array, length/2) == 0); 143 | cr_assert(strinarray("0", (const char **)array, length/2) == 0); 144 | cr_assert(strinarray("99", (const char **)array, length/2) == -1); 145 | cr_assert(strinarray("37", (const char **)array, length/2) == 0); 146 | cr_assert(strinarray("100", (const char **)array, length/2) == -1); 147 | cr_assert(strinarray("50", (const char **)array, length/2) == -1); 148 | cr_assert(strinarray("49", (const char **)array, length/2) == 0); 149 | cr_assert(strinarray("testing123", (const char **)array, length/2) == -1); 150 | cr_assert(strinarray("lol", (const char **)array, length/2) == -1); 151 | 152 | for (i = 0; i < 100; i++) { 153 | WSS_free((void **)&array[i]); 154 | } 155 | WSS_free((void **)&array); 156 | } 157 | 158 | Test(strinarray, test_string_in_array) { 159 | int i; 160 | int length = 100; 161 | char **array = (char **) WSS_calloc(length, sizeof(char *)); 162 | 163 | for (i = 0; i < length; i++) { 164 | array[i] = WSS_malloc(3); 165 | sprintf(array[i], "%d", i); 166 | } 167 | 168 | cr_assert(strinarray("87", (const char **)array, length) == 0); 169 | cr_assert(strinarray("34", (const char **)array, length) == 0); 170 | cr_assert(strinarray("0", (const char **)array, length) == 0); 171 | cr_assert(strinarray("99", (const char **)array, length) == 0); 172 | cr_assert(strinarray("37", (const char **)array, length) == 0); 173 | cr_assert(strinarray("100", (const char **)array, length) == -1); 174 | cr_assert(strinarray("testing123", (const char **)array, length) == -1); 175 | cr_assert(strinarray("lol", (const char **)array, length) == -1); 176 | 177 | for (i = 0; i < length; i++) { 178 | WSS_free((void **)&array[i]); 179 | } 180 | WSS_free((void **)&array); 181 | } 182 | 183 | TestSuite(strload, .init = setup, .fini = teardown); 184 | 185 | Test(strload, none_existing_file) { 186 | int n; 187 | char *content; 188 | 189 | n = strload("lollern.txt", &content); 190 | 191 | cr_assert(n == 0); 192 | cr_assert(content == NULL); 193 | } 194 | 195 | Test(strload, load_txt_file) { 196 | int n; 197 | char *content; 198 | 199 | n = strload("resources/test.txt", &content); 200 | 201 | cr_assert(n == 34); 202 | cr_assert(strncmp(content, "This is a file, used for testing.\n", n) == 0); 203 | 204 | WSS_free((void **)&content); 205 | } 206 | --------------------------------------------------------------------------------