Async peer communication protocol & library

Overview

gotalk

Gotalk exists to make it easy for programs to talk with one another over the internet, like a web app coordinating with a web server, or a bunch of programs dividing work amongst each other.

GitHub tag (latest SemVer) PkgGoDev Go Report Card

Gotalk...

  • is an efficient, easily debuggable multiplexing data transfer protocol
  • is transport agnostic: works on any byte stream
  • offers a high-level, easy-to-get-started API for WebSockets
  • enables arbitrary number of requests & responses over a single persistent connection
  • includes a small built-in JavaScript library
  • provides a small and focused Go API

Go API Documentation on godoc.org →

Usage

Gotalk is a simple go module - import it into your program and go build:

import "github.com/rsms/gotalk"

To use a specific version, run go get github.com/rsms/[email protected] (substituting the version number for the version you desire.)

Examples can be found in the examples directory. Build them with go build:

$ cd examples/websocket-chat
$ go build
$ ./websocket-chat
Listening on http://localhost:1235/

Here's a minimal but complete example program: (examples/websocket-minimal)

package main
import (
  "net/http"
  "github.com/rsms/gotalk"
)
func main() {
  gotalk.Handle("echo", func(in string) (string, error) {
    return in, nil
  })
  http.Handle("/gotalk/", gotalk.WebSocketHandler())
  http.Handle("/", http.FileServer(http.Dir(".")))
  print("Listening on http://localhost:1234/\n")
  panic(http.ListenAndServe("localhost:1234", nil))
}

Developing Gotalk & contributing

See CONTRIBUTING.md

Other implementations

Introduction

A terribly boring amateur comic strip

Gotalk takes the natural approach of bidirectional and concurrent communication — any peer have the ability to expose "operations" as well as asking other peers to perform operations. The traditional restrictions of who can request and who can respond usually associated with a client-server model is nowhere to be found in Gotalk.

Gotalk in a nutshell

Bidirectional — There's no discrimination on capabilities depending on who connected or who accepted. Both "servers" and "clients" can expose operations as well as send requests to the other side.

Concurrent — Requests, results, and notifications all share a single connection without blocking each other by means of pipelining. There's no serialization on request-result or even for a single large message, as the Gotalk protocol is frame-based and multiplexes messages over a single connection. This means you can perform several requests at once without having to think about queueing or blocking.

Diagram of how Gotalk uses connection pipelining

Simple — Gotalk has a simple and opinionated API with very few components. You expose an operation via "handle" and send requests via "request".

Debuggable — The Gotalk protocol's wire format is ASCII-based for easy on-the-wire inspection of data. For example, here's a protocol message representing an operation request: r0001005hello00000005world. The Gotalk protocol can thus be operated over any reliable byte transport.

Practical — Gotalk includes a JavaScript implementation for Web Sockets alongside the full-featured Go implementation, making it easy to build real-time web applications. The Gotalk source code also includes a number of easily-readable examples.

By example

There are a few examples in the examples directory demonstrating Gotalk. But let's explore a simple program right now — here's a little something written in Go which demonstrates the use of an operation named "greet":

func server() {
  gotalk.Handle("greet", func(in GreetIn) (GreetOut, error) {
    return GreetOut{"Hello " + in.Name}, nil
  })
  if err := gotalk.Serve("tcp", "localhost:1234"); err != nil {
    log.Fatalln(err)
  }
}

func client() {
  s, err := gotalk.Connect("tcp", "localhost:1234")
  if err != nil {
    log.Fatalln(err)
  }
  greeting := &GreetOut{}
  if err := s.Request("greet", GreetIn{"Rasmus"}, greeting); err != nil {
    log.Fatalln(err)
  }
  log.Printf("greeting: %+v\n", greeting)
  s.Close()
}

Let's look at the above example in more detail, broken apart to see what's going on.

We begin by importing the gotalk library together with log which we use for printing to the console:

package main
import (
  "log"
  "github.com/rsms/gotalk"
)

We define two types: Expected input (request parameters) and output (result) for our "greet" operation:

type GreetIn struct {
  Name string `json:"name"`
}
type GreetOut struct {
  Greeting string `json:"greeting"`
}

Registers a process-global request handler for an operation called "greet" accepting parameters of type GreetIn, returning results of type GreetOut:

func server() {
  gotalk.Handle("greet", func(in GreetIn) (GreetOut, error) {
    return GreetOut{"Hello " + in.Name}, nil
  })

Finally at the bottom of our server function we call gotalk.Serve, which starts a local TCP server on port 1234:

  if err := gotalk.Serve("tcp", "localhost:1234"); err != nil {
    log.Fatalln(err)
  }
}

In out client function we start by connecting to the server:

func client() {
  s, err := gotalk.Connect("tcp", "localhost:1234")
  if err != nil {
    log.Fatalln(err)
  }

Finally we send a request for "greet" and print the result:

  greeting := GreetOut{}
  if err := s.Request("greet", GreetIn{"Rasmus"}, &greeting); err != nil {
    log.Fatalln(err)
  }
  log.Printf("greeting: %+v\n", greeting)

  s.Close()
}

Output:

greeting: {Greeting:Hello Rasmus}

Gotalk in the web browser

Gotalk is implemented not only in the full-fledged Go package, but also in a JavaScript library. This allows writing web apps talking Gotalk via Web Sockets possible.

// server.go:
package main
import (
  "net/http"
  "github.com/rsms/gotalk"
)
func main() {
  gotalk.Handle("echo", func(in string) (string, error) {
    return in, nil
  })
  http.Handle("/gotalk/", gotalk.WebSocketHandler())
  http.Handle("/", http.FileServer(http.Dir(".")))
  err := http.ListenAndServe("localhost:1234", nil)
  if err != nil {
    panic(err)
  }
}

In our html document, we begin by registering any operations we can handle:

">

<body>
<script type="text/javascript" src="/gotalk/gotalk.js">script>
<script>
gotalk.handle('greet', function (params, result) {
  result({ greeting: 'Hello ' + params.name });
});
script>

Notice how we load a JavaScript from "/gotalk/gotalk.js" — a gotalk web socket server embeds a matching web browser JS library which it returns from {path where gotalk web socket is mounted}/gotalk.js. It uses Etag cache validation, so you shouldn't need to think about "cache busting" the URL.

We can't "listen & accept" connections in a web browser, but we can "connect" so we do just that:

">

<body>
<script type="text/javascript" src="/gotalk/gotalk.js">script>
<script>
gotalk.handle('greet', function (params, result) {
  result({ greeting: 'Hello ' + params.name });
});

var s = gotalk.connection().on('open', function () {
  // do something useful
}).on('close', function (err) {
  if (err.isGotalkProtocolError) return console.error(err);
});
script>

This is enough for enabling the server to do things in the browser ...

But you probably want to have the browser send requests to the server, so let's send a "echo" request just as our connection opens:

var s = gotalk.connection().on('open', function () {
  s.request("echo", "Hello world", function (err, result) {
    if (err) return console.error('echo failed:', err);
    console.log('echo result:', result);
  });
});

We could rewrite our code like this to allow some UI component to send a request:

var s = gotalk.connection();

button.addEventListener('click', function () {
  s.request("echo", "Hello world", function (err, result) {
    if (err) return console.error('echo failed:', err);
    console.log('echo result:', result);
  });
});

The request will fail with an error "socket is closed" if the user clicks our button while the connection isn't open.

There are two ways to open a connection on a socket: Sock.prototype.open which simply opens a connection, and Sock.prototype.openKeepAlive which keeps the connection open, reconnecting as needed with exponential back-off and internet reachability knowledge. gotalk.connection() is a short-hand for creating a new Sock with gotalk.defaultHandlers and then calling openKeepAlive on it.

Protocol and wire format

The wire format is designed to be human-readable and flexible; it's byte-based and can be efficiently implemented in a number of environments ranging from HTTP and WebSocket in a web browser to raw TCP in Go or C. The protocol provides only a small set of operations on which more elaborate operations can be modeled by the user.

This document describes protocol version 1

Here's a complete description of the protocol:

operation = text3 name = text3 wait = hexUInt8 code = hexUInt8 time = hexUInt8 load = hexUInt4 text3 = text3Size text3Value text3Size = hexUInt3 text3Value = <{text3Size} as utf8 text> payload = payloadSize payloadData payloadSize = hexUInt8 payloadData = {payloadSize} hexUInt3 = hexUInt4 = hexUInt8 = ">
conversation    = ProtocolVersion Message*
message         = SingleRequest | StreamRequest
                | SingleResult | StreamResult
                | ErrorResult | RetryResult
                | Notification | ProtocolError

ProtocolVersion =  

SingleRequest   = "r" requestID operation payload
StreamRequest   = "s" requestID operation payload StreamReqPart*
StreamReqPart   = "p" requestID payload
SingleResult    = "R" requestID payload
StreamResult    = "S" requestID payload StreamResult*
ErrorResult     = "E" requestID payload
RetryResult     = "e" requestID wait payload
Notification    = "n" name payload
Heartbeat       = "h" load time
ProtocolError   = "f" code

requestID       =    

operation       = text3
name            = text3
wait            = hexUInt8
code            = hexUInt8
time            = hexUInt8
load            = hexUInt4

text3           = text3Size text3Value
text3Size       = hexUInt3
text3Value      = <{text3Size} as utf8 text>

payload         = payloadSize payloadData
payloadSize     = hexUInt8
payloadData     = {payloadSize}

hexUInt3        =   
hexUInt4        =    
hexUInt8        =    
                     

Handshake

A conversation begins with the protocol version:

01  -- ProtocolVersion 1

If the version of the protocol spoken by the other end is not supported by the reader, a ProtocolError message is sent with code 1 and the connection is terminated. Otherwise, any messages are read and/or written.

Single-payload requests and results

This is a "single-payload" request ...

+------------------ SingleRequest
|   +---------------- requestID   "0001"
|   |      +--------- operation   "echo" (text3Size 4, text3Value "echo")
|   |      |       +- payloadSize 25
|   |      |       |
r0001004echo00000019{"message":"Hello World"}

... and a corresponding "single-payload" result:

+------------------ SingleResult
|   +---------------- requestID   "0001"
|   |       +-------- payloadSize 25
|   |       |
R000100000019{"message":"Hello World"}

Each request is identified by exactly three bytes—the requestID—which is requestor-specific and has no purpose beyond identity, meaning the value is never interpreted. 4 bytes can express 4 294 967 296 different values, meaning we can send up to 4 294 967 295 requests while another request is still being served. Should be enough.

These "single" requests & results are the most common protocol messages, and as their names indicates, their payloads follow immediately after the header. For large payloads this can become an issue when dealing with many concurrent requests over a single connection, for which there's a more complicated "streaming" request & result type which we will explore later on.

Faults

There are two types of replies indicating a fault: ErrorResult for requestor faults and RetryResult for responder faults.

If a request is faulty, like missing some required input data or sent over an unauthorized connection, an "error" is send as the reply instead of a regular result:

+------------------ ErrorResult
|   +---------------- requestID   "0001"
|   |       +-------- payloadSize 38
|   |       |
E000100000026{"error":"Unknown operation \"echo\""}

A request that produces an error should not be retried as-is, similar to the 400-class of errors of the HTTP protocol.

In the scenario a fault occurs on the responder side, like suffering a temporary internal error or is unable to complete the request because of resource starvation, a RetryResult is sent as the reply to a request:

+-------------------- RetryResult
|   +------------------ requestID   "0001"
|   |       +---------- wait        0
|   |       |       +-- payloadSize 20
|   |       |       |
e00010000000000000014"service restarting"

In this case — where wait is zero — the requestor is free to retry the request at its convenience.

However in some scenarios the responder might require the requestor to wait for some time before retrying the request, in which case the wait property has a non-zero value:

+-------------------- RetryResult
|   +------------------ requestID   "0001"
|   |       +---------- wait        5000 ms
|   |       |       +-- payloadSize 20
|   |       |       |
e00010000138800000014"request rate limit"

In this case the requestor must not retry the request until at least 5000 milliseconds has passed.

If the protocol communication itself experiences issues—e.g. an illegal message is received—a ProtocolError is written and the connection is closed.

ProtocolError codes:

Code Meaning Comments
0 Abnormal Closed because of an abnormal condition (e.g. server fault, etc)
1 Unsupported protocol The other side does not support the callers protocol
2 Invalid message An invalid message was transmitted
3 Timeout The other side closed the connection because communicating took too long

Example of a peer which does not support the version of the protocol spoken by the sender:

+-------- ProtocolError
|       +-- code 1
|       |
f00000001

Streaming requests and results

For more complicated scenarios there are "streaming-payload" requests and results at our disposal. This allows transmitting of large amounts of data without the need for large buffers. For example this could be used to forward audio data to audio playback hardware, or to transmit a large file off of slow media like a tape drive or hard-disk drive.

Because transmitting a streaming request or result does not occupy "the line" (single-payloads are transmitted serially), they can also be useful when there are many concurrent requests happening over a single connection.

Here's an example of a "streaming-payload" request ...

+------------------ StreamRequest
|   +---------------- requestID   "0001"
|   |      +--------- operation   "echo" (text3Size 4, text3Value "echo")
|   |      |       +- payloadSize 11
|   |      |       |
s0001004echo0000000b{"message":

+------------------ streamReqPart
|   +---------------- requestID   "0001"
|   |       +-------- payloadSize 14
|   |       |
p00010000000e"Hello World"}

+------------------ streamReqPart
|   +---------------- requestID   "0001"
|   |       +-------- payloadSize 0 (end of stream)
|   |       |
p000100000000

... followed by a "streaming-payload" result:

+------------------ StreamResult (1st part)
|   +---------------- requestID   "0001"
|   |       +-------- payloadSize 11
|   |       |
S00010000000b{"message":

+------------------ StreamResult (2nd part)
|   +---------------- requestID   "0001"
|   |       +-------- payloadSize 14
|   |       |
S00010000000e"Hello World"}

+------------------ StreamResult
|   +---------------- requestID   "0001"
|   |       +-------- payloadSize 0 (end of stream)
|   |       |
S000100000000

Streaming requests occupy resources on the responder's side for the duration of the "stream session". Therefore handling of streaming requests should be limited and "RetryResult" used to throttle requests:

+-------------------- RetryResult
|   +------------------ requestID   "0001"
|   |       +---------- wait        5000 ms
|   |       |       +-- payloadSize 19
|   |       |       |
e00010000138800000013"stream rate limit"

This means that the requestor must not send any new requests until wait time has passed.

Notifications

When there's no expectation on a response, Gotalk provides a "notification" message type:

+---------------------- Notification
|              +--------- name        "chat message" (text3Size 12, text3Value "chat message")
|              |       +- payloadSize 46
|              |       |
n00cchat message0000002e{"message":"Hi","from":"nthn","room":"gonuts"}

Notifications are never replied to nor can they cause "error" results. Applications needing acknowledgement of notification delivery might consider using a request instead.

Heartbeats

Because most responders will limit the time it waits for reads, a heartbeat message is send at a certain interval. When a heartbeat is sent is up to the implementation.

A heartbeat contains the sender's local time in the form of an unsigned 32-bit UNIX timestamp. This is enought to cover usage until 2106. I really hope gotalk is nowhere to be found in 2106.

It also contains an optional "load" value, indicating how pressured, or under what load, the sender is. 0 means "idle" and 65535 (0xffff) means "omg I think I'm dying." This can be used to distribute work to less loaded responders in a load-balancing setup.

+------------------ Heartbeat
|   +--------- load 2
|   |       +- time 2015-02-08 22:09:30 UTC
|   |       |
h000254d7de9a

Notes

Requests and results does not need to match on the "single" vs "streaming" detail — it's perfectly fine to send a streaming request and read a single response, or send a single response just to receive a streaming result. The payload type is orthogonal to the message type, with the exception of an error response which is always a "single-payload" message, carrying any information about the error in its payload. Note however that the current version of the Go package does not provide a high-level API for mixed-kind request-response handling.

For transports which might need "heartbeats" to stay alive, like some raw TCP connections over the internet, the suggested way to implement this is by notifications, e.g. send a "heartbeat" notification at a certain interval while no requests are being sent. The Gotalk protocol does not include a "heartbeat" feature because of this reason, as well as the fact that some transports (like web socket) already provide "heartbeat" features.

Comments
  • Add an example with encrypted connections.

    Add an example with encrypted connections.

    Please add an example for establishing encrypted connections. A quite important feature nowadays.

    You are welcome to include my script if you use TLS: https://gist.github.com/wmark/c758ce1c2b8222afd69d

    enhancement 
    opened by wmark 7
  • Support for custom encoding. Defaults to JSON.

    Support for custom encoding. Defaults to JSON.

    The GoTalk protocol is encoding neutral, but the Go API currently requires JSON. This change allows for custom encoders and decoders to be provided. eg. protocol buffers.

    This does not change the default behaviour or break the API.

    enhancement 
    opened by alecthomas 4
  • [gotalk.js] Uncaught ReferenceError: s is not defined

    [gotalk.js] Uncaught ReferenceError: s is not defined

    when using gotalk.js in browser side, Sock.notify and Sock.stopSendingHeartbeats all have the error message:Uncaught ReferenceError: s is not defined. When I'm looking through the code I found this in gotalk/index.js:

    Sock.prototype.stopSendingHeartbeats = function() {
      clearTimeout(s._sendHeartbeatsTimer);
    };
    

    and this for Sock.bufferNotify

    Sock.prototype.bufferNotify = function(name, buf) {
      s.sendMsg(protocol.MsgTypeNotification, null, name, 0, buf);
    }
    

    is this right?

    opened by oskca 1
  • gotalk/js: exit status 3221225725

    gotalk/js: exit status 3221225725

    This happens on windows amd64, because of this file https://github.com/rsms/gotalk/blob/master/js/gotalk.js.go

    Is there any way to avoid large amount of string concatenations? According to @bradfitz it's the main problem, similar bug https://code.google.com/p/go/issues/detail?id=8240#c2

    opened by pavel-main 1
  • Possible Notification discrepancy

    Possible Notification discrepancy

    I noticed the following example:

    +---------------------- Notification
    |              +--------- type        "chat message"
    |              |       +- payloadSize 50
    |              |       |
    n00cchat message00000032{"message":"Hi","from":"nthn","chat_room":"gonuts"}
    

    With the following definition:

    Notification    = "n" type payload
    
    type            = text3
    payload         = payloadSize payloadData?
    payloadSize     = hexUInt8
    payloadData     = <byte>{payloadSize}
    
    text3           = text3Size text3Value
    text3Size       = hexUInt3
    text3Value      = <<byte>{text3Size} as utf8 text>
    

    It looks like the text3 isn't documented in the example. Should be:

    +---------------------- Notification
    |  +--------------------- text3Size   12
    |  |           +--------- type        "chat message"
    |  |           |       +- payloadSize 50
    |  |           |       |
    n00cchat message00000032{"message":"Hi","from":"nthn","chat_room":"gonuts"}
    

    This tripped me up a little when working on a quick and dirty Python implementation: https://github.com/gtaylor/python-gotalk

    opened by gtaylor 1
  • Gotalk connection close is called after 30 seconds

    Gotalk connection close is called after 30 seconds

    Hi everyone, I have server side

    gotalk.Handle("connect", func(req model.SubscribeMachineReq) (model.SubscribeMachineReq, error) {
    		log.Println("connect received")
    		return req, nil
    	})
    	if err := gotalk.Serve("tcp", "localhost:1234",func (s *gotalk.Sock){}); err != nil {
    		log.Println(err.Error())
    	}
    

    as well as client side

    sock, err := gotalk.Connect("tcp", "localhost:1234")
    	if err != nil {
    		log.Println("Couldnt connect to streamer 1")
    		time.Sleep(time.Second * 10)
    		return
    	}
    	log.Println("Connected to streamer")
    	sock.CloseHandler = func(socket *gotalk.Sock, code int) {
    		Socket=nil
    		log.Println(fmt.Sprintf("Streamer connection is broken with code:%d, retrying...",code))
    		time.Sleep(time.Second * 10)
    	}
    

    The function I've set as CloseHandler is called after exacly 30 seconds of connection, why is that?

    opened by berk-can 0
  • Heartbeat panics when connection is lost

    Heartbeat panics when connection is lost

    Hi there, I am using gotalk with linux devices and we sometimes reboot these devices, and at one time we encountered an error with panic in app

    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x28 pc=0xa64872]
    
    goroutine 3183863 [running]:
    github.com/rsms/gotalk.(*Sock).SendHeartbeat(0xc007e9d140, 0xc03d834c1b, 0xc001cd0dd0, 0x10, 0x10, 0x0, 0x0)
            /Users/berkcan/go/pkg/mod/github.com/rsms/[email protected]/sock.go:662 +0xd2
    github.com/rsms/gotalk.(*Sock).sendHeartbeats(0xc007e9d140, 0xc0050b6720)
            /Users/berkcan/go/pkg/mod/github.com/rsms/[email protected]/sock.go:643 +0xb5
    created by github.com/rsms/gotalk.(*Sock).Read
            /Users/berkcan/go/pkg/mod/github.com/rsms/[email protected]/sock.go:700 +0x8c5
    

    Is there a way to recover from panic

    opened by berk-can 0
  • Protocol test suite

    Protocol test suite

    I am writing a Swift client for GoTalk and it would be excellent if there were a protocol test suite available. I have the base protocol written, but verifying correctness is a matter of trial and error.

    I may tackle this myself, but thought I'd file an issue to gather thoughts first.

    I imagined a data-driven suite would probably be most useful for different language implementations. Perhaps something where an implementation can load a JSON file (or whatever) and playback simulated communications with a server or client.

    Thoughts?

    help wanted testing 
    opened by alecthomas 2
Releases(v1.3.7)
  • v1.3.7(Dec 6, 2020)

  • v1.3.6(Nov 20, 2020)

  • v1.3.5(Nov 20, 2020)

    Adds Handlers.NewSubHandlers() which returns a wrapper

    // NewSubHandlers returns a new Handlers object which wraps the receiver.
    // It can be used to override or extend h, without modifying h.
    // For example, it could be used to expose an extra set of operations to certain sockets,
    // like signed-in users.
    func (h *Handlers) NewSubHandlers() *Handlers
    
    Source code(tar.gz)
    Source code(zip)
  • v1.3.4(Nov 18, 2020)

    Adds ability to customize error logging with gotalk.ErrorLogger and gotalk.HandlerErrorLogger which both have the type gotalk.LoggerFunc

    Example:

    gotalk.HandlerErrorLogger = func(s *gotalk.Sock, format string, args ...interface{}) {
      myLogger.Warn("[gotalk %p] " + format, append([]interface{}{s}, args...)...)
    }
    
    Source code(tar.gz)
    Source code(zip)
  • v1.3.3(Nov 3, 2020)

  • v1.3.1(Oct 28, 2020)

    Adds type WebSocketConnection which is an alias for golang.org/x/net/websocket.Conn along with documentation about how to access the underlying web socket connection inside a gotalk handler.

    Source code(tar.gz)
    Source code(zip)
  • v1.3.0(Oct 27, 2020)

    API changes in version 1.3 compared to 1.2

    • Limits is no longer an interface but not a plain struct with fields you can set and edit. This simplifies the code and makes the API easier to use.

    • Limits now exposes wait times as BufferMinWait, BufferMaxWait, StreamMinWait and StreamMaxWait. These values default to the same as in Gotalk 1.2 (500–5000ms.)

    • The string message of ErrSockClosed has been changed to "socket closed" (was "socket is closed")

    Other changes:

    • Fixes a race condition with closing sockets which could lead to CloseHandler being called twice in case two user goroutines would call Sock.Close()

    • Fixes a race condition that could lead to a deadlock when sending requests and waiting for responses.

    Source code(tar.gz)
    Source code(zip)
  • v1.2.1(Oct 2, 2020)

    Go library: Adds the ability to gracefully shut down sockets via a new method Socket.Shutdown (low level). Note that Web Socket-based gotalk does not need this.

    JS library: Increases the minimum reconnect delay after a clean connection close in keepalive. This has no effect on the API and no effect on semantics. When a server restarts and cleanly shuts down sockets, avoid slamming the server immediately with connection attempts. A future version might introduce randomness to this delay as well.

    Source code(tar.gz)
    Source code(zip)
  • v1.2.0(Oct 1, 2020)

    Improvements to the Sock and WebSocketServer API:

    • WebSocketServer now uses a WebSocket struct type instead of Sock; a specialized derivative of Sock with methods for accessing the underlying web socket and http connection.

    • Adds new callback WebSocketServer.OnConnect, deprecating OnAccept. OnConnect receives a WebSocket struct rather than a Sock struct as is the case with OnAccept (i.e. why OnConnect was added instead of using OnAccept.)

    • Adds method Sock.String() which returns a name that uniquely identifies the socket during its lifetime. Useful for logging.

    • Deprecates the function WebSocketHandler() in favor for a better-named NewWebSocketServer(). Functionality is unchanged.

    Source code(tar.gz)
    Source code(zip)
  • v1.1.5(Oct 1, 2020)

    Addresses an issue in the JavaScript library where in certain conditions an exception "Can't find variable: protocol" would be thrown. This release contains no changes to the go library.

    Source code(tar.gz)
    Source code(zip)
  • v1.1.4(Sep 17, 2020)

    Very minor update that makes it possible to use a zero Handlers struct instead of going through the NewHandlers function. This makes it easier to allocate Handlers for example as part of another, parent struct.

    Example:

    type MyThing struct {
      Gotalk gotalk.Handlers
      myOtherThing Thing
    }
    func main() {
      thing := MyThing{}
      thing.Gotalk.Handle("foo", ...)
      thing.Start()
    }
    func (m *MyThing) Start() {
      gotalk := gotalk.WebSocketHandler()
      gotalk.Handlers = &m.Gotalk
    }
    

    Prior to v1.1.4 you would have to do something like this instead, allocating a Handlers struct via the NewHandlers function:

    type MyThing struct {
      Gotalk *gotalk.Handlers
      myOtherThing Thing
    }
    func NewMyThing() *MyThing {
      return &MyThing{ Gotalk: gotalk.NewHandlers() }
    }
    func main() {
      thing := NewMyThing()
      thing.Gotalk.Handle("foo", ...)
      thing.Start()
    }
    func (m *MyThing) Start() {
      gotalk := gotalk.WebSocketHandler()
      gotalk.Handlers = &m.Gotalk
    }
    
    Source code(tar.gz)
    Source code(zip)
  • v1.1.3(Sep 15, 2020)

    Improvements to the JavaScript library

    gotalk.defaultResponderAddress can now be set to a partial URL, like a path or a hostname without protocol. Examples of acceptable values:

    • ws://host/path -- fully qualified. What was previously required.
    • //host/path -- match protocol script is served over (ws for http, wss for https)
    • /path -- automatic protocol and hostname script is served over/from

    A warning message is now printed to the console when gotalk.developmentMode is true and connection to gotalk websocket server fails, with a helpful message about gotalk.defaultResponderAddress.

    New property gotalk.developmentMode can be set to true to enable aforementioned warning message. It also enables console.error calls with full error information for handler exceptions and messaging errors. gotalk.developmentMode defaults to true when the script is served from localhost, otherwise its value is false by default.

    Source code(tar.gz)
    Source code(zip)
  • v1.1.2(Sep 15, 2020)

    This release only contains improvements to the built-in JS library:

    • New version of JS library itself (see gotalk/js)
    • Simplified serving of the js file over HTTP, including some minor performance improvements
    • Delivers the js file with gzip compression if the HTTP client accepts it
    • No longer includes nor loads the large js.map file into memory
    Source code(tar.gz)
    Source code(zip)
  • v1.1.1(Sep 14, 2020)

  • v1.1.0(Sep 14, 2020)

    • Exposes net.Listener on Server.Listener (previously private)
    • Adds ListenTLS and ListenTLSCustom functions for starting servers with TLS
    • Adds ConnectTLS and Sock.ConnectTLS functions for connecting with TLS
    • Adds Sock.ConnectReader function for connecting over any io.ReadWriteCloser
    • Adds Server.EnableUnixSocketGC convenience helper function
    • Adds TLSCertPool and TLSAddRootCerts functions for customizing default TLS config
    • Semantic change to UNIX socket transport: gotalk no longer installs a signal handler when listening over a UNIX socket using the function Listen("unix", ...). If you want to retain that functionality, explicitly call Server.EnableUnixSocketGC on the server returned by Listen("unix", ...).
    Source code(tar.gz)
    Source code(zip)
  • v1.0.1(Sep 14, 2020)

  • v1.0.0(Sep 14, 2020)

Owner
Rasmus
Languages, compilers, making computers talk with eachother, and other fun things. Professional life is at Figma; previously Facebook, Spotify, Dropbox and more
Rasmus
Instant messaging server for the Extensible Messaging and Presence Protocol (XMPP).

Instant messaging server for the Extensible Messaging and Presence Protocol (XMPP).

Miguel Ángel Ortuño 1.4k Nov 25, 2022
Async peer communication protocol & library

Gotalk exists to make it easy for programs to talk with one another over the internet, like a web app coordinating with a web server, or a bunch of programs dividing work amongst each other.

Rasmus 1.2k Nov 26, 2022
A Go language binding for encodeing and decoding data in the bencode format that is used by the BitTorrent peer-to-peer file sharing protocol.

bencode-go A Go language binding for encoding and decoding data in the bencode format that is used by the BitTorrent peer-to-peer file sharing protoco

Jack Palevich 204 Nov 15, 2022
Peer-to-peer hypermedia protocol

IPFS powers the Distributed Web A peer-to-peer hypermedia protocol to make the web faster, safer, and more open. TL;DR Get help and talk about ideas i

IPFS 21.5k Nov 22, 2022
A Go library for master-less peer-to-peer autodiscovery and RPC between HTTP services

sleuth sleuth is a Go library that provides master-less peer-to-peer autodiscovery and RPC between HTTP services that reside on the same network. It w

null 360 Nov 17, 2022
An imageboard, but images are stored in a peer-to-peer network

Interplanetary File Dumpster An imageboard, but images are stored in a peer-to-peer network Features: Easy file sharing without registration and SMS.

George 94 Sep 30, 2022
DeepValueNetwork is a peer-to-peer database network managed and hosted by its community.

DeepValueNetwork To understand what DeepValueNetwork will be, I suggest you read this document. In progress This software is currently being developed

DeepValue Network 334 Nov 19, 2022
📦 Command line peer-to-peer data transfer tool based on libp2p.

pcp - Peer Copy Command line peer-to-peer data transfer tool based on libp2p. Table of Contents Motivation Project Status How does it work? Usage Inst

Dennis Trautwein 896 Nov 24, 2022
pcp - 📦 Command line peer-to-peer data transfer tool based on libp2p.

pcp - Command line peer-to-peer data transfer tool based on libp2p.

Dennis Trautwein 897 Nov 21, 2022
Group peer to peer video calls for everyone written in Go and TypeScript

Peer Calls v4 WebRTC peer to peer calls for everyone. See it live in action at peercalls.com. The server has been completely rewriten in Go and all th

Peer Calls 1.1k Nov 21, 2022
Berty is a secure peer-to-peer messaging app that works with or without internet access, cellular data or trust in the network

?? Berty is a secure peer-to-peer messaging app that works with or without internet access, cellular data or trust in the network Introduction Berty i

Berty Technologies 6.3k Nov 26, 2022
gopunch is a go implementation of a peer-to-peer chat service built using UDP hole punching.

Gopunch gopunch is a go implementation of a peer-to-peer chat service built using UDP hole punching. This is a toy implementation that I put together

Peyton Walters 10 May 24, 2022
decentralized,distributed,peer-to-peer database.

P2PDB 中文 | English 简介 P2PDB(p2p数据库),是一个去中心化、分布式、点对点数据库、P2PDB使用IPFS为其数据存储和IPFS Pubsub自动与对等方同步数据。P2PDB期望打造一个去中心化的分布式数据库,使P2PDB 成为去中心化应用程序 (dApps)、区块链应用程

Rock 14 Nov 13, 2022
Peer-to-peer encrypted message exchange

Constellation Constellation is a self-managing, peer-to-peer system in which each node: Hosts a number of NaCl (Curve25519) public/private key pairs.

ConsenSys Software 379 Nov 11, 2022
Steve - A peer-to-peer (p2p) decentralized network

Steve Steve is a peer-to-peer (p2p) decentralized network that enables people to

Steve Care Software Inc 4 Feb 5, 2022
peer-to-peer file sharing

what i want is a tool to use to send files my many virtual machines. I want to do this myself, and i want to make it work as expected. So maybe a daem

Joseph Attah 0 Jun 13, 2022
Interblockchain communication protocol (IBC) implementation in Golang.

ibc-go Interblockchain communication protocol (IBC) implementation in Golang built as a SDK module. Components Core The core/ directory contains the S

COSMOS 286 Nov 23, 2022
BrisGolang is a Go implementation of the game of briscola using the WebSocket protocol for client/server communication.

BrisGolang BrisGolang is a Go implementation of the game of briscola using the WebSocket protocol for client/server communication. Usage You can play

Calogero Miraglia 0 Nov 1, 2021
Simple, fast and safe cross-platform linear binary stream communication protocol. AES key exchange based on ecc secp256k1

FFAX Protocol 2 dev 简体中文 Welcome to FFAX Protocol v2 Quick start go get github.com/RealFax/FFAX func example() { listener, err := net.Listen("tcp",

Realfax Messenger 15 Mar 21, 2022
AppsFlyer 500 Dec 1, 2022