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
- Variables
- func CORS(fn http.Handler) http.HandlerFunc
- func MakeAsyncHTTPRequest(args HTTPRequestArgs, reply interface{}) chan HTTPRequestResult
- func RecoverErrors(fn http.Handler) http.HandlerFunc
- func RunStressTest(argsIn StressTestArgs)
- type CreateMode
- type DocumentDB
- func NewMariaDBConnection(server, user, password, dbname string) DocumentDB
- func NewMemoryDB() DocumentDB
- func NewMySQLConnection(server, user, password, dbname string) DocumentDB
- func NewPostgreSQLConnection(server, user, password, dbname string) DocumentDB
- func NewRedisClusterDB(options *redis.ClusterOptions) DocumentDB
- func NewRedisDB(options *redis.Options) DocumentDB
- func NewSQLITEDB(filename string) DocumentDB
- func NewSQLXConnection(driverName, dataSourceName string, schema string, maxOpenConnections int, ...) DocumentDB
- type HAE
- type HTTPError
- type HTTPRequestArgs
- type HTTPRequestResult
- type Handler
- func (zh *Handler) EnableHAE(hae HAE)
- func (zh *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (zh *Handler) SetCompressionAllowed(allowed bool)
- func (zh *Handler) SetJWTKey(key string, keyIsBase64 bool)
- func (zh *Handler) SetSecretUser(username, password string)
- func (zh *Handler) SetServerID(id string)
- func (zh *Handler) SetSwarmURLs(urls []string)
- func (zh *Handler) SetWebhookURL(url string)
- type Hub
- type Key
- type MemoryDocumentDB
- func (db *MemoryDocumentDB) AddToken(tokenIn, docID, userID, permissions string, expirationSeconds int64, ...) error
- func (db *MemoryDocumentDB) AppendDocument(docID string, oldLength uint64, newData []byte) (uint64, error)
- func (db *MemoryDocumentDB) CheckHealth() error
- func (db *MemoryDocumentDB) DeleteDocument(docID string) error
- func (db *MemoryDocumentDB) GetDocument(docID string, mode CreateMode, initialData []byte) ([]byte, bool, error)
- func (db *MemoryDocumentDB) GetDocumentKeys(docID string) ([]Key, error)
- func (db *MemoryDocumentDB) GetToken(tokenID string) (docID, userID, permissions string, err error)
- func (db *MemoryDocumentDB) SetDocumentKey(docID string, oldVersion int, key Key) error
- func (db *MemoryDocumentDB) SetExpiration(seconds int64)
- func (db *MemoryDocumentDB) UpdateUser(userid, permissions string) error
- type RedisDocumentDB
- func (db *RedisDocumentDB) AddToken(tokenID, docID, userID, permissions string, expirationSeconds int64, ...) error
- func (db *RedisDocumentDB) AppendDocument(docIDin string, oldLength uint64, newData []byte) (uint64, error)
- func (db *RedisDocumentDB) CheckHealth() error
- func (db *RedisDocumentDB) DeleteDocument(docID string) error
- func (db *RedisDocumentDB) GetDocument(docID string, mode CreateMode, initialData []byte) ([]byte, bool, error)
- func (db *RedisDocumentDB) GetDocumentKeys(docID string) ([]Key, error)
- func (db *RedisDocumentDB) GetToken(token string) (docID, userID, permissions string, err error)
- func (db *RedisDocumentDB) SetDocumentKey(docIDin string, oldVersion int, key Key) error
- func (db *RedisDocumentDB) SetExpiration(seconds int64)
- func (db *RedisDocumentDB) UpdateUser(userID, permissions string) error
- type SQLxDocumentDB
- func (db *SQLxDocumentDB) AddToken(tokenID, docID, userID, permissions string, expirationSeconds int64, ...) error
- func (db *SQLxDocumentDB) AppendDocument(docID string, oldLength uint64, newData []byte) (uint64, error)
- func (db *SQLxDocumentDB) CheckHealth() error
- func (db *SQLxDocumentDB) DeleteDocument(docID string) error
- func (db *SQLxDocumentDB) GetDocument(docID string, mode CreateMode, initialData []byte) ([]byte, bool, error)
- func (db *SQLxDocumentDB) GetDocumentKeys(docID string) ([]Key, error)
- func (db *SQLxDocumentDB) GetToken(token string) (docID, userID, permissions string, err error)
- func (db *SQLxDocumentDB) SetDocumentKey(docID string, oldVersion int, key Key) error
- func (db *SQLxDocumentDB) SetExpiration(seconds int64)
- func (db *SQLxDocumentDB) UpdateUser(userID, permissions string) error
- type StressTestArgs
Constants ¶
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 )
const NoExpiration = -1
NoExpiration is used in SetExpiration to indicate that documents should never expire.
Variables ¶
var ErrConflict error
ErrConflict indicates that the base change ID does not match the existing one.
var ErrExists error
ErrExists indicates that the document already exists
var ErrMissing error
ErrMissing indicates that the document cannot be found.
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 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 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 ¶
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 ¶
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 ¶
SetCompressionAllowed allows you to always disable socket compression. By default, socket compression is allowed.
func (*Handler) SetJWTKey ¶
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 ¶
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 ¶
SetServeID sets the server ID of the server for use with High Availability. If unset, a random server ID is chosen.
func (*Handler) SetSwarmURLs ¶
SetSwarmURLs sets the urls of other servers in the swarm.
func (*Handler) SetWebhookURL ¶
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 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.