lua-websockets

repository·master·Indexed 19 days ago

https://github.com/lipp/lua-websockets

RFC 6455 compliant WebSocket clients and servers for Lua. It supports multiple execution models, including synchronous, coroutine-based (via copas), and asynchronous (via lua-ev). The library provides a factory for clients and server implementations that map protocol names to callback functions, allowing for flexible concurrency needs.

Tokens
3K
Snippets
11
Records
13
Agent score
65%

What's inside lua-websockets

  1. How lua-websockets clients and servers work

    master

    The library provides WebSocket (RFC 6455) compliant implementations in different execution models:

    Clients

    • Synchronous: Standard blocking client.
    • Coroutine-based: Uses copas for non-blocking coroutine management.
    • Asynchronous: Uses lua-ev for event-driven asynchronous operations.

    Servers

    • Coroutine-based: Uses copas.
    • Asynchronous: Uses lua-ev.

    Important Note: lua-websockets is not a full webserver framework. It handles the WebSocket protocol specifically. It is designed to work alongside existing webservers (like Orbit) by running on different ports or processes, as WebSockets are not restricted by the Same-Origin Policy.

  2. Run the test-server examples

    master

    The test-server directory contains implementations of the libwebsocket test-server.c example.

    To run the lua-ev version:

    cd test-server
    lua test-server-ev.lua

    To run the copas version:

    cd test-server
    lua test-server-copas.lua

    To connect from a browser (e.g., Chrome console) using the echo protocol:

    var echoWs = new WebSocket('ws://127.0.0.1:8002','echo');
  3. Install lua-websockets via luarocks

    master

    To install lua-websockets, clone the repository and use luarocks to install from the provided rockspec.

    Dependencies:

    • luasocket (required)
    • luabitop (required if not using Lua 5.2 or LuaJIT)
    • luasec (required)
    • copas (optional, for coroutine-based modules)
    • lua-ev (optional, for asynchronous modules)
    $ git clone git://github.com/lipp/lua-websockets.git
    $ cd lua-websockets
    $ luarocks make rockspecs/lua-websockets-scm-1.rockspec
  4. Create a coroutine-based echo server with copas

    master

    You can implement a WebSocket server using the copas coroutine-based flavor. Use require'websocket'.server.copas.listen to initialize the server.

    The protocols table maps protocol names to callback functions. The callback receives a ws (websocket instance) which provides methods like ws:receive(), ws:send(message), and ws:close().

    Note: You must run copas.loop() to start the server loop.

    local copas = require'copas'
    
    -- create a copas webserver and start listening
    local server = require'websocket'.server.copas.listen
    {
      -- listen on port 8080
      port = 8080,
      -- the protocols field holds
      --   key: protocol name
      --   value: callback on new connection
      protocols = {
        -- this callback is called, whenever a new client connects.
        -- ws is a new websocket instance
        echo = function(ws)
          while true do
            local message = ws:receive()
            if message then
               ws:send(message)
            else
               ws:close()
               return
            end
          end
        end
      }
    }
    
    -- use the copas loop
    copas.loop()
  5. Create an asynchronous echo server with lua-ev

    master

    You can implement a WebSocket server using the lua-ev asynchronous flavor. Use require'websocket'.server.ev.listen to initialize the server.

    The protocols table maps protocol names to callback functions. The callback receives a ws instance which uses an event-driven API: ws:on_message(callback) and ws:on_close(callback). Inside the message callback, you can use ws:send(message).

    Note: You must run ev.Loop.default:loop() to start the event loop.

    local ev = require'ev'
    
    -- create a copas webserver and start listening
    local server = require'websocket'.server.ev.listen
    {
      -- listen on port 8080
      port = 8080,
      -- the protocols field holds
      --   key: protocol name
      --   value: callback on new connection
      protocols = {
        -- this callback is called, whenever a new client connects.
        -- ws is a new websocket instance
        echo = function(ws)
          ws:on_message(function(ws,message)
              ws:send(message)
            end)
    
          -- this is optional
          ws:on_close(function()
              ws:close()
            end)
        end
      }
    }
    
    -- use the lua-ev loop
    ev.Loop.default:loop()
  6. Set up a Copas WebSocket server

    master

    Create a server using websocket.server.copas.listen(config). The server uses Copas-compatible event multiplexing.

    Configuration Options (config):

    • port: (Number) The port to listen on. Default is 80.
    • interface: (String) The network interface to listen on. Default is '*' (all interfaces).
    • protocols: (Table) A table of protocol handlers where keys are protocol names and values are functions. Each function receives a ws object (the client instance).
    • default: (Function, optional) The default protocol handler called if no specific protocol matches.

    Protocol Handlers: Handlers are called when a client connects. The handler receives a client instance that has the same API as the Copas Client (e.g., ws:receive(), ws:send(), ws:close()), but it does not have a connect() method because the connection is already established.

    local websocket = require'websocket'
    local config = {
      port = 8080,
      interface = '*',
      protocols = {
        ['echo'] = function(ws)
          while true do
            local message = ws:receive()
            if message then
              ws:send(message)
            else
              ws:close()
              return
            end
          end
        end,
        ['echo-uppercase'] = function(ws)
          while true do
            local message = ws:receive()
            if message then
              ws:send(message:upper())
            else
              ws:close()
              return
            end
          end
        end,
      },
      default = function(ws)
        ws:send('goodbye strange client')
        ws:close()
      end
    }
    local server = websocket.server.copas.listen(config)
  7. Connect a client to a WebSocket URL

    master

    Use client:connect(ws_url, [protocol]) to establish a connection.

    • ws_url: The WebSocket URL (e.g., 'ws://localhost:12345').
    • protocol: An optional string specifying the requested protocol.

    On success, it returns true. On error, it returns nil followed by an error description.

    local ok, err = client:connect('ws://localhost:12345', 'echo')
    if not ok then
       print('could not connect', err)
    end
  8. Close the WebSocket server

    master

    Use server:close([keep_clients]) to shut down the server.

    • keep_clients: A boolean. If falsy (e.g., false or nil), the server will also close all currently connected clients. If truthy, the server stops listening but existing connections remain open.
    server:close(false)
  9. Receive messages from a client

    master

    The client:receive() method retrieves messages from the socket.

    Return Values:

    1. message: The received message as a string. If the connection was closed, this may be nil.
    2. opcode: The message type, either websocket.TEXT or websocket.BINARY.
    3. close_was_clean: (Optional) Boolean indicating if the connection closed cleanly.
    4. close_code: (Optional) The numeric close code.
    5. close_reason: (Optional) The string reason for closure.

    If the connection is closed during a receive operation, it is not necessary to call client:close() manually.

    local message, opcode = client:receive()
    if message then
       print('msg', message, opcode)
    else
       print('connection closed')
    end
  10. Initialize a Synchronous or Copas Client

    master

    You can create a client using either the synchronous interface or the Copas (non-blocking) interface. websocket.client.new is an alias for websocket.client.sync. Both constructors accept an optional table to specify a timeout.

    Use websocket.client.sync for simple, blocking scripts. Use websocket.client.copas for non-blocking operations within a Copas environment.

    local websocket = require'websocket'
    
    -- Synchronous client
    local client = websocket.client.sync({timeout=2})
    
    -- Copas (non-blocking) client
    local client = websocket.client.copas({timeout=2})
  11. Send messages from a client

    master

    Use client:send(message, [type]) to transmit data.

    • message: A string containing the content.
    • type: Optional. Either websocket.TEXT (default) or websocket.BINARY.

    Return Values:

    • On success: true.
    • On error: nil followed by close_was_clean, close_code, and close_reason.

    If you only care if the message was sent successfully, you can ignore the error details.

    local ok = client:send('hello')
    if ok then
       print('msg sent')
    else
       print('connection closed')
    end
  12. Close a client connection

    master

    Initiate a closing handshake using client:close([code], [reason]).

    • code: A number representing the close code (defaults to 1000).
    • reason: A string providing the reason for closure (defaults to an empty string).

    Returns close_was_clean, close_code, and close_reason according to the protocol.

    -- Close with custom code and reason
    local close_was_clean, close_code, close_reason = client:close(4001, 'lost interest')
    
    -- Default closure
    client:close()