2
0
mirror of https://github.com/esiur/esiur-js.git synced 2025-05-06 04:22:58 +00:00
This commit is contained in:
Ahmed Zamil 2019-06-07 23:07:15 +03:00
parent 054a4a0bd8
commit 144db9ee8b
100 changed files with 25137 additions and 1552 deletions

View File

@ -3,9 +3,27 @@
del "build\esiur-debug.js"
del "build\esiur.js"
type "src\IEventHandler.js" > build\esiur-debug.js
type "src\CustomResourceEvent.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "external\sha256.min.js" >> build\esiur-debug.js
type "src\NetworkBuffer.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\AsyncException.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\Authentication.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\Session.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\DistributedPropertyContext.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\IPermissionsManager.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\KeyList.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\PropertyValue.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\IEventHandler.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\SHA256.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\IDestructible.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
@ -55,8 +73,7 @@ type "src\IIPPacket.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\Instance.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\NetworkBuffer.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\NotModified.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
type "src\PropertyTemplate.js" >> build\esiur-debug.js
@ -68,5 +85,9 @@ echo. >> build\esiur-debug.js
type "src\Warehouse.js" >> build\esiur-debug.js
echo. >> build\esiur-debug.js
copy build\esiur-debug.js build\esiur-debug-node.js
type "module.js" >> build\esiur-debug-node.js
echo. >> build\esiur-debug-node.js
REM minify build/esiur-debug.js -o build/esiur.js
uglifyjs build/esiur-debug.js -c -o build/esiur.js

7652
build/esiur-debug-node.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

8
module.js Normal file
View File

@ -0,0 +1,8 @@
exports.printMsg = function() {
console.log("Esiur 1.1");
}
module.exports = { Warehouse, DistributedConnection};
var WebSocket = require('ws')

7
node_modules/async-limiter/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,7 @@
language: node_js
node_js:
- "6"
- "node"
script: npm run travis
cache:
yarn: true

8
node_modules/async-limiter/LICENSE generated vendored Normal file
View File

@ -0,0 +1,8 @@
The MIT License (MIT)
Copyright (c) 2017 Samuel Reed <samuel.trace.reed@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1
node_modules/async-limiter/coverage/coverage.json generated vendored Normal file
View File

@ -0,0 +1 @@
{"/Users/samuelreed/git/forks/async-throttle/index.js":{"path":"/Users/samuelreed/git/forks/async-throttle/index.js","s":{"1":1,"2":7,"3":1,"4":6,"5":6,"6":6,"7":6,"8":6,"9":6,"10":1,"11":1,"12":3,"13":13,"14":13,"15":13,"16":1,"17":19,"18":1,"19":45,"20":6,"21":39,"22":13,"23":13,"24":13,"25":13,"26":39,"27":18,"28":6,"29":6,"30":1,"31":6,"32":6,"33":6,"34":1,"35":13,"36":13,"37":1},"b":{"1":[1,6],"2":[6,5],"3":[6,5],"4":[6,39],"5":[13,26],"6":[18,21],"7":[6,0]},"f":{"1":7,"2":3,"3":13,"4":19,"5":45,"6":6,"7":13},"fnMap":{"1":{"name":"Queue","line":3,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":24}}},"2":{"name":"(anonymous_2)","line":22,"loc":{"start":{"line":22,"column":24},"end":{"line":22,"column":41}}},"3":{"name":"(anonymous_3)","line":23,"loc":{"start":{"line":23,"column":28},"end":{"line":23,"column":39}}},"4":{"name":"(anonymous_4)","line":31,"loc":{"start":{"line":31,"column":7},"end":{"line":31,"column":18}}},"5":{"name":"(anonymous_5)","line":36,"loc":{"start":{"line":36,"column":23},"end":{"line":36,"column":34}}},"6":{"name":"(anonymous_6)","line":55,"loc":{"start":{"line":55,"column":25},"end":{"line":55,"column":38}}},"7":{"name":"done","line":62,"loc":{"start":{"line":62,"column":0},"end":{"line":62,"column":16}}}},"statementMap":{"1":{"start":{"line":3,"column":0},"end":{"line":14,"column":1}},"2":{"start":{"line":4,"column":2},"end":{"line":6,"column":3}},"3":{"start":{"line":5,"column":4},"end":{"line":5,"column":30}},"4":{"start":{"line":8,"column":2},"end":{"line":8,"column":26}},"5":{"start":{"line":9,"column":2},"end":{"line":9,"column":53}},"6":{"start":{"line":10,"column":2},"end":{"line":10,"column":19}},"7":{"start":{"line":11,"column":2},"end":{"line":11,"column":17}},"8":{"start":{"line":12,"column":2},"end":{"line":12,"column":16}},"9":{"start":{"line":13,"column":2},"end":{"line":13,"column":31}},"10":{"start":{"line":16,"column":0},"end":{"line":20,"column":2}},"11":{"start":{"line":22,"column":0},"end":{"line":28,"column":3}},"12":{"start":{"line":23,"column":2},"end":{"line":27,"column":4}},"13":{"start":{"line":24,"column":4},"end":{"line":24,"column":75}},"14":{"start":{"line":25,"column":4},"end":{"line":25,"column":16}},"15":{"start":{"line":26,"column":4},"end":{"line":26,"column":24}},"16":{"start":{"line":30,"column":0},"end":{"line":34,"column":3}},"17":{"start":{"line":32,"column":4},"end":{"line":32,"column":43}},"18":{"start":{"line":36,"column":0},"end":{"line":53,"column":2}},"19":{"start":{"line":37,"column":2},"end":{"line":39,"column":3}},"20":{"start":{"line":38,"column":4},"end":{"line":38,"column":11}},"21":{"start":{"line":40,"column":2},"end":{"line":45,"column":3}},"22":{"start":{"line":41,"column":4},"end":{"line":41,"column":32}},"23":{"start":{"line":42,"column":4},"end":{"line":42,"column":19}},"24":{"start":{"line":43,"column":4},"end":{"line":43,"column":20}},"25":{"start":{"line":44,"column":4},"end":{"line":44,"column":16}},"26":{"start":{"line":47,"column":2},"end":{"line":52,"column":3}},"27":{"start":{"line":48,"column":4},"end":{"line":51,"column":5}},"28":{"start":{"line":49,"column":6},"end":{"line":49,"column":30}},"29":{"start":{"line":50,"column":6},"end":{"line":50,"column":27}},"30":{"start":{"line":55,"column":0},"end":{"line":60,"column":2}},"31":{"start":{"line":56,"column":2},"end":{"line":59,"column":3}},"32":{"start":{"line":57,"column":4},"end":{"line":57,"column":22}},"33":{"start":{"line":58,"column":4},"end":{"line":58,"column":16}},"34":{"start":{"line":62,"column":0},"end":{"line":65,"column":1}},"35":{"start":{"line":63,"column":2},"end":{"line":63,"column":17}},"36":{"start":{"line":64,"column":2},"end":{"line":64,"column":14}},"37":{"start":{"line":67,"column":0},"end":{"line":67,"column":23}}},"branchMap":{"1":{"line":4,"type":"if","locations":[{"start":{"line":4,"column":2},"end":{"line":4,"column":2}},{"start":{"line":4,"column":2},"end":{"line":4,"column":2}}]},"2":{"line":8,"type":"binary-expr","locations":[{"start":{"line":8,"column":12},"end":{"line":8,"column":19}},{"start":{"line":8,"column":23},"end":{"line":8,"column":25}}]},"3":{"line":9,"type":"binary-expr","locations":[{"start":{"line":9,"column":21},"end":{"line":9,"column":40}},{"start":{"line":9,"column":44},"end":{"line":9,"column":52}}]},"4":{"line":37,"type":"if","locations":[{"start":{"line":37,"column":2},"end":{"line":37,"column":2}},{"start":{"line":37,"column":2},"end":{"line":37,"column":2}}]},"5":{"line":40,"type":"if","locations":[{"start":{"line":40,"column":2},"end":{"line":40,"column":2}},{"start":{"line":40,"column":2},"end":{"line":40,"column":2}}]},"6":{"line":47,"type":"if","locations":[{"start":{"line":47,"column":2},"end":{"line":47,"column":2}},{"start":{"line":47,"column":2},"end":{"line":47,"column":2}}]},"7":{"line":56,"type":"if","locations":[{"start":{"line":56,"column":2},"end":{"line":56,"column":2}},{"start":{"line":56,"column":2},"end":{"line":56,"column":2}}]}}}}

View File

@ -0,0 +1,73 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for async-throttle/</title>
<meta charset="utf-8">
<link rel="stylesheet" href="../prettify.css">
<link rel="stylesheet" href="../base.css">
<style type='text/css'>
div.coverage-summary .sorter {
background-image: url(../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class="header high">
<h1>Code coverage report for <span class="entity">async-throttle/</span></h1>
<h2>
Statements: <span class="metric">100% <small>(37 / 37)</small></span> &nbsp;&nbsp;&nbsp;&nbsp;
Branches: <span class="metric">92.86% <small>(13 / 14)</small></span> &nbsp;&nbsp;&nbsp;&nbsp;
Functions: <span class="metric">100% <small>(7 / 7)</small></span> &nbsp;&nbsp;&nbsp;&nbsp;
Lines: <span class="metric">100% <small>(37 / 37)</small></span> &nbsp;&nbsp;&nbsp;&nbsp;
Ignored: <span class="metric"><span class="ignore-none">none</span></span> &nbsp;&nbsp;&nbsp;&nbsp;
</h2>
<div class="path"><a href="../index.html">All files</a> &#187; async-throttle/</div>
</div>
<div class="body">
<div class="coverage-summary">
<table>
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file high" data-value="index.js"><a href="index.js.html">index.js</a></td>
<td data-value="100" class="pic high"><span class="cover-fill cover-full" style="width: 100px;"></span><span class="cover-empty" style="width:0px;"></span></td>
<td data-value="100" class="pct high">100%</td>
<td data-value="37" class="abs high">(37&nbsp;/&nbsp;37)</td>
<td data-value="92.86" class="pct high">92.86%</td>
<td data-value="14" class="abs high">(13&nbsp;/&nbsp;14)</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="7" class="abs high">(7&nbsp;/&nbsp;7)</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="37" class="abs high">(37&nbsp;/&nbsp;37)</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="footer">
<div class="meta">Generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Mon Sep 11 2017 11:14:14 GMT-0500 (CDT)</div>
</div>
<script src="../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../sorter.js"></script>
</body>
</html>

View File

@ -0,0 +1,246 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for async-throttle/index.js</title>
<meta charset="utf-8">
<link rel="stylesheet" href="../prettify.css">
<link rel="stylesheet" href="../base.css">
<style type='text/css'>
div.coverage-summary .sorter {
background-image: url(../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class="header high">
<h1>Code coverage report for <span class="entity">async-throttle/index.js</span></h1>
<h2>
Statements: <span class="metric">100% <small>(37 / 37)</small></span> &nbsp;&nbsp;&nbsp;&nbsp;
Branches: <span class="metric">92.86% <small>(13 / 14)</small></span> &nbsp;&nbsp;&nbsp;&nbsp;
Functions: <span class="metric">100% <small>(7 / 7)</small></span> &nbsp;&nbsp;&nbsp;&nbsp;
Lines: <span class="metric">100% <small>(37 / 37)</small></span> &nbsp;&nbsp;&nbsp;&nbsp;
Ignored: <span class="metric"><span class="ignore-none">none</span></span> &nbsp;&nbsp;&nbsp;&nbsp;
</h2>
<div class="path"><a href="../index.html">All files</a> &#187; <a href="index.html">async-throttle/</a> &#187; index.js</div>
</div>
<div class="body">
<pre><table class="coverage">
<tr><td class="line-count">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68</td><td class="line-coverage"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1</span>
<span class="cline-any cline-yes">7</span>
<span class="cline-any cline-yes">1</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">6</span>
<span class="cline-any cline-yes">6</span>
<span class="cline-any cline-yes">6</span>
<span class="cline-any cline-yes">6</span>
<span class="cline-any cline-yes">6</span>
<span class="cline-any cline-yes">6</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1</span>
<span class="cline-any cline-yes">3</span>
<span class="cline-any cline-yes">13</span>
<span class="cline-any cline-yes">13</span>
<span class="cline-any cline-yes">13</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">19</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1</span>
<span class="cline-any cline-yes">45</span>
<span class="cline-any cline-yes">6</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">39</span>
<span class="cline-any cline-yes">13</span>
<span class="cline-any cline-yes">13</span>
<span class="cline-any cline-yes">13</span>
<span class="cline-any cline-yes">13</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">39</span>
<span class="cline-any cline-yes">18</span>
<span class="cline-any cline-yes">6</span>
<span class="cline-any cline-yes">6</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1</span>
<span class="cline-any cline-yes">6</span>
<span class="cline-any cline-yes">6</span>
<span class="cline-any cline-yes">6</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1</span>
<span class="cline-any cline-yes">13</span>
<span class="cline-any cline-yes">13</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">'use strict';
&nbsp;
function Queue(options) {
if (!(this instanceof Queue)) {
return new Queue(options);
}
&nbsp;
options = options || {};
this.concurrency = options.concurrency || Infinity;
this.pending = 0;
this.jobs = [];
this.cbs = [];
this._done = done.bind(this);
}
&nbsp;
var arrayAddMethods = [
'push',
'unshift',
'splice'
];
&nbsp;
arrayAddMethods.forEach(function(method) {
Queue.prototype[method] = function() {
var methodResult = Array.prototype[method].apply(this.jobs, arguments);
this._run();
return methodResult;
};
});
&nbsp;
Object.defineProperty(Queue.prototype, 'length', {
get: function() {
return this.pending + this.jobs.length;
}
});
&nbsp;
Queue.prototype._run = function() {
if (this.pending === this.concurrency) {
return;
}
if (this.jobs.length) {
var job = this.jobs.shift();
this.pending++;
job(this._done);
this._run();
}
&nbsp;
if (this.pending === 0) {
while (this.cbs.length !== 0) {
var cb = this.cbs.pop();
process.nextTick(cb);
}
}
};
&nbsp;
Queue.prototype.onDone = function(cb) {
<span class="missing-if-branch" title="else path not taken" >E</span>if (typeof cb === 'function') {
this.cbs.push(cb);
this._run();
}
};
&nbsp;
function done() {
this.pending--;
this._run();
}
&nbsp;
module.exports = Queue;
&nbsp;</pre></td></tr>
</table></pre>
</div>
<div class="footer">
<div class="meta">Generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Mon Sep 11 2017 11:14:14 GMT-0500 (CDT)</div>
</div>
<script src="../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../sorter.js"></script>
</body>
</html>

View File

@ -0,0 +1,182 @@
body, html {
margin:0; padding: 0;
}
body {
font-family: Helvetica Neue, Helvetica,Arial;
font-size: 10pt;
}
div.header, div.footer {
background: #eee;
padding: 1em;
}
div.header {
z-index: 100;
position: fixed;
top: 0;
border-bottom: 1px solid #666;
width: 100%;
}
div.footer {
border-top: 1px solid #666;
}
div.body {
margin-top: 10em;
}
div.meta {
font-size: 90%;
text-align: center;
}
h1, h2, h3 {
font-weight: normal;
}
h1 {
font-size: 12pt;
}
h2 {
font-size: 10pt;
}
pre {
font-family: Consolas, Menlo, Monaco, monospace;
margin: 0;
padding: 0;
line-height: 1.3;
font-size: 14px;
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
}
div.path { font-size: 110%; }
div.path a:link, div.path a:visited { color: #000; }
table.coverage { border-collapse: collapse; margin:0; padding: 0 }
table.coverage td {
margin: 0;
padding: 0;
color: #111;
vertical-align: top;
}
table.coverage td.line-count {
width: 50px;
text-align: right;
padding-right: 5px;
}
table.coverage td.line-coverage {
color: #777 !important;
text-align: right;
border-left: 1px solid #666;
border-right: 1px solid #666;
}
table.coverage td.text {
}
table.coverage td span.cline-any {
display: inline-block;
padding: 0 5px;
width: 40px;
}
table.coverage td span.cline-neutral {
background: #eee;
}
table.coverage td span.cline-yes {
background: #b5d592;
color: #999;
}
table.coverage td span.cline-no {
background: #fc8c84;
}
.cstat-yes { color: #111; }
.cstat-no { background: #fc8c84; color: #111; }
.fstat-no { background: #ffc520; color: #111 !important; }
.cbranch-no { background: yellow !important; color: #111; }
.cstat-skip { background: #ddd; color: #111; }
.fstat-skip { background: #ddd; color: #111 !important; }
.cbranch-skip { background: #ddd !important; color: #111; }
.missing-if-branch {
display: inline-block;
margin-right: 10px;
position: relative;
padding: 0 4px;
background: black;
color: yellow;
}
.skip-if-branch {
display: none;
margin-right: 10px;
position: relative;
padding: 0 4px;
background: #ccc;
color: white;
}
.missing-if-branch .typ, .skip-if-branch .typ {
color: inherit !important;
}
.entity, .metric { font-weight: bold; }
.metric { display: inline-block; border: 1px solid #333; padding: 0.3em; background: white; }
.metric small { font-size: 80%; font-weight: normal; color: #666; }
div.coverage-summary table { border-collapse: collapse; margin: 3em; font-size: 110%; }
div.coverage-summary td, div.coverage-summary table th { margin: 0; padding: 0.25em 1em; border-top: 1px solid #666; border-bottom: 1px solid #666; }
div.coverage-summary th { text-align: left; border: 1px solid #666; background: #eee; font-weight: normal; }
div.coverage-summary th.file { border-right: none !important; }
div.coverage-summary th.pic { border-left: none !important; text-align: right; }
div.coverage-summary th.pct { border-right: none !important; }
div.coverage-summary th.abs { border-left: none !important; text-align: right; }
div.coverage-summary td.pct { text-align: right; border-left: 1px solid #666; }
div.coverage-summary td.abs { text-align: right; font-size: 90%; color: #444; border-right: 1px solid #666; }
div.coverage-summary td.file { border-left: 1px solid #666; white-space: nowrap; }
div.coverage-summary td.pic { min-width: 120px !important; }
div.coverage-summary a:link { text-decoration: none; color: #000; }
div.coverage-summary a:visited { text-decoration: none; color: #777; }
div.coverage-summary a:hover { text-decoration: underline; }
div.coverage-summary tfoot td { border-top: 1px solid #666; }
div.coverage-summary .sorter {
height: 10px;
width: 7px;
display: inline-block;
margin-left: 0.5em;
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
}
div.coverage-summary .sorted .sorter {
background-position: 0 -20px;
}
div.coverage-summary .sorted-desc .sorter {
background-position: 0 -10px;
}
.high { background: #b5d592 !important; }
.medium { background: #ffe87c !important; }
.low { background: #fc8c84 !important; }
span.cover-fill, span.cover-empty {
display:inline-block;
border:1px solid #444;
background: white;
height: 12px;
}
span.cover-fill {
background: #ccc;
border-right: 1px solid #444;
}
span.cover-empty {
background: white;
border-left: none;
}
span.cover-full {
border-right: none !important;
}
pre.prettyprint {
border: none !important;
padding: 0 !important;
margin: 0 !important;
}
.com { color: #999 !important; }
.ignore-none { color: #999; font-weight: normal; }

View File

@ -0,0 +1,73 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for All files</title>
<meta charset="utf-8">
<link rel="stylesheet" href="prettify.css">
<link rel="stylesheet" href="base.css">
<style type='text/css'>
div.coverage-summary .sorter {
background-image: url(sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class="header high">
<h1>Code coverage report for <span class="entity">All files</span></h1>
<h2>
Statements: <span class="metric">100% <small>(37 / 37)</small></span> &nbsp;&nbsp;&nbsp;&nbsp;
Branches: <span class="metric">92.86% <small>(13 / 14)</small></span> &nbsp;&nbsp;&nbsp;&nbsp;
Functions: <span class="metric">100% <small>(7 / 7)</small></span> &nbsp;&nbsp;&nbsp;&nbsp;
Lines: <span class="metric">100% <small>(37 / 37)</small></span> &nbsp;&nbsp;&nbsp;&nbsp;
Ignored: <span class="metric"><span class="ignore-none">none</span></span> &nbsp;&nbsp;&nbsp;&nbsp;
</h2>
<div class="path"></div>
</div>
<div class="body">
<div class="coverage-summary">
<table>
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file high" data-value="async-throttle/"><a href="async-throttle/index.html">async-throttle/</a></td>
<td data-value="100" class="pic high"><span class="cover-fill cover-full" style="width: 100px;"></span><span class="cover-empty" style="width:0px;"></span></td>
<td data-value="100" class="pct high">100%</td>
<td data-value="37" class="abs high">(37&nbsp;/&nbsp;37)</td>
<td data-value="92.86" class="pct high">92.86%</td>
<td data-value="14" class="abs high">(13&nbsp;/&nbsp;14)</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="7" class="abs high">(7&nbsp;/&nbsp;7)</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="37" class="abs high">(37&nbsp;/&nbsp;37)</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="footer">
<div class="meta">Generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Mon Sep 11 2017 11:14:14 GMT-0500 (CDT)</div>
</div>
<script src="prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="sorter.js"></script>
</body>
</html>

View File

@ -0,0 +1 @@
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

View File

@ -0,0 +1,156 @@
var addSorting = (function () {
"use strict";
var cols,
currentSort = {
index: 0,
desc: false
};
// returns the summary table element
function getTable() { return document.querySelector('.coverage-summary table'); }
// returns the thead element of the summary table
function getTableHeader() { return getTable().querySelector('thead tr'); }
// returns the tbody element of the summary table
function getTableBody() { return getTable().querySelector('tbody'); }
// returns the th element for nth column
function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; }
// loads all columns
function loadColumns() {
var colNodes = getTableHeader().querySelectorAll('th'),
colNode,
cols = [],
col,
i;
for (i = 0; i < colNodes.length; i += 1) {
colNode = colNodes[i];
col = {
key: colNode.getAttribute('data-col'),
sortable: !colNode.getAttribute('data-nosort'),
type: colNode.getAttribute('data-type') || 'string'
};
cols.push(col);
if (col.sortable) {
col.defaultDescSort = col.type === 'number';
colNode.innerHTML = colNode.innerHTML + '<span class="sorter"></span>';
}
}
return cols;
}
// attaches a data attribute to every tr element with an object
// of data values keyed by column name
function loadRowData(tableRow) {
var tableCols = tableRow.querySelectorAll('td'),
colNode,
col,
data = {},
i,
val;
for (i = 0; i < tableCols.length; i += 1) {
colNode = tableCols[i];
col = cols[i];
val = colNode.getAttribute('data-value');
if (col.type === 'number') {
val = Number(val);
}
data[col.key] = val;
}
return data;
}
// loads all row data
function loadData() {
var rows = getTableBody().querySelectorAll('tr'),
i;
for (i = 0; i < rows.length; i += 1) {
rows[i].data = loadRowData(rows[i]);
}
}
// sorts the table using the data for the ith column
function sortByIndex(index, desc) {
var key = cols[index].key,
sorter = function (a, b) {
a = a.data[key];
b = b.data[key];
return a < b ? -1 : a > b ? 1 : 0;
},
finalSorter = sorter,
tableBody = document.querySelector('.coverage-summary tbody'),
rowNodes = tableBody.querySelectorAll('tr'),
rows = [],
i;
if (desc) {
finalSorter = function (a, b) {
return -1 * sorter(a, b);
};
}
for (i = 0; i < rowNodes.length; i += 1) {
rows.push(rowNodes[i]);
tableBody.removeChild(rowNodes[i]);
}
rows.sort(finalSorter);
for (i = 0; i < rows.length; i += 1) {
tableBody.appendChild(rows[i]);
}
}
// removes sort indicators for current column being sorted
function removeSortIndicators() {
var col = getNthColumn(currentSort.index),
cls = col.className;
cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
col.className = cls;
}
// adds sort indicators for current column being sorted
function addSortIndicators() {
getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted';
}
// adds event listeners for all sorter widgets
function enableUI() {
var i,
el,
ithSorter = function ithSorter(i) {
var col = cols[i];
return function () {
var desc = col.defaultDescSort;
if (currentSort.index === i) {
desc = !currentSort.desc;
}
sortByIndex(i, desc);
removeSortIndicators();
currentSort.index = i;
currentSort.desc = desc;
addSortIndicators();
};
};
for (i =0 ; i < cols.length; i += 1) {
if (cols[i].sortable) {
el = getNthColumn(i).querySelector('.sorter');
if (el.addEventListener) {
el.addEventListener('click', ithSorter(i));
} else {
el.attachEvent('onclick', ithSorter(i));
}
}
}
}
// adds sorting functionality to the UI
return function () {
if (!getTable()) {
return;
}
cols = loadColumns();
loadData(cols);
addSortIndicators();
enableUI();
};
})();
window.addEventListener('load', addSorting);

74
node_modules/async-limiter/coverage/lcov.info generated vendored Normal file
View File

@ -0,0 +1,74 @@
TN:
SF:/Users/samuelreed/git/forks/async-throttle/index.js
FN:3,Queue
FN:22,(anonymous_2)
FN:23,(anonymous_3)
FN:31,(anonymous_4)
FN:36,(anonymous_5)
FN:55,(anonymous_6)
FN:62,done
FNF:7
FNH:7
FNDA:7,Queue
FNDA:3,(anonymous_2)
FNDA:13,(anonymous_3)
FNDA:19,(anonymous_4)
FNDA:45,(anonymous_5)
FNDA:6,(anonymous_6)
FNDA:13,done
DA:3,1
DA:4,7
DA:5,1
DA:8,6
DA:9,6
DA:10,6
DA:11,6
DA:12,6
DA:13,6
DA:16,1
DA:22,1
DA:23,3
DA:24,13
DA:25,13
DA:26,13
DA:30,1
DA:32,19
DA:36,1
DA:37,45
DA:38,6
DA:40,39
DA:41,13
DA:42,13
DA:43,13
DA:44,13
DA:47,39
DA:48,18
DA:49,6
DA:50,6
DA:55,1
DA:56,6
DA:57,6
DA:58,6
DA:62,1
DA:63,13
DA:64,13
DA:67,1
LF:37
LH:37
BRDA:4,1,0,1
BRDA:4,1,1,6
BRDA:8,2,0,6
BRDA:8,2,1,5
BRDA:9,3,0,6
BRDA:9,3,1,5
BRDA:37,4,0,6
BRDA:37,4,1,39
BRDA:40,5,0,13
BRDA:40,5,1,26
BRDA:47,6,0,18
BRDA:47,6,1,21
BRDA:56,7,0,6
BRDA:56,7,1,0
BRF:14
BRH:13
end_of_record

67
node_modules/async-limiter/index.js generated vendored Normal file
View File

@ -0,0 +1,67 @@
'use strict';
function Queue(options) {
if (!(this instanceof Queue)) {
return new Queue(options);
}
options = options || {};
this.concurrency = options.concurrency || Infinity;
this.pending = 0;
this.jobs = [];
this.cbs = [];
this._done = done.bind(this);
}
var arrayAddMethods = [
'push',
'unshift',
'splice'
];
arrayAddMethods.forEach(function(method) {
Queue.prototype[method] = function() {
var methodResult = Array.prototype[method].apply(this.jobs, arguments);
this._run();
return methodResult;
};
});
Object.defineProperty(Queue.prototype, 'length', {
get: function() {
return this.pending + this.jobs.length;
}
});
Queue.prototype._run = function() {
if (this.pending === this.concurrency) {
return;
}
if (this.jobs.length) {
var job = this.jobs.shift();
this.pending++;
job(this._done);
this._run();
}
if (this.pending === 0) {
while (this.cbs.length !== 0) {
var cb = this.cbs.pop();
process.nextTick(cb);
}
}
};
Queue.prototype.onDone = function(cb) {
if (typeof cb === 'function') {
this.cbs.push(cb);
this._run();
}
};
function done() {
this.pending--;
this._run();
}
module.exports = Queue;

69
node_modules/async-limiter/package.json generated vendored Normal file
View File

@ -0,0 +1,69 @@
{
"_from": "async-limiter@~1.0.0",
"_id": "async-limiter@1.0.0",
"_inBundle": false,
"_integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
"_location": "/async-limiter",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "async-limiter@~1.0.0",
"name": "async-limiter",
"escapedName": "async-limiter",
"rawSpec": "~1.0.0",
"saveSpec": null,
"fetchSpec": "~1.0.0"
},
"_requiredBy": [
"/ws"
],
"_resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"_shasum": "78faed8c3d074ab81f22b4e985d79e8738f720f8",
"_spec": "async-limiter@~1.0.0",
"_where": "C:\\www\\esiur-js\\node_modules\\ws",
"author": {
"name": "Samuel Reed"
},
"bugs": {
"url": "https://github.com/strml/async-limiter/issues"
},
"bundleDependencies": false,
"dependencies": {},
"deprecated": false,
"description": "asynchronous function queue with adjustable concurrency",
"devDependencies": {
"coveralls": "^2.11.2",
"eslint": "^4.6.1",
"eslint-plugin-mocha": "^4.11.0",
"intelli-espower-loader": "^1.0.1",
"istanbul": "^0.3.2",
"mocha": "^3.5.2",
"power-assert": "^1.4.4"
},
"homepage": "https://github.com/strml/async-limiter#readme",
"keywords": [
"throttle",
"async",
"limiter",
"asynchronous",
"job",
"task",
"concurrency",
"concurrent"
],
"license": "MIT",
"name": "async-limiter",
"repository": {
"type": "git",
"url": "git+https://github.com/strml/async-limiter.git"
},
"scripts": {
"coverage": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | coveralls",
"example": "node example",
"lint": "eslint .",
"test": "mocha --R intelli-espower-loader test/",
"travis": "npm run lint && npm run coverage"
},
"version": "1.0.0"
}

132
node_modules/async-limiter/readme.md generated vendored Normal file
View File

@ -0,0 +1,132 @@
# Async-Limiter
A module for limiting concurrent asynchronous actions in flight. Forked from [queue](https://github.com/jessetane/queue).
[![npm](http://img.shields.io/npm/v/async-limiter.svg?style=flat-square)](http://www.npmjs.org/async-limiter)
[![tests](https://img.shields.io/travis/STRML/async-limiter.svg?style=flat-square&branch=master)](https://travis-ci.org/STRML/async-limiter)
[![coverage](https://img.shields.io/coveralls/STRML/async-limiter.svg?style=flat-square&branch=master)](https://coveralls.io/r/STRML/async-limiter)
This module exports a class `Limiter` that implements some of the `Array` API.
Pass async functions (ones that accept a callback or return a promise) to an instance's additive array methods.
## Motivation
Certain functions, like `zlib`, have [undesirable behavior](https://github.com/nodejs/node/issues/8871#issuecomment-250915913) when
run at infinite concurrency.
In this case, it is actually faster, and takes far less memory, to limit concurrency.
This module should do the absolute minimum work necessary to queue up functions. PRs are welcome that would
make this module faster or lighter, but new functionality is not desired.
Style should confirm to nodejs/node style.
## Example
``` javascript
var Limiter = require('async-limiter')
var t = new Limiter({concurrency: 2});
var results = []
// add jobs using the familiar Array API
t.push(function (cb) {
results.push('two')
cb()
})
t.push(
function (cb) {
results.push('four')
cb()
},
function (cb) {
results.push('five')
cb()
}
)
t.unshift(function (cb) {
results.push('one')
cb()
})
t.splice(2, 0, function (cb) {
results.push('three')
cb()
})
// Jobs run automatically. If you want a callback when all are done,
// call 'onDone()'.
t.onDone(function () {
console.log('all done:', results)
})
```
## Zlib Example
```js
const zlib = require('zlib');
const Limiter = require('async-limiter');
const message = {some: "data"};
const payload = new Buffer(JSON.stringify(message));
// Try with different concurrency values to see how this actually
// slows significantly with higher concurrency!
//
// 5: 1398.607ms
// 10: 1375.668ms
// Infinity: 4423.300ms
//
const t = new Limiter({concurrency: 5});
function deflate(payload, cb) {
t.push(function(done) {
zlib.deflate(payload, function(err, buffer) {
done();
cb(err, buffer);
});
});
}
console.time('deflate');
for(let i = 0; i < 30000; ++i) {
deflate(payload, function (err, buffer) {});
}
q.onDone(function() {
console.timeEnd('deflate');
});
```
## Install
`npm install async-limiter`
## Test
`npm test`
## API
### `var t = new Limiter([opts])`
Constructor. `opts` may contain inital values for:
* `q.concurrency`
## Instance methods
### `q.onDone(fn)`
`fn` will be called once and only once, when the queue is empty.
## Instance methods mixed in from `Array`
Mozilla has docs on how these methods work [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array).
### `q.push(element1, ..., elementN)`
### `q.unshift(element1, ..., elementN)`
### `q.splice(index , howMany[, element1[, ...[, elementN]]])`
## Properties
### `q.concurrency`
Max number of jobs the queue should process concurrently, defaults to `Infinity`.
### `q.length`
Jobs pending + jobs to process (readonly).

21
node_modules/ws/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

449
node_modules/ws/README.md generated vendored Normal file
View File

@ -0,0 +1,449 @@
# ws: a Node.js WebSocket library
[![Version npm](https://img.shields.io/npm/v/ws.svg?logo=npm)](https://www.npmjs.com/package/ws)
[![Linux Build](https://img.shields.io/travis/websockets/ws/master.svg?logo=travis)](https://travis-ci.org/websockets/ws)
[![Windows Build](https://img.shields.io/appveyor/ci/lpinca/ws/master.svg?logo=appveyor)](https://ci.appveyor.com/project/lpinca/ws)
[![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg)](https://coveralls.io/github/websockets/ws)
ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and
server implementation.
Passes the quite extensive Autobahn test suite: [server][server-report],
[client][client-report].
**Note**: This module does not work in the browser. The client in the docs is a
reference to a back end with the role of a client in the WebSocket
communication. Browser clients must use the native
[`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
object. To make the same code work seamlessly on Node.js and the browser, you
can use one of the many wrappers available on npm, like
[isomorphic-ws](https://github.com/heineiuo/isomorphic-ws).
## Table of Contents
- [Protocol support](#protocol-support)
- [Installing](#installing)
- [Opt-in for performance and spec compliance](#opt-in-for-performance-and-spec-compliance)
- [API docs](#api-docs)
- [WebSocket compression](#websocket-compression)
- [Usage examples](#usage-examples)
- [Sending and receiving text data](#sending-and-receiving-text-data)
- [Sending binary data](#sending-binary-data)
- [Simple server](#simple-server)
- [External HTTP/S server](#external-https-server)
- [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server)
- [Server broadcast](#server-broadcast)
- [echo.websocket.org demo](#echowebsocketorg-demo)
- [Other examples](#other-examples)
- [Error handling best practices](#error-handling-best-practices)
- [FAQ](#faq)
- [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client)
- [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections)
- [How to connect via a proxy?](#how-to-connect-via-a-proxy)
- [Changelog](#changelog)
- [License](#license)
## Protocol support
- **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
- **HyBi drafts 13-17** (Current default, alternatively option
`protocolVersion: 13`)
## Installing
```
npm install ws
```
### Opt-in for performance and spec compliance
There are 2 optional modules that can be installed along side with the ws
module. These modules are binary addons which improve certain operations.
Prebuilt binaries are available for the most popular platforms so you don't
necessarily need to have a C++ compiler installed on your machine.
- `npm install --save-optional bufferutil`: Allows to efficiently perform
operations such as masking and unmasking the data payload of the WebSocket
frames.
- `npm install --save-optional utf-8-validate`: Allows to efficiently check if a
message contains valid UTF-8 as required by the spec.
## API docs
See [`/doc/ws.md`](./doc/ws.md) for Node.js-like docs for the ws classes.
## WebSocket compression
ws supports the [permessage-deflate extension][permessage-deflate] which enables
the client and server to negotiate a compression algorithm and its parameters,
and then selectively apply it to the data payloads of each WebSocket message.
The extension is disabled by default on the server and enabled by default on the
client. It adds a significant overhead in terms of performance and memory
consumption so we suggest to enable it only if it is really needed.
Note that Node.js has a variety of issues with high-performance compression,
where increased concurrency, especially on Linux, can lead to [catastrophic
memory fragmentation][node-zlib-bug] and slow performance. If you intend to use
permessage-deflate in production, it is worthwhile to set up a test
representative of your workload and ensure Node.js/zlib will handle it with
acceptable performance and memory usage.
Tuning of permessage-deflate can be done via the options defined below. You can
also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly
into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs].
See [the docs][ws-server-options] for more options.
```js
const WebSocket = require('ws');
const wss = new WebSocket.Server({
port: 8080,
perMessageDeflate: {
zlibDeflateOptions: {
// See zlib defaults.
chunkSize: 1024,
memLevel: 7,
level: 3
},
zlibInflateOptions: {
chunkSize: 10 * 1024
},
// Other options settable:
clientNoContextTakeover: true, // Defaults to negotiated value.
serverNoContextTakeover: true, // Defaults to negotiated value.
serverMaxWindowBits: 10, // Defaults to negotiated value.
// Below options specified as default values.
concurrencyLimit: 10, // Limits zlib concurrency for perf.
threshold: 1024 // Size (in bytes) below which messages
// should not be compressed.
}
});
```
The client will only use the extension if it is supported and enabled on the
server. To always disable the extension on the client set the
`perMessageDeflate` option to `false`.
```js
const WebSocket = require('ws');
const ws = new WebSocket('ws://www.host.com/path', {
perMessageDeflate: false
});
```
## Usage examples
### Sending and receiving text data
```js
const WebSocket = require('ws');
const ws = new WebSocket('ws://www.host.com/path');
ws.on('open', function open() {
ws.send('something');
});
ws.on('message', function incoming(data) {
console.log(data);
});
```
### Sending binary data
```js
const WebSocket = require('ws');
const ws = new WebSocket('ws://www.host.com/path');
ws.on('open', function open() {
const array = new Float32Array(5);
for (var i = 0; i < array.length; ++i) {
array[i] = i / 2;
}
ws.send(array);
});
```
### Simple server
```js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});
```
### External HTTP/S server
```js
const fs = require('fs');
const https = require('https');
const WebSocket = require('ws');
const server = new https.createServer({
cert: fs.readFileSync('/path/to/cert.pem'),
key: fs.readFileSync('/path/to/key.pem')
});
const wss = new WebSocket.Server({ server });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});
server.listen(8080);
```
### Multiple servers sharing a single HTTP/S server
```js
const http = require('http');
const WebSocket = require('ws');
const server = http.createServer();
const wss1 = new WebSocket.Server({ noServer: true });
const wss2 = new WebSocket.Server({ noServer: true });
wss1.on('connection', function connection(ws) {
// ...
});
wss2.on('connection', function connection(ws) {
// ...
});
server.on('upgrade', function upgrade(request, socket, head) {
const pathname = url.parse(request.url).pathname;
if (pathname === '/foo') {
wss1.handleUpgrade(request, socket, head, function done(ws) {
wss1.emit('connection', ws, request);
});
} else if (pathname === '/bar') {
wss2.handleUpgrade(request, socket, head, function done(ws) {
wss2.emit('connection', ws, request);
});
} else {
socket.destroy();
}
});
server.listen(8080);
```
### Server broadcast
```js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
// Broadcast to all.
wss.broadcast = function broadcast(data) {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
};
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(data) {
// Broadcast to everyone else.
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});
});
```
### echo.websocket.org demo
```js
const WebSocket = require('ws');
const ws = new WebSocket('wss://echo.websocket.org/', {
origin: 'https://websocket.org'
});
ws.on('open', function open() {
console.log('connected');
ws.send(Date.now());
});
ws.on('close', function close() {
console.log('disconnected');
});
ws.on('message', function incoming(data) {
console.log(`Roundtrip time: ${Date.now() - data} ms`);
setTimeout(function timeout() {
ws.send(Date.now());
}, 500);
});
```
### Other examples
For a full example with a browser client communicating with a ws server, see the
examples folder.
Otherwise, see the test cases.
## Error handling best practices
```js
// If the WebSocket is closed before the following send is attempted
ws.send('something');
// Errors (both immediate and async write errors) can be detected in an optional
// callback. The callback is also the only way of being notified that data has
// actually been sent.
ws.send('something', function ack(error) {
// If error is not defined, the send has been completed, otherwise the error
// object will indicate what failed.
});
// Immediate errors can also be handled with `try...catch`, but **note** that
// since sends are inherently asynchronous, socket write failures will *not* be
// captured when this technique is used.
try {
ws.send('something');
} catch (e) {
/* handle error */
}
```
## FAQ
### How to get the IP address of the client?
The remote IP address can be obtained from the raw socket.
```js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws, req) {
const ip = req.connection.remoteAddress;
});
```
When the server runs behind a proxy like NGINX, the de-facto standard is to use
the `X-Forwarded-For` header.
```js
wss.on('connection', function connection(ws, req) {
const ip = req.headers['x-forwarded-for'].split(/\s*,\s*/)[0];
});
```
### How to detect and close broken connections?
Sometimes the link between the server and the client can be interrupted in a way
that keeps both the server and the client unaware of the broken state of the
connection (e.g. when pulling the cord).
In these cases ping messages can be used as a means to verify that the remote
endpoint is still responsive.
```js
const WebSocket = require('ws');
function noop() {}
function heartbeat() {
this.isAlive = true;
}
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
ws.isAlive = true;
ws.on('pong', heartbeat);
});
const interval = setInterval(function ping() {
wss.clients.forEach(function each(ws) {
if (ws.isAlive === false) return ws.terminate();
ws.isAlive = false;
ws.ping(noop);
});
}, 30000);
```
Pong messages are automatically sent in response to ping messages as required by
the spec.
Just like the server example above your clients might as well lose connection
without knowing it. You might want to add a ping listener on your clients to
prevent that. A simple implementation would be:
```js
const WebSocket = require('ws');
function heartbeat() {
clearTimeout(this.pingTimeout);
// Use `WebSocket#terminate()` and not `WebSocket#close()`. Delay should be
// equal to the interval at which your server sends out pings plus a
// conservative assumption of the latency.
this.pingTimeout = setTimeout(() => {
this.terminate();
}, 30000 + 1000);
}
const client = new WebSocket('wss://echo.websocket.org/');
client.on('open', heartbeat);
client.on('ping', heartbeat);
client.on('close', function clear() {
clearTimeout(this.pingTimeout);
});
```
### How to connect via a proxy?
Use a custom `http.Agent` implementation like [https-proxy-agent][] or
[socks-proxy-agent][].
## Changelog
We're using the GitHub [releases][changelog] for changelog entries.
## License
[MIT](LICENSE)
[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent
[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent
[client-report]: http://websockets.github.io/ws/autobahn/clients/
[server-report]: http://websockets.github.io/ws/autobahn/servers/
[permessage-deflate]: https://tools.ietf.org/html/rfc7692
[changelog]: https://github.com/websockets/ws/releases
[node-zlib-bug]: https://github.com/nodejs/node/issues/8871
[node-zlib-deflaterawdocs]:
https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options
[ws-server-options]:
https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback

8
node_modules/ws/browser.js generated vendored Normal file
View File

@ -0,0 +1,8 @@
'use strict';
module.exports = function() {
throw new Error(
'ws does not work in the browser. Browser clients must use the native ' +
'WebSocket object'
);
};

9
node_modules/ws/index.js generated vendored Normal file
View File

@ -0,0 +1,9 @@
'use strict';
const WebSocket = require('./lib/websocket');
WebSocket.Server = require('./lib/websocket-server');
WebSocket.Receiver = require('./lib/receiver');
WebSocket.Sender = require('./lib/sender');
module.exports = WebSocket;

144
node_modules/ws/lib/buffer-util.js generated vendored Normal file
View File

@ -0,0 +1,144 @@
'use strict';
const { EMPTY_BUFFER } = require('./constants');
/**
* Merges an array of buffers into a new buffer.
*
* @param {Buffer[]} list The array of buffers to concat
* @param {Number} totalLength The total length of buffers in the list
* @return {Buffer} The resulting buffer
* @public
*/
function concat(list, totalLength) {
if (list.length === 0) return EMPTY_BUFFER;
if (list.length === 1) return list[0];
const target = Buffer.allocUnsafe(totalLength);
var offset = 0;
for (var i = 0; i < list.length; i++) {
const buf = list[i];
buf.copy(target, offset);
offset += buf.length;
}
return target;
}
/**
* Masks a buffer using the given mask.
*
* @param {Buffer} source The buffer to mask
* @param {Buffer} mask The mask to use
* @param {Buffer} output The buffer where to store the result
* @param {Number} offset The offset at which to start writing
* @param {Number} length The number of bytes to mask.
* @public
*/
function _mask(source, mask, output, offset, length) {
for (var i = 0; i < length; i++) {
output[offset + i] = source[i] ^ mask[i & 3];
}
}
/**
* Unmasks a buffer using the given mask.
*
* @param {Buffer} buffer The buffer to unmask
* @param {Buffer} mask The mask to use
* @public
*/
function _unmask(buffer, mask) {
// Required until https://github.com/nodejs/node/issues/9006 is resolved.
const length = buffer.length;
for (var i = 0; i < length; i++) {
buffer[i] ^= mask[i & 3];
}
}
/**
* Converts a buffer to an `ArrayBuffer`.
*
* @param {Buffer} buf The buffer to convert
* @return {ArrayBuffer} Converted buffer
* @public
*/
function toArrayBuffer(buf) {
if (buf.byteLength === buf.buffer.byteLength) {
return buf.buffer;
}
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
}
/**
* Converts `data` to a `Buffer`.
*
* @param {*} data The data to convert
* @return {Buffer} The buffer
* @throws {TypeError}
* @public
*/
function toBuffer(data) {
toBuffer.readOnly = true;
if (Buffer.isBuffer(data)) return data;
var buf;
if (data instanceof ArrayBuffer) {
buf = Buffer.from(data);
} else if (ArrayBuffer.isView(data)) {
buf = viewToBuffer(data);
} else {
buf = Buffer.from(data);
toBuffer.readOnly = false;
}
return buf;
}
/**
* Converts an `ArrayBuffer` view into a buffer.
*
* @param {(DataView|TypedArray)} view The view to convert
* @return {Buffer} Converted view
* @private
*/
function viewToBuffer(view) {
const buf = Buffer.from(view.buffer);
if (view.byteLength !== view.buffer.byteLength) {
return buf.slice(view.byteOffset, view.byteOffset + view.byteLength);
}
return buf;
}
try {
const bufferUtil = require('bufferutil');
const bu = bufferUtil.BufferUtil || bufferUtil;
module.exports = {
concat,
mask(source, mask, output, offset, length) {
if (length < 48) _mask(source, mask, output, offset, length);
else bu.mask(source, mask, output, offset, length);
},
toArrayBuffer,
toBuffer,
unmask(buffer, mask) {
if (buffer.length < 32) _unmask(buffer, mask);
else bu.unmask(buffer, mask);
}
};
} catch (e) /* istanbul ignore next */ {
module.exports = {
concat,
mask: _mask,
toArrayBuffer,
toBuffer,
unmask: _unmask
};
}

10
node_modules/ws/lib/constants.js generated vendored Normal file
View File

@ -0,0 +1,10 @@
'use strict';
module.exports = {
BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'],
GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
kStatusCode: Symbol('status-code'),
kWebSocket: Symbol('websocket'),
EMPTY_BUFFER: Buffer.alloc(0),
NOOP: () => {}
};

170
node_modules/ws/lib/event-target.js generated vendored Normal file
View File

@ -0,0 +1,170 @@
'use strict';
/**
* Class representing an event.
*
* @private
*/
class Event {
/**
* Create a new `Event`.
*
* @param {String} type The name of the event
* @param {Object} target A reference to the target to which the event was dispatched
*/
constructor(type, target) {
this.target = target;
this.type = type;
}
}
/**
* Class representing a message event.
*
* @extends Event
* @private
*/
class MessageEvent extends Event {
/**
* Create a new `MessageEvent`.
*
* @param {(String|Buffer|ArrayBuffer|Buffer[])} data The received data
* @param {WebSocket} target A reference to the target to which the event was dispatched
*/
constructor(data, target) {
super('message', target);
this.data = data;
}
}
/**
* Class representing a close event.
*
* @extends Event
* @private
*/
class CloseEvent extends Event {
/**
* Create a new `CloseEvent`.
*
* @param {Number} code The status code explaining why the connection is being closed
* @param {String} reason A human-readable string explaining why the connection is closing
* @param {WebSocket} target A reference to the target to which the event was dispatched
*/
constructor(code, reason, target) {
super('close', target);
this.wasClean = target._closeFrameReceived && target._closeFrameSent;
this.reason = reason;
this.code = code;
}
}
/**
* Class representing an open event.
*
* @extends Event
* @private
*/
class OpenEvent extends Event {
/**
* Create a new `OpenEvent`.
*
* @param {WebSocket} target A reference to the target to which the event was dispatched
*/
constructor(target) {
super('open', target);
}
}
/**
* Class representing an error event.
*
* @extends Event
* @private
*/
class ErrorEvent extends Event {
/**
* Create a new `ErrorEvent`.
*
* @param {Object} error The error that generated this event
* @param {WebSocket} target A reference to the target to which the event was dispatched
*/
constructor(error, target) {
super('error', target);
this.message = error.message;
this.error = error;
}
}
/**
* This provides methods for emulating the `EventTarget` interface. It's not
* meant to be used directly.
*
* @mixin
*/
const EventTarget = {
/**
* Register an event listener.
*
* @param {String} method A string representing the event type to listen for
* @param {Function} listener The listener to add
* @public
*/
addEventListener(method, listener) {
if (typeof listener !== 'function') return;
function onMessage(data) {
listener.call(this, new MessageEvent(data, this));
}
function onClose(code, message) {
listener.call(this, new CloseEvent(code, message, this));
}
function onError(error) {
listener.call(this, new ErrorEvent(error, this));
}
function onOpen() {
listener.call(this, new OpenEvent(this));
}
if (method === 'message') {
onMessage._listener = listener;
this.on(method, onMessage);
} else if (method === 'close') {
onClose._listener = listener;
this.on(method, onClose);
} else if (method === 'error') {
onError._listener = listener;
this.on(method, onError);
} else if (method === 'open') {
onOpen._listener = listener;
this.on(method, onOpen);
} else {
this.on(method, listener);
}
},
/**
* Remove an event listener.
*
* @param {String} method A string representing the event type to remove
* @param {Function} listener The listener to remove
* @public
*/
removeEventListener(method, listener) {
const listeners = this.listeners(method);
for (var i = 0; i < listeners.length; i++) {
if (listeners[i] === listener || listeners[i]._listener === listener) {
this.removeListener(method, listeners[i]);
}
}
}
};
module.exports = EventTarget;

222
node_modules/ws/lib/extension.js generated vendored Normal file
View File

@ -0,0 +1,222 @@
'use strict';
//
// Allowed token characters:
//
// '!', '#', '$', '%', '&', ''', '*', '+', '-',
// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~'
//
// tokenChars[32] === 0 // ' '
// tokenChars[33] === 1 // '!'
// tokenChars[34] === 0 // '"'
// ...
//
// prettier-ignore
const tokenChars = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127
];
/**
* Adds an offer to the map of extension offers or a parameter to the map of
* parameters.
*
* @param {Object} dest The map of extension offers or parameters
* @param {String} name The extension or parameter name
* @param {(Object|Boolean|String)} elem The extension parameters or the
* parameter value
* @private
*/
function push(dest, name, elem) {
if (Object.prototype.hasOwnProperty.call(dest, name)) dest[name].push(elem);
else dest[name] = [elem];
}
/**
* Parses the `Sec-WebSocket-Extensions` header into an object.
*
* @param {String} header The field value of the header
* @return {Object} The parsed object
* @public
*/
function parse(header) {
const offers = {};
if (header === undefined || header === '') return offers;
var params = {};
var mustUnescape = false;
var isEscaping = false;
var inQuotes = false;
var extensionName;
var paramName;
var start = -1;
var end = -1;
for (var i = 0; i < header.length; i++) {
const code = header.charCodeAt(i);
if (extensionName === undefined) {
if (end === -1 && tokenChars[code] === 1) {
if (start === -1) start = i;
} else if (code === 0x20 /* ' ' */ || code === 0x09 /* '\t' */) {
if (end === -1 && start !== -1) end = i;
} else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) {
if (start === -1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
if (end === -1) end = i;
const name = header.slice(start, end);
if (code === 0x2c) {
push(offers, name, params);
params = {};
} else {
extensionName = name;
}
start = end = -1;
} else {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
} else if (paramName === undefined) {
if (end === -1 && tokenChars[code] === 1) {
if (start === -1) start = i;
} else if (code === 0x20 || code === 0x09) {
if (end === -1 && start !== -1) end = i;
} else if (code === 0x3b || code === 0x2c) {
if (start === -1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
if (end === -1) end = i;
push(params, header.slice(start, end), true);
if (code === 0x2c) {
push(offers, extensionName, params);
params = {};
extensionName = undefined;
}
start = end = -1;
} else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) {
paramName = header.slice(start, i);
start = end = -1;
} else {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
} else {
//
// The value of a quoted-string after unescaping must conform to the
// token ABNF, so only token characters are valid.
// Ref: https://tools.ietf.org/html/rfc6455#section-9.1
//
if (isEscaping) {
if (tokenChars[code] !== 1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
if (start === -1) start = i;
else if (!mustUnescape) mustUnescape = true;
isEscaping = false;
} else if (inQuotes) {
if (tokenChars[code] === 1) {
if (start === -1) start = i;
} else if (code === 0x22 /* '"' */ && start !== -1) {
inQuotes = false;
end = i;
} else if (code === 0x5c /* '\' */) {
isEscaping = true;
} else {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
} else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) {
inQuotes = true;
} else if (end === -1 && tokenChars[code] === 1) {
if (start === -1) start = i;
} else if (start !== -1 && (code === 0x20 || code === 0x09)) {
if (end === -1) end = i;
} else if (code === 0x3b || code === 0x2c) {
if (start === -1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
if (end === -1) end = i;
var value = header.slice(start, end);
if (mustUnescape) {
value = value.replace(/\\/g, '');
mustUnescape = false;
}
push(params, paramName, value);
if (code === 0x2c) {
push(offers, extensionName, params);
params = {};
extensionName = undefined;
}
paramName = undefined;
start = end = -1;
} else {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
}
}
if (start === -1 || inQuotes) {
throw new SyntaxError('Unexpected end of input');
}
if (end === -1) end = i;
const token = header.slice(start, end);
if (extensionName === undefined) {
push(offers, token, {});
} else {
if (paramName === undefined) {
push(params, token, true);
} else if (mustUnescape) {
push(params, paramName, token.replace(/\\/g, ''));
} else {
push(params, paramName, token);
}
push(offers, extensionName, params);
}
return offers;
}
/**
* Builds the `Sec-WebSocket-Extensions` header field value.
*
* @param {Object} extensions The map of extensions and parameters to format
* @return {String} A string representing the given object
* @public
*/
function format(extensions) {
return Object.keys(extensions)
.map((extension) => {
var configurations = extensions[extension];
if (!Array.isArray(configurations)) configurations = [configurations];
return configurations
.map((params) => {
return [extension]
.concat(
Object.keys(params).map((k) => {
var values = params[k];
if (!Array.isArray(values)) values = [values];
return values
.map((v) => (v === true ? k : `${k}=${v}`))
.join('; ');
})
)
.join('; ');
})
.join(', ');
})
.join(', ');
}
module.exports = { format, parse };

502
node_modules/ws/lib/permessage-deflate.js generated vendored Normal file
View File

@ -0,0 +1,502 @@
'use strict';
const Limiter = require('async-limiter');
const zlib = require('zlib');
const bufferUtil = require('./buffer-util');
const { kStatusCode, NOOP } = require('./constants');
const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
const EMPTY_BLOCK = Buffer.from([0x00]);
const kPerMessageDeflate = Symbol('permessage-deflate');
const kTotalLength = Symbol('total-length');
const kCallback = Symbol('callback');
const kBuffers = Symbol('buffers');
const kError = Symbol('error');
//
// We limit zlib concurrency, which prevents severe memory fragmentation
// as documented in https://github.com/nodejs/node/issues/8871#issuecomment-250915913
// and https://github.com/websockets/ws/issues/1202
//
// Intentionally global; it's the global thread pool that's an issue.
//
let zlibLimiter;
/**
* permessage-deflate implementation.
*/
class PerMessageDeflate {
/**
* Creates a PerMessageDeflate instance.
*
* @param {Object} options Configuration options
* @param {Boolean} options.serverNoContextTakeover Request/accept disabling
* of server context takeover
* @param {Boolean} options.clientNoContextTakeover Advertise/acknowledge
* disabling of client context takeover
* @param {(Boolean|Number)} options.serverMaxWindowBits Request/confirm the
* use of a custom server window size
* @param {(Boolean|Number)} options.clientMaxWindowBits Advertise support
* for, or request, a custom client window size
* @param {Object} options.zlibDeflateOptions Options to pass to zlib on deflate
* @param {Object} options.zlibInflateOptions Options to pass to zlib on inflate
* @param {Number} options.threshold Size (in bytes) below which messages
* should not be compressed
* @param {Number} options.concurrencyLimit The number of concurrent calls to
* zlib
* @param {Boolean} isServer Create the instance in either server or client
* mode
* @param {Number} maxPayload The maximum allowed message length
*/
constructor(options, isServer, maxPayload) {
this._maxPayload = maxPayload | 0;
this._options = options || {};
this._threshold =
this._options.threshold !== undefined ? this._options.threshold : 1024;
this._isServer = !!isServer;
this._deflate = null;
this._inflate = null;
this.params = null;
if (!zlibLimiter) {
const concurrency =
this._options.concurrencyLimit !== undefined
? this._options.concurrencyLimit
: 10;
zlibLimiter = new Limiter({ concurrency });
}
}
/**
* @type {String}
*/
static get extensionName() {
return 'permessage-deflate';
}
/**
* Create an extension negotiation offer.
*
* @return {Object} Extension parameters
* @public
*/
offer() {
const params = {};
if (this._options.serverNoContextTakeover) {
params.server_no_context_takeover = true;
}
if (this._options.clientNoContextTakeover) {
params.client_no_context_takeover = true;
}
if (this._options.serverMaxWindowBits) {
params.server_max_window_bits = this._options.serverMaxWindowBits;
}
if (this._options.clientMaxWindowBits) {
params.client_max_window_bits = this._options.clientMaxWindowBits;
} else if (this._options.clientMaxWindowBits == null) {
params.client_max_window_bits = true;
}
return params;
}
/**
* Accept an extension negotiation offer/response.
*
* @param {Array} configurations The extension negotiation offers/reponse
* @return {Object} Accepted configuration
* @public
*/
accept(configurations) {
configurations = this.normalizeParams(configurations);
this.params = this._isServer
? this.acceptAsServer(configurations)
: this.acceptAsClient(configurations);
return this.params;
}
/**
* Releases all resources used by the extension.
*
* @public
*/
cleanup() {
if (this._inflate) {
this._inflate.close();
this._inflate = null;
}
if (this._deflate) {
this._deflate.close();
this._deflate = null;
}
}
/**
* Accept an extension negotiation offer.
*
* @param {Array} offers The extension negotiation offers
* @return {Object} Accepted configuration
* @private
*/
acceptAsServer(offers) {
const opts = this._options;
const accepted = offers.find((params) => {
if (
(opts.serverNoContextTakeover === false &&
params.server_no_context_takeover) ||
(params.server_max_window_bits &&
(opts.serverMaxWindowBits === false ||
(typeof opts.serverMaxWindowBits === 'number' &&
opts.serverMaxWindowBits > params.server_max_window_bits))) ||
(typeof opts.clientMaxWindowBits === 'number' &&
!params.client_max_window_bits)
) {
return false;
}
return true;
});
if (!accepted) {
throw new Error('None of the extension offers can be accepted');
}
if (opts.serverNoContextTakeover) {
accepted.server_no_context_takeover = true;
}
if (opts.clientNoContextTakeover) {
accepted.client_no_context_takeover = true;
}
if (typeof opts.serverMaxWindowBits === 'number') {
accepted.server_max_window_bits = opts.serverMaxWindowBits;
}
if (typeof opts.clientMaxWindowBits === 'number') {
accepted.client_max_window_bits = opts.clientMaxWindowBits;
} else if (
accepted.client_max_window_bits === true ||
opts.clientMaxWindowBits === false
) {
delete accepted.client_max_window_bits;
}
return accepted;
}
/**
* Accept the extension negotiation response.
*
* @param {Array} response The extension negotiation response
* @return {Object} Accepted configuration
* @private
*/
acceptAsClient(response) {
const params = response[0];
if (
this._options.clientNoContextTakeover === false &&
params.client_no_context_takeover
) {
throw new Error('Unexpected parameter "client_no_context_takeover"');
}
if (!params.client_max_window_bits) {
if (typeof this._options.clientMaxWindowBits === 'number') {
params.client_max_window_bits = this._options.clientMaxWindowBits;
}
} else if (
this._options.clientMaxWindowBits === false ||
(typeof this._options.clientMaxWindowBits === 'number' &&
params.client_max_window_bits > this._options.clientMaxWindowBits)
) {
throw new Error(
'Unexpected or invalid parameter "client_max_window_bits"'
);
}
return params;
}
/**
* Normalize parameters.
*
* @param {Array} configurations The extension negotiation offers/reponse
* @return {Array} The offers/response with normalized parameters
* @private
*/
normalizeParams(configurations) {
configurations.forEach((params) => {
Object.keys(params).forEach((key) => {
var value = params[key];
if (value.length > 1) {
throw new Error(`Parameter "${key}" must have only a single value`);
}
value = value[0];
if (key === 'client_max_window_bits') {
if (value !== true) {
const num = +value;
if (!Number.isInteger(num) || num < 8 || num > 15) {
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}
value = num;
} else if (!this._isServer) {
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}
} else if (key === 'server_max_window_bits') {
const num = +value;
if (!Number.isInteger(num) || num < 8 || num > 15) {
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}
value = num;
} else if (
key === 'client_no_context_takeover' ||
key === 'server_no_context_takeover'
) {
if (value !== true) {
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}
} else {
throw new Error(`Unknown parameter "${key}"`);
}
params[key] = value;
});
});
return configurations;
}
/**
* Decompress data. Concurrency limited by async-limiter.
*
* @param {Buffer} data Compressed data
* @param {Boolean} fin Specifies whether or not this is the last fragment
* @param {Function} callback Callback
* @public
*/
decompress(data, fin, callback) {
zlibLimiter.push((done) => {
this._decompress(data, fin, (err, result) => {
done();
callback(err, result);
});
});
}
/**
* Compress data. Concurrency limited by async-limiter.
*
* @param {Buffer} data Data to compress
* @param {Boolean} fin Specifies whether or not this is the last fragment
* @param {Function} callback Callback
* @public
*/
compress(data, fin, callback) {
zlibLimiter.push((done) => {
this._compress(data, fin, (err, result) => {
done();
callback(err, result);
});
});
}
/**
* Decompress data.
*
* @param {Buffer} data Compressed data
* @param {Boolean} fin Specifies whether or not this is the last fragment
* @param {Function} callback Callback
* @private
*/
_decompress(data, fin, callback) {
const endpoint = this._isServer ? 'client' : 'server';
if (!this._inflate) {
const key = `${endpoint}_max_window_bits`;
const windowBits =
typeof this.params[key] !== 'number'
? zlib.Z_DEFAULT_WINDOWBITS
: this.params[key];
this._inflate = zlib.createInflateRaw(
Object.assign({}, this._options.zlibInflateOptions, { windowBits })
);
this._inflate[kPerMessageDeflate] = this;
this._inflate[kTotalLength] = 0;
this._inflate[kBuffers] = [];
this._inflate.on('error', inflateOnError);
this._inflate.on('data', inflateOnData);
}
this._inflate[kCallback] = callback;
this._inflate.write(data);
if (fin) this._inflate.write(TRAILER);
this._inflate.flush(() => {
const err = this._inflate[kError];
if (err) {
this._inflate.close();
this._inflate = null;
callback(err);
return;
}
const data = bufferUtil.concat(
this._inflate[kBuffers],
this._inflate[kTotalLength]
);
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
this._inflate.close();
this._inflate = null;
} else {
this._inflate[kTotalLength] = 0;
this._inflate[kBuffers] = [];
}
callback(null, data);
});
}
/**
* Compress data.
*
* @param {Buffer} data Data to compress
* @param {Boolean} fin Specifies whether or not this is the last fragment
* @param {Function} callback Callback
* @private
*/
_compress(data, fin, callback) {
if (!data || data.length === 0) {
process.nextTick(callback, null, EMPTY_BLOCK);
return;
}
const endpoint = this._isServer ? 'server' : 'client';
if (!this._deflate) {
const key = `${endpoint}_max_window_bits`;
const windowBits =
typeof this.params[key] !== 'number'
? zlib.Z_DEFAULT_WINDOWBITS
: this.params[key];
this._deflate = zlib.createDeflateRaw(
Object.assign({}, this._options.zlibDeflateOptions, { windowBits })
);
this._deflate[kTotalLength] = 0;
this._deflate[kBuffers] = [];
//
// An `'error'` event is emitted, only on Node.js < 10.0.0, if the
// `zlib.DeflateRaw` instance is closed while data is being processed.
// This can happen if `PerMessageDeflate#cleanup()` is called at the wrong
// time due to an abnormal WebSocket closure.
//
this._deflate.on('error', NOOP);
this._deflate.on('data', deflateOnData);
}
this._deflate.write(data);
this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {
if (!this._deflate) {
//
// This `if` statement is only needed for Node.js < 10.0.0 because as of
// commit https://github.com/nodejs/node/commit/5e3f5164, the flush
// callback is no longer called if the deflate stream is closed while
// data is being processed.
//
return;
}
var data = bufferUtil.concat(
this._deflate[kBuffers],
this._deflate[kTotalLength]
);
if (fin) data = data.slice(0, data.length - 4);
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
this._deflate.close();
this._deflate = null;
} else {
this._deflate[kTotalLength] = 0;
this._deflate[kBuffers] = [];
}
callback(null, data);
});
}
}
module.exports = PerMessageDeflate;
/**
* The listener of the `zlib.DeflateRaw` stream `'data'` event.
*
* @param {Buffer} chunk A chunk of data
* @private
*/
function deflateOnData(chunk) {
this[kBuffers].push(chunk);
this[kTotalLength] += chunk.length;
}
/**
* The listener of the `zlib.InflateRaw` stream `'data'` event.
*
* @param {Buffer} chunk A chunk of data
* @private
*/
function inflateOnData(chunk) {
this[kTotalLength] += chunk.length;
if (
this[kPerMessageDeflate]._maxPayload < 1 ||
this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload
) {
this[kBuffers].push(chunk);
return;
}
this[kError] = new RangeError('Max payload size exceeded');
this[kError][kStatusCode] = 1009;
this.removeListener('data', inflateOnData);
this.reset();
}
/**
* The listener of the `zlib.InflateRaw` stream `'error'` event.
*
* @param {Error} err The emitted error
* @private
*/
function inflateOnError(err) {
//
// There is no need to call `Zlib#close()` as the handle is automatically
// closed when an error is emitted.
//
this[kPerMessageDeflate]._inflate = null;
err[kStatusCode] = 1007;
this[kCallback](err);
}

492
node_modules/ws/lib/receiver.js generated vendored Normal file
View File

@ -0,0 +1,492 @@
'use strict';
const { Writable } = require('stream');
const PerMessageDeflate = require('./permessage-deflate');
const {
BINARY_TYPES,
EMPTY_BUFFER,
kStatusCode,
kWebSocket
} = require('./constants');
const { concat, toArrayBuffer, unmask } = require('./buffer-util');
const { isValidStatusCode, isValidUTF8 } = require('./validation');
const GET_INFO = 0;
const GET_PAYLOAD_LENGTH_16 = 1;
const GET_PAYLOAD_LENGTH_64 = 2;
const GET_MASK = 3;
const GET_DATA = 4;
const INFLATING = 5;
/**
* HyBi Receiver implementation.
*
* @extends stream.Writable
*/
class Receiver extends Writable {
/**
* Creates a Receiver instance.
*
* @param {String} binaryType The type for binary data
* @param {Object} extensions An object containing the negotiated extensions
* @param {Number} maxPayload The maximum allowed message length
*/
constructor(binaryType, extensions, maxPayload) {
super();
this._binaryType = binaryType || BINARY_TYPES[0];
this[kWebSocket] = undefined;
this._extensions = extensions || {};
this._maxPayload = maxPayload | 0;
this._bufferedBytes = 0;
this._buffers = [];
this._compressed = false;
this._payloadLength = 0;
this._mask = undefined;
this._fragmented = 0;
this._masked = false;
this._fin = false;
this._opcode = 0;
this._totalPayloadLength = 0;
this._messageLength = 0;
this._fragments = [];
this._state = GET_INFO;
this._loop = false;
}
/**
* Implements `Writable.prototype._write()`.
*
* @param {Buffer} chunk The chunk of data to write
* @param {String} encoding The character encoding of `chunk`
* @param {Function} cb Callback
*/
_write(chunk, encoding, cb) {
if (this._opcode === 0x08 && this._state == GET_INFO) return cb();
this._bufferedBytes += chunk.length;
this._buffers.push(chunk);
this.startLoop(cb);
}
/**
* Consumes `n` bytes from the buffered data.
*
* @param {Number} n The number of bytes to consume
* @return {Buffer} The consumed bytes
* @private
*/
consume(n) {
this._bufferedBytes -= n;
if (n === this._buffers[0].length) return this._buffers.shift();
if (n < this._buffers[0].length) {
const buf = this._buffers[0];
this._buffers[0] = buf.slice(n);
return buf.slice(0, n);
}
const dst = Buffer.allocUnsafe(n);
do {
const buf = this._buffers[0];
if (n >= buf.length) {
this._buffers.shift().copy(dst, dst.length - n);
} else {
buf.copy(dst, dst.length - n, 0, n);
this._buffers[0] = buf.slice(n);
}
n -= buf.length;
} while (n > 0);
return dst;
}
/**
* Starts the parsing loop.
*
* @param {Function} cb Callback
* @private
*/
startLoop(cb) {
var err;
this._loop = true;
do {
switch (this._state) {
case GET_INFO:
err = this.getInfo();
break;
case GET_PAYLOAD_LENGTH_16:
err = this.getPayloadLength16();
break;
case GET_PAYLOAD_LENGTH_64:
err = this.getPayloadLength64();
break;
case GET_MASK:
this.getMask();
break;
case GET_DATA:
err = this.getData(cb);
break;
default:
// `INFLATING`
this._loop = false;
return;
}
} while (this._loop);
cb(err);
}
/**
* Reads the first two bytes of a frame.
*
* @return {(RangeError|undefined)} A possible error
* @private
*/
getInfo() {
if (this._bufferedBytes < 2) {
this._loop = false;
return;
}
const buf = this.consume(2);
if ((buf[0] & 0x30) !== 0x00) {
this._loop = false;
return error(RangeError, 'RSV2 and RSV3 must be clear', true, 1002);
}
const compressed = (buf[0] & 0x40) === 0x40;
if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
this._loop = false;
return error(RangeError, 'RSV1 must be clear', true, 1002);
}
this._fin = (buf[0] & 0x80) === 0x80;
this._opcode = buf[0] & 0x0f;
this._payloadLength = buf[1] & 0x7f;
if (this._opcode === 0x00) {
if (compressed) {
this._loop = false;
return error(RangeError, 'RSV1 must be clear', true, 1002);
}
if (!this._fragmented) {
this._loop = false;
return error(RangeError, 'invalid opcode 0', true, 1002);
}
this._opcode = this._fragmented;
} else if (this._opcode === 0x01 || this._opcode === 0x02) {
if (this._fragmented) {
this._loop = false;
return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002);
}
this._compressed = compressed;
} else if (this._opcode > 0x07 && this._opcode < 0x0b) {
if (!this._fin) {
this._loop = false;
return error(RangeError, 'FIN must be set', true, 1002);
}
if (compressed) {
this._loop = false;
return error(RangeError, 'RSV1 must be clear', true, 1002);
}
if (this._payloadLength > 0x7d) {
this._loop = false;
return error(
RangeError,
`invalid payload length ${this._payloadLength}`,
true,
1002
);
}
} else {
this._loop = false;
return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002);
}
if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
this._masked = (buf[1] & 0x80) === 0x80;
if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
else return this.haveLength();
}
/**
* Gets extended payload length (7+16).
*
* @return {(RangeError|undefined)} A possible error
* @private
*/
getPayloadLength16() {
if (this._bufferedBytes < 2) {
this._loop = false;
return;
}
this._payloadLength = this.consume(2).readUInt16BE(0);
return this.haveLength();
}
/**
* Gets extended payload length (7+64).
*
* @return {(RangeError|undefined)} A possible error
* @private
*/
getPayloadLength64() {
if (this._bufferedBytes < 8) {
this._loop = false;
return;
}
const buf = this.consume(8);
const num = buf.readUInt32BE(0);
//
// The maximum safe integer in JavaScript is 2^53 - 1. An error is returned
// if payload length is greater than this number.
//
if (num > Math.pow(2, 53 - 32) - 1) {
this._loop = false;
return error(
RangeError,
'Unsupported WebSocket frame: payload length > 2^53 - 1',
false,
1009
);
}
this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4);
return this.haveLength();
}
/**
* Payload length has been read.
*
* @return {(RangeError|undefined)} A possible error
* @private
*/
haveLength() {
if (this._payloadLength && this._opcode < 0x08) {
this._totalPayloadLength += this._payloadLength;
if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) {
this._loop = false;
return error(RangeError, 'Max payload size exceeded', false, 1009);
}
}
if (this._masked) this._state = GET_MASK;
else this._state = GET_DATA;
}
/**
* Reads mask bytes.
*
* @private
*/
getMask() {
if (this._bufferedBytes < 4) {
this._loop = false;
return;
}
this._mask = this.consume(4);
this._state = GET_DATA;
}
/**
* Reads data bytes.
*
* @param {Function} cb Callback
* @return {(Error|RangeError|undefined)} A possible error
* @private
*/
getData(cb) {
var data = EMPTY_BUFFER;
if (this._payloadLength) {
if (this._bufferedBytes < this._payloadLength) {
this._loop = false;
return;
}
data = this.consume(this._payloadLength);
if (this._masked) unmask(data, this._mask);
}
if (this._opcode > 0x07) return this.controlMessage(data);
if (this._compressed) {
this._state = INFLATING;
this.decompress(data, cb);
return;
}
if (data.length) {
//
// This message is not compressed so its lenght is the sum of the payload
// length of all fragments.
//
this._messageLength = this._totalPayloadLength;
this._fragments.push(data);
}
return this.dataMessage();
}
/**
* Decompresses data.
*
* @param {Buffer} data Compressed data
* @param {Function} cb Callback
* @private
*/
decompress(data, cb) {
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
perMessageDeflate.decompress(data, this._fin, (err, buf) => {
if (err) return cb(err);
if (buf.length) {
this._messageLength += buf.length;
if (this._messageLength > this._maxPayload && this._maxPayload > 0) {
return cb(
error(RangeError, 'Max payload size exceeded', false, 1009)
);
}
this._fragments.push(buf);
}
const er = this.dataMessage();
if (er) return cb(er);
this.startLoop(cb);
});
}
/**
* Handles a data message.
*
* @return {(Error|undefined)} A possible error
* @private
*/
dataMessage() {
if (this._fin) {
const messageLength = this._messageLength;
const fragments = this._fragments;
this._totalPayloadLength = 0;
this._messageLength = 0;
this._fragmented = 0;
this._fragments = [];
if (this._opcode === 2) {
var data;
if (this._binaryType === 'nodebuffer') {
data = concat(fragments, messageLength);
} else if (this._binaryType === 'arraybuffer') {
data = toArrayBuffer(concat(fragments, messageLength));
} else {
data = fragments;
}
this.emit('message', data);
} else {
const buf = concat(fragments, messageLength);
if (!isValidUTF8(buf)) {
this._loop = false;
return error(Error, 'invalid UTF-8 sequence', true, 1007);
}
this.emit('message', buf.toString());
}
}
this._state = GET_INFO;
}
/**
* Handles a control message.
*
* @param {Buffer} data Data to handle
* @return {(Error|RangeError|undefined)} A possible error
* @private
*/
controlMessage(data) {
if (this._opcode === 0x08) {
this._loop = false;
if (data.length === 0) {
this.emit('conclude', 1005, '');
this.end();
} else if (data.length === 1) {
return error(RangeError, 'invalid payload length 1', true, 1002);
} else {
const code = data.readUInt16BE(0);
if (!isValidStatusCode(code)) {
return error(RangeError, `invalid status code ${code}`, true, 1002);
}
const buf = data.slice(2);
if (!isValidUTF8(buf)) {
return error(Error, 'invalid UTF-8 sequence', true, 1007);
}
this.emit('conclude', code, buf.toString());
this.end();
}
} else if (this._opcode === 0x09) {
this.emit('ping', data);
} else {
this.emit('pong', data);
}
this._state = GET_INFO;
}
}
module.exports = Receiver;
/**
* Builds an error object.
*
* @param {(Error|RangeError)} ErrorCtor The error constructor
* @param {String} message The error message
* @param {Boolean} prefix Specifies whether or not to add a default prefix to
* `message`
* @param {Number} statusCode The status code
* @return {(Error|RangeError)} The error
* @private
*/
function error(ErrorCtor, message, prefix, statusCode) {
const err = new ErrorCtor(
prefix ? `Invalid WebSocket frame: ${message}` : message
);
Error.captureStackTrace(err, error);
err[kStatusCode] = statusCode;
return err;
}

362
node_modules/ws/lib/sender.js generated vendored Normal file
View File

@ -0,0 +1,362 @@
'use strict';
const { randomBytes } = require('crypto');
const PerMessageDeflate = require('./permessage-deflate');
const { EMPTY_BUFFER } = require('./constants');
const { isValidStatusCode } = require('./validation');
const { mask: applyMask, toBuffer } = require('./buffer-util');
/**
* HyBi Sender implementation.
*/
class Sender {
/**
* Creates a Sender instance.
*
* @param {net.Socket} socket The connection socket
* @param {Object} extensions An object containing the negotiated extensions
*/
constructor(socket, extensions) {
this._extensions = extensions || {};
this._socket = socket;
this._firstFragment = true;
this._compress = false;
this._bufferedBytes = 0;
this._deflating = false;
this._queue = [];
}
/**
* Frames a piece of data according to the HyBi WebSocket protocol.
*
* @param {Buffer} data The data to frame
* @param {Object} options Options object
* @param {Number} options.opcode The opcode
* @param {Boolean} options.readOnly Specifies whether `data` can be modified
* @param {Boolean} options.fin Specifies whether or not to set the FIN bit
* @param {Boolean} options.mask Specifies whether or not to mask `data`
* @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit
* @return {Buffer[]} The framed data as a list of `Buffer` instances
* @public
*/
static frame(data, options) {
const merge = data.length < 1024 || (options.mask && options.readOnly);
var offset = options.mask ? 6 : 2;
var payloadLength = data.length;
if (data.length >= 65536) {
offset += 8;
payloadLength = 127;
} else if (data.length > 125) {
offset += 2;
payloadLength = 126;
}
const target = Buffer.allocUnsafe(merge ? data.length + offset : offset);
target[0] = options.fin ? options.opcode | 0x80 : options.opcode;
if (options.rsv1) target[0] |= 0x40;
if (payloadLength === 126) {
target.writeUInt16BE(data.length, 2);
} else if (payloadLength === 127) {
target.writeUInt32BE(0, 2);
target.writeUInt32BE(data.length, 6);
}
if (!options.mask) {
target[1] = payloadLength;
if (merge) {
data.copy(target, offset);
return [target];
}
return [target, data];
}
const mask = randomBytes(4);
target[1] = payloadLength | 0x80;
target[offset - 4] = mask[0];
target[offset - 3] = mask[1];
target[offset - 2] = mask[2];
target[offset - 1] = mask[3];
if (merge) {
applyMask(data, mask, target, offset, data.length);
return [target];
}
applyMask(data, mask, data, 0, data.length);
return [target, data];
}
/**
* Sends a close message to the other peer.
*
* @param {(Number|undefined)} code The status code component of the body
* @param {String} data The message component of the body
* @param {Boolean} mask Specifies whether or not to mask the message
* @param {Function} cb Callback
* @public
*/
close(code, data, mask, cb) {
var buf;
if (code === undefined) {
buf = EMPTY_BUFFER;
} else if (typeof code !== 'number' || !isValidStatusCode(code)) {
throw new TypeError('First argument must be a valid error code number');
} else if (data === undefined || data === '') {
buf = Buffer.allocUnsafe(2);
buf.writeUInt16BE(code, 0);
} else {
buf = Buffer.allocUnsafe(2 + Buffer.byteLength(data));
buf.writeUInt16BE(code, 0);
buf.write(data, 2);
}
if (this._deflating) {
this.enqueue([this.doClose, buf, mask, cb]);
} else {
this.doClose(buf, mask, cb);
}
}
/**
* Frames and sends a close message.
*
* @param {Buffer} data The message to send
* @param {Boolean} mask Specifies whether or not to mask `data`
* @param {Function} cb Callback
* @private
*/
doClose(data, mask, cb) {
this.sendFrame(
Sender.frame(data, {
fin: true,
rsv1: false,
opcode: 0x08,
mask,
readOnly: false
}),
cb
);
}
/**
* Sends a ping message to the other peer.
*
* @param {*} data The message to send
* @param {Boolean} mask Specifies whether or not to mask `data`
* @param {Function} cb Callback
* @public
*/
ping(data, mask, cb) {
const buf = toBuffer(data);
if (this._deflating) {
this.enqueue([this.doPing, buf, mask, toBuffer.readOnly, cb]);
} else {
this.doPing(buf, mask, toBuffer.readOnly, cb);
}
}
/**
* Frames and sends a ping message.
*
* @param {*} data The message to send
* @param {Boolean} mask Specifies whether or not to mask `data`
* @param {Boolean} readOnly Specifies whether `data` can be modified
* @param {Function} cb Callback
* @private
*/
doPing(data, mask, readOnly, cb) {
this.sendFrame(
Sender.frame(data, {
fin: true,
rsv1: false,
opcode: 0x09,
mask,
readOnly
}),
cb
);
}
/**
* Sends a pong message to the other peer.
*
* @param {*} data The message to send
* @param {Boolean} mask Specifies whether or not to mask `data`
* @param {Function} cb Callback
* @public
*/
pong(data, mask, cb) {
const buf = toBuffer(data);
if (this._deflating) {
this.enqueue([this.doPong, buf, mask, toBuffer.readOnly, cb]);
} else {
this.doPong(buf, mask, toBuffer.readOnly, cb);
}
}
/**
* Frames and sends a pong message.
*
* @param {*} data The message to send
* @param {Boolean} mask Specifies whether or not to mask `data`
* @param {Boolean} readOnly Specifies whether `data` can be modified
* @param {Function} cb Callback
* @private
*/
doPong(data, mask, readOnly, cb) {
this.sendFrame(
Sender.frame(data, {
fin: true,
rsv1: false,
opcode: 0x0a,
mask,
readOnly
}),
cb
);
}
/**
* Sends a data message to the other peer.
*
* @param {*} data The message to send
* @param {Object} options Options object
* @param {Boolean} options.compress Specifies whether or not to compress `data`
* @param {Boolean} options.binary Specifies whether `data` is binary or text
* @param {Boolean} options.fin Specifies whether the fragment is the last one
* @param {Boolean} options.mask Specifies whether or not to mask `data`
* @param {Function} cb Callback
* @public
*/
send(data, options, cb) {
const buf = toBuffer(data);
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
var opcode = options.binary ? 2 : 1;
var rsv1 = options.compress;
if (this._firstFragment) {
this._firstFragment = false;
if (rsv1 && perMessageDeflate) {
rsv1 = buf.length >= perMessageDeflate._threshold;
}
this._compress = rsv1;
} else {
rsv1 = false;
opcode = 0;
}
if (options.fin) this._firstFragment = true;
if (perMessageDeflate) {
const opts = {
fin: options.fin,
rsv1,
opcode,
mask: options.mask,
readOnly: toBuffer.readOnly
};
if (this._deflating) {
this.enqueue([this.dispatch, buf, this._compress, opts, cb]);
} else {
this.dispatch(buf, this._compress, opts, cb);
}
} else {
this.sendFrame(
Sender.frame(buf, {
fin: options.fin,
rsv1: false,
opcode,
mask: options.mask,
readOnly: toBuffer.readOnly
}),
cb
);
}
}
/**
* Dispatches a data message.
*
* @param {Buffer} data The message to send
* @param {Boolean} compress Specifies whether or not to compress `data`
* @param {Object} options Options object
* @param {Number} options.opcode The opcode
* @param {Boolean} options.readOnly Specifies whether `data` can be modified
* @param {Boolean} options.fin Specifies whether or not to set the FIN bit
* @param {Boolean} options.mask Specifies whether or not to mask `data`
* @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit
* @param {Function} cb Callback
* @private
*/
dispatch(data, compress, options, cb) {
if (!compress) {
this.sendFrame(Sender.frame(data, options), cb);
return;
}
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
this._deflating = true;
perMessageDeflate.compress(data, options.fin, (_, buf) => {
this._deflating = false;
options.readOnly = false;
this.sendFrame(Sender.frame(buf, options), cb);
this.dequeue();
});
}
/**
* Executes queued send operations.
*
* @private
*/
dequeue() {
while (!this._deflating && this._queue.length) {
const params = this._queue.shift();
this._bufferedBytes -= params[1].length;
params[0].apply(this, params.slice(1));
}
}
/**
* Enqueues a send operation.
*
* @param {Array} params Send operation parameters.
* @private
*/
enqueue(params) {
this._bufferedBytes += params[1].length;
this._queue.push(params);
}
/**
* Sends a frame.
*
* @param {Buffer[]} list The frame to send
* @param {Function} cb Callback
* @private
*/
sendFrame(list, cb) {
if (list.length === 2) {
this._socket.write(list[0]);
this._socket.write(list[1], cb);
} else {
this._socket.write(list[0], cb);
}
}
}
module.exports = Sender;

30
node_modules/ws/lib/validation.js generated vendored Normal file
View File

@ -0,0 +1,30 @@
'use strict';
try {
const isValidUTF8 = require('utf-8-validate');
exports.isValidUTF8 =
typeof isValidUTF8 === 'object'
? isValidUTF8.Validation.isValidUTF8 // utf-8-validate@<3.0.0
: isValidUTF8;
} catch (e) /* istanbul ignore next */ {
exports.isValidUTF8 = () => true;
}
/**
* Checks if a status code is allowed in a close frame.
*
* @param {Number} code The status code
* @return {Boolean} `true` if the status code is valid, else `false`
* @public
*/
exports.isValidStatusCode = (code) => {
return (
(code >= 1000 &&
code <= 1013 &&
code !== 1004 &&
code !== 1005 &&
code !== 1006) ||
(code >= 3000 && code <= 4999)
);
};

397
node_modules/ws/lib/websocket-server.js generated vendored Normal file
View File

@ -0,0 +1,397 @@
'use strict';
const EventEmitter = require('events');
const crypto = require('crypto');
const http = require('http');
const PerMessageDeflate = require('./permessage-deflate');
const extension = require('./extension');
const WebSocket = require('./websocket');
const { GUID } = require('./constants');
const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
/**
* Class representing a WebSocket server.
*
* @extends EventEmitter
*/
class WebSocketServer extends EventEmitter {
/**
* Create a `WebSocketServer` instance.
*
* @param {Object} options Configuration options
* @param {String} options.host The hostname where to bind the server
* @param {Number} options.port The port where to bind the server
* @param {http.Server} options.server A pre-created HTTP/S server to use
* @param {Function} options.verifyClient An hook to reject connections
* @param {Function} options.handleProtocols An hook to handle protocols
* @param {String} options.path Accept only connections matching this path
* @param {Boolean} options.noServer Enable no server mode
* @param {Boolean} options.clientTracking Specifies whether or not to track clients
* @param {(Boolean|Object)} options.perMessageDeflate Enable/disable permessage-deflate
* @param {Number} options.maxPayload The maximum allowed message size
* @param {Function} callback A listener for the `listening` event
*/
constructor(options, callback) {
super();
options = Object.assign(
{
maxPayload: 100 * 1024 * 1024,
perMessageDeflate: false,
handleProtocols: null,
clientTracking: true,
verifyClient: null,
noServer: false,
backlog: null, // use default (511 as implemented in net.js)
server: null,
host: null,
path: null,
port: null
},
options
);
if (options.port == null && !options.server && !options.noServer) {
throw new TypeError(
'One of the "port", "server", or "noServer" options must be specified'
);
}
if (options.port != null) {
this._server = http.createServer((req, res) => {
const body = http.STATUS_CODES[426];
res.writeHead(426, {
'Content-Length': body.length,
'Content-Type': 'text/plain'
});
res.end(body);
});
this._server.listen(
options.port,
options.host,
options.backlog,
callback
);
} else if (options.server) {
this._server = options.server;
}
if (this._server) {
this._removeListeners = addListeners(this._server, {
listening: this.emit.bind(this, 'listening'),
error: this.emit.bind(this, 'error'),
upgrade: (req, socket, head) => {
this.handleUpgrade(req, socket, head, (ws) => {
this.emit('connection', ws, req);
});
}
});
}
if (options.perMessageDeflate === true) options.perMessageDeflate = {};
if (options.clientTracking) this.clients = new Set();
this.options = options;
}
/**
* Returns the bound address, the address family name, and port of the server
* as reported by the operating system if listening on an IP socket.
* If the server is listening on a pipe or UNIX domain socket, the name is
* returned as a string.
*
* @return {(Object|String|null)} The address of the server
* @public
*/
address() {
if (this.options.noServer) {
throw new Error('The server is operating in "noServer" mode');
}
if (!this._server) return null;
return this._server.address();
}
/**
* Close the server.
*
* @param {Function} cb Callback
* @public
*/
close(cb) {
if (cb) this.once('close', cb);
//
// Terminate all associated clients.
//
if (this.clients) {
for (const client of this.clients) client.terminate();
}
const server = this._server;
if (server) {
this._removeListeners();
this._removeListeners = this._server = null;
//
// Close the http server if it was internally created.
//
if (this.options.port != null) {
server.close(() => this.emit('close'));
return;
}
}
process.nextTick(emitClose, this);
}
/**
* See if a given request should be handled by this server instance.
*
* @param {http.IncomingMessage} req Request object to inspect
* @return {Boolean} `true` if the request is valid, else `false`
* @public
*/
shouldHandle(req) {
if (this.options.path) {
const index = req.url.indexOf('?');
const pathname = index !== -1 ? req.url.slice(0, index) : req.url;
if (pathname !== this.options.path) return false;
}
return true;
}
/**
* Handle a HTTP Upgrade request.
*
* @param {http.IncomingMessage} req The request object
* @param {net.Socket} socket The network socket between the server and client
* @param {Buffer} head The first packet of the upgraded stream
* @param {Function} cb Callback
* @public
*/
handleUpgrade(req, socket, head, cb) {
socket.on('error', socketOnError);
const key =
req.headers['sec-websocket-key'] !== undefined
? req.headers['sec-websocket-key'].trim()
: false;
const version = +req.headers['sec-websocket-version'];
const extensions = {};
if (
req.method !== 'GET' ||
req.headers.upgrade.toLowerCase() !== 'websocket' ||
!key ||
!keyRegex.test(key) ||
(version !== 8 && version !== 13) ||
!this.shouldHandle(req)
) {
return abortHandshake(socket, 400);
}
if (this.options.perMessageDeflate) {
const perMessageDeflate = new PerMessageDeflate(
this.options.perMessageDeflate,
true,
this.options.maxPayload
);
try {
const offers = extension.parse(req.headers['sec-websocket-extensions']);
if (offers[PerMessageDeflate.extensionName]) {
perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
}
} catch (err) {
return abortHandshake(socket, 400);
}
}
//
// Optionally call external client verification handler.
//
if (this.options.verifyClient) {
const info = {
origin:
req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`],
secure: !!(req.connection.authorized || req.connection.encrypted),
req
};
if (this.options.verifyClient.length === 2) {
this.options.verifyClient(info, (verified, code, message, headers) => {
if (!verified) {
return abortHandshake(socket, code || 401, message, headers);
}
this.completeUpgrade(key, extensions, req, socket, head, cb);
});
return;
}
if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
}
this.completeUpgrade(key, extensions, req, socket, head, cb);
}
/**
* Upgrade the connection to WebSocket.
*
* @param {String} key The value of the `Sec-WebSocket-Key` header
* @param {Object} extensions The accepted extensions
* @param {http.IncomingMessage} req The request object
* @param {net.Socket} socket The network socket between the server and client
* @param {Buffer} head The first packet of the upgraded stream
* @param {Function} cb Callback
* @private
*/
completeUpgrade(key, extensions, req, socket, head, cb) {
//
// Destroy the socket if the client has already sent a FIN packet.
//
if (!socket.readable || !socket.writable) return socket.destroy();
const digest = crypto
.createHash('sha1')
.update(key + GUID)
.digest('base64');
const headers = [
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
`Sec-WebSocket-Accept: ${digest}`
];
const ws = new WebSocket(null);
var protocol = req.headers['sec-websocket-protocol'];
if (protocol) {
protocol = protocol.trim().split(/ *, */);
//
// Optionally call external protocol selection handler.
//
if (this.options.handleProtocols) {
protocol = this.options.handleProtocols(protocol, req);
} else {
protocol = protocol[0];
}
if (protocol) {
headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
ws.protocol = protocol;
}
}
if (extensions[PerMessageDeflate.extensionName]) {
const params = extensions[PerMessageDeflate.extensionName].params;
const value = extension.format({
[PerMessageDeflate.extensionName]: [params]
});
headers.push(`Sec-WebSocket-Extensions: ${value}`);
ws._extensions = extensions;
}
//
// Allow external modification/inspection of handshake headers.
//
this.emit('headers', headers, req);
socket.write(headers.concat('\r\n').join('\r\n'));
socket.removeListener('error', socketOnError);
ws.setSocket(socket, head, this.options.maxPayload);
if (this.clients) {
this.clients.add(ws);
ws.on('close', () => this.clients.delete(ws));
}
cb(ws);
}
}
module.exports = WebSocketServer;
/**
* Add event listeners on an `EventEmitter` using a map of <event, listener>
* pairs.
*
* @param {EventEmitter} server The event emitter
* @param {Object.<String, Function>} map The listeners to add
* @return {Function} A function that will remove the added listeners when called
* @private
*/
function addListeners(server, map) {
for (const event of Object.keys(map)) server.on(event, map[event]);
return function removeListeners() {
for (const event of Object.keys(map)) {
server.removeListener(event, map[event]);
}
};
}
/**
* Emit a `'close'` event on an `EventEmitter`.
*
* @param {EventEmitter} server The event emitter
* @private
*/
function emitClose(server) {
server.emit('close');
}
/**
* Handle premature socket errors.
*
* @private
*/
function socketOnError() {
this.destroy();
}
/**
* Close the connection when preconditions are not fulfilled.
*
* @param {net.Socket} socket The socket of the upgrade request
* @param {Number} code The HTTP response status code
* @param {String} [message] The HTTP response body
* @param {Object} [headers] Additional HTTP response headers
* @private
*/
function abortHandshake(socket, code, message, headers) {
if (socket.writable) {
message = message || http.STATUS_CODES[code];
headers = Object.assign(
{
Connection: 'close',
'Content-type': 'text/html',
'Content-Length': Buffer.byteLength(message)
},
headers
);
socket.write(
`HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` +
Object.keys(headers)
.map((h) => `${h}: ${headers[h]}`)
.join('\r\n') +
'\r\n\r\n' +
message
);
}
socket.removeListener('error', socketOnError);
socket.destroy();
}

900
node_modules/ws/lib/websocket.js generated vendored Normal file
View File

@ -0,0 +1,900 @@
'use strict';
const EventEmitter = require('events');
const crypto = require('crypto');
const https = require('https');
const http = require('http');
const net = require('net');
const tls = require('tls');
const url = require('url');
const PerMessageDeflate = require('./permessage-deflate');
const EventTarget = require('./event-target');
const extension = require('./extension');
const Receiver = require('./receiver');
const Sender = require('./sender');
const {
BINARY_TYPES,
EMPTY_BUFFER,
GUID,
kStatusCode,
kWebSocket,
NOOP
} = require('./constants');
const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
const protocolVersions = [8, 13];
const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly.
/**
* Class representing a WebSocket.
*
* @extends EventEmitter
*/
class WebSocket extends EventEmitter {
/**
* Create a new `WebSocket`.
*
* @param {(String|url.Url|url.URL)} address The URL to which to connect
* @param {(String|String[])} protocols The subprotocols
* @param {Object} options Connection options
*/
constructor(address, protocols, options) {
super();
this.readyState = WebSocket.CONNECTING;
this.protocol = '';
this._binaryType = BINARY_TYPES[0];
this._closeFrameReceived = false;
this._closeFrameSent = false;
this._closeMessage = '';
this._closeTimer = null;
this._closeCode = 1006;
this._extensions = {};
this._receiver = null;
this._sender = null;
this._socket = null;
if (address !== null) {
this._isServer = false;
this._redirects = 0;
if (Array.isArray(protocols)) {
protocols = protocols.join(', ');
} else if (typeof protocols === 'object' && protocols !== null) {
options = protocols;
protocols = undefined;
}
initAsClient(this, address, protocols, options);
} else {
this._isServer = true;
}
}
get CONNECTING() {
return WebSocket.CONNECTING;
}
get CLOSING() {
return WebSocket.CLOSING;
}
get CLOSED() {
return WebSocket.CLOSED;
}
get OPEN() {
return WebSocket.OPEN;
}
/**
* This deviates from the WHATWG interface since ws doesn't support the required
* default "blob" type (instead we define a custom "nodebuffer" type).
*
* @type {String}
*/
get binaryType() {
return this._binaryType;
}
set binaryType(type) {
if (!BINARY_TYPES.includes(type)) return;
this._binaryType = type;
//
// Allow to change `binaryType` on the fly.
//
if (this._receiver) this._receiver._binaryType = type;
}
/**
* @type {Number}
*/
get bufferedAmount() {
if (!this._socket) return 0;
//
// `socket.bufferSize` is `undefined` if the socket is closed.
//
return (this._socket.bufferSize || 0) + this._sender._bufferedBytes;
}
/**
* @type {String}
*/
get extensions() {
return Object.keys(this._extensions).join();
}
/**
* Set up the socket and the internal resources.
*
* @param {net.Socket} socket The network socket between the server and client
* @param {Buffer} head The first packet of the upgraded stream
* @param {Number} maxPayload The maximum allowed message size
* @private
*/
setSocket(socket, head, maxPayload) {
const receiver = new Receiver(
this._binaryType,
this._extensions,
maxPayload
);
this._sender = new Sender(socket, this._extensions);
this._receiver = receiver;
this._socket = socket;
receiver[kWebSocket] = this;
socket[kWebSocket] = this;
receiver.on('conclude', receiverOnConclude);
receiver.on('drain', receiverOnDrain);
receiver.on('error', receiverOnError);
receiver.on('message', receiverOnMessage);
receiver.on('ping', receiverOnPing);
receiver.on('pong', receiverOnPong);
socket.setTimeout(0);
socket.setNoDelay();
if (head.length > 0) socket.unshift(head);
socket.on('close', socketOnClose);
socket.on('data', socketOnData);
socket.on('end', socketOnEnd);
socket.on('error', socketOnError);
this.readyState = WebSocket.OPEN;
this.emit('open');
}
/**
* Emit the `'close'` event.
*
* @private
*/
emitClose() {
this.readyState = WebSocket.CLOSED;
if (!this._socket) {
this.emit('close', this._closeCode, this._closeMessage);
return;
}
if (this._extensions[PerMessageDeflate.extensionName]) {
this._extensions[PerMessageDeflate.extensionName].cleanup();
}
this._receiver.removeAllListeners();
this.emit('close', this._closeCode, this._closeMessage);
}
/**
* Start a closing handshake.
*
* +----------+ +-----------+ +----------+
* - - -|ws.close()|-->|close frame|-->|ws.close()|- - -
* | +----------+ +-----------+ +----------+ |
* +----------+ +-----------+ |
* CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING
* +----------+ +-----------+ |
* | | | +---+ |
* +------------------------+-->|fin| - - - -
* | +---+ | +---+
* - - - - -|fin|<---------------------+
* +---+
*
* @param {Number} code Status code explaining why the connection is closing
* @param {String} data A string explaining why the connection is closing
* @public
*/
close(code, data) {
if (this.readyState === WebSocket.CLOSED) return;
if (this.readyState === WebSocket.CONNECTING) {
const msg = 'WebSocket was closed before the connection was established';
return abortHandshake(this, this._req, msg);
}
if (this.readyState === WebSocket.CLOSING) {
if (this._closeFrameSent && this._closeFrameReceived) this._socket.end();
return;
}
this.readyState = WebSocket.CLOSING;
this._sender.close(code, data, !this._isServer, (err) => {
//
// This error is handled by the `'error'` listener on the socket. We only
// want to know if the close frame has been sent here.
//
if (err) return;
this._closeFrameSent = true;
if (this._socket.writable) {
if (this._closeFrameReceived) this._socket.end();
//
// Ensure that the connection is closed even if the closing handshake
// fails.
//
this._closeTimer = setTimeout(
this._socket.destroy.bind(this._socket),
closeTimeout
);
}
});
}
/**
* Send a ping.
*
* @param {*} data The data to send
* @param {Boolean} mask Indicates whether or not to mask `data`
* @param {Function} cb Callback which is executed when the ping is sent
* @public
*/
ping(data, mask, cb) {
if (typeof data === 'function') {
cb = data;
data = mask = undefined;
} else if (typeof mask === 'function') {
cb = mask;
mask = undefined;
}
if (this.readyState !== WebSocket.OPEN) {
const err = new Error(
`WebSocket is not open: readyState ${this.readyState} ` +
`(${readyStates[this.readyState]})`
);
if (cb) return cb(err);
throw err;
}
if (typeof data === 'number') data = data.toString();
if (mask === undefined) mask = !this._isServer;
this._sender.ping(data || EMPTY_BUFFER, mask, cb);
}
/**
* Send a pong.
*
* @param {*} data The data to send
* @param {Boolean} mask Indicates whether or not to mask `data`
* @param {Function} cb Callback which is executed when the pong is sent
* @public
*/
pong(data, mask, cb) {
if (typeof data === 'function') {
cb = data;
data = mask = undefined;
} else if (typeof mask === 'function') {
cb = mask;
mask = undefined;
}
if (this.readyState !== WebSocket.OPEN) {
const err = new Error(
`WebSocket is not open: readyState ${this.readyState} ` +
`(${readyStates[this.readyState]})`
);
if (cb) return cb(err);
throw err;
}
if (typeof data === 'number') data = data.toString();
if (mask === undefined) mask = !this._isServer;
this._sender.pong(data || EMPTY_BUFFER, mask, cb);
}
/**
* Send a data message.
*
* @param {*} data The message to send
* @param {Object} options Options object
* @param {Boolean} options.compress Specifies whether or not to compress `data`
* @param {Boolean} options.binary Specifies whether `data` is binary or text
* @param {Boolean} options.fin Specifies whether the fragment is the last one
* @param {Boolean} options.mask Specifies whether or not to mask `data`
* @param {Function} cb Callback which is executed when data is written out
* @public
*/
send(data, options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
}
if (this.readyState !== WebSocket.OPEN) {
const err = new Error(
`WebSocket is not open: readyState ${this.readyState} ` +
`(${readyStates[this.readyState]})`
);
if (cb) return cb(err);
throw err;
}
if (typeof data === 'number') data = data.toString();
const opts = Object.assign(
{
binary: typeof data !== 'string',
mask: !this._isServer,
compress: true,
fin: true
},
options
);
if (!this._extensions[PerMessageDeflate.extensionName]) {
opts.compress = false;
}
this._sender.send(data || EMPTY_BUFFER, opts, cb);
}
/**
* Forcibly close the connection.
*
* @public
*/
terminate() {
if (this.readyState === WebSocket.CLOSED) return;
if (this.readyState === WebSocket.CONNECTING) {
const msg = 'WebSocket was closed before the connection was established';
return abortHandshake(this, this._req, msg);
}
if (this._socket) {
this.readyState = WebSocket.CLOSING;
this._socket.destroy();
}
}
}
readyStates.forEach((readyState, i) => {
WebSocket[readyState] = i;
});
//
// Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes.
// See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface
//
['open', 'error', 'close', 'message'].forEach((method) => {
Object.defineProperty(WebSocket.prototype, `on${method}`, {
/**
* Return the listener of the event.
*
* @return {(Function|undefined)} The event listener or `undefined`
* @public
*/
get() {
const listeners = this.listeners(method);
for (var i = 0; i < listeners.length; i++) {
if (listeners[i]._listener) return listeners[i]._listener;
}
return undefined;
},
/**
* Add a listener for the event.
*
* @param {Function} listener The listener to add
* @public
*/
set(listener) {
const listeners = this.listeners(method);
for (var i = 0; i < listeners.length; i++) {
//
// Remove only the listeners added via `addEventListener`.
//
if (listeners[i]._listener) this.removeListener(method, listeners[i]);
}
this.addEventListener(method, listener);
}
});
});
WebSocket.prototype.addEventListener = EventTarget.addEventListener;
WebSocket.prototype.removeEventListener = EventTarget.removeEventListener;
module.exports = WebSocket;
/**
* Initialize a WebSocket client.
*
* @param {WebSocket} websocket The client to initialize
* @param {(String|url.Url|url.URL)} address The URL to which to connect
* @param {String} protocols The subprotocols
* @param {Object} options Connection options
* @param {(Boolean|Object)} options.perMessageDeflate Enable/disable
* permessage-deflate
* @param {Number} options.handshakeTimeout Timeout in milliseconds for the
* handshake request
* @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version`
* header
* @param {String} options.origin Value of the `Origin` or
* `Sec-WebSocket-Origin` header
* @param {Number} options.maxPayload The maximum allowed message size
* @param {Boolean} options.followRedirects Whether or not to follow redirects
* @param {Number} options.maxRedirects The maximum number of redirects allowed
* @private
*/
function initAsClient(websocket, address, protocols, options) {
const opts = Object.assign(
{
protocolVersion: protocolVersions[1],
maxPayload: 100 * 1024 * 1024,
perMessageDeflate: true,
followRedirects: false,
maxRedirects: 10
},
options,
{
createConnection: undefined,
socketPath: undefined,
hostname: undefined,
protocol: undefined,
timeout: undefined,
method: undefined,
auth: undefined,
host: undefined,
path: undefined,
port: undefined
}
);
if (!protocolVersions.includes(opts.protocolVersion)) {
throw new RangeError(
`Unsupported protocol version: ${opts.protocolVersion} ` +
`(supported versions: ${protocolVersions.join(', ')})`
);
}
var parsedUrl;
if (typeof address === 'object' && address.href !== undefined) {
parsedUrl = address;
websocket.url = address.href;
} else {
//
// The WHATWG URL constructor is not available on Node.js < 6.13.0
//
parsedUrl = url.URL ? new url.URL(address) : url.parse(address);
websocket.url = address;
}
const isUnixSocket = parsedUrl.protocol === 'ws+unix:';
if (!parsedUrl.host && (!isUnixSocket || !parsedUrl.pathname)) {
throw new Error(`Invalid URL: ${websocket.url}`);
}
const isSecure =
parsedUrl.protocol === 'wss:' || parsedUrl.protocol === 'https:';
const defaultPort = isSecure ? 443 : 80;
const key = crypto.randomBytes(16).toString('base64');
const get = isSecure ? https.get : http.get;
const path = parsedUrl.search
? `${parsedUrl.pathname || '/'}${parsedUrl.search}`
: parsedUrl.pathname || '/';
var perMessageDeflate;
opts.createConnection = isSecure ? tlsConnect : netConnect;
opts.defaultPort = opts.defaultPort || defaultPort;
opts.port = parsedUrl.port || defaultPort;
opts.host = parsedUrl.hostname.startsWith('[')
? parsedUrl.hostname.slice(1, -1)
: parsedUrl.hostname;
opts.headers = Object.assign(
{
'Sec-WebSocket-Version': opts.protocolVersion,
'Sec-WebSocket-Key': key,
Connection: 'Upgrade',
Upgrade: 'websocket'
},
opts.headers
);
opts.path = path;
opts.timeout = opts.handshakeTimeout;
if (opts.perMessageDeflate) {
perMessageDeflate = new PerMessageDeflate(
opts.perMessageDeflate !== true ? opts.perMessageDeflate : {},
false,
opts.maxPayload
);
opts.headers['Sec-WebSocket-Extensions'] = extension.format({
[PerMessageDeflate.extensionName]: perMessageDeflate.offer()
});
}
if (protocols) {
opts.headers['Sec-WebSocket-Protocol'] = protocols;
}
if (opts.origin) {
if (opts.protocolVersion < 13) {
opts.headers['Sec-WebSocket-Origin'] = opts.origin;
} else {
opts.headers.Origin = opts.origin;
}
}
if (parsedUrl.auth) {
opts.auth = parsedUrl.auth;
} else if (parsedUrl.username || parsedUrl.password) {
opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;
}
if (isUnixSocket) {
const parts = path.split(':');
opts.socketPath = parts[0];
opts.path = parts[1];
}
var req = (websocket._req = get(opts));
if (opts.timeout) {
req.on('timeout', () => {
abortHandshake(websocket, req, 'Opening handshake has timed out');
});
}
req.on('error', (err) => {
if (websocket._req.aborted) return;
req = websocket._req = null;
websocket.readyState = WebSocket.CLOSING;
websocket.emit('error', err);
websocket.emitClose();
});
req.on('response', (res) => {
const location = res.headers.location;
const statusCode = res.statusCode;
if (
location &&
opts.followRedirects &&
statusCode >= 300 &&
statusCode < 400
) {
if (++websocket._redirects > opts.maxRedirects) {
abortHandshake(websocket, req, 'Maximum redirects exceeded');
return;
}
req.abort();
const addr = url.URL
? new url.URL(location, address)
: url.resolve(address, location);
initAsClient(websocket, addr, protocols, options);
} else if (!websocket.emit('unexpected-response', req, res)) {
abortHandshake(
websocket,
req,
`Unexpected server response: ${res.statusCode}`
);
}
});
req.on('upgrade', (res, socket, head) => {
websocket.emit('upgrade', res);
//
// The user may have closed the connection from a listener of the `upgrade`
// event.
//
if (websocket.readyState !== WebSocket.CONNECTING) return;
req = websocket._req = null;
const digest = crypto
.createHash('sha1')
.update(key + GUID)
.digest('base64');
if (res.headers['sec-websocket-accept'] !== digest) {
abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header');
return;
}
const serverProt = res.headers['sec-websocket-protocol'];
const protList = (protocols || '').split(/, */);
var protError;
if (!protocols && serverProt) {
protError = 'Server sent a subprotocol but none was requested';
} else if (protocols && !serverProt) {
protError = 'Server sent no subprotocol';
} else if (serverProt && !protList.includes(serverProt)) {
protError = 'Server sent an invalid subprotocol';
}
if (protError) {
abortHandshake(websocket, socket, protError);
return;
}
if (serverProt) websocket.protocol = serverProt;
if (perMessageDeflate) {
try {
const extensions = extension.parse(
res.headers['sec-websocket-extensions']
);
if (extensions[PerMessageDeflate.extensionName]) {
perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
websocket._extensions[
PerMessageDeflate.extensionName
] = perMessageDeflate;
}
} catch (err) {
abortHandshake(
websocket,
socket,
'Invalid Sec-WebSocket-Extensions header'
);
return;
}
}
websocket.setSocket(socket, head, opts.maxPayload);
});
}
/**
* Create a `net.Socket` and initiate a connection.
*
* @param {Object} options Connection options
* @return {net.Socket} The newly created socket used to start the connection
* @private
*/
function netConnect(options) {
//
// Override `options.path` only if `options` is a copy of the original options
// object. This is always true on Node.js >= 8 but not on Node.js 6 where
// `options.socketPath` might be `undefined` even if the `socketPath` option
// was originally set.
//
if (options.protocolVersion) options.path = options.socketPath;
return net.connect(options);
}
/**
* Create a `tls.TLSSocket` and initiate a connection.
*
* @param {Object} options Connection options
* @return {tls.TLSSocket} The newly created socket used to start the connection
* @private
*/
function tlsConnect(options) {
options.path = undefined;
options.servername = options.servername || options.host;
return tls.connect(options);
}
/**
* Abort the handshake and emit an error.
*
* @param {WebSocket} websocket The WebSocket instance
* @param {(http.ClientRequest|net.Socket)} stream The request to abort or the
* socket to destroy
* @param {String} message The error message
* @private
*/
function abortHandshake(websocket, stream, message) {
websocket.readyState = WebSocket.CLOSING;
const err = new Error(message);
Error.captureStackTrace(err, abortHandshake);
if (stream.setHeader) {
stream.abort();
stream.once('abort', websocket.emitClose.bind(websocket));
websocket.emit('error', err);
} else {
stream.destroy(err);
stream.once('error', websocket.emit.bind(websocket, 'error'));
stream.once('close', websocket.emitClose.bind(websocket));
}
}
/**
* The listener of the `Receiver` `'conclude'` event.
*
* @param {Number} code The status code
* @param {String} reason The reason for closing
* @private
*/
function receiverOnConclude(code, reason) {
const websocket = this[kWebSocket];
websocket._socket.removeListener('data', socketOnData);
websocket._socket.resume();
websocket._closeFrameReceived = true;
websocket._closeMessage = reason;
websocket._closeCode = code;
if (code === 1005) websocket.close();
else websocket.close(code, reason);
}
/**
* The listener of the `Receiver` `'drain'` event.
*
* @private
*/
function receiverOnDrain() {
this[kWebSocket]._socket.resume();
}
/**
* The listener of the `Receiver` `'error'` event.
*
* @param {(RangeError|Error)} err The emitted error
* @private
*/
function receiverOnError(err) {
const websocket = this[kWebSocket];
websocket._socket.removeListener('data', socketOnData);
websocket.readyState = WebSocket.CLOSING;
websocket._closeCode = err[kStatusCode];
websocket.emit('error', err);
websocket._socket.destroy();
}
/**
* The listener of the `Receiver` `'finish'` event.
*
* @private
*/
function receiverOnFinish() {
this[kWebSocket].emitClose();
}
/**
* The listener of the `Receiver` `'message'` event.
*
* @param {(String|Buffer|ArrayBuffer|Buffer[])} data The message
* @private
*/
function receiverOnMessage(data) {
this[kWebSocket].emit('message', data);
}
/**
* The listener of the `Receiver` `'ping'` event.
*
* @param {Buffer} data The data included in the ping frame
* @private
*/
function receiverOnPing(data) {
const websocket = this[kWebSocket];
websocket.pong(data, !websocket._isServer, NOOP);
websocket.emit('ping', data);
}
/**
* The listener of the `Receiver` `'pong'` event.
*
* @param {Buffer} data The data included in the pong frame
* @private
*/
function receiverOnPong(data) {
this[kWebSocket].emit('pong', data);
}
/**
* The listener of the `net.Socket` `'close'` event.
*
* @private
*/
function socketOnClose() {
const websocket = this[kWebSocket];
this.removeListener('close', socketOnClose);
this.removeListener('end', socketOnEnd);
websocket.readyState = WebSocket.CLOSING;
//
// The close frame might not have been received or the `'end'` event emitted,
// for example, if the socket was destroyed due to an error. Ensure that the
// `receiver` stream is closed after writing any remaining buffered data to
// it. If the readable side of the socket is in flowing mode then there is no
// buffered data as everything has been already written and `readable.read()`
// will return `null`. If instead, the socket is paused, any possible buffered
// data will be read as a single chunk and emitted synchronously in a single
// `'data'` event.
//
websocket._socket.read();
websocket._receiver.end();
this.removeListener('data', socketOnData);
this[kWebSocket] = undefined;
clearTimeout(websocket._closeTimer);
if (
websocket._receiver._writableState.finished ||
websocket._receiver._writableState.errorEmitted
) {
websocket.emitClose();
} else {
websocket._receiver.on('error', receiverOnFinish);
websocket._receiver.on('finish', receiverOnFinish);
}
}
/**
* The listener of the `net.Socket` `'data'` event.
*
* @param {Buffer} chunk A chunk of data
* @private
*/
function socketOnData(chunk) {
if (!this[kWebSocket]._receiver.write(chunk)) {
this.pause();
}
}
/**
* The listener of the `net.Socket` `'end'` event.
*
* @private
*/
function socketOnEnd() {
const websocket = this[kWebSocket];
websocket.readyState = WebSocket.CLOSING;
websocket._receiver.end();
this.end();
}
/**
* The listener of the `net.Socket` `'error'` event.
*
* @private
*/
function socketOnError() {
const websocket = this[kWebSocket];
this.removeListener('error', socketOnError);
this.on('error', NOOP);
if (websocket) {
websocket.readyState = WebSocket.CLOSING;
this.destroy();
}
}

80
node_modules/ws/package.json generated vendored Normal file
View File

@ -0,0 +1,80 @@
{
"_from": "ws@^6.2.0",
"_id": "ws@6.2.0",
"_inBundle": false,
"_integrity": "sha512-deZYUNlt2O4buFCa3t5bKLf8A7FPP/TVjwOeVNpw818Ma5nk4MLXls2eoEGS39o8119QIYxTrTDoPQ5B/gTD6w==",
"_location": "/ws",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "ws@^6.2.0",
"name": "ws",
"escapedName": "ws",
"rawSpec": "^6.2.0",
"saveSpec": null,
"fetchSpec": "^6.2.0"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/ws/-/ws-6.2.0.tgz",
"_shasum": "13806d9913b2a5f3cbb9ba47b563c002cbc7c526",
"_spec": "ws@^6.2.0",
"_where": "C:\\www\\esiur-js",
"author": {
"name": "Einar Otto Stangvik",
"email": "einaros@gmail.com",
"url": "http://2x.io"
},
"browser": "browser.js",
"bugs": {
"url": "https://github.com/websockets/ws/issues"
},
"bundleDependencies": false,
"dependencies": {
"async-limiter": "~1.0.0"
},
"deprecated": false,
"description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js",
"devDependencies": {
"benchmark": "~2.1.4",
"bufferutil": "~4.0.0",
"coveralls": "~3.0.3",
"eslint": "~5.15.0",
"eslint-config-prettier": "~4.1.0",
"eslint-plugin-prettier": "~3.0.0",
"mocha": "~6.0.0",
"nyc": "~13.3.0",
"prettier": "~1.16.1",
"utf-8-validate": "~5.0.0"
},
"files": [
"browser.js",
"index.js",
"lib/*.js"
],
"homepage": "https://github.com/websockets/ws",
"keywords": [
"HyBi",
"Push",
"RFC-6455",
"WebSocket",
"WebSockets",
"real-time"
],
"license": "MIT",
"main": "index.js",
"name": "ws",
"repository": {
"type": "git",
"url": "git+https://github.com/websockets/ws.git"
},
"scripts": {
"integration": "npm run lint && mocha test/*.integration.js",
"lint": "eslint --ignore-path .gitignore . && prettier --check --ignore-path .gitignore \"**/*.{json,md,yml}\"",
"test": "npm run lint && nyc --reporter=html --reporter=text mocha test/*.test.js"
},
"version": "6.2.0"
}

21
package-lock.json generated Normal file
View File

@ -0,0 +1,21 @@
{
"name": "esiur",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"async-limiter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
},
"ws": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.0.tgz",
"integrity": "sha512-deZYUNlt2O4buFCa3t5bKLf8A7FPP/TVjwOeVNpw818Ma5nk4MLXls2eoEGS39o8119QIYxTrTDoPQ5B/gTD6w==",
"requires": {
"async-limiter": "~1.0.0"
}
}
}
}

22
package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "esiur",
"version": "1.0.0",
"description": "Distributed Object Framework",
"main": "build/esiur-debug-node.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/esiur/esiur-js.git"
},
"author": "Ahmed Zamil",
"license": "MIT",
"bugs": {
"url": "https://github.com/esiur/esiur-js/issues"
},
"homepage": "https://github.com/esiur/esiur-js#readme",
"dependencies": {
"ws": "^6.2.0"
}
}

View File

@ -23,6 +23,8 @@
* Created by Ahmed Zamil on 25/07/2017.
*/
"use strict";
class AsyncBag extends AsyncReply
{
constructor() {
@ -42,13 +44,27 @@ class AsyncBag extends AsyncReply
var self = this;
var singleTaskCompleted = function(taskIndex)
{
return function(results, reply){
self.results[taskIndex] = results;
self.count++;
if (self.count == self.results.length)
self.trigger(self.results);
};
};
for(var i = 0; i < this.results.length; i++)
this.replies[i].then(singleTaskCompleted(i));
/*
this.replies[i].then(function(r, reply){
self.results[self.replies.indexOf(reply)] = r;
self.count++;
if (self.count == self.results.length)
self.trigger(self.results);
});
*/
}
add(reply)

88
src/AsyncException.js Normal file
View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2017 Ahmed Kh. Zamil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Created by Ahmed Zamil on 18/11/2017.
*/
"use strict";
const ExceptionCode =
{
HostNotReachable: 0,
AccessDenied: 1,
ResourceNotFound: 2,
AttachDenied: 3,
InvalidMethod: 4,
InvokeDenied: 5,
CreateDenied: 6,
AddParentDenied: 7,
AddChildDenied: 8,
ViewAttributeDenied: 9,
UpdateAttributeDenied: 10,
StoreNotFound: 11,
ParentNotFound: 12,
ChildNotFound: 13,
ResourceIsNotStore: 14,
DeleteDenied: 15,
DeleteFailed: 16,
UpdateAttributeFailed: 17,
GetAttributesFailed: 18,
ClearAttributesFailed: 19,
TemplateNotFound: 20,
RenameDenied: 21,
ClassNotFound: 22,
MethodNotFound: 23,
PropertyNotFound: 24,
SetPropertyDenied: 25,
ReadOnlyProperty: 26
};
class AsyncException extends Error
{
constructor()
{
super();
this.raised = false;
}
raise(type, code, message)
{
this.type = (type == 0 ? "Management" : "Execusion");
this.code = code;
if (type == 0)
for(var i in ExceptionCode)
if (ExceptionCode[i] == code)
{
this.message = i;
break;
}
else
this.message = message;
this.raised = true;
}
toString()
{
return this.type + " " + this.code + " " + this.message;
}
}

View File

@ -22,6 +22,9 @@
/**
* Created by Ahmed Zamil on 25/07/2017.
*/
"use strict";
class AsyncQueue extends AsyncReply
{

View File

@ -23,13 +23,72 @@
/**
* Created by Ahmed Zamil on 25/07/2017.
*/
"use strict";
const ErrorType = {
Management: 0,
Exception: 1
};
const ProgressType = {
Execution: 0,
Network: 1
};
class AsyncReply
{
then(callback)
{
this.callbacks.push(callback);
if (this.ready)
{
callback(this.result, this);
if (!this.taskExpired)
{
this.taskExpired = true;
this.resolveTask(this.result);
}
}
return this;
}
catch(callback)
{
return error(callback);
}
error(callback)
{
this.errorCallbacks.push(callback);
if (this.exception.raised)
{
callback(this.exception);
if (!this.taskExpired)
{
this.taskExpired = true;
this.rejectTask(this.exception);
}
}
return this;
}
progress(callback)
{
this.progressCallbacks.push(callback);
return this;
}
chunk(callback)
{
this.chunkCallbacks.push(callback);
return this;
}
trigger(result)
@ -39,15 +98,79 @@ class AsyncReply
for(var i = 0; i < this.callbacks.length; i++)
this.callbacks[i](result, this);
if (!this.taskExpired)
{
this.taskExpired = true;
this.resolveTask(this.result);
}
}
triggerError(type, code, message)//exception)
{
if (this.ready)
return;
this.exception.raise(type, code, message);// = exception;
for(var i = 0; i < this.errorCallbacks.length; i++)
this.errorCallbacks[i](this.exception, this);
if (!this.taskExpired)
{
this.taskExpired = true;
this.rejectTask(this.exception);
}
}
triggerProgress(type, value, max)
{
if (this.ready)
return;
for(var i = 0; i < this.progressCallbacks.length; i++)
this.progressCallbacks[i](type, value, max, this);
}
triggerChunk(value)
{
if (this.ready)
return;
for(var i = 0; i < this.chunkCallbacks.length; i++)
this.chunkCallbacks[i](value, this);
}
constructor(result)
{
this.callbacks = [];
this.errorCallbacks = [];
this.progressCallbacks = [];
this.chunkCallbacks = [];
this.exception = new AsyncException();// null;
if (result) {
var self = this;
this.task = new Promise(function(resolve, reject){
self.resolveTask = resolve;
self.rejectTask = reject;
});
if (result !== undefined) {
this.result = result;
this.ready = true;
this.taskExpired = true;
this.resolveTask(result);
}
else
{
this.taskExpired = false;
this.ready = false;
this.result = null;
}
}
}

50
src/Authentication.js Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2017 Ahmed Kh. Zamil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Created by Ahmed Zamil on 16/11/2017.
*/
"use strict";
const AuthenticationType = {
Host: 0,
CoHost: 1,
Client: 2,
Alien: 3
};
class Authentication
{
constructor(type)
{
this.type = type;
this.state = 0;
this.domain = null;
this.username = null;
}
get fullName()
{
return this.domain + "@" + this.username;
}
}

View File

@ -24,6 +24,8 @@
* Created by Ahmed Zamil on 05/09/2017.
*/
"use strict";
class AutoList extends IEventHandler
{
constructor()
@ -32,10 +34,15 @@ class AutoList extends IEventHandler
this.list = [];
}
get length()
{
return this.list.length;
}
add(value)
{
if (value instanceof IDestructible)
value.on("destroy", this._item_destroyed);
value.on("destroy", this._item_destroyed, this);
this.list.push(value);
@ -48,7 +55,7 @@ class AutoList extends IEventHandler
return;
if (value instanceof IDestructible)
value.on("destroy", this._item_destroyed);
value.on("destroy", this._item_destroyed, this);
if (this.list[index] instanceof IDestructible)
this.list[index].off("destroy", this._item_destroyed);
@ -56,6 +63,16 @@ class AutoList extends IEventHandler
this.list[index] = value;
}
at(index)
{
return this.list[index];
}
item(index)
{
return this.list[index];
}
remove(value)
{
this.removeAt(this.list.indexOf(value));
@ -66,6 +83,11 @@ class AutoList extends IEventHandler
return this.list.indexOf(value) > -1;
}
toArray()
{
return this.list.slice(0);
}
removeAt(index)
{
if (index >= this.list.length || index < 0)

View File

@ -24,6 +24,8 @@
* Created by Ahmed Zamil on 05/09/2017.
*/
"use strict";
class AutoMap extends IEventHandler
{
constructor()

View File

@ -24,7 +24,9 @@
* Created by Ahmed Zamil on 25/08/2017.
*/
var BL = function(){
"use strict";
function BL(){
return new BinaryList();
};
@ -140,6 +142,16 @@ class BinaryList
return rt;
}
toDC()
{
return new DC(this.toArray());
}
addDateTime(value, position)
{
return this.add({type: DataType.DateTime, value: value}, position);
}
addUint8Array(value, position)
{
return this.add({type: DataType.UInt8Array, value: value}, position);

View File

@ -24,16 +24,17 @@
* Created by Ahmed Zamil on 25/07/2017.
*/
var ResourceComparisonResult =
"use strict";
const ResourceComparisonResult =
{
Null: 0,
Distributed: 1,
DistributedSameClass: 2,
Local: 3,
Same: 4
Local: 2,
Same: 3
};
var StructureComparisonResult =
const StructureComparisonResult =
{
Null: 0,
Structure: 1,
@ -44,6 +45,7 @@ var StructureComparisonResult =
class Codec {
static parse(data, offset, sizeObject, connection, dataType = DataType.Unspecified) {
var size;
@ -215,16 +217,251 @@ class Codec {
return connection.fetch(iid);// Warehouse.Get(iid);
}
/// <summary>
/// Parse an array of bytes into array of resources
/// </summary>
/// <param name="data">Array of bytes.</param>
/// <param name="length">Number of bytes to parse.</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="connection">DistributedConnection is required to fetch resources.</param>
/// <returns>Array of resources.</returns>
static parseResourceArray(data, offset, length, connection)
{
var reply = new AsyncBag();
if (length == 0)
{
reply.seal();
return reply;
}
static parseStructure(data, offset, contentLength, connection, keylist = null, typelist = null, keys = null, types = null) {
var reply = new AsyncReply();
var bag = new AsyncBag();
var end = offset + length;
//
var result = data[offset++];
var previous = null;
if (result == ResourceComparisonResult.Null)
previous = new AsyncReply(null);
else if (result == ResourceComparisonResult.Local)
{
previous = Warehouse.get(data.getUint32(offset));
offset += 4;
}
else if (result == ResourceComparisonResult.Distributed)
{
previous = connection.fetch(data.getUint32(offset));
offset += 4;
}
reply.add(previous);
if (keylist == null)
keylist = [];
if (typelist == null)
typelist = [];
while (offset < end)
{
result = data[offset++];
var current = null;
if (result == ResourceComparisonResult.Null)
{
current = new AsyncReply(null);
}
else if (result == ResourceComparisonResult.Same)
{
current = previous;
}
else if (result == ResourceComparisonResult.Local)
{
current = Warehouse.get(data.getUint32(offset));
offset += 4;
}
else if (result == ResourceComparisonResult.Distributed)
{
current = connection.fetch(data.getUint32(offset));
offset += 4;
}
reply.add(current);
previous = current;
}
reply.seal();
return reply;
}
/// <summary>
/// Compose an array of property values.
/// </summary>
/// <param name="array">PropertyValue array.</param>
/// <param name="connection">DistributedConnection is required to check locality.</param>
/// <param name="prependLength">If True, prepend the length as UInt32 at the beginning of the output.</param>
/// <returns>Array of bytes in the network byte order.</returns>
static composePropertyValueArray(array, connection, prependLength = false)
{
var rt = BL();
for (var i = 0; i < array.Length; i++)
rt.addUint8Array(Codec.composePropertyValue(array[i], connection));
if (prependLength)
rt.addUint32(rt.length, 0);
return rt.toArray();
}
/// <summary>
/// Compose a property value.
/// </summary>
/// <param name="propertyValue">Property value</param>
/// <param name="connection">DistributedConnection is required to check locality.</param>
/// <returns>Array of bytes in the network byte order.</returns>
static composePropertyValue(propertyValue, connection)
{
// age, date, value
return BL().addUint64(propertyValue.age)
.addDateTime(propertyValue.date)
.addUint8Array(Codec.compose(propertyValue.value, connection))
.toArray();
}
/// <summary>
/// Parse property value.
/// </summary>
/// <param name="data">Array of bytes.</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="connection">DistributedConnection is required to fetch resources.</param>
/// <param name="cs">Output content size.</param>
/// <returns>PropertyValue.</returns>
static parsePropertyValue(data, offset, sizeObject, connection)
{
var reply = new AsyncReply();
var age = data.getUint64(offset);
offset += 8;
var date = data.getDateTime(offset);
offset += 8;
var cs = {};
Codec.parse(data, offset, cs, connection).then(function(value)
{
reply.trigger(new PropertyValue(value, age, date));
});
sizeObject.size = 16 + cs.size;
return reply;
}
/// <summary>
/// Parse resource history
/// </summary>
/// <param name="data">Array of bytes.</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="length">Number of bytes to parse.</param>
/// <param name="resource">Resource</param>
/// <param name="fromAge">Starting age.</param>
/// <param name="toAge">Ending age.</param>
/// <param name="connection">DistributedConnection is required to fetch resources.</param>
/// <returns></returns>
static parseHistory(data, offset, length, resource, connection)
{
var list = new KeyList();
var reply = new AsyncReply();
var bagOfBags = new AsyncBag();
var ends = offset + length;
while (offset < ends)
{
var index = data[offset++];
var pt = resource.instance.template.getPropertyTemplateByIndex(index);
list.add(pt, null);
var cs = data.getUint32(offset);
offset += 4;
bagOfBags.add(Codec.parsePropertyValueArray(data, offset, cs, connection));
offset += cs;
}
bagOfBags.seal();
bagOfBags.then(x =>
{
for(var i = 0; i < list.length; i++)
list.values[i] = x[i];
reply.trigger(list);
});
return reply;
}
/// <summary>
/// Compose resource history
/// </summary>
/// <param name="history">History</param>
/// <param name="connection">DistributedConnection is required to fetch resources.</param>
/// <returns></returns>
static composeHistory(history, connection, prependLength = false)
{
var rt = new BinaryList();
for (var i = 0; i < history.length; i++)
rt.addUint8(history.keys[i].index).addUint8Array(Codec.composePropertyValueArray(history.values[i], connection, true));
if (prependLength)
rt.addUint32(rt.length, 0);
return rt.toArray();
}
/// <summary>
/// Parse an array of ProperyValue.
/// </summary>
/// <param name="data">Array of bytes.</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="length">Number of bytes to parse.</param>
/// <param name="connection">DistributedConnection is required to fetch resources.</param>
/// <returns></returns>
static parsePropertyValueArray(data, offset, length, connection)
{
var rt = new AsyncBag();
while (length > 0)
{
var cs = {};
rt.add(Codec.parsePropertyValue(data, offset, cs, connection));
if (cs.size > 0)
{
offset += cs.size;
length -= cs.size;
}
else
throw new Exception("Error while parsing ValueInfo structured data");
}
rt.seal();
return rt;
}
static parseStructure(data, offset, contentLength, connection, metadata = null, keys = null, types = null)
{
var reply = new AsyncReply();
var bag = new AsyncBag();
var keylist = [];
var typelist = [];
if (keys == null) {
while (contentLength > 0) {
@ -249,11 +486,12 @@ class Codec {
var rt = {};
bag.add(Codec.parse(data, offset, rt, connection));
contentLength -= rt.size + 1;
offset += rt.size + 1;
contentLength -= rt.size;
offset += rt.size;
}
}
else {
for (var i = 0; i < keys.length; i++) {
keylist.push(keys[i]);
typelist.push(types[i]);
@ -279,7 +517,12 @@ class Codec {
reply.trigger(s);
});
if (metadata != null)
{
metadata.keys = keylist;
metadata.types = typelist;
}
return reply;
}
@ -308,6 +551,11 @@ class Codec {
static compose(value, connection, prependType = true) {
if (value instanceof Function)
value = value(connection);
else if (value instanceof DistributedPropertyContext)
value = value.method(this);
var type = Codec.getDataType(value, connection);
var rt = new BinaryList();
@ -322,7 +570,7 @@ class Codec {
break;
case DataType.Resource:
rt.addUint32(value.instance.id);
rt.addUint32(value._p.instanceId);
break;
case DataType.DistributedResource:
@ -462,7 +710,7 @@ class Codec {
static isLocalResource(resource, connection) {
if (resource instanceof DistributedResource)
if (resource.connection == connection)
if (resource._p.connection == connection)
return true;
return false;
@ -477,58 +725,47 @@ static isLocalResource(resource, connection) {
}
static compareResource(previous, next, connection) {
if (next == null)
return ResourceComparisonResult.Null;
if (next == previous)
else if (next == previous)
return ResourceComparisonResult.Same;
if (Codec.isLocalResource(next, connection))
else if (Codec.isLocalResource(next, connection))
return ResourceComparisonResult.Local;
if (previous == null)
else
return ResourceComparisonResult.Distributed;
if (previous.instance.template.classId.valueOf() == next.instance.template.classId.valueOf())
return ResourceComparisonResult.DistributedSameClass;
return ResourceComparisonResult.Distributed;
}
static composeResourceArray(resources, connection, prependLength = false) {
if (resources == null || resources.length == 0 || !(resources instanceof ResourceArray))
return new DC(0);
var rt = new BinaryList();
var comparsion = Codec.compareResource(null, resources[0], connection);
if (resources == null || resources.length == 0)// || !(resources instanceof ResourceArray))
return prependLength ? new DC(4) : new DC(0);
rt.addUint8(comparsion);
var rt = new BinaryList();
var comparsion = Codec.compareResource(null, resources[0], connection);
if (comparsion == ResourceComparisonResult.Local)
rt.addUint32(resources[0].id);
else if (comparsion == ResourceComparisonResult.Distributed) {
rt.addUint8Array(resources[0].instance.template.classId.value);
rt.addUint32(resources[0].instance.id);
}
rt.addUint8(comparsion);
for (var i = 1; i < resources.length; i++) {
comparsion = Codec.compareResource(resources[i - 1], resources[i], connection);
rt.addUint8(comparsion);
if (comparsion == ResourceComparisonResult.Local)
rt.addUint32(resources[0].id);
else if (comparsion == ResourceComparisonResult.Distributed) {
rt.addUint8Array(resources[0].instance.template.classId.value);
rt.addUint32(resources[0].instance.id);
}
else if (comparsion == ResourceComparisonResult.DistributedSameClass) {
rt.addUint32(resources[0].instance.id);
}
}
if (comparsion == ResourceComparisonResult.Local)
rt.addUint32(resources[0]._p.instanceId);
else if (comparsion == ResourceComparisonResult.Distributed)
rt.addUint32(resources[0].instance.id);
if (prependLength)
rt.addUint32(0, rt.length);
for (var i = 1; i < resources.Length; i++)
{
comparsion = Codec.compareResource(resources[i - 1], resources[i], connection);
rt.addUint8(comparsion);
if (comparsion == ResourceComparisonResult.Local)
rt.addUint32(resources[i]._p.instanceId);
else if (comparsion == ResourceComparisonResult.Distributed)
rt.addUint32(resources[i].instance.id);
}
return rt.toArray();
if (prependLength)
rt.addUint32(rt.length, 0);
return rt.toArray();
}
@ -619,4 +856,83 @@ static getDataType(value) {
return DataType.Void;
}
}
/// <summary>
/// Parse an array of structures
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset</param>
/// <param name="length">Number of bytes to parse</param>
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end</param>
/// <returns>Array of structures</returns>
static parseStructureArray(data, offset, length, connection)
{
var reply = new AsyncBag();
if (length == 0)
{
reply.seal();
return reply;
}
var end = offset + length;
var result = data[offset++];
var previous = null;
//var previousKeys = [];
//var previousTypes = [];
var metadata = {keys: null, types: null};
if (result == StructureComparisonResult.Null)
previous = new AsyncReply(null);
else if (result == StructureComparisonResult.Structure)
{
var cs = data.getUint32(offset);
offset += 4;
previous = this.parseStructure(data, offset, cs, connection, metadata);
offset += cs;
}
reply.add(previous);
while (offset < end)
{
result = data[offset++];
if (result == StructureComparisonResult.Null)
previous = new AsyncReply(null);
else if (result == StructureComparisonResult.Structure)
{
var cs = data.getUint32(offset);
offset += 4;
previous = this.parseStructure(data, offset, cs, connection, metadata);
offset += cs;
}
else if (result == StructureComparisonResult.StructureSameKeys)
{
var cs = data.getUint32(offset);
offset += 4;
previous = this.parseStructure(data, offset, cs, connection, metadata, metadata.keys);
offset += cs;
}
else if (result == StructureComparisonResult.StructureSameTypes)
{
var cs = data.getUint32(offset);
offset += 4;
previous = this.parseStructure(data, offset, cs, connection, metadata, metadata.keys, metadata.types);
offset += cs;
}
reply.add(previous);
}
reply.seal();
return reply;
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2017-2018 Ahmed Kh. Zamil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Created by Ahmed Zamil on 10/11/2018.
*/
"use strict";
class CustomResourceEvent
{
constructor(issuer, receivers, params)
{
this.issuer = issuer;
this.receivers = receivers;
this.params = params;
}
}

View File

@ -24,8 +24,10 @@
* Created by Ahmed Zamil on 25/07/2017.
*/
var UNIX_EPOCH = 621355968000000000;
var TWO_PWR_32 = (1 << 16) * (1 << 16);
"use strict";
const UNIX_EPOCH = 621355968000000000;
const TWO_PWR_32 = (1 << 16) * (1 << 16);
class DC extends Uint8Array// extends DataView // Data Converter
{
@ -155,7 +157,17 @@ class DC extends Uint8Array// extends DataView // Data Converter
return new DC(rt);
}
static stringArrayToBytes(values)
{
var list = new BinaryList();
for(var i = 0; i < values.length; i++)
{
var s = DC.stringToBytes(values[i]);
list.addUint32(s.length).addUint8Array(s);
}
return list.toArray();
}
append(src, offset, length)
{
@ -438,7 +450,7 @@ class DC extends Uint8Array// extends DataView // Data Converter
getDateTime(offset)
{
var ticks = this.getUint64(offset);
return new Date(Math.round((ticks-DCStatic.UNIX_EPOCH)/10000));
return new Date(Math.round((ticks-UNIX_EPOCH)/10000));
}
getDateTimeArray(offset)
@ -451,7 +463,7 @@ class DC extends Uint8Array// extends DataView // Data Converter
getGuid(offset)
{
return new Guid(this.getUint8Array(offset, 16));
return new Guid(this.clip(offset, 16));
/*
var d = this.getUint8Array(offset, 16);

View File

@ -23,7 +23,11 @@
/**
* Created by Ahmed Zamil on 25/07/2017.
*/
DataType = {
"use strict";
const DataType =
{
Void: 0x0,
//Variant,
Bool: 1,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2017-2018 Ahmed Kh. Zamil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Created by Ahmed Zamil on 27/10/2018.
*/
"use strict";
class DistributedPropertyContext
{
constructor(p1, p2)
{
if(arguments.length == 1)
{
this.method = p1;
}
else if (arguments.length == 2)
{
this.connection = p1;
this.value = p2;
}
}
}

View File

@ -24,18 +24,17 @@
* Created by Ahmed Zamil on 25/07/2017.
*/
"use strict";
class DistributedResource extends IResource
{
destroy()
{
this.destroyed = true;
this._emit("destroy");
this._emit("destroy", this);
}
constructor(connection, template, instanceId, age)
constructor(connection, instanceId, age, link)
{
super();
@ -44,20 +43,38 @@ class DistributedResource extends IResource
connection: connection,
instanceId: instanceId,
age: age,
template: template
link: link,
properties: []
};
}
_serialize()
{
var props = [];
for (var i = 0; i < this._p.properties.length; i++)
props.push(new PropertyValue(this._p.properties[i],
this.instance.getAge(i),
this.instance.getModificationDate(i)));
return props;
}
_attached(properties)
{
if (this._isAttached)
return false;
else
{
this._p.properties = properties;
this._p.ages = new Uint32Array(properties.length);
//this.events = [];//new [this.template.events.length];
{
for(var i = 0; i < properties.length; i++)
{
this.instance.setAge(i, properties[i].age);
this.instance.setModificationDate(i, properties[i].date);
this._p.properties.push(properties[i].value);
}
this._p.isAttached = true;
var self = this;
@ -83,15 +100,15 @@ class DistributedResource extends IResource
};
};
for(var i = 0; i < this._p.template.functions.length; i++)
for(var i = 0; i < this.instance.template.functions.length; i++)
{
var ft = this._p.template.functions[i];
var ft = this.instance.template.functions[i];
this[ft.name] = makeFunc(ft.index);
}
for(var i = 0; i < this._p.template.properties.length; i++)
for(var i = 0; i < this.instance.template.properties.length; i++)
{
var pt = this._p.template.properties[i];
var pt = this.instance.template.properties[i];
Object.defineProperty(this, pt.name, {
get: makeGetter(pt.index),
@ -107,18 +124,21 @@ class DistributedResource extends IResource
_emitEventByIndex(index, args)
{
var et = this._p.template.getEventTemplateByIndex(index);
this._emit(et.name, args);
this.instance.emitResourceEvent(et.name, null, args);
var et = this.instance.template.getEventTemplateByIndex(index);
this._emitArgs(et.name, args);
this.instance._emitResourceEvent(null, null, et.name, args);
}
_invoke(index, args) {
if (this.destroyed)
throw new Exception("Trying to access destroyed object");
if (index >= this._p.template.functions.length)
if (index >= this.instance.template.functions.length)
throw new Exception("Function index is incorrect");
return this._p.connection.sendInvoke(this._p.instanceId, index, args);
/*
var reply = new AsyncReply();
var parameters = Codec.composeVarArray(args, this._p.connection, true);
@ -135,7 +155,7 @@ class DistributedResource extends IResource
return reply;
*/
}
@ -147,12 +167,11 @@ class DistributedResource extends IResource
}
_updatePropertyByIndex(index, value)
{
var pt = this._p.template.getPropertyTemplateByIndex(index);
var pt = this.instance.template.getPropertyTemplateByIndex(index);
this._p.properties[index] = value;
this.instance.modified(pt.name, value);
this.instance.emitModification(pt, value);
}
_set(index, value)
@ -165,15 +184,15 @@ class DistributedResource extends IResource
var parameters = Codec.compose(value, this._p.connection);
var self = this;
this._p.connection.sendRequest(IIPPacketAction.SetProperty,
BL().addUint32(self._p.instanceId).addUint8(index).addUint8Array(parameters))
this._p.connection.sendRequest(IIPPacketAction.SetProperty)
.addUint32(self._p.instanceId).addUint8(index).addUint8Array(parameters)
.done()
.then(function(res)
{
// not really needed, server will always send property modified, this only happens if the programmer forgot to emit in property setter
//Update(index, value);
self._p.properties[index] = value;
reply.trigger(null);
// nothing to do here
});
});
return reply;
}

View File

@ -23,7 +23,10 @@
/**
* Created by Ahmed Zamil on 25/07/2017.
*/
var DistributedResourceQueueItemType =
"use strict";
const DistributedResourceQueueItemType =
{
Propery: 0,
Event: 1

View File

@ -24,6 +24,8 @@
* Created by Ahmed Zamil on 24/08/2017.
*/
"use strict";
class EventTemplate extends MemberTemplate
{

View File

@ -24,6 +24,8 @@
* Created by Ahmed Zamil on 27/08/2017.
*/
"use strict";
class FunctionTemplate extends MemberTemplate {
compose() {
var name = super.compose();
@ -32,12 +34,12 @@ class FunctionTemplate extends MemberTemplate {
if (this.expansion != null) {
var exp = DC.stringToBytes(this.expansion);
return rt.addUint8(0x10 | (IsVoid ? 0x8 : 0x0))
return rt.addUint8(0x10 | (this.isVoid ? 0x8 : 0x0))
.addUint32(exp.length).addUint8Array(exp)
.addUint8(name.length).addUint8Array(name).toArray();
}
else
return rt.addUint8(IsVoid ? 0x8 : 0x0).addUint8(name.length).addUint8Array(name).toArray();
return rt.addUint8(this.isVoid ? 0x8 : 0x0).addUint8(name.length).addUint8Array(name).toArray();
}

View File

@ -23,6 +23,9 @@
/**
* Created by Ahmed Zamil on 02/09/2017.
*/
"use strict";
class Guid
{
constructor(dc)

View File

@ -23,6 +23,9 @@
/**
* Created by Ahmed Zamil on 31/08/2017.
*/
"use strict";
class IDestructible extends IEventHandler
{
destroy()

View File

@ -23,6 +23,7 @@
/**
* Created by Ahmed Zamil on 30/08/2017.
*/
"use strict";
class IEventHandler
{
@ -42,19 +43,32 @@ class IEventHandler
var args = Array.prototype.slice.call(arguments, 1);
if (this._events[event])
for(var i = 0; i < this._events[event].length; i++)
if (this._events[event][i].apply(this, args))
if (this._events[event][i].f.apply(this._events[event][i].i, args))
return true;
return false;
}
on(event, fn)
_emitArgs(event, args)
{
event = event.toLowerCase();
if (this._events[event])
for(var i = 0; i < this._events[event].length; i++)
if (this._events[event][i].f.apply(this._events[event][i].i, args))
return true;
return this;
}
on(event, fn, issuer)
{
if (!(fn instanceof Function))
return this;
event = event.toLowerCase();
// add
if (!this._events[event])
this._events[event] = [];
this._events[event].push(fn);
this._events[event].push({f: fn, i: issuer == null ? this: issuer});
return this;
}
@ -65,9 +79,13 @@ class IEventHandler
{
if (fn)
{
var index = this._events[event].indexOf(fn);
if (index > -1)
this._events[event].splice(index, 1);
for(var i = 0; i < this._events[event].length; i++)
if (this._events[event][i].f == fn)
this._events[event].splice(i--, 1);
//var index = this._events[event].indexOf(fn);
//if (index > -1)
//this._events[event].splice(index, 1);
}
else
{

View File

@ -23,7 +23,10 @@
/**
* Created by Ahmed Zamil on 25/07/2017.
*/
var IIPAuthPacketCommand =
"use strict";
const IIPAuthPacketCommand =
{
Action: 0,
Declare: 1,
@ -31,7 +34,7 @@ var IIPAuthPacketCommand =
Error: 3
};
var IIPAuthPacketAction =
const IIPAuthPacketAction =
{
// Authenticate
AuthenticateHash: 0,
@ -41,7 +44,7 @@ var IIPAuthPacketAction =
};
var IIPAuthPacketMethod =
const IIPAuthPacketMethod =
{
None: 0,
Certificate: 1,

View File

@ -24,27 +24,43 @@
* Created by Ahmed Zamil on 25/07/2017.
*/
"use strict";
var IIPPacketCommand =
const IIPPacketCommand =
{
Event: 0,
Request: 1,
Reply: 2,
Error: 3
Report: 3
};
var IIPPacketEvent =
const IIPPacketReport =
{
ManagementError: 0,
ExecutionError: 1,
ProgressReport: 0x8,
ChunkStream: 0x9
};
const IIPPacketEvent =
{
// Event Manage
ResourceReassigned : 0,
ResourceDestroyed: 1,
ChildAdded: 2,
ChildRemoved: 3,
Renamed: 4,
// Event Invoke
PropertyUpdated : 0x10,
EventOccured: 0x11
EventOccurred: 0x11,
// Attribute
AttributesUpdated: 0x18
};
var IIPPacketAction =
const IIPPacketAction =
{
// Request Manage
AttachResource: 0,
@ -52,25 +68,35 @@ var IIPPacketAction =
DetachResource: 2,
CreateResource: 3,
DeleteResource: 4,
AddChild: 5,
RemoveChild: 6,
RenameResource: 7,
// Request Inquire
TemplateFromClassName: 0x8,
TemplateFromClassId: 0x9,
TemplateFromResourceLink: 0xA,
TemplateFromResourceId: 0xB,
ResourceIdFromResourceLink: 0xC,
TemplateFromClassName: 8,
TemplateFromClassId: 9,
TemplateFromResourceId: 10,
QueryLink: 11,
ResourceHistory: 12,
ResourceChildren: 13,
ResourceParents: 14,
// Request Invoke
InvokeFunction: 0x10,
GetProperty: 0x11,
GetPropertyIfModified: 0x12,
SetProperty: 0x13
InvokeFunction: 16,
GetProperty: 17,
GetPropertyIfModified: 18,
SetProperty: 19,
// Request Attribute
GetAllAttributes: 24,
UpdateAllAttributes: 25,
ClearAllAttributes: 26,
GetAttributes: 27,
UpdateAttributes: 28,
ClearAttributes: 29
};
class IIPPacket
{
constructor()
@ -91,13 +117,14 @@ class IIPPacket
this.methodName = "";
this.callbackId = 0;
this.dataLengthNeeded = 0;
this.originalOffset = 0;
}
notEnough(offset, ends, needed)
{
if (offset + needed > ends)
{
this.dataLengthNeeded = needed - (ends - offset);
this.dataLengthNeeded = needed - (ends - this.originalOffset);
return true;
}
else
@ -106,7 +133,7 @@ class IIPPacket
parse(data, offset, ends)
{
var oOffset = offset;
this.originalOffset = offset;
if (this.notEnough(offset, ends, 1))
return -this.dataLengthNeeded;
@ -123,6 +150,16 @@ class IIPPacket
this.resourceId = data.getUint32(offset);
offset += 4;
}
else if (this.command == IIPPacketCommand.Report)
{
this.report = (data.getUint8(offset++) & 0x3f);
if (this.notEnough(offset, ends, 4))
return -this.dataLengthNeeded;
this.callbackId = data.getUint32(offset);
offset += 4;
}
else
{
this.action = (data.getUint8(offset++) & 0x3f);
@ -149,6 +186,30 @@ class IIPPacket
{
// nothing to parse
}
else if (this.event == IIPPacketEvent.ChildAdded
|| this.event == IIPPacketEvent.ChildRemoved)
{
if (this.notEnough(offset, ends, 4))
return -this.dataLengthNeeded;
this.childId = data.getUint32(offset);
offset += 4;
}
else if(this.event == IIPPacketEvent.Renamed)
{
if (this.notEnough(offset, ends, 2))
return -this.dataLengthNeeded;
var cl = data.getUint16(offset);
offset += 2;
if (this.notEnough(offset, ends, cl))
return -this.dataLengthNeeded;
this.content = data.clip(offset, cl);
offset += cl;
}
else if (this.event == IIPPacketEvent.PropertyUpdated)
{
if (this.notEnough(offset, ends, 2))
@ -182,7 +243,7 @@ class IIPPacket
offset += size;
}
}
else if (this.event == IIPPacketEvent.EventOccured)
else if (this.event == IIPPacketEvent.EventOccurred)
{
if (this.notEnough(offset, ends, 5))
return -this.dataLengthNeeded;
@ -195,6 +256,22 @@ class IIPPacket
this.content = data.clip(offset, cl);
offset += cl;
}
// Attribute
else if (this.event == IIPPacketEvent.AttributesUpdated)
{
if (this.notEnough(offset, ends, 4))
return -this.dataLengthNeeded;
var cl = data.getUint32(offset);
offset += 4;
if (this.notEnough(offset, ends, cl))
return -this.dataLengthNeeded;
this.content = data.clip(offset, cl);
offset += cl;
}
}
else if (this.command == IIPPacketCommand.Request)
{
@ -208,14 +285,14 @@ class IIPPacket
}
else if (this.action == IIPPacketAction.ReattachResource)
{
if (this.notEnough(offset, ends, 8))
if (this.notEnough(offset, ends, 12))
return -this.dataLengthNeeded;
this.resourceId = data.getUint32(offset);
offset += 4;
this.resourceAge = data.getUint32(offset);
offset += 4;
this.resourceAge = data.getUint64(offset);
offset += 8;
}
else if (this.action == IIPPacketAction.DetachResource)
{
@ -228,16 +305,21 @@ class IIPPacket
}
else if (this.action == IIPPacketAction.CreateResource)
{
if (this.notEnough(offset, ends, 1))
return -this.dataLengthNeeded;
if (this.notEnough(offset, ends, 12))
return -dataLengthNeeded;
var cl = data.getUint8(offset++);
this.storeId = data.getUint32(offset);
offset += 4;
this.resourceId = data.getUint32(offset);
offset += 4;
var cl = data.getUint32(offset);
offset += 4;
if (this.notEnough(offset, ends, cl))
return -this.dataLengthNeeded;
return -dataLengthNeeded;
this.className = data.getString(offset, cl);
offset += cl;
this.content = data.clip(offset, cl);
}
else if (this.action == IIPPacketAction.DeleteResource)
{
@ -247,6 +329,37 @@ class IIPPacket
this.resourceId = data.getUint32(offset);
offset += 4;
}
else if (this.action == IIPPacketAction.AddChild
|| this.action == IIPPacketAction.RemoveChild)
{
if (this.notEnough(offset, ends, 8))
return -this.dataLengthNeeded;
this.resourceId = data.getUint32(offset);
offset += 4;
this.childId = data.getUint32(offset);
offset += 4;
}
else if (this.action == IIPPacketAction.RenameResource)
{
if (this.notEnough(offset, ends, 6))
return -this.dataLengthNeeded;
this.resourceId = data.getUint32(offset);
offset += 4;
var cl = data.getUint16(offset);
offset += 2;
if (this.notEnough(offset, ends, cl))
return -this.dataLengthNeeded;
this.content = data.clip(offset, cl);
offset += cl;
}
else if (this.action == IIPPacketAction.TemplateFromClassName)
{
@ -270,20 +383,6 @@ class IIPPacket
this.classId = data.getGuid(offset);
offset += 16;
}
else if (this.action == IIPPacketAction.TemplateFromResourceLink)
{
if (this.notEnough(offset, ends, 2))
return -this.dataLengthNeeded;
var cl = data.getUint16(offset);
offset += 2;
if (this.notEnough(offset, ends, cl))
return -this.dataLengthNeeded;
this.resourceLink = data.getString(offset, cl);
offset += cl;
}
else if (this.action == IIPPacketAction.TemplateFromResourceId)
{
if (this.notEnough(offset, ends, 4))
@ -292,7 +391,7 @@ class IIPPacket
this.resourceId = data.getUint32(offset);
offset += 4;
}
else if (this.action == IIPPacketAction.ResourceIdFromResourceLink)
else if (this.action == IIPPacketAction.QueryLink)
{
if (this.notEnough(offset, ends, 2))
return -this.dataLengthNeeded;
@ -306,6 +405,30 @@ class IIPPacket
this.resourceLink = data.getString(offset, cl);
offset += cl;
}
else if (this.action == IIPPacketAction.ResourceChildren
|| this.action == IIPPacketAction.ResourceParents)
{
if (this.notEnough(offset, ends, 4))
return -this.dataLengthNeeded;
this.resourceId = data.getUint32(offset);
offset += 4;
}
else if (this.action == IIPPacketAction.ResourceHistory)
{
if (this.notEnough(offset, ends, 20))
return -this.dataLengthNeeded;
this.resourceId = data.getUint32(offset);
offset += 4;
this.fromDate = data.getDateTime(offset);
offset += 8;
this.toDate = data.getDateTime(offset);
offset += 8;
}
else if (this.action == IIPPacketAction.InvokeFunction)
{
if (this.notEnough(offset, ends, 9))
@ -347,8 +470,8 @@ class IIPPacket
this.methodIndex = data[offset++];
this.resourceAge = data.getUint32(offset);
offset += 4;
this.resourceAge = data.getUint64(offset);
offset += 8;
}
else if (this.action == IIPPacketAction.SetProperty)
@ -388,6 +511,28 @@ class IIPPacket
offset += size;
}
}
// Attribute
else if (this.action == IIPPacketAction.UpdateAllAttributes
|| this.action == IIPPacketAction.GetAttributes
|| this.action == IIPPacketAction.UpdateAttributes
|| this.action == IIPPacketAction.ClearAttributes)
{
if (this.notEnough(offset, ends, 8))
return -this.dataLengthNeeded;
this.resourceId = data.getUint32(offset);
offset += 4;
var cl = data.getUint32(offset);
offset += 4;
if (this.notEnough(offset, ends, cl))
return -this.dataLengthNeeded;
this.content = data.clip(offset, cl);
offset += cl;
}
}
else if (this.command == IIPPacketCommand.Reply)
{
@ -400,8 +545,8 @@ class IIPPacket
this.classId = data.getGuid(offset);
offset += 16;
this.resourceAge = data.getUint32(offset);
offset += 4;
this.resourceAge = data.getUint64(offset);
offset += 8;
var cl = data.getUint16(offset);
offset+=2;
@ -433,9 +578,6 @@ class IIPPacket
if (this.notEnough(offset, ends, 20))
return -this.dataLengthNeeded;
this.classId = data.GetGuid(offset);
offset += 16;
this.resourceId = data.getUint32(offset);
offset += 4;
@ -446,8 +588,14 @@ class IIPPacket
}
else if (this.action == IIPPacketAction.TemplateFromClassName
|| this.action == IIPPacketAction.TemplateFromClassId
|| this.action == IIPPacketAction.TemplateFromResourceLink
|| this.action == IIPPacketAction.TemplateFromResourceId)
|| this.action == IIPPacketAction.TemplateFromResourceId
|| this.action == IIPPacketAction.QueryLink
|| this.action == IIPPacketAction.ResourceChildren
|| this.action == IIPPacketAction.ResourceParents
|| this.action == IIPPacketAction.ResourceHistory
// Attribute
|| this.action == IIPPacketAction.GetAllAttributes
|| this.action == IIPPacketAction.GetAttributes)
{
if (this.notEnough(offset, ends, 4))
return -this.dataLengthNeeded;
@ -461,20 +609,6 @@ class IIPPacket
this.content = data.clip(offset, cl);
offset += cl;
}
else if (this.action == IIPPacketAction.ResourceIdFromResourceLink)
{
if (this.notEnough(offset, ends, 24))
return -this.dataLengthNeeded;
this.classId = data.getGuid(offset);
offset += 16;
this.resourceId = data.getUint32(offset);
offset += 4;
this.resourceAge = data.getUint32(offset);
offset += 4;
}
else if (this.action == IIPPacketAction.InvokeFunction
|| this.action == IIPPacketAction.GetProperty
|| this.action == IIPPacketAction.GetPropertyIfModified)
@ -513,32 +647,76 @@ class IIPPacket
// nothing to do
}
}
else if (this.command == IIPPacketCommand.Error)
else if (this.command == IIPPacketCommand.Report)
{
// Error
if (this.notEnough(offset, ends, 4))
return -this.dataLengthNeeded;
if (this.report == IIPPacketReport.ManagementError)
{
if (this.notEnough(offset, ends, 2))
return -this.dataLengthNeeded;
this.callbackId = data.getUint32(offset);
this.errorCode = data.getUint16(offset);
offset += 2;
}
else if (this.report == IIPPacketReport.ExecutionError)
{
if (this.notEnough(offset, ends, 2))
return -this.dataLengthNeeded;
if (this.notEnough(offset, ends, 1))
return -this.dataLengthNeeded;
this.errorCode = data.getUint16(offset);
offset += 2;
this.errorCode = data.getUint8(offset++);
if (this.notEnough(offset, ends, 2))
return -this.dataLengthNeeded;
if (this.notEnough(offset, ends, 4))
return -this.dataLengthNeeded;
var cl = data.getUint16(offset);
offset += 2;
var cl = data.getUint32(offset);
offset += 4;
if (this.notEnough(offset, ends, cl))
return -this.dataLengthNeeded;
if (this.notEnough(offset, ends, cl))
return -this.dataLengthNeeded;
this.errorMessage = data.getString(offset, cl);
offset += cl;
}
else if (this.report == IIPPacketReport.ProgressReport)
{
if (this.notEnough(offset, ends, 8))
return -this.dataLengthNeeded;
this.errorMessage = data.getString(offset, cl);
offset += cl;
this.progressValue = data.getInt32(offset);
offset += 4;
this.progressMax = data.getInt32(offset);
offset += 4;
}
else if (this.report == IIPPacketReport.ChunkStream)
{
var dt = data.getUint8(offset++);
var size = DataType.sizeOf(dt);
if (size < 0)
{
if (this.notEnough(offset, ends, 4))
return -this.dataLengthNeeded;
var cl = data.getUint32(offset);
offset += 4;
if (this.notEnough(offset, ends, cl))
return -this.dataLengthNeeded;
this.content = data.clip(offset - 5, cl + 5);
offset += cl;
}
else
{
if (this.notEnough(offset, ends, size))
return -this.dataLengthNeeded;
this.content = data.clip(offset - 1, size + 1);
offset += size;
}
}
}
return offset - oOffset;
return offset - this.originalOffset;
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2017 Ahmed Kh. Zamil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Created by Ahmed Zamil on 16/11/2017.
*/
"use strict";
const ActionType =
{
Attach: 0,
Delete: 1,
Execute: 2,
GetProperty: 3,
SetProperty: 4,
CreateResource: 5,
UpdateAttributes: 6,
InquireAttributes: 7,
AddParent: 8,
RemoveParent: 9,
AddChild: 10,
RemoveChild: 11,
Rename: 12,
ReceiveEvent: 13
};
const Ruling = {
Denied: 0,
Allowed: 1,
DontCare: 2,
};
class IPermissionsManager
{
/// <summary>
/// Check for permission.
/// </summary>
/// <param name="resource">IResource.</param>
/// <param name="session">Caller sessions.</param>
/// <param name="action">Action type</param>
/// <param name="member">Function or property to check for permission.</param>
/// <returns>Allowed or denined.</returns>
applicable(resource, session, action, member, inquirer)
{
}
initialize(settings, resource)
{
}
get settings()
{
}
}

View File

@ -24,7 +24,9 @@
* Created by Ahmed Zamil on 25/07/2017.
*/
var ResourceTrigger =
"use strict";
const ResourceTrigger =
{
Loaded : 0,
Initialize: 1,

View File

@ -24,6 +24,8 @@
* Created by Ahmed Zamil on 25/07/2017.
*/
"use strict";
class IStore extends IResource {
get(path) {
@ -37,6 +39,21 @@ class IStore extends IResource {
}
record(resource, propertyName, value, age, dateTime)
{
}
getRecord(resource, fromDate, toDate)
{
}
remove(resource)
{
}
constructor()
{
super();

View File

@ -23,22 +23,77 @@
/**
* Created by Ahmed Zamil on 29/08/2017.
*/
"use strict";
class Instance extends IEventHandler
{
getAge(index)
{
if (index < this.ages.Count)
if (index < this.ages.length)
return this.ages[index];
else
return 0;
}
setAge(index, value)
{
if (index < this.ages.length)
{
this.ages[index] = value;
if (value > this.instanceAge)
this.instanceAge = value;
}
}
getModificationDate(index)
{
if (index < this.modificationDates.length)
return this.modificationDates[index];
else
return new Date(0);
}
setModificationDate(index, value)
{
if (index < this.modificationDates.length)
{
this.modificationDates[index] = value;
if (value > this.instanceModificationDate)
this.instanceModificationDate = value;
}
}
loadProperty(name, age, modificationDate, value)
{
var pt = this.template.getPropertyTemplateByName(name);
if (pt == null)
return false;
this.resource[name] = value;
this.setAge(pt.index, age);
this.setModificationDate(pt.index, modificationDate);
return true;
}
deserialize(properties)
{
for(var i = 0; i < this.template.properties.length; i++)
this.resource[this.template.properties[i].name] = properties[i];
for (var i = 0; i < properties.length; i++)
{
var pt = this.template.GetPropertyTemplateByIndex(i);
if (pt != null)
{
var pv = properties[i];
this.loadProperty(pt.name, pv.age, pv.date, pv.value);
}
}
return true;
}
@ -47,8 +102,10 @@ class Instance extends IEventHandler
var props = [];
for (var i = 0; i < this.template.properties.length; i++)
props.push(this.resource[this.template.properties[i].name]);
props.push(new PropertyValue(this.resource[this.template.properties[i].name],
this.ages[this.template.properties[i].index],
this.modificationDates[this.template.properties[i].index]));
return props;
}
@ -57,25 +114,38 @@ class Instance extends IEventHandler
return resource instanceof Storable;
}
emitModification(pt, value)
{
this.instanceAge++;
modified(propertyName = null, newValue = null, oldValue = null)
var now = new Date();
this.ages[pt.index] = this.instanceAge;
this.modificationDates[pt.index] = now;
if (pt.recordable)
this.store.record(this.resource, pt.name, value, this.ages[pt.index], now);
super._emit("ResourceModified", this.resource, pt.name, value);
this.resource._emit("modified", pt.name, value);
}
modified(propertyName = null)
{
if (propertyName == null)
propertyName = modified.caller.name;
if (newValue == null)
var val = {};
if (this.getPropertyValue(propertyName, val))
{
var val = {};
if (this.getPropertyValue(propertyName, val))
super._emit("ResourceModified", this.resource, propertyName, val.value, oldValue);
var pt = this.template.getPropertyTemplateByName(propertyName);
this.emitModification(pt, val.value)
}
else
super._emit("ResourceModified", this.resource, propertyName, newValue, oldValue);
}
emitResourceEvent(name, receivers, args)
_emitResourceEvent(issuer, receivers, name, args)
{
super._emit("ResourceEventOccured", this.resource, name, receivers, args);
super._emit("ResourceEventOccurred", this.resource, issuer, receivers, name, args);
}
getPropertyValue(name, resultObject)
@ -92,7 +162,7 @@ class Instance extends IEventHandler
constructor(id, name, resource, store)
constructor(id, name, resource, store, customTemplate = null, age = 0)
{
super();
@ -101,8 +171,14 @@ class Instance extends IEventHandler
this.id = id;
this.name = name;
this.instanceAge = age;
this.instanceModificationDate = new Date(0);
this.children = new AutoList();
this.parents = new AutoList();
this.managers = new AutoList();
this.attributes = new KeyList();
var self = this;
@ -119,24 +195,157 @@ class Instance extends IEventHandler
self._emit("ResourceDestroyed", sender);
});
this.template = Warehouse.getTemplateByType(this.resource.constructor);
if (customTemplate != null)
this.template = customTemplate;
else
this.template = Warehouse.getTemplateByType(this.resource.constructor);
// set ages
this.ages = new Uint32Array(this.template.properties.length);
this.ages = [];
this.modificationDates = [];
for(var i = 0; i < this.template.properties.length; i++)
{
this.ages.push(0);
this.modificationDates.push(new Date(0));
}
// connect events
var makeHandler = function(name, receivers, args)
{
return new function(receivers, args)
{
self.emitResourceEvent(name, receivers, args);
};
};
for (var i = 0; i < this.template.events.length; i++)
this.resource.on(this.template.events[i].name, makeHandler(this.template.events[i].name));
this.resource.on(this.template.events[i].name, this._makeHandler(this.template.events[i].name));
}
}
_makeHandler(name)
{
var self = this;
return function(args)
{
if (args instanceof CustomResourceEvent)
self._emitResourceEvent(args.issuer, args.receivers, name, args.params);
else
self._emitResourceEvent(null, null, name, args);
};
}
/// <summary>
/// Check for permission.
/// </summary>
/// <param name="session">Caller sessions.</param>
/// <param name="action">Action type</param>
/// <param name="member">Function or property to check for permission.</param>
/// <returns>Ruling.</returns>
applicable(session, action, member, inquirer)
{
for (var i = 0; i < this.managers.length; i++)
{
var r = this.managers.item(i).applicable(this.resource, session, action, member, inquirer);
if (r != Ruling.DontCare)
return r;
}
return Ruling.DontCare;
}
removeAttributes(attributes = null)
{
if (attributes == null)
this.attributes.clear();
else
{
for(var i = 0; i < attributes.length; i++)
this.attributes.remove(attributes[i]);
}
return true;
}
getAttributes(attributes = null)
{
var st = new Structure();
if (attributes == null)
{
attributes = this.attributes.keys.slice(0);
attributes.push("managers");
}
for(var i = 0; i < attributes.length; i++)
{
var attr = attributes[i];
if (attr == "name")
st["name"] = this.name;
else if (attr == "managers")
{
var mngrs = new StructureArray();
for(var j = 0; j < this.managers.length; j++)
{
var manager = this.managers.item(j);
var sm = new Structure();
sm["type"] = manager.constructor.name;
sm["settings"] = manager.settings;
mngrs.push(sm);
}
st["managers"] = mngrs;
}
else
st[attr] = this.attributes.item(attr);
}
return st;
}
setAttributes(attributes, clearAttributes = false)
{
if (clearAttributes)
this.attributes.clear();
for (var attr in attributes)
if (attr == "name")
this.name = attributes[attr];
else if (attr == "managers")
{
this.managers.clear();
var mngrs = attributes[attr];
for (var i = 0; i < mngrs.length; i++)
{
var mngr = mngrs[i];
var type = window[mngr];
var settings = mngr["settings"];
var manager = new (Function.prototype.bind.apply(type));
if (manager instanceof IPermissionsManager)
{
manager.initialize(settings, this.resource);
this.managers.add(manager);
}
else
return false;
}
}
else
{
this.attributes.set(attr, attributes[attr]);
}
return true;
}
}

115
src/KeyList.js Normal file
View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2017 Ahmed Kh. Zamil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Created by Ahmed Zamil on 06/11/2017.
*/
"use strict";
class KeyList
{
constructor()
{
this.keys = [];
this.values = [];
}
at(index)
{
return this.values[index];
}
item(key)
{
for(var i = 0; i < this.keys.length; i++)
if (this.keys[i] == key)
return this.values[i];
}
get(key)
{
for(var i = 0; i < this.keys.length; i++)
if (this.keys[i] == key)
return this.values[i];
}
_item_destroyed(sender)
{
for(var i = 0; i < this.values.length; i++)
if (sender == this.values[i])
{
this.removeAt(i);
break;
}
}
add(key, value)
{
this.remove(key);
if (value instanceof IDestructible)
value.on("destroy", this._item_destroyed, this);
this.keys.push(key);
this.values.push(value);
}
contains(key)
{
for(var i = 0; i < this.keys.length; i++)
if (this.keys[i] == key)
return true;
return false;
}
set(key, value)
{
this.remove(key);
this.add(key, value);
}
remove(key)
{
for(var i = 0; i < this.keys.length; i++)
if (key == this.keys[i])
{
this.removeAt(i);
break;
}
}
removeAt(index)
{
if (this.values[index] instanceof IDestructible)
this.values[index].off("destroy", this._item_destroyed);
this.keys.splice(index, 1);
this.values.splice(index, 1);
}
get length()
{
return this.keys.length;
}
}

View File

@ -24,8 +24,9 @@
* Created by Ahmed Zamil on 24/08/2017.
*/
"use strict";
var MemberType = {
const MemberType = {
Function: 0,
Property: 1,
Event: 2

75
src/MemoryStore.js Normal file
View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2017 Ahmed Kh. Zamil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Created by Ahmed Zamil on 12/11/2017.
*/
"use strict";
class MemoryStore extends IStore
{
constructor()
{
super();
this.resources = [];
}
put(resource)
{
this.resources[resource.instance.id] = resource;
}
retrive(id)
{
if (this.resources[resource.instance.id])
return new AsyncReply(this.resources[resource.instance.id]);
else
return new AsyncReply(null);
}
get(resource)
{
return new AsyncReply(null);
}
link(resource)
{
if (resource.instance.store == this)
return this.instance.name + "/" + resource.instance.id;
}
trigger(trigger)
{
return new AsyncReply(true);
}
record(resource, propertyName, value, age, dateTime)
{
}
getRecord(resource, fromDate, toDate)
{
}
}

View File

@ -24,6 +24,8 @@
* Created by Ahmed Zamil on 01/09/2017.
*/
"use strict";
class NetworkBuffer {
constructor() {
this.neededDataLength = 0;

View File

@ -24,6 +24,8 @@
* Created by Ahmed Zamil on 26/08/2017.
*/
"use strict";
class NotModified
{

View File

@ -24,13 +24,14 @@
* Created by Ahmed Zamil on 27/08/2017.
*/
var PropertyPermission = {
"use strict";
const PropertyPermission = {
Read: 1,
Write: 2,
ReadWrite: 3
};
class PropertyTemplate extends MemberTemplate
{
@ -44,12 +45,13 @@ class PropertyTemplate extends MemberTemplate
{
var name = super.compose();
var rt = new BinaryList();
var pv = (this.permission >> 1) | (this.recordable ? 1 : 0);
if (this.writeExpansion != null && this.readExpansion != null)
{
var rexp = DC.stringToBytes(this.readExpansion);
var wexp = DC.stringToBytes(this.writeExpansion);
return rt.addUint8(0x38 | this.permission)
return rt.addUint8(0x38 | pv)
.addUint32(wexp.length)
.addUint8Array(wexp)
.addUint32(rexp.length)
@ -60,7 +62,7 @@ class PropertyTemplate extends MemberTemplate
else if (this.writeExpansion != null)
{
var wexp = DC.stringToBytes(this.writeExpansion);
return rt.addUint8(0x30 | this.permission)
return rt.addUint8(0x30 | pv)
.addUint32(wexp.length)
.addUint8Array(wexp)
.addUint8(name.length)
@ -69,14 +71,14 @@ class PropertyTemplate extends MemberTemplate
else if (this.readExpansion != null)
{
var rexp = DC.stringToBytes(this.readExpansion);
return rt.addUint8(0x28 | this.permission)
return rt.addUint8(0x28 | pv)
.addUint32(rexp.length)
.addUint8Array(rexp)
.addUint8(name.length)
.addUint8Array(name).toArray();
}
else
return rt.addUint8(0x20 | this.permission)
return rt.addUint8(0x20 | pv)
.addUint32(name.length)
.addUint8Array(name).toArray();
}

37
src/PropertyValue.js Normal file
View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2017 Ahmed Kh. Zamil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Created by Ahmed Zamil on 06/11/2017.
*/
"use strict";
class PropertyValue
{
constructor(value, age, date)
{
this.value = value;
this.age = age;
this.date = date;
}
}

View File

@ -24,6 +24,8 @@
* Created by Ahmed Zamil on 26/08/2017.
*/
"use strict";
class ResourceArray extends Array
{
push(value)

View File

@ -20,18 +20,9 @@
* SOFTWARE.
*/
class ResourceTemplate {
"use strict";
getMemberTemplate(member) {
if (member instanceof MethodInfo)
return this.getFunctionTemplate(member.name);
else if (member instanceof EventInfo)
return this.getEventTemplate(member.name);
else if (member instanceof PropertyInfo)
return this.getPropertyTemplate(member.name);
else
return null;
}
class ResourceTemplate {
getEventTemplateByName(eventName) {
for (var i = 0; i < this.events.length; i++)
@ -107,7 +98,7 @@ class ResourceTemplate {
// set guid
this.className = template.namespace + "." + type.prototype.constructor.name;
this.classId = (new DC(sha256.arrayBuffer(this.className))).getGuid(0);
this.classId = SHA256.compute(DC.stringToBytes(this.className)).getGuid(0);
//byte currentIndex = 0;
@ -117,6 +108,7 @@ class ResourceTemplate {
pt.index = i;
pt.readExpansion = template.properties[i].read;
pt.writeExpansion = template.properties[i].write;
pt.recordable = template.properties[i].recordable;
this.properties.push(pt);
}
@ -224,6 +216,7 @@ class ResourceTemplate {
pt.index = propertyIndex++;
var readExpansion = ((data.getUint8(offset) & 0x8) == 0x8);
var writeExpansion = ((data.getUint8(offset) & 0x10) == 0x10);
pt.recordable = ((data.getUint8(offset) & 1) == 1);
pt.permission = ((data.getUint8(offset++) >> 1) & 0x3);
pt.name = data.getString(offset + 1, data.getUint8(offset));// Encoding.ASCII.getString(data, (int)offset + 1, data.getUint8(offset));
offset += data.getUint8(offset) + 1;

177
src/SHA256.js Normal file
View File

@ -0,0 +1,177 @@
/*
* Copyright (c) 2017 Ahmed Kh. Zamil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Created by Ahmed Zamil on 25/12/2017.
* Ref: https://en.wikipedia.org/wiki/SHA-2
*/
class SHA256
{
static RROT(n, d)
{
return (n >>> d)|(n << (32 - d));
}
static compute(msg)
{
/*
Note 1: All variables are 32 bit unsigned integers and addition is calculated modulo 2^32
Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 i 63
Note 3: The compression function uses 8 working variables, a through h
Note 4: Big-endian convention is used when expressing the constants in this pseudocode,
and when parsing message block data from bytes to words, for example,
the first word of the input message "abc" after padding is 0x61626380
*/
// Initialize hash values:
// (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
const hash = new Uint32Array([0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]);
// Initialize array of round constants:
// (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
const k = new Uint32Array([
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]);
// Pre-processing:
// begin with the original message of length L bits
var L = msg.length * 8;
// append a single '1' bit
// append K '0' bits, where K is the minimum number >= 0 such that L + 1 + K + 64 is a multiple of 512
var K = 512 - ((L + 1 + 64) % 512);
if (K == 512)
K = 0;
var paddingLength = (K + 1) / 8;
var paddingBytes = new Uint8Array(paddingLength);
paddingBytes[0] = 0x80;
var data = new DC(BL().addUint8Array(msg).addUint8Array(paddingBytes).addUint64(L).toArray());
// append L as a 64-bit big-endian integer, making the total post-processed length a multiple of 512 bits
// Process the message in successive 512-bit chunks:
// break message into 512-bit chunks
// for each chunk
for(var chunk = 0; chunk < data.length; chunk+=64)
{
// create a 64-entry message schedule array w[0..63] of 32-bit words
// (The initial values in w[0..63] don't matter, so many implementations zero them here)
// copy chunk into first 16 words w[0..15] of the message schedule array
var w = new Uint32Array(64);
for(var i = 0; i < 16; i++)
w[i] = data.getInt32(chunk + (i * 4));
//for(var i = 16; i < 64; i++)
// w[i] = 0;
// Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array:
// for i from 16 to 63
// s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3)
// s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10)
// w[i] := w[i-16] + s0 + w[i-7] + s1
for (var i = 16; i < 64; i++)
{
var s0 = SHA256.RROT(w[i-15], 7) ^ SHA256.RROT(w[i-15], 18) ^ (w[i-15] >>> 3);
var s1 = SHA256.RROT(w[i-2], 17) ^ SHA256.RROT(w[i-2], 19) ^ (w[i-2] >>> 10);
w[i] = w[i-16] + s0 + w[i-7] + s1;
}
// Initialize working variables to current hash value:
var a = hash[0];
var b = hash[1];
var c = hash[2];
var d = hash[3];
var e = hash[4];
var f = hash[5];
var g = hash[6];
var h = hash[7];
// Compression function main loop:
for (var i = 0; i < 64; i++)
{
var S1 = SHA256.RROT(e, 6) ^ SHA256.RROT(e, 11) ^ SHA256.RROT(e, 25);
var ch = (e & f) ^ ((~e) & g);
var temp1 = h + S1 + ch + k[i] + w[i];
var S0 = SHA256.RROT(a, 2) ^ SHA256.RROT(a, 13) ^ SHA256.RROT(a, 22);
var maj = (a & b) ^ (a & c) ^ (b & c);
var temp2 = S0 + maj;
h = g;
g = f;
f = e;
e = (d + temp1) >>> 0;
d = c;
c = b;
b = a;
a = (temp1 + temp2) >>> 0;
}
// Add the compressed chunk to the current hash value:
hash[0] = (hash[0] + a) >>> 0;
hash[1] = (hash[1] + b) >>> 0;
hash[2] = (hash[2] + c) >>> 0;
hash[3] = (hash[3] + d) >>> 0;
hash[4] = (hash[4] + e) >>> 0;
hash[5] = (hash[5] + f) >>> 0;
hash[6] = (hash[6] + g) >>> 0;
hash[7] = (hash[7] + h) >>> 0;
}
// Produce the final hash value (big-endian):
//digest := hash := h0 append h1 append h2 append h3 append h4 append h5 append h6 append h7
var results = new BinaryList();
for(var i = 0; i < 8; i++)
results.addUint32(hash[i]);
return results.toDC();
}
}

View File

@ -24,16 +24,20 @@
* Created by Ahmed Zamil on 02/09/2017.
*/
"use strict";
class SendList extends BinaryList
{
constructor(connection)
constructor(connection, doneReply)
{
super();
this.connection = connection;
this.reply = doneReply;
}
done()
{
this.connection.send(this.toArray());
return this.reply;
}
}

41
src/Session.js Normal file
View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2017 Ahmed Kh. Zamil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Created by Ahmed Zamil on 16/11/2017.
*/
"use strict";
class Session
{
constructor(localAuthentication, remoteAuthentication)
{
this.localAuthentication = localAuthentication;
this.remoteAuthentication = remoteAuthentication;
this.id = null;
this.creation = null;
this.modification = null;
}
}

View File

@ -24,6 +24,8 @@
* Created by Ahmed Zamil on 26/08/2017.
*/
"use strict";
class Structure
{
getKeys() {

View File

@ -24,6 +24,8 @@
* Created by Ahmed Zamil on 06/09/2017.
*/
"use strict";
class StructureArray extends Array
{
push(value)

View File

@ -23,33 +23,112 @@
/**
* Created by Ahmed Zamil on 25/07/2017.
*/
var Warehouse = {
stores: [],
resources: {},
resourceCounter: 0,
templates: {},
"use strict";
new(type, name, store = null, parent = null)
class Warehouse
{
static new(type, name, store = null, parent = null, manager = null)
{
var res = type();
Warehouse.put(res, name, store, parent);
Warehouse.put(res, name, store, parent, null, 0, manager);
return res;
},
}
get: function(id)
static get(id)
{
if (Warehouse.resources[id])
return new AsyncReply(Warehouse.resources[id]);
if (Number.isInteger(id))
{
//if (Warehouse.resources.contains(id))
return new AsyncReply(Warehouse.resources.item(id));
//else
// return null;
}
else
return null;
},
{
var p = id.split('/');
var res = null;
put: function(resource, name, store, parent){
resource.instance = new Instance(Warehouse.resourceCounter++, name, resource, store);
for(var s = 0; s < this.stores.length; s++)
{
var d = this.stores.at(s);
if (p[0] == d.instance.name)
{
var i = 1;
res = d;
while(p.length > i)
{
var si = i;
for (var r = 0; r < res.instance.children.length; r++)
if (res.instance.children.item(r).instance.name == p[i])
{
i++;
res = res.instance.children.item(r);
break;
}
if (si == i)
// not found, ask the store
return d.get(id.substring(p[0].length + 1));
}
return new AsyncReply(res);
}
}
return new AsyncReply(null);
}
}
static remove(resource)
{
if (Warehouse.resources.contains(resource.instance.id))
Warehouse.resources.remove(resource.instance.id);
else
return false;
if (resource instanceof IStore)
{
Warehouse.stores.remove(resource);
// remove all objects associated with the store
var toBeRemoved = null;
for (var i = 0; i < Warehouse.resources.length; i++)
{
var o = Warehouse.resources.at(i);
if (o.instance.store == resource)
{
if (toBeRemoved == null)
toBeRemoved = [];
toBeRemoved.push(o);
}
}
if (toBeRemoved != null)
for(var i = 0; i < toBeRemoved.length; i++)
Warehouse.remove(toBeRemoved[i]);
}
if (resource.instance.store != null)
resource.instance.store.remove(resource);
resource.destroy();
return true;
}
static put(resource, name, store, parent, customTemplate = null, age = 0, manager = null){
resource.instance = new Instance(Warehouse.resourceCounter++, name, resource, store, customTemplate, age);
//resource.instance.children.on("add", Warehouse._onChildrenAdd).on("remove", Warehouse._onChildrenRemove);
//resource.instance.parents.on("add", Warehouse._onParentsAdd).on("remove", Warehouse._onParentsRemove);
if (manager != null)
resource.instance.managers.add(manager);
if (parent)
{
parent.instance.children.add(resource);
@ -61,69 +140,101 @@ get: function(id)
}
if (resource instanceof IStore)
Warehouse.stores.push(resource);
Warehouse.stores.add(resource);
else
store.put(resource);
Warehouse.resources[resource.instance.id] = resource;
},
Warehouse.resources.add(resource.instance.id, resource);
}
_onParentsRemove: function(value)
static _onParentsRemove(value)
{
if (value.instance.children.contains(value))
value.instance.children.remove(value);
},
}
_onParentsAdd: function(value)
static _onParentsAdd(value)
{
if (!value.instance.children.contains(value))
value.instance.children.add(value);
},
}
_onChildrenRemove: function(value)
static _onChildrenRemove(value)
{
if (value.instance.parents.contains(value))
value.instance.parents.remove(value);
},
}
_onChildrenAdd: function(value)
static _onChildrenAdd(value)
{
if (!value.instance.parents.contains(value))
value.instance.parents.add(value);
},
}
putTemplate: function(template)
static putTemplate(template)
{
if (Warehouse.templates[template.classId])
Warehouse.templates[template.classId] = template;
},
Warehouse.templates.add(template.classId.valueOf(), template);
}
getTemplateByType: function(type)
static getTemplateByType(type)
{
// loaded ?
for (var t in Warehouse.templates)
if (Warehouse.templates[t].className == typeof(type))
return t;
for (var i = 0; i < Warehouse.templates.length; i++)
if (Warehouse.templates.at(i).className == typeof(type))
return Warehouse.templates.at(i);
var template = new ResourceTemplate(type);
Warehouse.templates[template.classId] = template;
Warehouse.templates.add(template.classId.valueOf(), template);
return template;
},
getTemplateByClassId: function(classId)
{
if (Warehouse.templates[classId])
return new AsyncReply(Warehouse.templates[classId]);
return null;
},
getTemplateByClassName: function(className)
{
for(var t in Warehouse.templates)
if (Warehouse.templates[t].className == className)
return new AsyncReply(t);
return null;
}
};
static getTemplateByClassId(classId)
{
var template = Warehouse.templates.item(classId);
return new AsyncReply(template);
}
static getTemplateByClassName(className)
{
for(var i = 0; i < Warehouse.templates.length; i++)
if (Warehouse.templates.at(i).className == className)
return new AsyncReply(Warehouse.templates.at(i));
return new AsyncReply(null);
}
static _qureyIn(path, index, resources)
{
var rt = [];
if (index == path.length - 1)
{
if (path[index] == "")
for(var i = 0; i < resources.length; i++)
rt.push(resources.at(i));
else
for(var i = 0; i < resources.length; i++)
if (resources.at(i).instance.name == path[index])
rt.push(resources.at(i));
}
else
for(var i = 0; i < resources.length; i++)
if (resources.at(i).instance.name == path[index])
rt = rt.concat(Warehouse._qureyIn(path, index+1, resources.at(i).instance.children));
return rt;
}
static query(path)
{
var p = path.split('/');
return new AsyncReply(Warehouse._qureyIn(p, 0, Warehouse.stores));
}
}
// Initialize
Warehouse.stores = new AutoList();
Warehouse.resources = new KeyList();
Warehouse.resourceCounter = 0;
Warehouse.templates = new KeyList();

30
test.js Normal file
View File

@ -0,0 +1,30 @@
const http = require('http');
const esiur = require('./build/esiur-debug-node.js');
var IIP_LINK = "ws://media.delta.iq:5001/iip/system/iip";
var connection = new esiur.DistributedConnection(IIP_LINK, "sawadland", "guest", "guest");
esiur.Warehouse.put(connection, "remote");
connection.on("ready", function (d) {
console.log(d);
connection.get("vod/home").then(function(rt){
console.log("Home");
rt.getMyList().then(x => x.map(y=>console.log(y.name)));
})});
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World\n');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

77
test/app.js Normal file
View File

@ -0,0 +1,77 @@
"use strict";
var demo = null;
class JSResource extends IResource
{
static getTemplate()
{
return {
namespace: "JS",
properties: [{name: "message", recordable: true}],
functions: [{name: "send", void: true, expansion: null}],
events: [{name: "published", expansion: null}]
}
}
constructor()
{
super();
this.message = "hi";
}
send(message)
{
console.log(message);
}
}
function init() {
var local = new MemoryStore();
var con = new DistributedConnection("ws://localhost:5001/iip/system/iip", "localhost", "demo", "1234");
Warehouse.put(con, "remote");
Warehouse.put(local, "local");
Warehouse.put(new JSResource(), "js", local);
con.on("ready", async function (d) {
// var list = await con.query("").task;
// console.log(list);
// debugger;
Warehouse.get("remote/db/my").then(async function(rt){
console.log("Object received.");
demo = rt;
rt.on("LevelUp", function(v){
console.log("LevelUp", v);
});
con.getRecord(rt, new Date(new Date() - 60000000), new Date()).then(function(h){
console.log(h);
});
var l = await rt.Subtract(3).task;
console.log("Await", l);
//rt.Stream(10).then(x=> console.log("finished S"))
// .chunk(x => console.log ("chunk", x))
// .progress((x,y,z) => console.log("progress", x, y, z));
//rt.Enum(4).then(x=> console.log("Enum finished"))
// .chunk(x => console.log ("Enum chunk", x))
// .progress((x,y,z) => console.log("progress", x, y, z));
});
}).on("error", function(sender, code, msg){
console.log(sender, code, msg);
});
}

5
test/import.js Normal file
View File

@ -0,0 +1,5 @@
"use strict";
export * from "../src/IEventHandler.js";
export * from "../src/IDestructible.js";
export * from "../src/IResource.js";

View File

@ -1,10 +1,11 @@
<html>
<head>
<script src="../external/sha256.min.js"></script>
<script src="../src/IEventHandler.js"></script>
<script src="../src/IDestructible.js"></script>
<script src="../src/AutoList.js"></script>
<script src="../src/KeyList.js"></script>
<script src="../src/PropertyValue.js"></script>
<script src="../src/IResource.js"></script>
<script src="../src/IStore.js"></script>
<script src="../src/Structure.js"></script>
@ -14,12 +15,19 @@
<script src="../src/MemberTemplate.js"></script>
<script src="../src/AsyncReply.js"></script>
<script src="../src/AsyncException.js"></script>
<script src="../src/Authentication.js"></script>
<script src="../src/Session.js"></script>
<script src="../src/IPermissionsManager.js"></script>
<script src="../src/AsyncBag.js"></script>
<script src="../src/AsyncQueue.js"></script>
<script src="../src/BinaryList.js"></script>
<script src="../src/Codec.js"></script>
<script src="../src/DataConverter.js"></script>
<script src="../src/SHA256.js"></script>
<script src="../src/DataType.js"></script>
<script src="../src/DistributedConnection.js"></script>
<script src="../src/DistributedResource.js"></script>
@ -37,34 +45,12 @@
<script src="../src/ResourceTemplate.js"></script>
<script src="../src/SendList.js"></script>
<script src="../src/Warehouse.js"></script>
<script>
var demo;
<script src="../src/MemoryStore.js"></script>
<script src="app.js"></script>
<script>
class MyStore extends IStore
{
}
function init() {
var con = new DistributedConnection("ws://localhost:5001/iip/system/iip", "localhost", "ahmed", "password");
Warehouse.put(con, "remote");
con.on("ready", function (d) {
con.get("db/my").then(function(rt){
console.log("Object received.");
demo = rt;
rt.on("LevelUp", function(v){
console.log("LevelUp", v);
});
rt.Add(3);
});
}).on("error", function(sender, code, msg){
console.log(sender, code, msg);
});
}
</script>
</script>
</head>
<body onload="init()">

2337
tools/manager/css/font-awesome.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,67 @@
.app
{
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}
.app-tool-bar
{
display: flex;
width: 100%;
height: 60px;
background-color: var(--default-background-color);
}
.app-center
{
flex: 1;
width: 100%;
display: flex;
background-color: yellow;
}
.app-content
{
flex: 1;
}
.app-menu
{
min-width: 300px;
background-color: red;
}
.app-status-bar
{
height: 60px;
background-color: blue;
}
.browser
{
display: flex;
flex-wrap: wrap;
}
.browser > .bar
{
width: 100%;
flex-grow:2;
height: 40px;
}
.browser > .table:nth-child(2)
{
width: 200px;
height: calc(100% - 40px);
}
.browser > .table:nth-child(3)
{
width: calc(100% - 200px);
height: calc(100% - 40px);
}

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

134
tools/manager/index.html Normal file
View File

@ -0,0 +1,134 @@
<html>
<head>
<script src="../../src/IEventHandler.js"></script>
<script src="../../src/IDestructible.js"></script>
<script src="../../src/AutoList.js"></script>
<script src="../../src/KeyList.js"></script>
<script src="../../src/PropertyValue.js"></script>
<script src="../../src/IResource.js"></script>
<script src="../../src/IStore.js"></script>
<script src="../../src/Structure.js"></script>
<script src="../../src/StructureArray.js"></script>
<script src="../../src/ResourceArray.js"></script>
<script src="../../src/MemberTemplate.js"></script>
<script src="../../src/AsyncReply.js"></script>
<script src="../../src/AsyncException.js"></script>
<script src="../../src/Authentication.js"></script>
<script src="../../src/Session.js"></script>
<script src="../../src/IPermissionsManager.js"></script>
<script src="../../src/AsyncBag.js"></script>
<script src="../../src/AsyncQueue.js"></script>
<script src="../../src/BinaryList.js"></script>
<script src="../../src/Codec.js"></script>
<script src="../../src/DataConverter.js"></script>
<script src="../../src/SHA256.js"></script>
<script src="../../src/DataType.js"></script>
<script src="../../src/DistributedConnection.js"></script>
<script src="../../src/DistributedResource.js"></script>
<script src="../../src/DistributedResourceQueueItem.js"></script>
<script src="../../src/EventTemplate.js"></script>
<script src="../../src/FunctionTemplate.js"></script>
<script src="../../src/Guid.js"></script>
<script src="../../src/IIPAuthPacket.js"></script>
<script src="../../src/IIPPacket.js"></script>
<script src="../../src/Instance.js"></script>
<script src="../../src/NetworkBuffer.js"></script>
<script src="../../src/NotModified.js"></script>
<script src="../../src/PropertyTemplate.js"></script>
<script src="../../src/ResourceTemplate.js"></script>
<script src="../../src/SendList.js"></script>
<script src="../../src/Warehouse.js"></script>
<script src="../../src/MemoryStore.js"></script>
<link href="/iui-js/iui.css" rel="stylesheet">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link href="css/style.css" rel="stylesheet">
<script src="/iui-js/iui.js"></script>
<script src="viewers/Esiur.Stores.MemoryStore.js"></script>
<script src="js/browser.js"></script>
<script src="js/app.js"></script>
<script>
</script>
</head>
<body onload="init()">
<div class="app">
<div class="app-tool-bar">
<div class="button-flat" style="width: 50px; padding: 10px">
<i class="fa fa-bars fa-3x" aria-hidden="true"></i>
</div>
<div style="font-size: 30px; padding: 10px; width: 280px">
Resource Manager
</div>
<div style="padding: 10px; font-size: 20px;">
<input type="search" style="font-size: 25px">
</div>
</div>
<div class="app-center">
<div id="treeList" class="app-menu">
</div>
<div class="app-content" id="divContent">
</div>
</div>
<div class="app-status-bar">
</div>
</div>
<div id="dlgLogin">
<table class="dialog-body">
<tr>
<td>Server URL:</td>
<td><input id="txtURL" type="text" class="text" value="ws://localhost:5001/iip/system/iip"></td>
</tr>
<tr>
<td>Domain:</td>
<td><input id="txtDomain" type="text" class="text" value="localhost"></td>
</tr>
<tr>
<td>Username:</td>
<td><input id="txtUsername" type="text" class="text" value="admin"></td>
</tr>
<tr>
<td>Password:</td>
<td><input id="txtPassword" type="password" class="text" value="dijtel"></td>
</tr>
</table>
<div class="dialog-footer">
<button class="button" onclick="connect()">
Connect
</button>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,4 @@
class IUIView extends IUIWidget
{
}

30
tools/manager/js/app.js Normal file
View File

@ -0,0 +1,30 @@
function init()
{
iui("dlgLogin").dialog().show();
var local = new MemoryStore();
Warehouse.put(local, "local");
iui("divContent").browser();
}
function connect()
{
var url = document.getElementById("txtURL").value;
var domain = document.getElementById("txtDomain").value;
var username = document.getElementById("txtUsername").value;
var password = document.getElementById("txtPassword").value;
iui("dlgLogin").setLoading(true);
var con = new DistributedConnection(url, domain, username, password);
Warehouse.put(con, "remote");
con.on("ready", function (d) {
iui("divContent").setConnection(con);
iui("dlgLogin").hide();
}).on("error", function(sender, code, msg){
console.log(sender, code, msg);
});
}

132
tools/manager/js/browser.js Normal file
View File

@ -0,0 +1,132 @@
class ResourceBrowser extends IUIWidget
{
loadClass(className)
{
var rt = new AsyncReply();
if (this.scripts[className] != null)
{
rt.trigger(true);
}
else
{
var script = document.createElement('script');
var self = this;
script.onload = function () {
self.scripts[classsName] = script;
rt.trigger(true);
};
script.src = className + ".js";
document.head.appendChild(script);
}
}
constructor(el, properties)
{
super(el, IUI.extend(properties,
{
customClass: "browser",
visible: false
})
);
var treeLayout = [
{width: '200px', field: "instance", title: "Name", formatter: function(d, v){
if(v.instance.attributes.item("name") == null)
return v.instance.name;
else
return v.instance.attributes.item("name");
}},
];
/*
var contentLayout = [
{field: "instance", title: "Name", formatter: function(d, v){
return v.instance.attributes.item("name");
}},
{field: "instance", title: "Type", formatter: function(d, v){
return v.instance.template.name;
}}
];
*/
this.tree = iui(document.createElement("div")).table({tree: true, layout: treeLayout});
// this.content = iui(document.createElement("div")).table({layout: contentLayout});
this.bar = iui(document.createElement("div")).bar();
this.el.classList.add(this.customClass);
this.el.appendChild(this.bar.el);
this.el.appendChild(this.tree.el);
// this.el.appendChild(this.content.el);
var self = this;
this.tree.on("dblclick", function(item){
self.tree.clear();
self.connection.getAttributes(item, ["name", "parents", "type"]).then(function(attrs){
self.tree.add(item);
// self.content.add(item);
});
}).on("expand", function(e){
self.connection.getAttributes(e.item, ["children"]).then(function(attrs){
attrs.children.forEach(function(child){
self.connection.getAttributes(child, ["name", "parents", "childrenCount", "type"]).then(function(cAttars){
self.tree.add(child, cAttars.childrenCount > 0);
e.success();
});
});
});
});
}
setConnection(connection, query = "")
{
var self = this;
this.connection = connection;
this.connection.query(query).then(function(rt)
{
rt.forEach(function(item){
connection.getAttributes(item, ["name", "childrenCount", "type"]).then(function(attrs){
//connection.getAttributes(item, ["name", "children", "type"]).then(function(attrs){
self.tree.add(item, attrs.childrenCount > 0);
/*
var children = attrs.children;
for(var i = 0; i < children.length; i++)
{
if (!children[i].instance.attributes.contains("parents"))
children[i].instance.attributes.add("parents", []);
children[i].instance.attributes.item("parents").push(item);
self.tree.add(children[i]);
}
*/
});
});
/*
for(var i = 0; i < rt.length; i++)
{
var item = rt[i];
connection.getAttributes(rt[i]).then(function(attrs){
self.tree.add(rt[i]);
self.content.add(rt[i]);
});
}
*/
});
}
}
IUI.module("browser", ResourceBrowser);

View File

@ -0,0 +1,9 @@
class Esiur_Stores_MemoryStore extends IUIWidget
{
constuctor()
{
}
}
IUI.module("Esiur.Stores.MemoryStore", Esiur_Stores_MemoryStore);