zwibserve

package module
v0.0.0-...-630ccd4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 30, 2024 License: MIT Imports: 29 Imported by: 0

README

zwibserve GoDoc

Package zwibserve is an example collaboration server for zwibbler.com. It lets users work on the same document together. The server portion is responsible only for storing the changes and broadcasting them to all connected clients.

The collaboration works similar to source control. When I try to submit my changes, and you have submitted yours first, then my changes are rejected by the server. I must then modify my changes so they no longer conflict with yours, and try to submit them again.

The protocol is described in Zwibbler Collaboration Server Protocol V3. There are additional methods that add security and the ability to delete documents described in Zwibbler Collaboration Server Management API.

Test your server online.

What do I need?

  • Linux or Windows server. .rpm, .deb, and .exe installers are provided.
  • Security certificates are highly recommended. Your server needs to be running on HTTPS if your web page containing the drawing tool is also served over HTTPS, otherwise the browser will not allow it to connect.

Quick install

Use the .deb or .rpm packages from https://github.com/smhanov/zwibbler-service/releases . This will install a system service that automatically restarts if interrupted. After installation, it will be running on port 3000 as non-https. You can check this by going to http://yourserver.com:3000 in a web browser. You should receive a message that says it is working.

The next step is to enable HTTPS using your certificate and private key file. You need HTTPS because if your web site is served using HTTPS, it will not be able to contact the collaboration server unless it is also HTTPS.

Edit /etc/zwibbler.conf and change the port to the HTTPS port 443:

ServerBindAddress=0.0.0.0
ServerPort=443
CertFile=
KeyFile=
Compression=

Change CertFile and KeyFile to be the path to your SSL certificate information on the system. CertFile is your certificate, and KeyFile is your private key. You can leave the other settings as they are. Specifying Compression=0 will disable socket compression. Otherwise, it will be enabled by default.

Next, restart the service using

systemctl restart zwibbler

You can view the logs using

sudo tail -f /var/log/zwibbler/zwibbler.log

You should now be able to test using https://zwibbler.com/collaboration and entering wss://yourserver/socket in the URL with no port.

Using an nginx proxy

The method above will dedicate your server to running only Zwibbler, since it takes over the HTTPS port 443. If you want to run other things on the same machine, you will likely be using the nginx web server. You can run zwibbler on a different port (eg, 3000) and configure nginx to forward requests from a certain URL to zwibbler.

In this case, you can leave zwibbler.conf at the default, with blank CertFile and KeyFile. They will not be necessary since nginx will handle the security.

ServerBindAddress=0.0.0.0
ServerPort=3000
CertFile=
KeyFile=

In your nginx configuration, include this in your server {} block. This will redirect anything on https://yourserver/socket to the zwibbler service running on port 3000.

location /socket {
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
}
Using an Apache proxy

If you are running the Apache web server and have existing services running on it, you will need to configure it to forward requests for the socket to the Zwibbler collaboration service.

When using a proxy, leave the CertFile/KeyFile blank and keep the Zwibbler service on a different port than 3000. In this case, Apache will handle the security. Example /etc/zwibbler.conf file:

ServerBindAddress=0.0.0.0
ServerPort=3000
CertFile=
KeyFile=

If you are running CentOS, you may need to install the Apache proxy module, and configure security to allow Apache to make connections. If the second line fails because you do not have SELinux installed, that is OK.

sudo yum install mod_proxy
sudo /usr/sbin/setsebool -P httpd_can_network_connect 1

Find the virtual host configuration for your web site. It may be in /etc/httpd/conf/httpd.conf or in a file under /etc/httpd/conf.d/. Add the SSLProxyEngine and lines to your virtual host configuration for your web site. It says to forward any request to /socket to our collaboration service running on port 3000. Here is an example.

<VirtualHost *:443>
    SSLEngine on 
    SSLProxyEngine On
    ServerName www.example.com
    DocumentRoot "/var/www/html"
    SSLCertificateFile /path/to/.crt
    SSLCertificateKeyFile /path/to/.key
    
    # ADD THESE LINES
    SSLProxyEngine on 
    <Location "/socket">
	ProxyPass ws://localhost:3000/socket
	ProxyPassReverse ws://localhost:3000/socket
    </Location>
</VirtualHost>
Using Windows Server

After installing the setup file, follow the instructions to redirect traffic from the /socket url to your server.

On Windows, the zwibbler.log, zwibbler.conf, and zwibbler.db files are all located in C:\zwibbler, and they will not be removed if you uninstall the server.

Testing if it is working

Once you have the server running, test if it is working by going to https://yourserver.com/socket in your web browser. You should see a message like this:

Zwibbler collaboration Server is running.

After that, go to https://zwibbler.com/collaboration/testing.html and enter the your server wss:// url in the box (eg, wss://yourserver.com/socket) and click on "Run Tests". You should see the tests all pass and turn green.

Where is the data? What if it fails?

If the collaboration server restarts, clients will gracefully reconnect without the user noticing anything wrong and any active sessions are preserved.

In the basic version, each document must be hosted on one server, although a shared database can be used for higher reliability. All users for a particular whiteboard must connect to the same server. You can manage this by sharding the users based on their session identifier.

If more redundancy is required, or for complex load balancing, then a special, non-open source version is available at extra cost that allows any user to connect to any of the servers, regardless of which whiteboard they are connecting to. See High Availablility Extensions for Zwibbler Collaboration Server.

There are two options for data storage.

Sqlite (default)

The data is stored in an SQLITE database in /var/lib/zwibbler/zwibbler.db. The collaboration server is designed to store data only while a session is active. Long term storage should use ctx.save() and store the data using other means. Sessions never expire by default, but you can add expiration by editing the zwibbler.conf file.

Redis

Redis must be used if using more than one server instance. To use redis, add these lines to the zwibbler.conf file:

# Change this to redis
Database=redis

# Default DbServer for Redis: localhost:6379
DbServer=
DbPassword=

The DbUser field is unnecessary for Redis.

MariaDB, PostgreSQL, and MySQL

You can use another relational database for the collaboration server. To use MariaDB, PostgreSQL, or MySQL, change the Database config:

Database=mariadb

Then insert the DB settings:

# Default DbServer for MariaDB and MySQL: localhost:3306
# Default DbServer for Redis: localhost:6379
DbServer=
DbPassword=
DbUser=

Advanced options

Document lifetime

By default, documents never expire. You can set an expiration time for documents, so they will automatically be deleted when they have not been accessed for a certain number of seconds.

# The number of seconds after a document is last accessed to purge unused 
# documents. Set to the special value "never" to disable. 
Expiration=never
Security

The Zwibbler Collaboration Server Management API adds additional security, so that a skilled student hacker will be unable to alter the Javascript and write to a teacher's whiteboard unless given permission to do so. In this case, you must configure a username and password, and configure your own server software make a request to add a token with permissions before each persion connects to a session. That way, participants connect using a token instead of a session identifier, and the permissions are enforced by the collaboration server instead of the client browser. Any management requests are authenticated using HTTP Basic Authentication with the given username and password.

# If set, the management API is enabled to allow deleting and dumping documents.
SecretUser=
SecretPassword=

# If set, this webhook will be called when all users have left a session.
# See the API documents on Google Drive for details.
Webhook=
JWT (Javascript Web Tokens)

If desired, the server can be configured to only accept session identifiers contained inside of a JWT. The JWT also contains permission information, but are signed using a preconfigured key. That way, only authorized individuals will be able to write to a whiteboard. Using JWT means that the tokens do not need to be registered in advance with the collaboration server. The format of the tokens is described in Zwibbler Collaboration Server Management API

# If set, only JWT tokens will be accepted as document identifiers from the client.
# The HMAC-SHA256 signature method is used.
JWTKey=
JWTKeyIsBase64=False
Increasing maximum number of connections

To support more than 1024 connections on Linux, you will have to increase your system limit on the number of file handles. This is often done by adding these lines to /etc/security/limits.conf:

* hard nofiles 100000
* soft nofiles 100000

To tell Zwibbler to take advantage of these limits, you set MaxFiles to the same value in /etc/zwibbler.conf:

# Attempt to set the open file limit of the operating system to this value.
# This must be less than or equal to the hard limit of the operating system.
 MaxFiles=100000

Load testing

The current load testing results are available in the Zwibbler Collaboration Server Load Testing guide. A single server can support many thousands of connections, even when using the built-in SQLITE database.

The collaboration server has command line options that enable it to perform load testing on a server on another machine. It does this by simulating a number of participants viewing and writing to a single whiteboard. These are termed students and teachers.

Command Description
--test Test the server at the given websocket url (eg. ws://yourserver:3000/socket)
--docid The name of the whiteboard all participants connect to
--teachers The number of teachers to simulate.
--students The number of students to simulate.
--verbose Show all actions from every student and teacher

All initial connections will be made over a few seconds, to avoid a long backlog. A simulated student simply connects to the whiteboard and receives the initial contents as well as updates from it. A teacher will connect, and continuously update the whiteboard with new changes as well as receiving updates from other teachers. The teachers make changes at random intervals that average to one second. If there are multiple teachers, the changes may conflict and have to be resent. The test continues until stopped by the user using CTRL-C.

During the test you will see statistics about the test, including the screen-to-screen time. This is the amount of time between when a teacher makes a change to when a student sees that change on his whiteboard.

 Connections=51 docLength=149360 Screen-to-screen time avg=71ms min=19ms max=958ms

The changes are nonsensical data, not real whiteboard commands, so the real zwibbler will be unable to connect to the document used.

Using it from a go project

This is a go package. To run it, you will create a main program like this:

Step 1: Get the package

Run go get github.com/smhanov/zwibserve to get go to install the package in its cache.

Step 2: Create the main.go file

Create a main.go file:

package main

import (
	"log"
	"net/http"

	"github.com/smhanov/zwibserve"
)

func main() {
	http.Handle("/socket", zwibserve.NewHandler(zwibserve.NewSQLITEDB("zwibbler.db")))
	log.Printf("Running...")
	http.ListenAndServe(":3000", http.DefaultServeMux)
}

Step 3: Build and run

Run go build and the server will be compiled as main. It will run on port 3000 by default but you can change this in the main() function above.

Architecture

Architecturally, It uses gorilla websockets and follows closely the hub and client example

A hub goroutine is responsible for keeping a collection of document IDs. Each document ID has a list of clients connected to it.

Clients each run one goroutine for receiving messages, and one goroutine for sending.

The DocumentDB, which you can implement, actually stores the contents of the documents. Before appending to a document, it must atomically check if the length the client has given matches the actual length of the document.

The server is meant to only store documents during the time that multiple people are working on them. You should have a more permanent solution to store them for saving / opening.

Source tree method

Using the project as a module is the recommended way. You should not need to make modifications to the zwibserve package. However, if you want to make modifications to zwibserve in your project, you can directly include the files in your project.

Step 1: Check out the files
mkdir myproject
cd myproject
git clone https://github.com/smhanov/zwibserve.git

Now you have your project folder and zwibserve/ inside of it.

Step 2: Create go.mod file

Now, create a go.mod file which tells go where to find the zwibserve package.

module example.com/server

go 1.14

require (
	zwibserve v0.0.0
)

replace zwibserve v0.0.0 => ./zwibserve
Step 3: Update main.go to refer to the local package

Create the main.go file from above, but remove github.com from the package import.

package main

import (
	"log"
	"net/http"

	"zwibserve" // <-- Was github.com/smhanov/zwibserve
)

func main() {
	http.Handle("/socket", zwibserve.NewHandler(zwibserve.NewSQLITEDB("zwibbler.db")))
	log.Printf("Running...")
	http.ListenAndServe(":3000", http.DefaultServeMux)
}
Step 4: Build and run

Run go build to build the project. Run ./server to start it.

Documentation

Overview

Package zwibserve is an example collaboration server for zwibbler.com

The protocol is described in this Google Doc: https://docs.google.com/document/d/1X3_fzFqPUzTbPqF2GrYlSveWuv_L-xX7Cc69j13i6PY/edit?usp=sharing

Architecturally, It uses gorilla websockets and follows closely the hub and client example given at https://github.com/gorilla/websocket/tree/master/examples/chat

A hub goroutine is responsible for keeping a collection of document IDs. Each document ID has a list of clients connected to it.

Clients each run one goroutine for receiving messages, and one goroutine for sending.

The DocumentDB, which you can implement, actually stores the contents of the documents. Before appending to a document, it must atomically check if the length the client has given matches the actual length of the document. Default implementations MemoryDocumentDB and SQLITEDocumentDB are provided.

Index

Constants

View Source
const (
	// PossiblyCreate creates file if it does not exist, otherwise return the existing one.
	PossiblyCreate = 0

	// NeverCreate returns the existing file. If it does not exist, return ErrMissing
	NeverCreate = 1

	// AlwaysCreate creates the file. If it exists already, return ErrExists
	AlwaysCreate = 2
)
View Source
const NoExpiration = -1

NoExpiration is used in SetExpiration to indicate that documents should never expire.

Variables

View Source
var ErrConflict error

ErrConflict indicates that the base change ID does not match the existing one.

View Source
var ErrExists error

ErrExists indicates that the document already exists

View Source
var ErrMissing error

ErrMissing indicates that the document cannot be found.

View Source
var ErrTokenExists error

ErrTokenExists indicates that the token being added already exists.

Functions

func CORS

func CORS(fn http.Handler) http.HandlerFunc

CORS wraps an HTTP request handler, adding appropriate cors headers. If CORS is desired, you can wrap the handler with it.

func MakeAsyncHTTPRequest

func MakeAsyncHTTPRequest(args HTTPRequestArgs, reply interface{}) chan HTTPRequestResult

MakeAsyncHTTPRequest makes an asyncronous http request, returning it result in a channel.

func RecoverErrors

func RecoverErrors(fn http.Handler) http.HandlerFunc

RecoverErrors will wrap an HTTP handler. When a panic occurs, it will print the stack to the log. Secondly, it will return the internal server error with the status header equal to the error string.

func RunStressTest

func RunStressTest(argsIn StressTestArgs)

RunStressTest runs a stress test against another server. The test continues forever, or until you quit the process.

Types

type CreateMode

type CreateMode int

CreateMode determines if the document should be created if it does not exists.

type DocumentDB

type DocumentDB interface {
	// GetDocument creates or retrieves the document or returns an error, depending on the value of mode.
	// It returns the document and whether or not it was created in this call.
	GetDocument(docID string, mode CreateMode, initialData []byte) ([]byte, bool, error)

	// AppendDocument appends to the document if it exists and the oldLength
	// matches the actual one.
	// If the document is not present, it returns ErrMissing.
	// If the oldLength does not match the one recorded, then it returns ErrConflict and the current document length.
	AppendDocument(docID string, oldLength uint64, newData []byte) (uint64, error)

	// SetKey sets a key associated with a document
	// If the oldVersion matches the current version of the document, or the key is 0 and the document
	// does not exist, then set the key. In all other cases, return ErrConflict
	SetDocumentKey(docID string, oldVersion int, key Key) error

	// GetKey returns all keys associated with the document.
	GetDocumentKeys(docID string) ([]Key, error)

	// SetExpirationTime sets the number of seconds that a document is kept without any activity
	// before it is deleted. The zero value is the default (never)
	SetExpiration(seconds int64)

	DeleteDocument(docID string) error

	// AddToken shall associate token/document/user/permissions together
	// if token already exists, return ErrExists
	// if contents specified and document already exists, return ErrConflict
	AddToken(token, docID, userID, permissions string, expirationSeconds int64, contents []byte) error

	// Given a token, returns docID, userID, permissions. If it does not exist or is expired,
	// the error is ErrMissing
	GetToken(token string) (string, string, string, error)

	// If the user has any tokens, the permissions of all of them are updated.
	UpdateUser(userID, permissions string) error

	// Check the health of the database. Used periodically by a watchdog to determine if the entire
	// server is still functioning correctly.
	CheckHealth() error
}

DocumentDB is the interface to a document storage.

func NewMariaDBConnection

func NewMariaDBConnection(server, user, password, dbname string) DocumentDB

func NewMemoryDB

func NewMemoryDB() DocumentDB

NewMemoryDB ...

func NewMySQLConnection

func NewMySQLConnection(server, user, password, dbname string) DocumentDB

func NewPostgreSQLConnection

func NewPostgreSQLConnection(server, user, password, dbname string) DocumentDB

func NewRedisClusterDB

func NewRedisClusterDB(options *redis.ClusterOptions) DocumentDB

NewRedisClusterDB creates a new document storage based on Redis cluster

func NewRedisDB

func NewRedisDB(options *redis.Options) DocumentDB

NewRedisDB creates a new document storage based on Redis

func NewSQLITEDB

func NewSQLITEDB(filename string) DocumentDB

NewSQLITEDB creates a new document storage based on SQLITE

func NewSQLXConnection

func NewSQLXConnection(driverName, dataSourceName string, schema string, maxOpenConnections int, rebind bool) DocumentDB

type HAE

type HAE interface {
	SetServerID(id string)
	SetUrls(urls []string)
	SetSecurityInfo(secretUser, secretPassword, jwtKey string, keyIsBase64 bool)
	HandleIncomingConnection(ws *websocket.Conn, m []uint8)
	NotifyClientAddRemove(docID string, clientID string, docLength uint64, added bool)
	NotifyAppend(docID string, offset uint64, data []byte)
	NotifyBroadcast(docID string, data []byte)
	NotifyKeyUpdated(docID, clientID, name, value string, sessionLifetime bool)
}

HAE is an interface that enables High Availability. It is not included in the open source version.

type HTTPError

type HTTPError interface {
	Error() string
	StatusCode() int
}

func HTTPPanic

func HTTPPanic(status int, fmtStr string, args ...interface{}) HTTPError

HTTPPanic will cause a panic with an HTTPError. This is expected to be recovered at a higher level, for example using the RecoverErrors middleware so the error is returned to the client.

type HTTPRequestArgs

type HTTPRequestArgs struct {
	// GET, POST, ETC
	Method string

	URI string

	Headers map[string]string

	Data map[string]interface{}

	Body []byte

	// If JSON is to be sent in the post request
	JSON interface{}

	// If basic authentication is used
	Username string
	Password string
}

HTTPRequestArgs is the parameters for the request

type HTTPRequestResult

type HTTPRequestResult struct {
	Err        error
	StatusCode int
	Header     http.Header
	RawReply   []byte
}

HTTPRequestResult contains the meta information about the reply.

func MakeHTTPRequest

func MakeHTTPRequest(args HTTPRequestArgs, reply interface{}) HTTPRequestResult

MakeHTTPRequest makes an HTTP request

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler is an HTTP handler that will enable collaboration between clients.

func NewHandler

func NewHandler(db DocumentDB) *Handler

NewHandler returns a new Zwibbler Handler. You must pass it a document database to use. You may use one of MemoryDocumentDB, SQLITEDocumentDB or create your own.

func (*Handler) EnableHAE

func (zh *Handler) EnableHAE(hae HAE)

EnableHAE enables High Availability Extensions using the given interface to the implementation. This must be the first method that you call, before setting the security info, server id, etc.

func (*Handler) ServeHTTP

func (zh *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP ...

func (*Handler) SetCompressionAllowed

func (zh *Handler) SetCompressionAllowed(allowed bool)

SetCompressionAllowed allows you to always disable socket compression. By default, socket compression is allowed.

func (*Handler) SetJWTKey

func (zh *Handler) SetJWTKey(key string, keyIsBase64 bool)

SetJWTKey enables JWT mode, so that only document IDs contained inside a valid JWT will be accepted. The tokens must be signed using HMAC-SHA256 and this secret key.

func (*Handler) SetSecretUser

func (zh *Handler) SetSecretUser(username, password string)

SetSecretUser allow you to set the secret username and password used in Webhooks and to authenticate requests like dumping and deleting documents.

func (*Handler) SetServerID

func (zh *Handler) SetServerID(id string)

SetServeID sets the server ID of the server for use with High Availability. If unset, a random server ID is chosen.

func (*Handler) SetSwarmURLs

func (zh *Handler) SetSwarmURLs(urls []string)

SetSwarmURLs sets the urls of other servers in the swarm.

func (*Handler) SetWebhookURL

func (zh *Handler) SetWebhookURL(url string)

SetWebhookURL sets a url to receive an event, a few minutes after all users have left a session.

type Hub

type Hub interface {
	Append(docid, clientid string, offset uint64, data []byte)
	Broadcast(docid, clientid string, data []byte)
	EachKey(f func(docID, clientID, name, value string, sessionLifetime bool))
	EachClient(f func(docID, clientID string, docLength uint64))
	SetClientKey(docID string, sourceID string, oldVersion, newVersion int, name, value string) bool
	SetSessionKey(docID string, sourceID string, key Key)
	RemoveClient(docID string, clientID string)
	CheckMissedUpdate(docid string, doc []byte, keys []Key)
}

Hub is an interface to the collaboration hub that is used for high availability extensions.

type Key

type Key struct {
	Version int
	Name    string
	Value   string
}

Key is a key that can be set by clients, related to the session.

type MemoryDocumentDB

type MemoryDocumentDB struct {
	// contains filtered or unexported fields
}

MemoryDocumentDB ...

func (*MemoryDocumentDB) AddToken

func (db *MemoryDocumentDB) AddToken(tokenIn, docID, userID, permissions string, expirationSeconds int64, contents []byte) error

func (*MemoryDocumentDB) AppendDocument

func (db *MemoryDocumentDB) AppendDocument(docID string, oldLength uint64, newData []byte) (uint64, error)

AppendDocument ...

func (*MemoryDocumentDB) CheckHealth

func (db *MemoryDocumentDB) CheckHealth() error

func (*MemoryDocumentDB) DeleteDocument

func (db *MemoryDocumentDB) DeleteDocument(docID string) error

func (*MemoryDocumentDB) GetDocument

func (db *MemoryDocumentDB) GetDocument(docID string, mode CreateMode, initialData []byte) ([]byte, bool, error)

GetDocument ...

func (*MemoryDocumentDB) GetDocumentKeys

func (db *MemoryDocumentDB) GetDocumentKeys(docID string) ([]Key, error)

GetDocumentKeys ...

func (*MemoryDocumentDB) GetToken

func (db *MemoryDocumentDB) GetToken(tokenID string) (docID, userID, permissions string, err error)

func (*MemoryDocumentDB) SetDocumentKey

func (db *MemoryDocumentDB) SetDocumentKey(docID string, oldVersion int, key Key) error

SetDocumentKey ...

func (*MemoryDocumentDB) SetExpiration

func (db *MemoryDocumentDB) SetExpiration(seconds int64)

SetExpiration ...

func (*MemoryDocumentDB) UpdateUser

func (db *MemoryDocumentDB) UpdateUser(userid, permissions string) error

type RedisDocumentDB

type RedisDocumentDB struct {
	// contains filtered or unexported fields
}

RedisDocumentDB is a document database using Redis The documents are stored as a string with the key "zwibbler:"+docID The keys for the document are stored as an HKEY with the key "zwibbler-keys:"+docID The tokens are stored as an hkey under the name: zwibbler-token: and have docID, userID, permissions. zwibbler-user: maps from userid to a set of tokens associated with the user. The zwibbler-user keys must be periodically cleaned because they do not automatically expire, but the tokens do.

func (*RedisDocumentDB) AddToken

func (db *RedisDocumentDB) AddToken(tokenID, docID, userID, permissions string, expirationSeconds int64, contents []byte) error

AddToken ...

func (*RedisDocumentDB) AppendDocument

func (db *RedisDocumentDB) AppendDocument(docIDin string, oldLength uint64, newData []byte) (uint64, error)

AppendDocument ...

func (*RedisDocumentDB) CheckHealth

func (db *RedisDocumentDB) CheckHealth() error

func (*RedisDocumentDB) DeleteDocument

func (db *RedisDocumentDB) DeleteDocument(docID string) error

func (*RedisDocumentDB) GetDocument

func (db *RedisDocumentDB) GetDocument(docID string, mode CreateMode, initialData []byte) ([]byte, bool, error)

GetDocument ...

func (*RedisDocumentDB) GetDocumentKeys

func (db *RedisDocumentDB) GetDocumentKeys(docID string) ([]Key, error)

GetDocumentKeys ...

func (*RedisDocumentDB) GetToken

func (db *RedisDocumentDB) GetToken(token string) (docID, userID, permissions string, err error)

Given a token, returns docID, userID, permissions. If it does not exist or is expired, the error is ErrMissing

func (*RedisDocumentDB) SetDocumentKey

func (db *RedisDocumentDB) SetDocumentKey(docIDin string, oldVersion int, key Key) error

SetDocumentKey ...

func (*RedisDocumentDB) SetExpiration

func (db *RedisDocumentDB) SetExpiration(seconds int64)

SetExpiration ...

func (*RedisDocumentDB) UpdateUser

func (db *RedisDocumentDB) UpdateUser(userID, permissions string) error

If the user has any tokens, the permissions of all of them are updated.

type SQLxDocumentDB

type SQLxDocumentDB struct {
	// contains filtered or unexported fields
}

func (*SQLxDocumentDB) AddToken

func (db *SQLxDocumentDB) AddToken(tokenID, docID, userID, permissions string, expirationSeconds int64, contents []byte) error

AddToken ...

func (*SQLxDocumentDB) AppendDocument

func (db *SQLxDocumentDB) AppendDocument(docID string, oldLength uint64, newData []byte) (uint64, error)

AppendDocument ...

func (*SQLxDocumentDB) CheckHealth

func (db *SQLxDocumentDB) CheckHealth() error

func (*SQLxDocumentDB) DeleteDocument

func (db *SQLxDocumentDB) DeleteDocument(docID string) error

func (*SQLxDocumentDB) GetDocument

func (db *SQLxDocumentDB) GetDocument(docID string, mode CreateMode, initialData []byte) ([]byte, bool, error)

GetDocument ...

func (*SQLxDocumentDB) GetDocumentKeys

func (db *SQLxDocumentDB) GetDocumentKeys(docID string) ([]Key, error)

GetDocumentKeys ...

func (*SQLxDocumentDB) GetToken

func (db *SQLxDocumentDB) GetToken(token string) (docID, userID, permissions string, err error)

func (*SQLxDocumentDB) SetDocumentKey

func (db *SQLxDocumentDB) SetDocumentKey(docID string, oldVersion int, key Key) error

SetDocumentKey ...

func (*SQLxDocumentDB) SetExpiration

func (db *SQLxDocumentDB) SetExpiration(seconds int64)

SetExpiration ...

func (*SQLxDocumentDB) UpdateUser

func (db *SQLxDocumentDB) UpdateUser(userID, permissions string) error

type StressTestArgs

type StressTestArgs struct {
	// The address of the other server, eg wss://otherserver.com/socket
	Address string

	// The document to connect to
	DocumentID string

	// The number of clients which are modifying the document
	NumTeachers int

	// The number of clients which are merely listening for changes
	NumStudents int

	// The average number of milliseconds a teacher waits before making each change.
	// Default: 1000
	DelayMS int

	// The number of bytes in each change (default: 200)
	ChangeLength int

	// Show all steps
	Verbose bool
}

StressTestArgs gives the parameters for performing a stress test against another server.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL