-
Notifications
You must be signed in to change notification settings - Fork 377
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
367 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# bind2 | ||
|
||
bind2 for `@xmpp/client`. | ||
|
||
Included and enabled in `@xmpp/client`. | ||
|
||
## Usage | ||
|
||
Resource is optional and will be chosen by the server if omitted. | ||
|
||
### string | ||
|
||
```js | ||
import { xmpp } from "@xmpp/client"; | ||
|
||
const client = xmpp({ resource: "laptop" }); | ||
``` | ||
|
||
### function | ||
|
||
Instead, you can provide a function that will be called every time resource binding occurs (every (re)connect). | ||
|
||
```js | ||
import { xmpp } from "@xmpp/client"; | ||
|
||
const client = xmpp({ resource: onBind }); | ||
|
||
async function onBind(bind) { | ||
const resource = await fetchResource(); | ||
return resource; | ||
} | ||
``` | ||
|
||
## References | ||
|
||
[XEP-0386: Bind 2](https://xmpp.org/extensions/xep-0386.html) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import xml from "@xmpp/xml"; | ||
|
||
const NS_BIND = "urn:xmpp:bind:0"; | ||
|
||
export default function bind2({ sasl2, entity }, tag) { | ||
const features = new Map(); | ||
|
||
sasl2.use( | ||
NS_BIND, | ||
async (element) => { | ||
if (!element.is("bind", NS_BIND)) return; | ||
|
||
tag = typeof tag === "function" ? await tag() : tag; | ||
|
||
const sessionFeatures = await getSessionFeatures({ element, features }); | ||
|
||
return xml( | ||
"bind", | ||
{ xmlns: "urn:xmpp:bind:0" }, | ||
tag && xml("tag", null, tag), | ||
...sessionFeatures, | ||
); | ||
}, | ||
(element) => { | ||
const aid = element.root().getChildText("authorization-identifier"); | ||
if (aid) entity._jid(aid); | ||
|
||
for (const child of element.getChildElements()) { | ||
const feature = features.get(child.getNS()); | ||
if (!feature?.[1]) continue; | ||
feature?.[1](child); | ||
} | ||
}, | ||
); | ||
|
||
return { | ||
use(ns, req, res) { | ||
features.set(ns, [req, res]); | ||
}, | ||
}; | ||
} | ||
|
||
function getSessionFeatures({ element, features }) { | ||
const promises = []; | ||
|
||
const inline = element.getChild("inline"); | ||
if (!inline) return promises; | ||
|
||
for (const element of inline.getChildElements()) { | ||
const xmlns = element.attrs.var; | ||
const feature = features.get(xmlns); | ||
if (!feature) continue; | ||
promises.push(feature[0](element)); | ||
} | ||
|
||
return Promise.all(promises); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { mockClient, id, promiseError } from "@xmpp/test"; | ||
|
||
function mockFeatures(entity) { | ||
entity.mockInput( | ||
<features xmlns="http://etherx.jabber.org/streams"> | ||
<authentication xmlns="urn:xmpp:sasl:2"> | ||
<mechanism>PLAIN</mechanism> | ||
<inline> | ||
<bind xmlns="urn:xmpp:bind:0" /> | ||
</inline> | ||
</authentication> | ||
</features>, | ||
); | ||
} | ||
|
||
function catchAuthenticate(entity) { | ||
return entity.catchOutgoing((stanza) => { | ||
if (stanza.is("authenticate", "urn:xmpp:sasl:2")) return true; | ||
}); | ||
} | ||
|
||
test("without tag", async () => { | ||
const { entity } = mockClient(); | ||
mockFeatures(entity); | ||
const stanza = await catchAuthenticate(entity); | ||
|
||
await expect(stanza.getChild("bind", "urn:xmpp:bind:0").toString()).toEqual( | ||
(<bind xmlns="urn:xmpp:bind:0" />).toString(), | ||
); | ||
}); | ||
|
||
test("with string tag", async () => { | ||
const resource = id(); | ||
const { entity } = mockClient({ resource }); | ||
mockFeatures(entity); | ||
const stanza = await catchAuthenticate(entity); | ||
|
||
expect(stanza.getChild("bind", "urn:xmpp:bind:0").toString()).toEqual( | ||
( | ||
<bind xmlns="urn:xmpp:bind:0"> | ||
<tag>{resource}</tag> | ||
</bind> | ||
).toString(), | ||
); | ||
}); | ||
|
||
test("with function resource returning string", async () => { | ||
// eslint-disable-next-line unicorn/consistent-function-scoping | ||
function resource() { | ||
return "1k2k3"; | ||
} | ||
|
||
const { entity } = mockClient({ resource }); | ||
mockFeatures(entity); | ||
const stanza = await catchAuthenticate(entity); | ||
|
||
expect(stanza.getChild("bind", "urn:xmpp:bind:0").toString()).toEqual( | ||
( | ||
<bind xmlns="urn:xmpp:bind:0"> | ||
<tag>{resource()}</tag> | ||
</bind> | ||
).toString(), | ||
); | ||
}); | ||
|
||
test("with function resource throwing", async () => { | ||
const error = new Error("foo"); | ||
|
||
|
||
function resource() { | ||
throw error; | ||
} | ||
|
||
const { entity } = mockClient({ resource }); | ||
|
||
const willError = promiseError(entity); | ||
|
||
mockFeatures(entity); | ||
|
||
expect(await willError).toBe(error); | ||
}); | ||
|
||
test("with function resource returning resolved promise", async () => { | ||
// eslint-disable-next-line unicorn/consistent-function-scoping | ||
async function resource() { | ||
return "1k2k3"; | ||
} | ||
|
||
const { entity } = mockClient({ resource }); | ||
mockFeatures(entity); | ||
const stanza = await catchAuthenticate(entity); | ||
|
||
expect(stanza.getChild("bind", "urn:xmpp:bind:0").toString()).toEqual( | ||
( | ||
<bind xmlns="urn:xmpp:bind:0"> | ||
<tag>{await resource()}</tag> | ||
</bind> | ||
).toString(), | ||
); | ||
}); | ||
|
||
test("with function resource returning rejected promise", async () => { | ||
const error = new Error("foo"); | ||
|
||
|
||
async function resource() { | ||
throw error; | ||
} | ||
|
||
const { entity } = mockClient({ resource }); | ||
|
||
const willError = promiseError(entity); | ||
|
||
mockFeatures(entity); | ||
|
||
expect(await willError).toBe(error); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.