Skip to content

A websocket implementation for Tarantool

License

Notifications You must be signed in to change notification settings

demonoid81/websocket

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

55 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Library to use websocket channels

Use cases

The advantages of this library are:

  • persistent connection (no need to reconnect)
  • full-duplex data transmission

For example, it can help when you want to:

  • make a chat
  • send financial quotes
  • write a backend for a rich internet application
  • send push-notifications to your users

Installation

master

tarantoolctl rocks install https://github.com/tarantool/websocket/raw/master/websocket-scm-1.rockspec

Example

Client to echo

./example/client.lua

#!/usr/bin/env tarantool

local log = require('log')
local websocket = require('websocket')
local json = require('json')

local ws, err = websocket.connect('wss://echo.websocket.org',
                                  nil, {timeout=3})

if not ws then
    log.info(err)
    return
end

ws:write('HELLO')
local response = ws:read()
log.info(response)
assert(response.data == 'HELLO')

Client to exchange ticker

./example/gdax.lua

#!/usr/bin/env tarantool

local log = require('log')
local websocket = require('websocket')
local json = require('json')

local ws, err = websocket.connect(
    'wss://ws-feed.pro.coinbase.com', nil, {timeout=3})

if not ws then
    log.info(err)
    return
end

ws:write(
    json.encode(
        {type="subscribe",
         product_ids={"ETH-USD","ETH-EUR"},
         channels={"level2", "heartbeat",
                   {name="ticker",
                    product_ids={"ETH-BTC","ETH-USD"}}}}))

local packet = ws:read()
while packet ~= nil do
    log.info(packet)
    packet = ws:read()
end

Echo server

#!/usr/bin/env tarantool

local ws = require('websocket')

ws.server('ws://0.0.0.0:8080', function (ws_peer)
    while true do
        local message, err = ws_peer:read()
        if not message or message.opcode == nil then
            break
        end
	ws_peer:write(message.data)
    end
end)

Send a message to all connected clients

#!/usr/bin/env tarantool

local ws = require('websocket')
local json = require('json')
local ws_peers = {}

ws.server('ws://0.0.0.0:8080', function (ws_peer)
    local id = ws_peer.peer:fd()
    table.insert(ws_peers, id, ws_peer) -- save after connection

    while true do
        local message, err = ws_peer:read()
        if not message or message.opcode == nil then
            break
        end
    end

    ws_peers[id] = nil -- remove after disconnection
end)

return {
    push = function (data)
        for _, ws_peer in pairs(ws_peers) do
            ws_peer:write(json.encode(data)) -- send message to all subscribers
        end
    end
}

Server with ssl

#!/usr/bin/env tarantool

local log = require('log')
local ssl = require('websocket.ssl')
local websocket = require('websocket')
local json = require('json')

local ctx = ssl.ctx()
if not ssl.ctx_use_private_key_file(ctx, './certificate.pem') then
    log.info('Error private key')
    return
end

if not ssl.ctx_use_certificate_file(ctx, './certificate.pem') then
    log.info('Error certificate')
    return
end

websocket.server(
    'wss://0.0.0.0:8443/',
    function(wspeer)
        while true do
            local message, err = wspeer:read()
            if message then
                if message.opcode == nil then
                    log.info('Normal close')
                    break
                end
                log.info('echo ' .. json.encode(message))
                wspeer:write(message)
            else
                log.info('Exception close ' .. tostring(err))
                if wspeer:error() then
                    log.info('Socket error '..wspeer:error())
                end
                break
            end
        end
    end,
    {
        ping_timeout = 120,
        ctx = ctx
    }
)

Tests

Server

Load echo server

./example/echo_server.lua
# python2
virtualenv ptest # -p python2
cp test/fuzzyclient.json ptest/
cd ptest
source bin/activate
pip install autobahntestsuite
wstest -m fuzzingclient -s fuzzyclient.json

Open reports open reports/servers/index.html

Client

Load test server

# python2
virtualenv ptest # -p python2
cp test/fuzzyserver.json ptest/
cd ptest
source bin/activate
pip install autobahntestsuite
wstest -m fuzzingserver -s fuzzyserver.json

Start client

./example/echo_client.lua

Open reports open reports/clients/index.html

SSL API

local ssl = require('websocket.ssl')

ssl.methods

ssl.ctx(method)

ssl.ctx_use_private_key_file(ctx, filepath)

ssl.ctx_use_certificate_file(ctx, filepath)

API

websocket.server(url, handler, options)

  • url url for serving (e.g. wss://127.0.0.1:8443/endpoint)
  • handler callback with one param wspeer (e.g. function (wspeer) wspeer:read() end)
  • options
    • timeout - accept timeout
    • ping_frequency - ping frequency in seconds
    • ctx - ssl context

Returns:

  • server socket

websocket.connect(url, request, options)

  • url url for connect (e.g. ws://echo.websocket.org)
  • request
    • method
    • path
    • version
    • headers dict of headers
  • options
    • timeout connect timeout
    • ctx ssl context

Returns:

  • wspeer (socket like object)

websocket.bind(url, options)

  • url url for serving (e.g. wss://127.0.0.1:8443/endpoint)
  • options
    • timeout - accept timeout
    • ping_frequency - ping frequency in seconds
    • ctx - ssl context

Returns:

  • server socket

websocket.new(peer, ping_freq, is_client, is_handshaked, client_request)

  • peer tarantool socket object
  • ping_freq ping frequency in seconds
  • is_client is client side
  • is_handshaked whether socket already http handshaked or not
  • client_request http client handshake request

Returns:

  • wspeer (socket like object)

wspeer:read([timeout])

Returns:

  • data frame in following format
{
    "opcode":frame.TEXT|frame.BINARY,
    "fin":true|false,
    "data":string
}
  • nil, error if error or timeout

wspeer:write(frame[, timeout])

Send data frame. Frame structure the same as returned from wspeer:read:

{
    "opcode":frame.TEXT|frame.BINARY,
    "fin":true|false,
    "data":string
}

Returns:

  • frame size written
  • nil if error

wspeer:shutdown(code, reason[, timeout])

Graceful shutdown

Returns:

  • true graceful shutdown.
  • false, err - if error

wspeer:close()

Immediately close wspeer connection. Any pending data discarded.

About

A websocket implementation for Tarantool

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • HTML 99.7%
  • Lua 0.3%