{"id":16086571,"url":"https://github.com/ksdaemon/wiola","last_synced_at":"2025-03-16T08:32:20.062Z","repository":{"id":15055829,"uuid":"17781991","full_name":"KSDaemon/wiola","owner":"KSDaemon","description":"WAMP implementation in Lua","archived":false,"fork":false,"pushed_at":"2023-10-26T09:34:25.000Z","size":811,"stargazers_count":70,"open_issues_count":4,"forks_count":12,"subscribers_count":10,"default_branch":"dev","last_synced_at":"2024-04-13T21:48:01.455Z","etag":null,"topics":["lua-nginx","pubsub","rpc","rpc-router","wamp","wamp-protocol","wamp-router","wamp-server"],"latest_commit_sha":null,"homepage":"http://ksdaemon.github.io/wiola","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KSDaemon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2014-03-15T18:09:10.000Z","updated_at":"2024-03-07T23:29:54.000Z","dependencies_parsed_at":"2023-12-26T03:08:12.799Z","dependency_job_id":"7b8a9518-c8a8-42fa-81a2-8bd22e0a2117","html_url":"https://github.com/KSDaemon/wiola","commit_stats":{"total_commits":286,"total_committers":4,"mean_commits":71.5,"dds":0.1923076923076923,"last_synced_commit":"19f62be5553a97c93ca7c693d53ba8d389e80bd1"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KSDaemon%2Fwiola","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KSDaemon%2Fwiola/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KSDaemon%2Fwiola/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KSDaemon%2Fwiola/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KSDaemon","download_url":"https://codeload.github.com/KSDaemon/wiola/tar.gz/refs/heads/dev","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243806070,"owners_count":20350775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["lua-nginx","pubsub","rpc","rpc-router","wamp","wamp-protocol","wamp-router","wamp-server"],"created_at":"2024-10-09T13:13:40.072Z","updated_at":"2025-03-16T08:32:19.618Z","avatar_url":"https://github.com/KSDaemon.png","language":"Lua","readme":"wiola\n=====\n\nWAMP (WebSocket Application Messaging Protocol) implementation on Lua, using the power of Lua Nginx module,\nLua WebSocket addon, and Redis as cache store.\n\nTable of Contents\n=================\n\n- [wiola](#wiola)\n- [Table of Contents](#table-of-contents)\n- [Description](#description)\n- [Usage example](#usage-example)\n- [Installation](#installation)\n- [Authentication](#authentication)\n- [Call and Publication trust levels](#call-and-publication-trust-levels)\n- [Methods](#methods)\n  - [config(config)](#configconfig)\n  - [addConnection(sid, wampProto)](#addconnectionsid-wampproto)\n  - [receiveData(regId, data)](#receivedataregid-data)\n  - [getPendingData(regId)](#getpendingdataregid)\n  - [processPostData(sid, realm, data)](#processpostdatasid-realm-data)\n- [Copyright and License](#copyright-and-license)\n- [See Also](#see-also)\n\nDescription\n===========\n\nWiola implements [WAMP specification][] v2 router specification on top of OpenResty web server,\n which is actually nginx plus a bunch of 3rd party modules, such as lua-nginx-module, lua-resty-websocket,\n lua-resty-redis and so on.\n\nWiola supports next WAMP roles and features:\n\n* broker: advanced profile with features:\n    * pattern based subscription\n    * publisher exclusion\n    * publisher identification\n    * publication trust levels\n    * session meta api\n    * subscriber blackwhite listing\n    * subscription meta api (partly)\n* dealer: advanced profile with features:\n    * call canceling\n    * call timeout\n    * caller identification\n    * call trust levels\n    * pattern based registration\n    * progressive call results\n    * registration meta api (partly)\n    * session meta api\n* Challenge Response Authentication (\"WAMP-CRA\")\n* Cookie Authentication\n* Rawsocket transport\n* Session Meta API\n\nWiola supports JSON and msgpack serializers.\n\nFrom v0.3.1 Wiola also supports lightweight POST event publishing. See processPostData method and post-handler.lua for details.\n\n[Back to TOC](#table-of-contents)\n\nUsage example\n=============\n\nFor example usage, please see [ws-handler.lua](src/wiola/ws-handler.lua) file.\n\n[Back to TOC](#table-of-contents)\n\nInstallation\n============\n\nTo use wiola you need:\n\n* Nginx or OpenResty\n* [luajit][]\n* [lua-nginx-module][]\n* [lua-resty-websocket][]\n* [lua-resty-redis][]\n* [Redis server][]\n* [lua-rapidjson][]\n* [lua-resty-hmac][] (optional, required for WAMP-CRA)\n* [lua-MessagePack][] (optional)\n* [redis-lua][] (optional)\n* [stream-lua-nginx-module][] (optional)\n\nInstead of compiling lua-* modules into nginx, you can simply use [OpenResty][] server.\n\nIn any case, for your convenience, you can install Wiola through [luarocks](http://luarocks.org/modules/ksdaemon/wiola)\nby `luarocks install wiola` or through [OpenResty Package Manager]\nby `opm install KSDaemon/wiola`. Unfortunately, not all dependencies are available in opm, so you need to manually\ninstall missing ones.\n\nNext thing is configuring nginx host. See example below.\n\n```nginx\nhttp {\n\n    # set search paths for pure Lua external libraries (';;' is the default path):\n    # add paths for wiola and msgpack libs\n    lua_package_path '/usr/local/lualib/wiola/?.lua;/usr/local/lualib/lua-MessagePack/?.lua;;';\n\n    init_worker_by_lua_block {\n        -- Initializing math.randomseed for every worker/luaVM\n        local f = io.open('/dev/random', 'rb')\n        local seed\n        if f then\n            local b1, b2, b3, b4 = string.byte(f:read(4), 1, 4)\n            seed = b1 * 0x1000000 + b2 * 0x10000 + b3 * 0x100 + b4\n            f:close()\n        else\n            seed = ngx.time() + ngx.worker.pid()\n        end\n        math.randomseed(seed)\n        math.randomseed = function()end\n    }\n\n\n    init_by_lua_block {\n        -- Wiola configuration. You can read more in description of .configure() method below.\n        local cfg = require \"wiola.config\"\n        cfg.config({\n            socketTimeout = 1000,           -- one second\n            maxPayloadLen = 65536,\n            pingInterval = 1000,  -- interval in ms for sending ping frames. set to 0 for disabling\n            realms = { \"app\", \"admin\" },\n            store = \"redis\",\n            storeConfig = {\n                host = \"unix:///tmp/redis.sock\",  -- Optional parameter. Can be hostname/ip or socket path\n                --port = 6379                     -- Optional parameter. Should be set when using hostname/ip\n                                                  -- Omit for socket connection\n                --db = 5                          -- Optional parameter. Redis db to use\n            },\n            callerIdentification = \"auto\",        -- Optional parameter. auto | never | always\n            cookieAuth = {                        -- Optional parameter.\n                authType = \"none\",                -- none | static | dynamic\n                cookieName = \"wampauth\",\n                staticCredentials = nil, --{\n                    -- \"user1\", \"user2:password2\", \"secretkey3\"\n                --},\n                authCallback = nil\n            },\n            wampCRA = {                           -- Optional parameter.\n                authType = \"none\",                -- none | static | dynamic\n                staticCredentials = nil, --{\n                    -- user1 = { authrole = \"userRole1\", secret=\"secret1\" },\n                    -- user2 = { authrole = \"userRole2\", secret=\"secret2\" }\n                --},\n                challengeCallback = nil,\n                authCallback = nil\n            },\n            trustLevels = {                       -- Optional parameter.\n                authType = \"none\",                -- none | static | dynamic\n                defaultTrustLevel = nil,\n                staticCredentials = {\n                    byAuthid = {\n                        --{ authid = \"user1\", trustlevel = 1 },\n                        --{ authid = \"admin1\", trustlevel = 5 }\n                    },\n                    byAuthRole = {\n                        --{ authrole = \"user-role\", trustlevel = 2 },\n                        --{ authrole = \"admin-role\", trustlevel = 4 }\n                    },\n                    byClientIp = {\n                        --{ clientip = \"127.0.0.1\", trustlevel = 10 }\n                    }\n                },\n                authCallback = nil -- function that accepts (client ip address, realm,\n                                   -- authid, authrole) and returns trust level\n            },\n            metaAPI = {                           -- Expose META API ? Optional parameter.\n                session = true,\n                subscription = true,\n                registration = true\n            }\n        })\n\n        -- If you want automatically clean up redis db during nginx restart uncomment next two lines\n        -- for this to work, you need redis-lua library\n        -- Use it only with lua_code_cache on; !!!\n        --local wflush = require \"wiola.flushdb\"\n        --wflush.flushAll()\n    }\n\n    # Configure a vhost\n    server {\n       # example location for websocket WAMP connection\n       location /ws/ {\n          set $wiola_max_payload_len 65535; # Optional parameter. Set the value to suit your needs\n\n          lua_socket_log_errors off;\n          lua_check_client_abort on;\n\n          # This is needed to set additional websocket protocol headers\n          header_filter_by_lua_file $document_root/lua/wiola/headers.lua;\n          # Set a handler for connection\n          content_by_lua_file $document_root/lua/wiola/ws-handler.lua;\n       }\n\n       # example location for a lightweight POST event publishing\n       location /wslight/ {\n          lua_socket_log_errors off;\n          lua_check_client_abort on;\n\n          content_by_lua_file $document_root/lua/wiola/post-handler.lua;\n       }\n\n    }\n}\n```\n\nIf you want to use raw socket transport instead of (or additional to) websocket, you need also to configure nginx stream\n\n```nginx\nstream {\n    # set search paths for pure Lua external libraries (';;' is the default path):\n    # add paths for wiola and msgpack libs\n    lua_package_path '/usr/local/lualib/wiola/?.lua;/usr/local/lualib/lua-MessagePack/?.lua;;';\n\n    init_worker_by_lua_block {\n        # Actually same one as in http example above...\n    }\n\n    init_by_lua_block {\n        # Actually same one as in http example above...\n    }\n\n    server {\n        listen 1234;\n        lua_check_client_abort on;\n        content_by_lua_file $document_root/lua/wiola/raw-handler.lua;\n    }\n\n}\n```\n\nAlso, starting from v0.12.0 Wiola uses Redis pubsub system instead of polling for retreiving client data.\nSo you need to configure Redis server and enable keyspace-events. Btw, you do not need to enable all events.\nWiola needs only keyspace events for list.\n\nEdit redis.conf and set notify-keyspace-events option.\n\n```\nnotify-keyspace-events \"Kl\"\n```\n\nActually, you do not need to do anything else. Just take any WAMP client and make a connection.\n\n[Back to TOC](#table-of-contents)\n\nAuthentication\n==============\n\nBeginning with v0.6.0 Wiola supports several types of authentication:\n\n* Cookie authentication:\n     * Static configuration\n     * Dynamic callback\n* Challenge Response Authentication:\n     * Static configuration\n     * Dynamic callback\n\nAlso it is possible to use both types of authentication :)\nTo setup authentication you need to [config](#configconfig) Wiola somewhere in nginx/openresty before request processing.\nIn simple case, you can do it just in nginx http config section.\n\n```lua\nlocal cfg = require \"wiola.config\"\ncfg.config({\n    cookieAuth = {\n        authType = \"dynamic\",              -- none | static | dynamic\n        cookieName = \"wampauth\",\n        staticCredentials = { \"user1:pass1\", \"user2:pass2\"},\n        authCallback = function (creds)\n            -- Validate credentials somehow\n            -- return true, if valid\n            if isValid(creds) then\n                return true\n            end\n\n            return false\n        end\n    },\n    wampCRA = {\n        authType = \"dynamic\",              -- none | static | dynamic\n        staticCredentials = {\n            user1 = { authrole = \"userRole1\", secret=\"secret1\" },\n            user2 = { authrole = \"userRole2\", secret=\"secret2\" }\n        },\n        challengeCallback = function (sessionid, authid)\n            -- Generate a challenge string somehow and return it\n            -- Do not forget to save it somewhere for response validation!\n\n            return \"{ \\\"nonce\\\": \\\"LHRTC9zeOIrt_9U3\\\",\" ..\n                     \"\\\"authprovider\\\": \\\"usersProvider\\\", \\\"authid\\\": \\\"\" .. authid .. \"\\\",\" ..\n                     \"\\\"timestamp\\\": \\\"\" .. os.date(\"!%FT%TZ\") .. \"\\\",\" ..\n                     \"\\\"authrole\\\": \\\"userRole1\\\", \\\"authmethod\\\": \\\"wampcra\\\",\" ..\n                     \"\\\"session\\\": \" .. sessionid .. \"}\"\n        end,\n        authCallback = function (sessionid, signature)\n            -- Validate responsed signature against challenge\n            -- return auth info object (like bellow) or nil if failed\n            return { authid=\"user1\", authrole=\"userRole1\", authmethod=\"wampcra\", authprovider=\"usersProvider\" }\n        end\n    }\n})\n```\n\n[Back to TOC](#table-of-contents)\n\nCall and Publication trust levels\n==================================\n\nBeginning with v0.9.0 Wiola supports Call and Publication trust levels labeling\nTo setup trust levels you need to [config](#configconfig) Wiola somewhere in nginx/openresty before request processing.\nIn simple case, you can do it just in nginx http config section.\nFor static configuration, authid option takes precendence over authrole, which takes precendence over client ip.\nFor example, if client match all three options (authid, authrole, client ip), than trust level from auth id will be set.\n\n```lua\nlocal cfg = require \"wiola.config\"\n\n-- Static trustlevel configuration\ncfg.config({\n    trustLevels = {\n        authType = \"static\",\n        defaultTrustLevel = 5,\n        staticCredentials = {\n            byAuthid = {\n                { authid = \"user1\", trustlevel = 1 },\n                { authid = \"admin1\", trustlevel = 5 }\n            },\n            byAuthRole = {\n                { authrole = \"user-role\", trustlevel = 2 },\n                { authrole = \"admin-role\", trustlevel = 4 }\n            },\n            byClientIp = {\n                { clientip = \"127.0.0.1\", trustlevel = 10 }\n            }\n        }\n    }\n})\n\n-- Dynamic trustlevel configuration\ncfg.config({\n    trustLevels = {\n        authType = \"dynamic\",\n        authCallback = function (clientIp, realm, authid, authrole)\n\n            -- write your own logic for setting trust level\n            -- just a simple example\n\n            if clientIp == \"127.0.0.1\" then\n                return 15\n            end\n\n            if realm == \"test\" then\n                return nil\n            end\n\n            return 5\n        end\n    }\n})\n```\n\n[Back to TOC](#table-of-contents)\n\nMethods\n========\n\nconfig(config)\n------------------------------------------\n\nConfigure Wiola Instance or retrieve current configuration. All options are optional. Some options have default value,\nor are nils if not specified.\n\nParameters:\n\n * **config** - Configuration table with possible options:\n    * **socketTimeout** - Timeout for underlying socket connection operations. Default: 100 ms\n    * **maxPayloadLen** - Maximal length of payload allowed when sending and receiving using underlying socket.\n    Default: 65536 bytes (2^16). For raw socket transport please use values, aligned with power of two between 9 and 24. 2^9, 2^10 .. 2^24.\n    * **pingInterval** - Interval in ms for sending ping frames. Set to 0 for disabling server initiated ping. Default: 1000 ms\n    * **realms** - Array of allowed WAMP realms. Default value: {} - so no clients will connect to router. Also it's possible\n    to set special realm { \"*\" } - which allows to create any realm on client request if it not exists, great for development use.\n    * **redis** - Redis connection configuration table:\n        * **host** - Redis server host or Redis unix socket path. Default: \"unix:/tmp/redis.sock\"\n        * **port** - Redis server port (in case of use network connection). Omit for socket connection\n        * **db** - Redis database index to select\n    * **callerIdentification** - Disclose caller identification? Possible values: auto | never | always. Default: \"auto\"\n    * **cookieAuth** - Cookie-based Authentication configuration table:\n        * **authType** - Type of auth. Possible values: none | static | dynamic. Default: \"none\", which means - don't use\n        * **cookieName** - Name of cookie with auth info. Default: \"wampauth\"\n        * **staticCredentials** - Array-like table with string items, allowed to connect. Is used with authType=\"static\"\n        * **authCallback** - Callback function for authentication. Is used with authType=\"dynamic\". Value of cookieName\n        is passed as first parameter. Should return boolean flag, true - allows connection, false - prevent connection\n    * **wampCRA** - WAMP Challenge-Response (\"WAMP-CRA\") authentication configuration table:\n        * **authType** - Type of auth. Possible values: none | static | dynamic. Default: \"none\", which means - don't use\n        * **staticCredentials** - table with keys, named as authids and values like { authrole = \"userRole1\", secret=\"secret1\" },\n        allowed to connect. Is used with authType=\"static\"\n        * **challengeCallback** - Callback function for generating challenge info. Is used with authType=\"dynamic\".\n        Is called on HELLO message, passing session ID as first parameter and authid as second one.\n        Should return challenge string the client needs to create a signature for.\n        Check [Challenge Response Authentication section in WAMP Specification][] for more info.\n        * **authCallback** - Callback function for checking auth signature. Is used with authType=\"dynamic\".\n        Is called on AUTHENTICATE message, passing session ID as first parameter and signature as second one.\n        Should return auth info object { authid=\"user1\", authrole=\"userRole\", authmethod=\"wampcra\", authprovider=\"usersProvider\" }\n        or nil | false in case of failure.\n    * **trustLevels** - Trust levels configuration table:\n        * **authType** - Type of auth. Possible values: none | static | dynamic. Default: \"none\", which means - don't use\n        * **defaultTrustLevel** - Default trust level for clients that doesn't match to any static credentials.\n        Should be any positive integer or nil for omitting\n        * **staticCredentials** - Is used with authType=\"static\". Has 3 subtables:\n            * byAuthid. This array-like table holds items like `{ authid = \"user1\", trustlevel = 1 }`\n            * byAuthRole. This array-like table holds items like `{ authrole = \"user-role\", trustlevel = 2 }`\n            * byClientIp. This array-like table holds items like `{ clientip = \"127.0.0.1\", trustlevel = 10 }`\n        * **authCallback** - Callback function for getting trust level for client. It accepts (client ip address, realm,\n        authid, authrole) and returns trust level (positive integer or nil)\n    * **metaAPI** - Meta API configuration table:\n        * **session** - Expose session meta api? Possible values: true | false. Default: false.\n        * **subscription** - Expose subscription meta api? Possible values: true | false. Default: false.\n        * **registration** - Expose registration meta api? Possible values: true | false. Default: false.\n\nWhen called without parameters, returns current configuration.\nWhen setting configuration, returns nothing.\n\nConfig example (multiple options, just for showcase):\n```lua\n    init_by_lua_block {\n        local cfg = require \"wiola.config\"\n        cfg.config({\n            socketTimeout = 1000,           -- one second\n            maxPayloadLen = 65536,\n            realms = { \"test\", \"app\" },\n            callerIdentification = \"always\",\n            redis = {\n                host = \"unix:/tmp/redis.sock\"   -- Optional parameter. Can be hostname/ip or socket path\n                --port = 6379                     -- Optional parameter. Should be set when using hostname/ip\n                                                -- Omit for socket connection\n                --db = 5                         -- Optional parameter. Redis db to use\n            },\n            cookieAuth = {\n                authType = \"none\",              -- none | static | dynamic\n                cookieName = \"wampauth\",\n                staticCredentials = { \"user1:pass1\", \"user2:pass2\"},\n                authCallback = function (creds)\n                    if creds ~= \"\" then\n                        return true\n                    end\n\n                    return false\n                end\n            },\n            wampCRA = {\n                authType = \"dynamic\",              -- none | static | dynamic\n                staticCredentials = {\n                    user1 = { authrole = \"userRole1\", secret=\"secret1\" },\n                    user2 = { authrole = \"userRole2\", secret=\"secret2\" }\n                },\n                challengeCallback = function (sessionid, authid)\n                    return \"{ \\\"nonce\\\": \\\"LHRTC9zeOIrt_9U3\\\",\" ..\n                             \"\\\"authprovider\\\": \\\"usersProvider\\\", \\\"authid\\\": \\\"\" .. authid .. \"\\\",\" ..\n                             \"\\\"timestamp\\\": \\\"\" .. os.date(\"!%FT%TZ\") .. \"\\\",\" ..\n                             \"\\\"authrole\\\": \\\"userRole1\\\", \\\"authmethod\\\": \\\"wampcra\\\",\" ..\n                             \"\\\"session\\\": \" .. sessionid .. \"}\"\n                end,\n                authCallback = function (sessionid, signature)\n                    return { authid=\"user1\", authrole=\"userRole1\", authmethod=\"wampcra\", authprovider=\"usersProvider\" }\n                end\n            },\n            metaAPI = {\n                session = true,\n                subscription = false,\n                registration = false\n            }\n        })\n    }\n```\n\n\n[Back to TOC](#table-of-contents)\n\naddConnection(sid, wampProto)\n------------------------------------------\n\nAdds new connection instance to wiola control.\n\nParameters:\n\n * **sid** - nginx session id\n * **wampProto** - chosen WAMP subprotocol. It is set in header filter. So just pass here ngx.header[\"Sec-WebSocket-Protocol\"]. It's done just in order not to use shared variables.\n\nReturns:\n\n * **WAMP session ID** (integer)\n * **Connection data type** (string: 'text' or 'binary')\n\n[Back to TOC](#table-of-contents)\n\nreceiveData(regId, data)\n------------------------------------------\n\nThis method should be called, when new data is received from web socket. This method analyze all incoming messages, set states and prepare response data for clients.\n\nParameters:\n\n * **regId** - WAMP session ID\n * **data** - received data\n\nReturns: nothing\n\n[Back to TOC](#table-of-contents)\n\ngetPendingData(regId)\n------------------------------------------\n\nChecks the store for new data for client.\n\nParameters:\n\n * **regId** - WAMP session ID\n\nReturns:\n\n * **client data** (type depends on session data type) or **null**\n * **error description** in case of error\n\n This method is actualy a proxy for redis:lpop() method.\n\n[Back to TOC](#table-of-contents)\n\nprocessPostData(sid, realm, data)\n------------------------------------------\n\nProcess lightweight POST data from client containing a publish message. This method is intended for fast publishing\nan event, for example, in case when WAMP client is a browser application, which makes some changes on backend server,\nso backend is a right place to notify other WAMP subscribers, but making a full WAMP connection is not optimal.\n\nParameters:\n\n * **sid** - nginx session connection ID\n * **realm** - WAMP Realm to operate in\n * **data** - data, received through POST (JSON-encoded WAMP publish event)\n\nReturns:\n\n * **response data** (JSON encoded WAMP response message in case of error, or { result = true })\n * **httpCode** HTTP status code (HTTP_OK/200 in case of success, HTTP_FORBIDDEN/403 in case of error)\n\n[Back to TOC](#table-of-contents)\n\nCopyright and License\n=====================\n\nWiola library is licensed under the BSD 2-Clause license.\n\nCopyright (c) 2014-2017, Konstantin Burkalev\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n[Back to TOC](#table-of-contents)\n\nSee Also\n========\n\n* [WAMP specification][]\n* [Challenge Response Authentication section in WAMP Specification][]\n* [Wampy.js][]. WAMP Javascript client implementation\n* [OpenResty][]\n* [lua-nginx-module][]\n* [lua-resty-websocket][]\n* [lua-rapidjson][]\n* [lua-resty-redis][]\n* [Redis server][]\n* [lua-MessagePack][]\n\n[Back to TOC](#table-of-contents)\n\nThanks JetBrains for the best IDEs and support for open source!\n\n[![jetbrains logo]][jetbrains url]\n\n[WAMP specification]: http://wamp-proto.org/\n[Challenge Response Authentication section in WAMP Specification]: https://tools.ietf.org/html/draft-oberstet-hybi-tavendo-wamp-02#section-13.7.2.3\n[Wampy.js]: https://github.com/KSDaemon/wampy.js\n[OpenResty]: http://openresty.org\n[OpenResty Package Manager]: http://opm.openresty.org/\n[luajit]: http://luajit.org/\n[lua-nginx-module]: https://github.com/chaoslawful/lua-nginx-module\n[lua-resty-websocket]: https://github.com/agentzh/lua-resty-websocket\n[lua-rapidjson]: https://github.com/xpol/lua-rapidjson\n[lua-resty-redis]: https://github.com/agentzh/lua-resty-redis\n[Redis server]: http://redis.io\n[lua-MessagePack]: http://fperrad.github.io/lua-MessagePack/\n[lua-resty-hmac]: https://github.com/jamesmarlowe/lua-resty-hmac\n[redis-lua]: https://github.com/nrk/redis-lua\n[stream-lua-nginx-module]: https://github.com/openresty/stream-lua-nginx-module\n\n[jetbrains logo]: jetbrains.svg\n[jetbrains url]: (https://www.jetbrains.com)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fksdaemon%2Fwiola","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fksdaemon%2Fwiola","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fksdaemon%2Fwiola/lists"}