https://github.com/braid-org/braid-http
An implementation of Braid-HTTP for Node.js and Browsers
https://github.com/braid-org/braid-http
Last synced: 5 months ago
JSON representation
An implementation of Braid-HTTP for Node.js and Browsers
- Host: GitHub
- URL: https://github.com/braid-org/braid-http
- Owner: braid-org
- License: other
- Created: 2024-08-02T10:00:12.000Z (almost 2 years ago)
- Default Branch: master
- Last Pushed: 2026-01-18T02:59:13.000Z (5 months ago)
- Last Synced: 2026-01-18T14:42:57.701Z (5 months ago)
- Language: JavaScript
- Size: 448 KB
- Stars: 6
- Watchers: 7
- Forks: 1
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- Contributing: contributing.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# Braid-HTTP
This [ponyfill](https://ponyfill.com/) library extends the HTTP
implementations of Browsers and Nodejs with Braid-HTTP; transforming them from
*state transfer* to *state synchronization* systems.
These features are provided in an elegant, backwards-compatible way:
- Browsers: get a drop-in replacement for `fetch()`
- Nodejs: get a route handler that adds abilities to the `http`, `https`, and `http2` modules
It conforms to the [Braid-HTTP
v04](https://github.com/braid-org/braid-spec/blob/master/draft-toomim-httpbis-braid-http-04.txt)
specification, with the additional [HTTP
Multiresponse](https://braid.org/meeting-89) and [Multiplexing
v1.0](https://braid.org/protocol/multiplexing) extensions.
Developed in [braid.org](https://braid.org).
## Installing
Browsers:
```html
// To live on the cutting edge, you can now replace the browser's fetch() if desired:
// window.fetch = braid_fetch
```
Node.js:
```shell
npm install braid-http
```
```javascript
// Import with require()
require('braid-http').fetch // A polyfill for fetch
require('braid-http').http_client // A polyfill for require('http') clients
require('braid-http').http_server // A polyfill for require('http') servers
// Or as es6 module
import {fetch, http_client, http_server} from 'braid-http'
```
## Using it in Browsers
This library adds a `{subscribe: true}` option to `fetch()`, and lets you
access the result of a subscription with these new fields on the fetch response:
- `response.subscribe( update => ... )`
- `response.subscription`: an iterator that can be used with `for await`
- `response.version`: the parsed version from the response headers (if present)
### Example Subscription with Promises
Here is an example of subscribing to a Braid resource using promises:
```javascript
fetch('https://braid.org/chat', {subscribe: true}).then(
res => res.subscribe(
(update) => {
console.log('We got a new update!', update)
// {
// version: ["me"],
// parents: ["mom", "dad"],
// patches: [{
//. unit: "json",
// range: ".foo",
// content: new Uint8Array([51]),
// content_text: "3" <-- getter
//. }],
// body: new Uint8Array([51]),
// body_text: "3" <-- getter
// }
//
// Note that `update` will contain either patches *or* body
}
)
)
```
If you want automatic reconnections, this library add a `{retry: true}` option to `fetch()`.
```javascript
fetch('https://braid.org/chat', {subscribe: true, retry: true}).then(
res => res.subscribe(
(update) => {
console.log('We got a new update!', update)
// Do something with the update
}
)
)
```
For use in conjunction with `{retry: true}`, it's possible to make the `parents` param equal to a function, which will be called to get the current parents each time the fetch establishes a new connection.
```javascript
fetch('https://braid.org/chat', {subscribe: true, retry: true, parents: () => {
return current_parents
}}).then(
res => res.subscribe(
(update) => {
console.log('We got a new update!', update)
// Do something with the update
}
)
)
```
### Example Subscription with Async/Await
```javascript
(await fetch('/chat', {subscribe: true, retry: true})).subscribe(
(update) => {
// We got a new update!
})
```
### Example Subscription with `for await`
```javascript
var subscription_iterator = (await fetch('/chat',
{subscribe: true, retry: true})).subscription
for await (var update of subscription_iterator) {
// Updates might come in the form of patches:
if (update.patches)
chat = apply_patches(update.patches, chat)
// Or complete snapshots:
else
// Beware the server doesn't send these yet.
chat = JSON.parse(update.body_text)
render_stuff()
}
```
## Using it in Nodejs
You can braidify your nodejs server with:
```
var braidify = require('braid-http').http_server
```
Braidify adds these new abilities to requests and responses:
- `req.subscribe`
- `req.startSubscription({onClose: cb})`
- `await req.parseUpdate()`
- `res.sendUpdate()`
You can call it in two ways:
1. `braidify((req, res) => ...)` wraps your HTTP request handler, and gives it
perfectly braidified requests and responses.
2. `braidify(req, res, next)` will add arguments to your existing requests and
responses. You can use this as express middleware.
### Example Nodejs server with the built-in HTTP module
```javascript
var braidify = require('braid-http').http_server
// or:
import {http_server as braidify} from 'braid-http'
require('http').createServer(
braidify((req, res) => {
// Now braid stuff is available on req and res
// So you can easily handle subscriptions
if (req.subscribe)
res.startSubscription({ onClose: _=> null })
// startSubscription automatically sets statusCode = 209
else
res.statusCode = 200
// And send updates over a subscription
res.sendUpdate({
version: ['greg'],
body: JSON.stringify({greg: 'greg'})
})
})
).listen(9935)
```
You can also use `braidify` within a request handler like this:
```javascript
require('http').createServer(
(req, res) => {
braidify(req, res); if (req.is_multiplexer) return
// Now braid stuff is available on req and res
// ...
})
).listen(9935)
```
The `is_multiplexer` test in this form is only necessary if multiplexing is
enabled.
### Example Nodejs server with Express
Or if you're using `express`, you can just call `app.use(braidify)` to get
braid features added to every request and response.
```javascript
var braidify = require('braid-http').http_server
// or:
import {http_server as braidify} from 'braid-http'
var app = require('express')()
app.use(braidify) // Add braid stuff to req and res
app.get('/', (req, res) => {
// Now use it
if (req.subscribe)
res.startSubscription({ onClose: _=> null })
// startSubscription automatically sets statusCode = 209
else
res.statusCode = 200
// Send the current version
res.sendUpdate({
version: ['greg'],
parents: ['gr','eg'],
body: JSON.stringify({greg: 'greg'})
})
// Or you can send patches like this:
// res.sendUpdate({
// version: ['greg'],
// parents: ['gr','eg'],
// patches: [{range: '.greg', unit: 'json', content: '"greg"'}]
// })
})
require('http').createServer(app).listen(8583)
```
### Example Nodejs client with `require('http')`
```javascript
// Use this line if necessary for self-signed certs
// process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0
var https = require('braid-http').http_client(require('https'))
// or:
// import braid_http from 'braid-http'
// https = braid_http.http_client(require('https'))
https.get(
'https://braid.org/chat',
{subscribe: true},
(res) => {
res.on('update', (update) => {
console.log('well we got one', update)
})
}
)
```
To get auto-reconnections use:
```javascript
function connect () {
https.get(
'https://braid.org/chat',
{subscribe: true},
(res) => {
res.on('update', (update) => {
// {
// version: ["me"],
// parents: ["mom", "dad"],
// patches: [{
//. unit: "json",
// range: ".foo",
// content: new Uint8Array([51]),
// content_text: "3" <-- getter
//. }],
// body: new Uint8Array([51]),
// body_text: "3" <-- getter
// }
// Update will contain either patches *or* body, but not both
console.log('We got a new update!', update)
})
res.on('end', e => setTimeout(connect, 1000))
res.on('error', e => setTimeout(connect, 1000))
})
}
connect()
```
### Example Nodejs client with `fetch()`
```javascript
var fetch = require('braid-http').fetch
// or:
import {fetch} from 'braid-http'
// process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0
fetch('https://localhost:3009/chat',
{subscribe: true}).andThen(
x => console.log('Got ', x)
)
```
## Configuring Multiplexing
You shouldn't need to, but can, configure which requests the library will
[multiplex](https://braid.org/protocol/multiplexing). You can configure
multiplexing on both the client and the server. They both need multiplexing
enabled for it to happen.
### Client
A client can globally disable multiplexing on `braid_fetch()` with:
```javascript
braid_fetch.enable_multiplex = false
```
It can enable multiplexing for all GET requests with:
```javascript
braid_fetch.enable_multiplex = true
```
It can also set it to multiplex after `N` connections to an origin with:
```javascript
braid_fetch.enable_multiplex = {after: N}
```
The default value is `{after: 1}`.
A client can override this global setting per-request by passing the same
value into `braid_fetch(url, {multiplex: })`, such as with:
```javascript
braid_fetch('/example', {multiplex: true, subscription: true})
braid_fetch('/example', {multiplex: false, subscription: true})
// or
braid_fetch('/example', {multiplex: {after: 1}, subscription: true})
```
### Server
Configure mutliplexing with:
```javascript
var braidify = require('braid-http').http-server
nbraidify.enable_multiplex = true // or false
```
## Test Procedure
Run tests from the command line:
```
node test/test.js
```
Or run tests in a browser by starting the test server:
```
node test/test.js --browser
```
Then open https://localhost:9000 and make sure all the boxes turn green.
You can also filter tests by name:
```
node test/test.js --filter="version"
```
For the complete browser test (including demos), use 3 terminals. In the first terminal start the demo chat server:
```
cd demos/chat
node server.js
```
In the second terminal start the demo blog server:
```
cd demos/blog
node server.js
```
And in the third terminal, start the test server:
```
node test/test.js --browser
```
Now open https://localhost:9000, make sure all the boxes turn green, and try out the demo chat and blog, sending a message in each.