Braid-HTTP: Synchronization for HTTP
draft-toomim-httpbis-braid-http-03
This document is an Internet-Draft (I-D).
Anyone may submit an I-D to the IETF.
This I-D is not endorsed by the IETF and has no formal standing in the
IETF standards process.
The information below is for an old version of the document.
| Document | Type |
This is an older version of an Internet-Draft whose latest revision state is "Expired".
|
|
|---|---|---|---|
| Authors | Michael Toomim , Greg Little , Rafie Walker , Bryn Bellomy , Seph Gentle | ||
| Last updated | 2023-10-23 (Latest revision 2020-03-09) | ||
| Replaces | draft-toomim-braid | ||
| RFC stream | (None) | ||
| Formats | |||
| Stream | Stream state | (No stream defined) | |
| Consensus boilerplate | Unknown | ||
| RFC Editor Note | (None) | ||
| IESG | IESG state | I-D Exists | |
| Telechat date | (None) | ||
| Responsible AD | (None) | ||
| Send notices to | (None) |
draft-toomim-httpbis-braid-http-03
Internet-Draft M. Toomim
Expires: Apr 10, 2020 Invisible College
Intended status: Proposed Standard G. Little
Invisible College
R. Walker
Bard College
B. Bellomy
Invisible College
J. Gentle
Invisible College
Oct 23, 2023
\=/====\\ |//===\\= /=\ =\==\|\=/== =|====\==
||/ |\\ ||\ |\\ /|| \|\ |// //| \\\
|\\ |// |\\ |// //| \\\ \\\ /\/ |||
\=|====|= |/====/=\ /=\/====|=\ =\= \\= =/=
//\ /\\ //| |\\ |/| ||| \\\ ||| |//
||| ||| |\\ |// |\/ \|/ /|\ |=\ |\\
=\=\==/=/ ==| |\= ||= /== ===/=|=\=== |==\===//
Braid-HTTP: Synchronization for HTTP
draft-toomim-httpbis-braid-http-03
Abstract
Braid is a set of extensions that generalize HTTP from a state
*transfer* protocol into a full state *synchronization* protocol.
Braid is composed of four independent extensions to HTTP:
1. VERSIONING of resource history
2. SUBSCRIPTIONS to updates over time
3. PATCHES to ranges of space
4. MERGE-TYPES that specify OT or CRDT behavior
Each extension provides a distinct value for a stand-alone use-case.
However, they can compose together to support the full power of CRDTs
and Operational Transforms on web resources. This allows multiple
writers to make simultaneous mutations to arbitrary content-types,
under arbitrary network delays and partitions, while guaranteeing
consistency across multiple clients and servers. This improves web
caching and network performance, and enables natively peer-to-peer,
collaboratively-editable, local-first web applications.
Status of this Memo
This Internet-Draft is submitted in full conformance with the
provisions of BCP 78 and BCP 79.
Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF), its areas, and its working groups. Note that
other groups may also distribute working documents as
Internet-Drafts. The list of current Internet-Drafts is at
http://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress."
The list of current Internet-Drafts can be accessed at
https://www.ietf.org/1id-abstracts.html
The list of Internet-Draft Shadow Directories can be accessed at
https://www.ietf.org/shadow.html
Table of Contents
1. Introduction ..................................................4
1.1. HTTP applications need state Synchronization ................4
1.2. The General Challenge of State Synchronization ..............4
1.3. Braid extends HTTP for State Synchronization ................5
2. Versioning for Resources ......................................6
2.1. Comparison with ETag ........................................6
2.2. PUT a new version ...........................................7
2.3. GET a specific version ......................................8
2.4. GET a range of historical versions ..........................9
2.5. Rules for Version and Parents headers ......................10
3. Updates can be expressed as Patches or Snapshots .............11
3.1. PUT an update as a patch ...................................12
3.2. GET an update as a patch ...................................13
3.3. PUT an update as a set of patches ..........................14
3.3. PUT an update as a custom patch-type .......................15
4. Subscriptions for GET ........................................16
4.1. Creating a Subscription ....................................17
4.2. Sending multiple updates per GET ...........................18
4.3. Continuing a Subscription ..................................19
4.4. Ending a Subscription ......................................20
4.5. Signaling "all caught up" ..................................21
4.6. Errors .....................................................21
5. Design Goals..................................................22
6. Use Cases ....................................................22
6.1. Dynamic Resources ..........................................22
6.2. Dynamic Proxies and Caches .................................23
6.3. A Serverless Chat Example ..................................23
7. Related Work .................................................24
7.1. Existing IETF Standards ....................................24
7.3. IETF Work in Progress ......................................24
7.3. Web Frameworks .............................................23
8. IANA Considerations ..........................................24
8.1. Header Field Registration ..................................24
9. Security Considerations ......................................24
10. Conventions .................................................24
11. Copyright Notice .............................................25
12. References ...................................................25
12.1. Normative References .......................................25
12.2. Informative References .....................................26
13. Acknowledgements .............................................27
14. Authors' Addresses ...........................................28
1. Introduction
1.1. HTTP applications need state Synchronization, not just Transfer
HTTP [RFC9110] transfers a static version of state within a single
request and response. If the state changes, HTTP does not
automatically update clients with the new versions. This design
satisficed when webpages were mostly static and written by hand;
however today's websites are dynamic, generated from layers of state
in databases, and provide realtime updates across multiple clients
and servers. Programmers today need to *synchronize*, not just
*transfer* state, and to do this, they must work around HTTP.
The web has a long history of these workarounds. The original web
required users to click reload when a page changed. XMLHTTPRequest
[XHR] made it possible to update just part of a page, running a GET
request behind the scenes. However, a GET request still could not
push updates. To work around this, web programmers would poll the
resource with repeated GETs, which was inefficient. Long-polling was
invented to reduce redundant requests, but still requires the client
to initiate a round-trip for each update. Server-Sent Events [SSE]
finally created a standard for the server to push events, but SSE
provides semantics of an event-stream, not an update-stream, and SSE
programmers must encode the semantics of updating a resource within
the event stream. Today there is still no standard way for a server
to update its clients when a resource changes.
In practice, web programmers today often give up on using standards
for "data that changes", and instead send custom messages over a
WebSocket -- a hand-rolled synchronization protocol. Unfortunately,
this forfeits the benefits of HTTP and ReST, such as caching and a
uniform interface [REST]. As the web becomes increasingly dynamic,
web applications are forced to implement additional layers of
non-standard Javascript frameworks to synchronize changes to state.
1.2. The General Challenge of State Synchronization
Part of the issue is that general case of the state synchronization
problem is inherently complex. Programmers find it easy to implement
a single update, for a single use-case, but it becomes difficult to
support multiple writers, for multiple types of edits, with robust
conflict resolution, offline edits, and different merge semantics on
arbitrary content types, network topologies, and network conditions.
Recent research on OT, CRDT, and Version Control algorithms have
solved many of these problems in the last decade -- but even today,
each OT or CRDT algorithm proposes a different protocol, creating
lock-in.
However, recent research by these authors has demonstrated that
although despite having different protocols; the information each
algorithm needs to send over the wire is equivalent. Thus, it is
possible to design a general protocol that supports any OT, CRDT, or
Version Control algorithm.
1.3. Braid extends HTTP for State Synchronization
Braid-HTTP expresses this general model of synchronization in four
extensions to HTTP:
1. Versioning (Section 2)
Each resource has a history of changes, ordered in time.
2. Patches (Section 3)
Each resource can express updates as either *snapshots* or
*patches*; bidrectionally as client->server and server->client.
3. Subscriptions (Section 4)
A Subscribe header can be added to GET requests. The server
responds by pushing future versions to the client while the
request is open.
4. Merge Types [MERGE-TYPES]
If multiple clients and servers simultaneously edit the same
resource, they can guarantee a consistent resulting state by
implementing the same Merge Type. Resources specify their Merge
Type with a header.
Each extension provides standalone value, and can be used separately.
For instance, a /current-temp URL can implement Subscriptions
(Section 4) to push a stream of updates, without needing any complex
OT/CRDT machinery.
However, if an implementation combines these extensions together, it
generalizes HTTP into a full synchronization protocol, and ReST into
a synchronization architecture:
HTTP: Hypertext *Transfer* Protocol
can become: State *Synchronization* Protocol
ReST: Representational State *Transfer*
can become: Representational State *Synchronization*
This allows an arbitrary set of clients and servers to make arbitrary
edits to arbitrary resources, under arbitrary network delays and
paritions, and merge all edits consistently, receiving updates as
soon as they reconnect. This enables caches to support dynamic
content, web applications to feature an offline mode, and textareas
to support collaborative editing.
2. Versioning for Resources
Each Braid resource has a current version, and a version history.
Versions are specified as a set of one or more strings (called
"version IDs") in the [RFC8941] format. Each version ID must be
unique, to differentiate distinct changes at distinct points in time.
To specify the version of content in a request or response body, a
Version header MAY be included in a request for a PUT, PATCH or POST,
or in the response to a GET:
Version: "dkn7ov2vwg"
Parallel edits can merge into a single version with multiple IDs:
Version: "dkn7ov2vwg", "v2vwgdkn7o"
Every version also has a set of parents, denoting the version(s)
immediately before the version, that it derives from. The full graph
of parents forms a Directed Acyclic Graph (DAG), representing the
partial order of all versions. A version A is known to have occurred
before a version B if and only if A is an ancestor of B in the
partial order.
Parents are specified with a header in a PUT request or GET response:
Parents: "ajtva12kid", "cmdpvkpll2"
For any two versions A and B that are specified in a Version or
Parents header, A cannot be a descendent of B or vice versa. The
ordering of versions in the list carries no meaning.
If a client or server does not specify a Version for a resource it
transfers, the recipient MAY generate and assign it new version IDs.
If a client or server does not specify a Parents header when
transferring a new version, the recipient MAY presume that the most
recent versions it has seen are the parents of the new version.
2.1. Comparison with ETag
The Version header is similar to an ETag, but has two differences:
1. ETags are sensitive to Content-Encoding. If you send the same
version with a GZip Content-Encoding, it will have a different
ETag, but the same Version.
2. A Version marks a unique point in time -- not unique content. If
a resource is changed from version A to B, and then to C, such
that the contents at A are the same as the contents at C, then it
is possible versions A and C to have the same ETag, even though
they have different Versions. This can break a CRDT or OT merge
algorithm.
Versions can be used in a variety of requests, as we explain next.
2.2. PUT a new version
When a PUT request changes the state of a resource, it can specify
the new version of the resource, the parent version IDs that existed
when it was created, and the way multiple simultaneous changes should
be merged (the "Merge-Type"):
Request:
PUT /chat
Version: "ej4lhb9z78" | Update
Parents: "oakwn5b8qh", "uc9zwhw7mf" |
Content-Type: application/json |
Merge-Type: sync9 |
Content-Length: 64 |
|
[{"text": "Hi, everyone!", | | Snapshot
"author": {"link": "/user/tommy"}}] | |
Response:
HTTP/1.1 200 OK
Merge-Types are specified in [MERGE-TYPES]. The Version and Parents
headers are optional. If Version is omitted, the recipient may
assign new version IDs. If Parents is omitted, the recipient may
assume that its current version is the version's parents.
We call the set of data that updates a resource from one version to
another an "update". An update consists of a set of headers and a
body. In this example, the update includes a snapshot of the entire
new value of the resource. However, one can also specify the update
as a set of patches.
2.3. GET a specific version
A server can allow clients to request historical versions of a
resource in GET requests by responding to the Version and Parents
headers. A client can specify a specific version that it wants with
the Version header:
Request:
GET /chat
Version: "ej4lhb9z78"
Response:
HTTP/1.1 200 OK
Version: "ej4lhb9z78" | Update
Parents: "oakwn5b8qh", "uc9zwhw7mf" |
Content-Type: application/json |
Merge-Type: sync9 |
Content-Length: 64 |
|
[{"text": "Hi, everyone!", | | Snapshot
"author": {"link": "/user/tommy"}}] | |
2.4. GET a range of historical versions
A client can request a range of history by including a Parents and a
Version header together. The Parents marks the beginning of the
range (the oldest versions) and the Version marks the end of the
range (the newest versions) that it requests.
Request:
GET /chat
Version: "3"
Parents: "1a", "1b"
Response:
HTTP/1.1 200 OK
Version: "2"
Parents: "1a", "1b"
Version: "2" | Update
Parents: "1a", "1b" |
Content-Type: application/json |
Merge-Type: sync9 |
Content-Length: 64 |
|
[{"text": "Hi, everyone!", | | Snapshot
"author": {"link": "/user/tommy"}}] | |
Version: "3" | Update
Parents: "2" |
Content-Type: application/json |
Merge-Type: sync9 |
Content-Length: 117 |
|
[{"text": "Hi, everyone!", | | Snapshot
"author": {"link": "/user/tommy"}} | |
{"text": "Yo!", | |
"author": {"link": "/user/yobot"}] | |
To express a range of updates, the response body contains a sequence
of updates; each with its own content-length. The format of this
sequence is defined in the upcoming (Section 4.2) on Subscriptions.
2.5. Rules for Version and Parents headers
If a GET request contains a Version header:
- The Subscribe header (Section 3) MUST be absent.
- The server SHOULD return a single response, containing that
version of the resource in its body, with the Version header set
to the version requested by the client.
- If the server does not support historical versions, it MAY ignore
the Version header and respond as usual, but MUST NOT include the
Version header in its response.
If a GET request contains a Parents header:
- If the request does not also contain a Version, then the request
MUST also contain a Subscribe header, and the server SHOULD send
a set of versions updating the Parents to the current Version,
and then subscribe the client to future updates.
- If the server does not support historical versions, then it MAY
ignore the Parents header, but MUST NOT include the Parents
header in its response.
If a GET request contains both a Version and Parents header:
- The server SHOULD respond with a set of updates from the
specified Parents to the specified Version.
A server MAY refactor or rebase the version history that it provides
to a client, so long as it does not affect the resulting state, or
the result of the patch-type's merges.
A server does not need to honor historical version requests for all
documents, for all history. If a server no longer has the historical
context needed to honor a request, it may respond using an error code
that will be defined in a subsequent version of this draft.
Request:
GET /chat
Version: "ej4lhb9z78"
Response:
HTTP/1.1 XXX
Version: "ej4lhb9z78"
3. Updates can be expressed as Patches or Snapshots
Whereas today's HTTP sends the current version of a resource as a
"snapshot" in the body of a GET response, or a PUT request, and
additionally allows clients to send a patch in a PATCH request, a
full state synchronization protocol also needs to allow patches to be
sent from server to client, within subscriptions and requests for
history.
This section describes a general form for updates that can be sent
both client->server or server->client. Updates can be sent as
snapshots or patches. When sent as patches, a single update can
contain a single patch, or multiple patches. Unlike the PATCH
method, these updates can be sent with idempotence when versioning
information is included -- a client or server that receives the same
update twice, for the same version, can discard the second update,
and thus maintain idempotence.
There are two reasons to send an update as a patch:
o Patches are smaller and more efficient
o Patches articulate *how* changes occur, which enables Merge-Types
to intelligently merge, e.g. in collaborative editing.
There are two types of patches:
o Custom patch-types: As defined in HTTP PATCH [RFC5789] method, a
custom patch type can be specified as a Content-Type. Any such
patch can included in an update, by adding a Content-Type header
to the udpate.
o Range Patches: If a Content-Range header is specified on the
update or patch, then it defines the region of the document that
is being replaced by the content, as specified in [RANGE-PATCH].
Every patch MUST include either a Content-Type or a Content-Range.
3.1. PUT an update as a patch
A Partial PUT [RFC9110] can express a patch with a Content-Range
header:
Request:
PUT /chat
Version: "g09ur8z74r" | Update
Parents: "ej4lhb9z78" |
Content-Type: application/json |
Merge-Type: sync9 |
Content-Length: 53 | | Patch
Content-Range: json .messages[1:1] | |
| |
[{"text": "Yo!", | |
"author": {"link": "/user/yobot"}] | |
Response:
HTTP/1.1 200 OK
3.2. GET an update as a patch
A GET response can also express a patch, using Content-Range:
Request:
GET /chat
Version: "g09ur8z74r" | Update
Parents: "ej4lhb9z78" |
Response:
HTTP/1.1 200 OK
Content-Type: application/json |
Merge-Type: sync9 |
Content-Length: 53 | | Patch
Content-Range: json .messages[1:1] | |
| |
[{"text": "Yo!", | |
"author": {"link": "/user/yobot"}] | |
3.3. PUT an update as a set of patches
To format an update as a set of patches, include a header called
"Patches" and assign it to the number of patches included, and format
those patches in the body as a sequence separated by blank lines:
Request:
PUT /chat
Version: "g09ur8z74r" | Update
Parents: "ej4lhb9z78" |
Content-Length: 189 |
Content-Type: application/json |
Merge-Type: sync9 |
Patches: 2 |
|
Content-Length: 53 | | Patch
Content-Range: json .messages[1:1] | |
| |
[{"text": "Yo!", | |
"author": {"link": "/user/yobot"}] | |
|
Content-Length: 40 | | Patch
Content-Range: json .latest_change | |
| |
{"type": "date", "value": 1573952202370} | |
Response:
HTTP/1.1 200 OK
To distinguish the boundaries between patches in an update, each
patch MUST include the following header:
Content-Length: N
This length determines where each patch ends, and next begins.
3.4. PUT an update as a custom patch-type
Since PATCH is not idempotent, a client may want to send a patch
idempotently using a PUT. The client SHOULD include a Version and
Parents header to ensure idempotency. The server can then discard
duplicate patches that it has already received.
Request:
PUT /chat
Version: "up12vyc5ib" | Update
Parents: "2bcbi84nsp" |
Content-Length: 371 |
Merge-Type: sync9 |
Patches: 1 |
|
Content-Length: 288 | | Patch
Content-Type: application/json-patch+json | |
| |
[ | |
{"op": "test", "path": "/a/b/c", "value": "foo"}, | |
{"op": "remove", "path": "/a/b/c"}, | |
{"op": "add", "path": "/a/b/c", "value": []}, | |
{"op": "replace", "path": "/a/b/c", "value": 42}, | |
{"op": "move", "from": "/a/b", "path": "/a/d"}, | |
{"op": "copy", "from": "/a/d", "path": "/a/d/e"} | |
] | |
Response:
HTTP/1.1 200 OK
4. Subscriptions for GET
If a GET request includes the Subscribe header, the server can return
a stream of updates; one for each new version. Each update can
express the new content either as a snapshot, or a set of Patches.
Request:
GET /chat
Subscribe:
Response:
HTTP/1.1 209 Subscription
Subscribe:
Version: "ej4lhb9z78" | Update
Parents: "oakwn5b8qh", "uc9zwhw7mf" |
Content-Type: application/json |
Merge-Type: sync9 |
Content-Length: 64 |
|
[{"text": "Hi, everyone!", | | Snapshot
"author": {"link": "/user/tommy"}}] | |
Version: "g09ur8z74r" | Update
Parents: "ej4lhb9z78" |
Content-Type: application/json |
Merge-Type: sync9 |
Patches: 1 |
|
Content-Length: 53 | | Patch
Content-Range: json .messages[1:1] | |
| |
[{"text": "Yo!", | |
"author": {"link": "/user/yobot"}] | |
Version: "2bcbi84nsp" | Update
Parents: "g09ur8z74r" |
Content-Type: application/json |
Merge-Type: sync9 |
Patches: 1 |
|
Content-Length: 58 | | Patch
Content-Range: json .messages[2:2] | |
| |
[{"text": "Hi, Tommy!", | |
"author": {"link": "/user/sal"}}] | |
Version: "up12vyc5ib" | Update
Parents: "2bcbi84nsp" |
Content-Type: application/json |
Merge-Type: sync9 |
Patches: 1 |
|
Content-Length: 288 | | Patch
Content-Type: application/json-patch+json | |
| |
[ | |
{"op": "test", "path": "/a/b/c", "value": "foo"}, | |
{"op": "remove", "path": "/a/b/c"}, | |
{"op": "add", "path": "/a/b/c", "value": []}, | |
{"op": "replace", "path": "/a/b/c", "value": 42}, | |
{"op": "move", "from": "/a/b", "path": "/a/d"}, | |
{"op": "copy", "from": "/a/d", "path": "/a/d/e"} | |
] | |
4.1. Creating a Subscription
A client requests a subscription by issuing a GET request with a
Subscribe header:
Subscribe: <Parameters>
<Parameters> may be blank, set to "true", or contain arbitrary data,
and is reserved for future use.
This header modifies the normal GET method's semantics, to request a
subscription to future updates to the data, rather than only
returning the current version of the representation data.
A server implementing Subscribe MUST include a Subscribe header in
its response. The server then SHOULD keep the connection open, and
send updates over it.
In general, a server that implements subscriptions promises to keep
its subscribed clients up-to-date by sending changes until the
connection is closed. Once closed, a subscription can be resumed by
the client issuing a subsequent GET request on the same document.
4.2. Sending multiple updates per GET
To send multiple updates, a server concatenates multiple updates into
a single response body. Each update MUST include headers and a body,
and MUST specify the end of its body by including at least one of the
following headers:
- Content-Length: N
- Patches: N
The body may be zero-length. A server MAY separate each update with
one or more blank lines. These lines do not count towards
Content-Length. They can by used to visually separate updates, or to
guide the behavior of certain proxies or clients:
1. Certain clients or proxies close inactive connections. A
server signal that a connection is still active by
periodically sending additional blank lines between updates.
2. Some clients (e.g. Firefox) only flush incoming data after a
receiving a chunk of a certain size. A server can ensure
small updates get flushed by padding them with blank lines.
4.3. Continuing a Subscription
Once closed, a Braid subscription may be restarted by the client
issuing a new subscription request.
When the client reconnects, it may specify its last known version
using the Parents header. The server SHOULD then send only the
updates since that version.
Example:
Initial request:
GET /chat
Subscribe:
Initial response:
HTTP/1.1 209 Subscription
Subscribe:
Version: "ej4lhb9z78" | Update
Content-Type: application/json |
Content-Length: 64 |
|
[{"text": "Hi, everyone!", | | Snapshot
"author": {"link": "/user/tommy"}}] | |
<Client disconnects>
Reconnection request:
GET /chat
Subscribe:
Parents: "ej4lhb9z78"
Reconnection response:
HTTP/1.1 209 Subscription
Subscribe:
Version: "g09ur8z74r" | Update
Parents: "ej4lhb9z78" |
Content-Type: application/json |
Merge-Type: sync9 |
Patches: 1 |
|
Content-Length: 53 | | Patch
Content-Range: json .messages[1:1] | |
| |
[{"text": "Yo!", | |
"author": {"link": "/user/yobot"}] | |
4.4. Signaling "all caught up"
When starting or resuming a subscription, the server can indicate
which version is current by specifying a "Current-Version" header
before starting the stream of versions. This should contain the
frontier of time -- the leaves of the currently-known time DAG. The
client can use this information to determine when it has caught up
with the server's version at the time it received the client's
request.
Request:
GET /chat
Subscribe:
Response:
HTTP/1.1 209 Subscription
Subscribe:
Current-Version: "ej4lhb9z78" <-- Current Version
Version: "b9z78ej4lh" | Updates
Content-Type: application/json |
Merge-Type: sync9 |
Content-Length: 2 |
|
[] |
|
Version: "ej4lhb9z78" | <-- Current Version
Parents: "b9z78ej4lh" |
Content-Type: application/json |
Merge-Type: sync9 |
Content-Length: 64 |
|
[{"text": "Hi, everyone!", |
"author": {"link": "/user/tommy"}}] V
<-- Now caught up
4.5. Errors
If a server has dropped the history that a client requests, the
server can return a 410 GONE response, to tell the client "sorry, I
don't have the history necessary to synchronize with you."
5. Design Goals
This spec is designed to be:
1. Backwards-compatible with existing HTTP
2. Easy to implement simple synchronizers with. For instance, it
should be easy to write a read-only synchronizer for an
append-only log.
3. Possible to implement arbitrary synchronization algorithms. For
instance, these extensions support any Operational Transform or
CRDT algorithm.
6. Use Cases
6.1. Dynamic Resources: Animating a PNG
Braid allows resources to become inherently dynamic -- able to change
over time. You can use this to make a resource animate.
In this example, a server streams changes to a PNG file in a sequence
of patches. When the client renders the new state of the PNG after
each patch, a new frame of animation is displayed.
Request:
GET /animated-braid.png
Subscribe
Response:
HTTP/1.1 209 Subscribe
Subscribe
Content-Type: image/png | Update
Content-Length: 170763 |
|
<binary data> | | Snapshot
Content-Type: image/png | Update
Patches: 2 |
|
Content-Length: 1239 | | Patch
Content-Range: bytes 100-200 | |
| |
<binary data> | |
|
Content-Length: 62638 | | Patch
Content-Range: bytes 348-887 | |
| |
<binary data> | |
6.2. Dynamic Proxies and Caches
Since updates aren't pushed, today's web often uses timeouts to
trigger a cache becoming stale. Unfortunately, sometimes the timeout
is wrong, and caches become out-of-date, and we have to wait for an
unknown cache to timeout before we can see the new version of
something. As a result, programmers have learned to force-reload
pages habitually, and caches become less efficient than necessary.
A cache supporting the Braid extensions, however, will automatically
update whenever a change occurs. If a client starts a GET
Subscription with a proxy, the proxy will then start and maintain a
GET Subscription with the origin server. The origin server will
promise to send the proxy updates over its GET Subscription, and the
proxy will then relay these changes to all connected clients. If a
set of clients and servers all support Braid, they will never need to
force-reload caches for any data amongst them.
6.3. A Serverless Chat Example
A Braid web application can operate offline. A user can use the app
from an airplane, and their edits can synchronize when they regain
internet connections. Additionally, the Braid protocol can be
expressed over peer-to-peer transports (e.g. WebRTC) to support a a a
a peer-to-peer synchronization without a server. For example, a chat
application might be served and synchronized on Braid-HTTP, while
also establishing redundant peer-to-peer connections on WebRTC, and
translating all Braid-HTTP messages over the WebRTC connections, and
vice versa. The server could then be shut down, and users of the
chat app could continue to send messages to one another.
Imagine the server serves the current set of trusted clients' IP
addresses at the /peers state. Each client then subscribes to the
/peers state with:
GET /peers
Subscribe:
-------
[ {ip: '13.55.32.158', pubkey: 'x371...8382'},
{ip: '244.38.55.83', pubkey: 'o2u8...2s73'},
...
]
Each peer can then choose a set of those peers with whom to establish
a WebRTC connection. It will then exchange Braid messages with those
peers over that connection.
7. Related Work
7.1. Existing IETF Standards
A number of IETF specifications already standardize aspects of
synchronization for specific domains. IMAP [RFC9051] provides
synchronization of email. WebDAV provides the synchronization of
"collections" [RFC6578], and has been extended specifically for
calendar data in CalDAV [RFC4791], and vCards in [RFC6350]. More
recently, JMAP [RFC8620] provides an updated method of
synchronization, supporting mail, calendars, and contacts.
7.2. IETF Work in Progress
We wish to integrate this work with the excellent related efforts
already underway:
Resumable Uploads [draft-ietf-httpbis-resumable-upload] can also be
expressed as a sequence of patches, from client to server. Each
patch can specify a version in an "uploading" branch of time. Once
the upload is complete, the branch can be "merged" back into the main
version history. A subsequent version of this draft will show an
example.
Byte-Range-Patch [draft-wright-http-patch-byterange] also enables
general patches using Content-Range.
These efforts share our goals.
7.3. Web Frameworks
Web applications typically synchronize the state of a client and
server with layers of models, views, and controllers in web
frameworks. By automating synchronization within HTTP, programmers
have to write fewer layers of code on top of it.
====== Legacy Websites ====== ====== Braid Websites ======
Today's webpages are Braid generalizes HTTP
generated from multiple into a standard for
layers of state. Each layer synchronizing state within
has a different API. and between websites.
x Non-standard state API o Standard state API
_Client__
/ \
: o o o o : Webpage DOM o o o o State
: \| \| : \| \|
: x x : HTML Templates o o State
: /| /| : /| /|
: x x x x : JS Models o o o o State
\ | | | | / | | | |
| | | | | | | |
o o o o - http:// - o o o o - http:// -
/ | | | | \ | | | |
: x x x x : Views o o o o State
: | \| | : | \| |
: x x x : Controllers o o o State
: \ / \| : \ / \|
: x x : Models o o State
: \ / : \ /
\.... x ../ Database o State
Server
Today's programmers have to Each piece of Braid state (o)
learn each API, and wire them has a URL; whether public or
together, making sure that internal. State can be a
changes to shared state function of other state, and
synchronize across all and automatically recompute
layers and computers. when its dependencies change.
Braid guarantees network
synchronization.
8. IANA Considerations
8.1. Header Field Registration
HTTP header fields are registered within the "Message Headers"
registry maintained at
<http://www.iana.org/assignments/message-headers/>.
This document defines the following HTTP header fields, so their
associated registry entries have been updated according to the
permanent registrations below (see [BCP90]):
+---------------------+----------+--------------+-------------+
| Header Field Name | Protocol | Status | Reference |
+---------------------+----------+--------------+-------------+
| Version | http | experimental | Section 2 |
| Parents | http | experimental | Section 2 |
| Merge-Type | http | experimental | Section 2.2 |
| Patches | http | experimental | Section 2.3 |
| Subscribe | http | experimental | Section 4 |
| Current-Version | http | experimental | Section 3.4 |
+---------------------+----------+--------------+-------------+
The change controller is: "IETF (iesg@ietf.org) - Internet
Engineering Task Force".
9. Security Considerations
XXX Todo
10. Conventions
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in [RFC2119].
11. Copyright Notice
Copyright (c) 2023 IETF Trust and the persons identified as the
document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents
(http://trustee.ietf.org/license-info) in effect on the date of
publication of this document. Please review these documents
carefully, as they describe your rights and restrictions with respect
to this document. Code Components extracted from this document must
include Simplified BSD License text as described in Section 4.e of
the Trust Legal Provisions and are provided without warranty as
described in the Simplified BSD License.
12. References
12.1. Normative References
[RFC5789] "PATCH Method for HTTP", RFC 5789.
[RFC9110] "HTTP Semantics", RFC 9110.
[RFC9111] "HTTP Caching", RFC 9111.
[RFC9112] "HTTP/1.1", RFC 9112.
[RFC8941] "Structured Field Values for HTTP"
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[MERGE-TYPES] draft-toomim-httpbis-merge-types-00
[RANGE-PATCH] draft-toomim-httpbis-range-patch-00
12.2. Informative References
[XHR] Van Kestern, A., Aubourg, J., Song, J., and R. M.
Steen, H. "XMLHttpRequest", September 2019.
<https://xhr.spec.whatwg.org/>
[SSE] Hickson, I. "Server-Sent Events", W3C Recommendation,
February 2015.
<https://www.w3.org/TR/2015/REC-eventsource-20150203/>
[REST] Fielding, R. "Architectural Styles and the Design of
Network-based Software Architectures" Doctoral
dissertation, University of California, Irvine, 2000.
[RFC9051] Melnikov, Ed., Leiba, Ed., "Internet Message Access
Protocol - Version 4rev2", RFC 9051, DOI 10.17487/RFC9051,
August 2021, <https://www.rfc-editor.org/info/rfc9051>.
[RFC6578] Daboo, C., Quillaud, A., "Collection Synchronization
for Web Distributed Authoring and Versioning (WebDAV)",
RFC 6578, DOI 10.17487/RFC6578, March 2012,
<https://www.rfc-editor.org/info/rfc6578>.
[RFC4791] Daboo, C., Desruisseaux, B., Dusseault, L., "Calendaring
Extensions to WebDAV (CalDAV)", RFC 4791,
DOI 10.17487/RFC4791, March 2007,
<https://www.rfc-editor.org/info/rfc4791>.
[RFC6350] Perreault, S., "vCard Format Specification", RFC 6350,
DOI 10.17487/RFC6350, August 2011,
<https://www.rfc-editor.org/info/rfc6350>.
[RFC8620] Jenkins, N., Newman, C., "The JSON Meta Application
Protocol (JMAP)", RFC 8620, DOI 10.17487/RFC8620,
July 2019, <https://www.rfc-editor.org/info/rfc8620>.
[RFC6902] Bryan, P., Nottingham, M., "Javascript Object Notation
(JSON) Patch", RFC 6902.
[RFC9110] Fielding, R., Nottingham, M., Reschke, J., "HTTP
Semantics", RFC 9110
[BCP90] Klyne, G., Nottingham, M., and J. Mogul, "Registration
Procedures for Message Header Fields", BCP 90, RFC 3864,
September 2004.
13. Acknowledgements
In addition to the authors, this spec contains intellectual
contributions from the following people:
- Mitar Milutinovic
- Sarah Allen
- Duane Johnson
- Travis Kriplean
- Marius Nita
- Paul Pham
- Morgan Dixon
- Karthik Palaniappan
We thank the following people for key feedback on previous drafts:
- Austin Wright
- Martin Thomson
- Eric Kinnear
- Olli Vanhoja
- Julian Reschke
- Chris Lemmons
- Rahul Gupta
We also thank Mark Nottingham, Fred Baker, Adam Roach, and Barry
Leiba for facilitating a productive environment within the IETF.
14. Authors' Addresses
For more information, the authors of this document are best contacted
via Internet mail:
Michael Toomim
Invisible College, Berkeley
2053 Berkeley Way
Berkeley, CA 94704
EMail: toomim@gmail.com
Web: https://invisible.college/@toomim
Greg Little
Invisible College, Berkeley
2053 Berkeley Way
Berkeley, CA 94704
EMail: glittle@gmail.com
Web: https://glittle.org/
Rafie Walker
Bard College
EMail: slickytail.mc@gmail.com
Bryn Bellomy
Invisible College, Berkeley
2053 Berkeley Way
Berkeley, CA 94704
EMail: bryn@signals.io
Web: https://invisible.college/@bryn
Joseph Gentle
Invisible College, Berkeley
2053 Berkeley Way
Berkeley, CA 94704
EMail: me@josephg.com
Web: https://josephg.com/