WIP: new server protocol

This commit is contained in:
Brian Warner 2016-05-16 22:04:25 -07:00
parent 5dd91c7311
commit a74b1b1e3a
4 changed files with 151 additions and 73 deletions

View File

@ -22,7 +22,7 @@ def get_db(dbfile, stderr=sys.stderr):
raise DBError("Unable to create/open db file %s: %s" % (dbfile, e))
db.row_factory = sqlite3.Row
VERSION = 1
VERSION = 2
if must_create:
schema = get_schema(VERSION)
db.executescript(schema)

View File

@ -1,43 +0,0 @@
-- note: anything which isn't an boolean, integer, or human-readable unicode
-- string, (i.e. binary strings) will be stored as hex
CREATE TABLE `version`
(
`version` INTEGER -- contains one row, set to 1
);
CREATE TABLE `messages`
(
`appid` VARCHAR,
`channelid` VARCHAR,
`side` VARCHAR,
`phase` VARCHAR, -- numeric or string
-- phase="_allocate" and "_deallocate" are used internally
`body` VARCHAR,
`server_rx` INTEGER,
`msgid` VARCHAR
);
CREATE INDEX `messages_idx` ON `messages` (`appid`, `channelid`);
CREATE TABLE `usage`
(
`type` VARCHAR, -- "rendezvous" or "transit"
`started` INTEGER, -- seconds since epoch, rounded to one day
`result` VARCHAR, -- happy, scary, lonely, errory, pruney
-- rendezvous moods:
-- "happy": both sides close with mood=happy
-- "scary": any side closes with mood=scary (bad MAC, probably wrong pw)
-- "lonely": any side closes with mood=lonely (no response from 2nd side)
-- "errory": any side closes with mood=errory (other errors)
-- "pruney": channels which get pruned for inactivity
-- "crowded": three or more sides were involved
-- transit moods:
-- "errory": this side have the wrong handshake
-- "lonely": good handshake, but the other side never showed up
-- "happy": both sides gave correct handshake
`total_bytes` INTEGER, -- for transit, total bytes relayed (both directions)
`total_time` INTEGER, -- seconds from start to closed, or None
`waiting_time` INTEGER -- seconds from start to 2nd side appearing, or None
);
CREATE INDEX `usage_idx` ON `usage` (`started`);

View File

@ -0,0 +1,98 @@
-- note: anything which isn't an boolean, integer, or human-readable unicode
-- string, (i.e. binary strings) will be stored as hex
CREATE TABLE `version`
(
`version` INTEGER -- contains one row, set to 2
);
-- Wormhole codes use a "nameplate": a short identifier which is only used to
-- reference a specific (long-named) mailbox. The codes only use numeric
-- nameplates, but the protocol and server allow can use arbitrary strings.
CREATE TABLE `nameplates`
(
`app_id` VARCHAR,
`id` VARCHAR PRIMARY KEY,
`mailbox_id` VARCHAR, -- really a foreign key
`side1` VARCHAR, -- side name, or NULL
`side2` VARCHAR -- side name, or NULL
);
CREATE INDEX `nameplates_idx` ON `nameplates` (`app_id`, `id`);
CREATE INDEX `nameplates_mailbox_idx` ON `nameplates` (`app_id`, `mailbox_id`);
-- Clients exchange messages through a "mailbox", which has a long (randomly
-- unique) identifier and a queue of messages.
CREATE TABLE `mailboxes`
(
`app_id` VARCHAR,
`id` VARCHAR,
`side1` VARCHAR -- side name, or NULL
`side2` VARCHAR -- side name, or NULL
-- timing data for the (optional) linked nameplate
`nameplate_started` INTEGER, -- time when related nameplace was opened
`nameplate_second` INTEGER, -- time when second side opened
`nameplate_closed` INTEGER, -- time when closed
-- timing data for the mailbox itself
`started` INTEGER, -- time when opened
`second` INTEGER, -- time when second side opened
`closed` INTEGER -- time when closed
);
CREATE INDEX `mailboxes_idx` ON `mailboxes` (`app_id`, `id`);
CREATE TABLE `messages`
(
`app_id` VARCHAR,
`mailbox_id` VARCHAR,
`side` VARCHAR,
`phase` VARCHAR, -- numeric or string
`body` VARCHAR,
`server_rx` INTEGER,
`msg_id` VARCHAR
);
CREATE INDEX `messages_idx` ON `messages` (`app_id`, `mailbox_id`);
CREATE TABLE `nameplate_usage`
(
`started` INTEGER, -- seconds since epoch, rounded to "blur time"
`total_time` INTEGER, -- seconds from open to last close
`result` VARCHAR, -- happy, lonely, pruney, crowded
-- nameplate moods:
-- "happy": two sides open and close
-- "lonely": one side opens and closes (no response from 2nd side)
-- "pruney": channels which get pruned for inactivity
-- "crowded": three or more sides were involved
`waiting_time` INTEGER -- seconds from start to 2nd side appearing, or None
);
CREATE INDEX `nameplate_usage_idx` ON `nameplate_usage` (`started`);
CREATE TABLE `mailbox_usage`
(
`started` INTEGER, -- seconds since epoch, rounded to "blur time"
`total_time` INTEGER, -- seconds from open to last close
`waiting_time` INTEGER, -- seconds from start to 2nd side appearing, or None
`result` VARCHAR -- happy, scary, lonely, errory, pruney
-- rendezvous moods:
-- "happy": both sides close with mood=happy
-- "scary": any side closes with mood=scary (bad MAC, probably wrong pw)
-- "lonely": any side closes with mood=lonely (no response from 2nd side)
-- "errory": any side closes with mood=errory (other errors)
-- "pruney": channels which get pruned for inactivity
-- "crowded": three or more sides were involved
);
CREATE INDEX `mailbox_usage_idx` ON `mailbox_usage` (`started`);
CREATE TABLE `transit_usage`
(
`started` INTEGER, -- seconds since epoch, rounded to "blur time"
`total_time` INTEGER, -- seconds from open to last close
`waiting_time` INTEGER, -- seconds from start to 2nd side appearing, or None
`total_bytes` INTEGER, -- total bytes relayed (both directions)
`result` VARCHAR -- happy, scary, lonely, errory, pruney
-- transit moods:
-- "errory": one side gave the wrong handshake
-- "lonely": good handshake, but the other side never showed up
-- "happy": both sides gave correct handshake
);
CREATE INDEX `transit_usage_idx` ON `transit_usage` (`started`);

View File

@ -3,26 +3,47 @@ from twisted.internet import reactor
from twisted.python import log
from autobahn.twisted import websocket
# Each WebSocket connection is bound to one "appid", one "side", and zero or
# more "channelids". The connection's appid and side are set by the "bind"
# message (which must be the first message on the connection). Both must be
# set before any other message (allocate, claim, watch, add, deallocate) will
# be accepted. Short channel IDs can be obtained from the server with an
# "allocate" message. Longer ones can be selected independently by the
# client. Channels are maintained (saved from deletion) by a "claim" message
# (and also incidentally by "allocate"). Channels are deleted when the last
# claim is released with "release".
# The WebSocket allows the client to send "commands" to the server, and the
# server to send "responses" to the client. Note that commands and responses
# are not necessarily one-to-one. All commands provoke an "ack" response
# (with a copy of the original message) for timing, testing, and
# synchronization purposes. All commands and responses are JSON-encoded.
# All websocket messages are JSON-encoded. The client can send us "inbound"
# messages (marked as "->" below), which may (or may not) provoke immediate
# (or delayed) "outbound" messages (marked as "<-"). There is no guaranteed
# correlation between requests and responses. In this list, "A -> B" means
# that some time after A is received, at least one message of type B will be
# sent out (probably).
# Each WebSocket connection is bound to one "appid" and one "side", which are
# set by the "bind" command (which must be the first command on the
# connection), and must be set before any other command will be accepted.
# All outbound messages include a "server_tx" key, which is a float (seconds
# since epoch) with the server clock just before the outbound message was
# written to the socket. Unrecognized keys will be ignored.
# Each connection can be bound to a single "mailbox" (a two-sided
# store-and-forward queue, identified by the "mailbox id": a long, randomly
# unique string identifier) by using the "open" command. This protects the
# mailbox from idle closure, enables the "add" command (to put new messages
# in the queue), and triggers delivery of past and future messages via the
# "message" response. The "close" command removes the binding (but note that
# it does not enable the subsequent binding of a second mailbox). When the
# last side closes a mailbox, its contents are deleted.
# Additionally, the connection can be bound a single "nameplate", which is
# short identifier that makes up the first component of a wormhole code. Each
# nameplate points to a single long-id "mailbox". The "allocate" message
# determines the shortest available numeric nameplate, reserves it, and
# returns the nameplate id. "list" returns a list of all numeric nameplates
# which currently have only one side active (i.e. they are waiting for a
# partner). The "claim" message reserves an arbitrary nameplate id (perhaps
# the receiver of a wormhole connection typed in a code they got from the
# sender, or perhaps the two sides agreed upon a code offline and are both
# typing it in), and the "release" message releases it. When every side that
# has claimed the nameplate has also released it, the nameplate is
# deallocated (but they will probably keep the underlying mailbox open).
# Inbound (client to server) commands are marked as "->" below. Unrecognized
# inbound keys will be ignored. Outbound (server to client) responses use
# "<-". There is no guaranteed correlation between requests and responses. In
# this list, "A -> B" means that some time after A is received, at least one
# message of type B will be sent out (probably).
# All responses include a "server_tx" key, which is a float (seconds since
# epoch) with the server clock just before the outbound response was written
# to the socket.
# connection -> welcome
# <- {type: "welcome", welcome: {}} # .welcome keys are all optional:
@ -31,19 +52,21 @@ from autobahn.twisted import websocket
# error: all clients display mesage, then terminate with error
# -> {type: "bind", appid:, side:}
#
# -> {type: "list"} -> channelids
# <- {type: "channelids", channelids: [int..]}
# -> {type: "allocate"} -> allocated
# <- {type: "allocated", channelid: int}
# -> {type: "claim", channelid: int}
# -> {type: "list"} -> nameplates
# <- {type: "nameplates", nameplates: [str..]}
# -> {type: "allocate"} -> nameplate, mailbox
# <- {type: "nameplate", nameplate: str}
# -> {type: "claim", nameplate: str} -> mailbox
# <- {type: "mailbox", mailbox: str}
# -> {type: "release"}
#
# -> {type: "watch", channelid: int} -> message
# sends old messages and more in future
# <- {type: "message", channelid: int, message: {phase:, body:}} # body is hex
# -> {type: "add", channelid: int, phase: str, body: hex} # will send echo
# -> {type: "open", mailbox: str} -> message
# sends old messages now, and subscribes to deliver future messages
# <- {type: "message", message: {phase:, body:}} # body is hex
# -> {type: "add", phase: str, body: hex} # will send echo in a "message"
#
# -> {type: "release", channelid: int, mood: str} -> deallocated
# <- {type: "released", channelid: int, status: waiting|deleted}
# -> {type: "close", mood: str} -> closed
# <- {type: "closed", status: waiting|deleted}
#
# <- {type: "error", error: str, orig: {}} # in response to malformed msgs