Skip to content
Permalink
Browse files

Add several protocol extensions and other improvements to the core.

  • Loading branch information...
Shinmera committed Jul 25, 2017
1 parent 362ff80 commit 7486ca9f3830018e26611de7ec3d3b69ecc70b84
Showing with 72 additions and 9 deletions.
  1. +28 −0 README.md
  2. +1 −1 lichat-protocol.asd
  3. +11 −1 package.lisp
  4. +29 −4 protocol.lisp
  5. +3 −3 reader.lisp
@@ -217,6 +217,34 @@ A server or client may provide extensions to the protocol in the following manne
* **Additional Update Types** -- If such an update is sent to a client that does not recognise it, it should be ignored. If such an update is sent to a server that does not recognise it, the server will respond with an `invalid-update`.
* **Additional Update Fields** -- A client or server may extend the existing update classes with additional, optional fields to provide further information or other kinds of behaviour. The server or client is not allowed to introduce additional required fields. When an update with unknown initargs is received, the unknown initargs are to be ignored.

Each extension to the protocol should receive a unique name of the form `producer-name` where `producer` is an identifier for who wrote up the extension's protocol, and `name` should be a name for the extension itself. For each extension that a server and client support, they must include the unique name of it as a string in the `connect` update's `extensions` list.

### 7. Protocol Extensions
The extensions outlined in this section are not mandatory and a server or client may choose not to implement them.

#### 7.1 Backfill (shirakumo-backfill)
A new update type called `backfill` is introduced, which is a `channel-update`. If the server receives such an update from a connection, it reacts as follows:

1. If the user is not in the named channel, a `not-in-channel` update is sent back and the request is dropped.
1. Following this, updates are sent back to the connection the update came from. These updates should include all updates that were distributed to users in the channel, spanning from now to an arbitrary point in time that is at most when the user of this connection last joined the channel. The fields of the updates must be the equal to the first time the update was sent out.

The purpose of this extension is to allow users to catch up with the contents of a channel should they initiate a new connection which does not currently have access to all the past updates of the channel. In order to facilitate this, the server is forced to keep copies of the updates. The server is allowed to only keep updates for a certain duration, or only a certain number of total updates. In order to avoid spying, the server must not distribute updates that the user did not already receive previously through another connection. The server does not have to make any guarantee about the order in which the updates are sent back to the connection. The client on the other side is responsible for ordering them as appropriate according to the clock.

#### 7.2 Data (shirakumo-data)
A new update type called `data` is introduced, which is a `channel-update`. Additionally, a new `failure` type called `bad-content-type` is introduced, which is an `update-failure`. If the server receives a `data` update from a connection, it reacts as follows:

1. If the user is not in the named channel, a `not-in-channel` update is sent back and the request is dropped.
1. If the update's `content-type` is not accepted by the server, a `bad-content-type` update is sent back and the request is dropped.
1. The user's `data` update is distributed to all users in the channel.

The `data` update contains three slots, with the following intentions:

* `content-type` A string representing the [content type](https://en.wikipedia.org/wiki/Media_type) of the payload data contained in the update.
* `filename` A string representing an arbitrary name given to the payload data.
* `payload` A base-64 encoded string of binary data payload.

The purpose of this extension is to allow users to send binary data over channels. Particularly, the intention is to allow embedding of images, audio, video, and other media.

## See Also

* [lichat-serverlib](https://shirakumo.github.io/lichat-serverlib) An agnostic implementation of the server-side protocol.
@@ -6,7 +6,7 @@

(in-package #:cl-user)
(asdf:defsystem lichat-protocol
:version "1.2"
:version "1.3"
:license "Artistic"
:author "Nicolas Hafner <shinmera@tymoon.eu>"
:maintainer "Nicolas Hafner <shinmera@tymoon.eu>"
@@ -22,6 +22,7 @@
#:incomplete-token
#:unknown-symbol
#:symbol-designator
#:read-limit-hit
#:missing-update-argument
#:update
#:missing-id
@@ -77,6 +78,7 @@
#:connect
#:password
#:version
#:extensions
#:disconnect
#:register
#:channel-update
@@ -99,8 +101,14 @@
#:user-info
#:registered
#:connections
#:backfill
#:data
#:content-type
#:filename
#:payload
#:failure
#:malformed-update
#:update-too-long
#:connection-unstable
#:too-many-connections
#:update-failure
@@ -119,7 +127,9 @@
#:insufficient-permissions
#:invalid-permissions
#:no-such-user
#:too-many-updates)
#:too-many-updates
#:bad-content-type
#:allowed-content-types)
;; reader.lisp
(:export
#:whitespace-p
@@ -19,7 +19,9 @@
(pull T)
(message T)
(users T)
(channels T)))
(channels T)
(backfill T)
(data T)))

(defparameter *default-anonymous-channel-permissions*
'((permissions)
@@ -29,7 +31,9 @@
(pull T)
(message T)
(users)
(channels)))
(channels)
(backfill T)
(data T)))

(defparameter *default-primary-channel-permissions*
'((permissions :registrant)
@@ -40,7 +44,9 @@
(pull)
(message :registrant)
(users T)
(channels T)))
(channels T)
(backfill :registrant)
(data :registrant)))

(deftype wireable ()
`(or real string cons symbol wire-object))
@@ -168,7 +174,8 @@

(define-protocol-class connect (update)
((password :initarg :password :accessor password :slot-type (or null password))
(version :initarg :version :accessor version :slot-type string))
(version :initarg :version :accessor version :slot-type string)
(extensions :initarg :extensions :accessor extensions :slot-type list))
(:default-initargs
:password NIL
:version (protocol-version)))
@@ -244,6 +251,15 @@
(connections :initarg :connections :accessor connections :slot-type (integer 1)))
(:default-initargs :registered NIL :connections 1))

(define-protocol-class backfill (channel-update)
())

(define-protocol-class data (channel-update)
((content-type :initarg :content-type :accessor content-type :slot-type string)
(filename :initarg :filename :accessor filename :slot-type (or null string))
(payload :initarg :payload :accessor payload :slot-type string))
(:default-initargs :file-name NIL))

;; Errors
(define-protocol-class failure (text-update)
())
@@ -252,6 +268,10 @@
()
(:default-initargs :text "Update was malformed and could not be parsed."))

(define-protocol-class update-too-long (failure)
()
(:default-initargs :text "The update was too long and has been dropped."))

(define-protocol-class connection-unstable (failure)
()
(:default-initargs :text "The connection is unstable. You may be disconnected soon."))
@@ -328,3 +348,8 @@
(define-protocol-class too-many-updates (update-failure)
()
(:default-initargs :text "You have been sending too many updates and have been throttled."))

(define-protocol-class bad-content-type (update-failure)
((allowed-content-types :initarg :allowed-content-types :accessor allowed-content-types :slot-type list))
(:default-initargs :text "The supplied content type for the data update is not accepted by this server."
:allowed-content-types ()))
@@ -12,12 +12,12 @@
(defvar *read-limit* NIL)
(defvar *read-counter*)

(defun lread (stream)
(defun lread (stream &optional eof)
(when *read-limit*
(when (<= *read-limit* *read-counter*)
(error 'read-limit-hit))
(incf *read-counter*))
(read-char stream))
(read-char stream (not eof) eof))

(defun lpeek (stream)
(when *read-limit*
@@ -97,7 +97,7 @@

(defun read-sexpr-symbol (stream)
(let ((token (read-sexpr-token stream)))
(cond ((eql #\: (lpeek stream NIL))
(cond ((eql #\: (lpeek stream))
(lread stream)
(if (string= token "#")
(make-symbol (read-sexpr-token stream))

0 comments on commit 7486ca9

Please sign in to comment.
You can’t perform that action at this time.