Switch branches/tags
Nothing to show
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
browser
gotalk
.gitignore
README.md
build-browser.js
gotalk.js
gotalk.js.go
gotalk.js.map

README.md

Gotalk in JavaScript

The JavaScript implementation of Gotalk currently only supports connecting over web sockets from modern web browsers. A future version might provide "listening" abilities and support for other environments like Nodejs.

Here's an example of connecting to a web server, providing a "prompt" operation (which the server can invoke at ay time to ask the user some question) and finally invoking a "echo" operation on the server.

gotalk.handle('prompt', function (params, result) {
  var answer = prompt(params.question, params.placeholderAnswer);
  result(answer);
});

gotalk.connection().on('open', function (err, s) {
  if (err) return console.error(err);
  s.request('echo', function(err, result) {
    alert('echo returned:', result);
  });
});

API

When using the gotalk.js file (e.g. in a web browser), the API is exposed as window.gotalk. If the gotalk directory containing the Gotalk CommonJS module is used, the API is returned as an object from require('./gotalk').

gotalk

// Connect to `addr`, invoking `cb` when either the connection is open and
// ready to be used, or with an error if connection failed.
connect(addr string, cb function(Error, Sock)) ➝ Sock
// Default `Handlers` utilized by the module-level `handle*` functions
defaultHandlers ➝ Handlers

// Default address to connect to. This is falsey if the JS library isn't served
// by gotalk.
defaultResponderAddress ➝ string
// Open a connection to a gotalk responder.
// If `addr` is not provided, `defaultResponderAddress` is used.
// Equivalent to `Sock(defaultHandlers).open(addr, cb)`
open([addr string], [cb function(Error, Sock)]) ➝ Sock

// Start a persistent (keep-alive) connection to a gotalk responder.
// If `addr` is not provided, `defaultResponderAddress` is used.
// Equivalent to `Sock(defaultHandlers).openKeepAlive(addr)`
connection([addr string]) ➝ Sock
// Convenience "shortcuts" to `defaultHandlers`
handle(op string, Handlers.ReqValueHandler)
handleNotification(name string, Handlers.NotValueHandler)
handleBufferRequest(op string, Handlers.ReqBufferHandler)
handleBufferNotification(name string, Handlers.NotBufferHandler)

Sock

A connection over which gotalk can be spoken.

type Sock prototypeof EventEmitter {
  handlers ➝ Handlers    // default: defaultHandlers
  protocol ⇄ ProtocolImp // default: protocol.binary

  // Open a connection to a gotalk responder.
  // If `addr` is not provided, `defaultResponderAddress` is used.
  open([addr string], [cb function(Error, Sock)]) ➝ Sock

  // Start a persistent (keep-alive) connection to a gotalk responder.
  // If `addr` is not provided, `defaultResponderAddress` is used.
  // Because the "open" step is abstracted away, this function does not accept
  // any "open callback". You should listen to the "open" and "close" events
  // instead.
  // The Sock will stay connected, and reconnect as needed, until you call `end()`.
  openKeepAlive([addr string]) ➝ Sock


  // Send request for operation `op` with `value` as the payload, using JSON
  // for encoding.
  request(op string, value any, cb function(Error, result any))

  // Send a request for operation `op` with raw-buffer `buf` as the payload,
  // if any. The type of result depends on the protocol used by the server
  // — a server sending a "text" frame means the result is a string, while a
  // server sending a "binary" frame causes the result to be a Buf.
  bufferRequest(op string,
                buf Buf|string|null,
                cb function(Error, result Buf|string))

  // Create a StreamRequest for operation `op` which is ready to be used.
  // Note that calling this method does not send any data — sending the request
  // and reading the response is performed by using the returned object.
  streamRequest(op string) ➝ StreamRequest

  // Send notification `name` with raw-buffer `buf` as the payload, if any.
  bufferNotify(name string, buf Buf|string|null)

  // Send notification `name` with `value`, using JSON for encoding.
  notify(name string, value any)

  // Send a heartbeat message with `load` which should be in the range [0-1]
  sendHeartbeat(load float)


  // Returns a string representing the address to which the socket is connected.
  address() ➝ string|null


  // Adopt a connection capable of being received from, written to and closed.
  // It should be in an "OPEN" ready-state.
  // You need to call `handshake` followed by `startReading` after adopting a previosuly
  // unadopted connection.
  // Throws an error if the provided connection type is not supported.
  // Currently only supports WebSocket.
  adopt(c Conn)

  // Perform protocol handshake.
  handshake()

  // Schedule reading from the underlying connection. Should only be called
  // once per connection.
  startReading()


  // Close the socket. If there are any outstanding responses from pending
  // requests, the socket will close when all pending requests has finished.
  // If you call this function a second time, the socket will close immediately,
  // even if there are outstanding responses.
  end()

  // Event emitted when the connection has opened.
  event "open" ()

  // Event emitted when the connection has closed. If it closed because of an
  // error, the error argument is non-falsey.
  event "close" (Error)

  event "heartbeat" ({time: Date, load: float})
}
// Create a new socket with `handlers`.
Sock(Handlers=defaultHandlers) ➝ Sock

StreamRequest

Represents a streaming request.

Response(s) arrive by the "data"(buf) event. When the response is complete, a "end"(error) event is emitted, where error is non-empty if the request failed.

type StreamRequest {
  op ➝ string  // Operation name
  id ➝ string  // Request ID

  // Write a request chunk. Writing an empty `buf` or null causes the request to end,
  // meaning no more chunks can be written. Calling `write()` or `end()` after the
  // request has finished has no effect.
  write(buf Buf|string)

  // End the request, indicating to the responder that it will not receive more payloads.
  end()

  // Event emitted when a response chunk is received.
  // Depending on the underlying transport, the argument is either a Buf or a string
  event "data" (Buf|string)

  // Event emitted when the response has ended.
  // If it ended because of an error, the argument is non-falsy.
  event "close" (Error)
}
// Create a StreamRequest operating on a certain socket `s`.
// You should probably use `Sock.streamRequest()` instead, as it sets up response
// tracking, generates a request ID, etc.
StreamRequest(s Sock, op string, id string) ➝ StreamRequest

Handlers

Container for mapping requests and notifications to code for handling them.

type Handlers {
  // Callable for producing the results value of an operation.
  interface BufferResult(Buf|string) {
    // Callable for producing an error result
    error(Error)
  }

  // Like BufferResult, but accepts any value which will be encoded as JSON.
  interface ValueResult(any) {
    error(Error)
  }

  // Signature for request handlers dealing with raw data
  interface ReqBufferHandler(Buf|string, BufferResult, op string)

  // Signature for request handlers dealing with JSON-decoded value
  interface ReqValueHandler(any, ValueResult, op string)

  // Signature for notification handlers dealing with raw data
  interface NotBufferHandler(Buf|string, name string)

  // Signature for request handlers dealing with JSON-decoded value
  interface NotValueHandler(any, name string)

  // Register a handler for an operation `op`. If `op` is the empty string the
  // handler will be registered as a "fallback" handler, meaning that if there are
  // no handlers registered for request "x", the fallback handler will be invoked.
  handleRequest(op string, ReqValueHandler)
  handleBufferRequest(op string, ReqBufferHandler)
  
  // Register a handler for notification `name`. Just as with request handlers,
  // registering a handler for the empty string means it's registered as the
  // fallback handler.
  handleNotification(name string, NotValueHandler)
  handleBufferNotification(name string, NotBufferHandler)

  // Find request and notification handlers
  findRequestHandler(op string) ➝ ReqBufferHandler|null
  findNotificationHandler(name string) ➝ NotBufferHandler|null
}
// Create a new Handlers object
Handlers() ➝ Handlers

protocol

Describes the gotalk protocol and provides functionality for encoding and decoding messages.

protocol = {
  // The version of the protocol implementation
  Version = 1

  // Message type constants
  MsgTypeSingleReq     = byte('r')
  MsgTypeStreamReq     = byte('s')
  MsgTypeStreamReqPart = byte('p')
  MsgTypeSingleRes     = byte('R')
  MsgTypeStreamRes     = byte('S')
  MsgTypeErrorRes      = byte('E')
  MsgTypeRetryRes      = byte('e')
  MsgTypeNotification  = byte('n')
  MsgTypeHeartbeat     = byte('h')
  MsgTypeProtocolError = byte('f')

  // ProtocolError codes
  ErrorAbnormal    = 0
  ErrorUnsupported = 1
  ErrorInvalidMsg  = 2
  ErrorTimeout     = 3

  // Maximum value of a heartbeat's "load"
  HeartbeatMsgMaxLoad = 0xffff

  // Implements a byte-binary version of the gotalk protocol
  binary ProtocolImp<Buf>

  // Implements a JavaScript text version of the gotalk protocol
  text ProtocolImp<string>
}

Generic interface implemented by protocol.binary and protocol.text:

type ProtocolImp<T> {
  // Produce a fixed-digit number for integer `n`
  makeFixnum(n int, digits int) ➝ T

  // protocol.Version as a T
  versionBuf T

  // Parse value as protocol version which is expected to have a length of 2.
  parseVersion(T) ➝ int

  // Parses a message from a T, which must not including any payload data.
  parseMsg(T) ➝ {t:int, id:T, name:string, size:int} | null

  // Create a T representing a message, not including any payload data.
  makeMsg(t int, id T|string, name string, payloadSize int) ➝ T
}

Buf

Represents a fixed-length, mutable sequence of bytes. This type is used as the payload value when operating the "binary" (byte-wise) protocol. In a web browser, this is implemented as a native UInt8Array backed by an ArrayBuffer, while in Nodejs it's implemented in the standard-library Buffer.

type Buf {
  // Access or assign byte value at `index`
  [index int] ⇄ uint8

  // Returns a new view into the same buffer
  slice(start int[, end int]) ➝ Buf

  // Copies data from a region of this buffer to a region in the target buffer.
  copy(target Buf[, targetStart[, sourceStart[, sourceEnd]]])

  // Returns a text version of the buffer by interpreting the buffer's bytes as
  // `encoding`. Providing optional `start` and `end` have the same effects as
  // calling `b.slice(start, end).toString()`. `encoding` defaults to "utf8".
  toString([encoding string[, start int[, end int]]]) ➝ string
}
// Create a new buffer capable of storing `size` bytes, or by wrapping a native-
// type array.
Buf(size int) ➝ Buf
Buf(ArrayBuffer) ➝ Buf
Buf(Uint8Array) ➝ Buf

// Create a new buffer by encoding a string as `encoding` (defaults to "utf8".)
Buf.fromString(string[, encoding string]) ➝ Buf
// Test if something is a Buf
Buf.isBuf(any) ➝ true|false