@sawa-siemens/socket.io-stream 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nvmrc +1 -0
- package/.travis.yml +28 -0
- package/.zuul.yml +6 -0
- package/LICENSE +21 -0
- package/README.md +219 -0
- package/index.d.ts +236 -0
- package/index.js +3 -0
- package/lib/blob-read-stream.js +67 -0
- package/lib/index.js +77 -0
- package/lib/iostream.js +265 -0
- package/lib/parser.js +105 -0
- package/lib/socket.js +287 -0
- package/lib/uuid.js +25 -0
- package/package.json +60 -0
- package/socket.io-stream.js +4 -0
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
v20.19.4
|
package/.travis.yml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
language: node_js
|
|
2
|
+
install:
|
|
3
|
+
- npm install
|
|
4
|
+
- make install
|
|
5
|
+
node_js:
|
|
6
|
+
- 0.10
|
|
7
|
+
- 0.12
|
|
8
|
+
- 4.2
|
|
9
|
+
sudo: false
|
|
10
|
+
env:
|
|
11
|
+
global:
|
|
12
|
+
- secure: BOJOcmv3/8m1e2W7c4MStir5VraXxCeXlslW1xg926FUFry4kayWJhG9FD9GzWopVpeERAHPvr63pou7ylp7m2lkt1zUnacicKdTtXrdSlq2518LasnGJSxfFB59JOTdmdHn/gBAZKrhLTtsYw+mU6AanctXE/NyCVsSZXAig1Y=
|
|
13
|
+
- secure: JoPDfwbrYEKY96XjVc59SSZ/cJ7ll2Vj2vmXqVk0yIwQiJdoXEHg3vwgZRPyCztwXlkjyQsLSLuJN1LSA/b04V8UdreAIILIbSazpQFDjH2ize18v6EVtyvP1PbQoicOsbLON8GdZGfP4kCJ4VNdr8JWLlpHS2Ef/Y0eCIQOxN0=
|
|
14
|
+
matrix:
|
|
15
|
+
- SOCKETIO_VERSION=
|
|
16
|
+
- SOCKETIO_VERSION=0.9
|
|
17
|
+
matrix:
|
|
18
|
+
include:
|
|
19
|
+
- node_js: 0.12
|
|
20
|
+
env: BROWSER_NAME=chrome BROWSER_VERSION=latest
|
|
21
|
+
- node_js: 0.12
|
|
22
|
+
env: BROWSER_NAME=safari BROWSER_VERSION=latest
|
|
23
|
+
- node_js: 0.12
|
|
24
|
+
env: BROWSER_NAME=ie BROWSER_VERSION=latest
|
|
25
|
+
- node_js: 0.12
|
|
26
|
+
env: BROWSER_NAME=iphone BROWSER_VERSION=9.0
|
|
27
|
+
- node_js: 0.12
|
|
28
|
+
env: BROWSER_NAME=android BROWSER_VERSION=5.1
|
package/.zuul.yml
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015 Naoyuki Kanezawa
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Socket.IO stream
|
|
2
|
+
|
|
3
|
+
[](https://travis-ci.org/nkzawa/socket.io-stream)
|
|
4
|
+
[](http://badge.fury.io/js/socket.io-stream)
|
|
5
|
+
|
|
6
|
+
This is the module for bidirectional binary data transfer with Stream API through [Socket.IO](https://github.com/socketio/socket.io).
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
npm install socket.io-stream
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
If you are not familiar with Stream API, be sure to check out [the docs](http://nodejs.org/api/stream.html).
|
|
15
|
+
I also recommend checking out the awesome [Stream Handbook](https://github.com/substack/stream-handbook).
|
|
16
|
+
|
|
17
|
+
For streaming between server and client, you will send stream instances first.
|
|
18
|
+
To receive streams, you just wrap `socket` with `socket.io-stream`, then listen any events as usual.
|
|
19
|
+
|
|
20
|
+
Server:
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
var io = require('socket.io').listen(80);
|
|
24
|
+
var ss = require('socket.io-stream');
|
|
25
|
+
var path = require('path');
|
|
26
|
+
|
|
27
|
+
io.of('/user').on('connection', function(socket) {
|
|
28
|
+
ss(socket).on('profile-image', function(stream, data) {
|
|
29
|
+
var filename = path.basename(data.name);
|
|
30
|
+
stream.pipe(fs.createWriteStream(filename));
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`createStream()` returns a new stream which can be sent by `emit()`.
|
|
36
|
+
|
|
37
|
+
Client:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
var io = require('socket.io-client');
|
|
41
|
+
var ss = require('socket.io-stream');
|
|
42
|
+
|
|
43
|
+
var socket = io.connect('http://example.com/user');
|
|
44
|
+
var stream = ss.createStream();
|
|
45
|
+
var filename = 'profile.jpg';
|
|
46
|
+
|
|
47
|
+
ss(socket).emit('profile-image', stream, {name: filename});
|
|
48
|
+
fs.createReadStream(filename).pipe(stream);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
You can stream data from a client to server, and vice versa.
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
// send data
|
|
55
|
+
ss(socket).on('file', function(stream) {
|
|
56
|
+
fs.createReadStream('/path/to/file').pipe(stream);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// receive data
|
|
60
|
+
ss(socket).emit('file', stream);
|
|
61
|
+
stream.pipe(fs.createWriteStream('file.txt'));
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Browser
|
|
65
|
+
|
|
66
|
+
This module can be used on the browser. To do so, just copy a file to a public directory.
|
|
67
|
+
|
|
68
|
+
$ cp node_modules/socket.io-stream/socket.io-stream.js somewhere/public/
|
|
69
|
+
|
|
70
|
+
You can also use [browserify](http://github.com/substack/node-browserify) to create your own bundle.
|
|
71
|
+
|
|
72
|
+
$ npm install browserify -g
|
|
73
|
+
$ cd node_modules/socket.io-stream
|
|
74
|
+
$ browserify index.js -s ss > socket.io-stream.js
|
|
75
|
+
|
|
76
|
+
```html
|
|
77
|
+
<input id="file" type="file" />
|
|
78
|
+
|
|
79
|
+
<script src="/socket.io/socket.io.js"></script>
|
|
80
|
+
<script src="/js/socket.io-stream.js"></script>
|
|
81
|
+
<script src="/js/jquery.js"></script>
|
|
82
|
+
<script>
|
|
83
|
+
$(function() {
|
|
84
|
+
var socket = io.connect('/foo');
|
|
85
|
+
|
|
86
|
+
$('#file').change(function(e) {
|
|
87
|
+
var file = e.target.files[0];
|
|
88
|
+
var stream = ss.createStream();
|
|
89
|
+
|
|
90
|
+
// upload a file to the server.
|
|
91
|
+
ss(socket).emit('file', stream, {size: file.size});
|
|
92
|
+
ss.createBlobReadStream(file).pipe(stream);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
</script>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### Upload progress
|
|
99
|
+
|
|
100
|
+
You can track upload progress like the following:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
var blobStream = ss.createBlobReadStream(file);
|
|
104
|
+
var size = 0;
|
|
105
|
+
|
|
106
|
+
blobStream.on('data', function(chunk) {
|
|
107
|
+
size += chunk.length;
|
|
108
|
+
console.log(Math.floor(size / file.size * 100) + '%');
|
|
109
|
+
// -> e.g. '42%'
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
blobStream.pipe(stream);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Socket.IO v0.9 support
|
|
116
|
+
|
|
117
|
+
You have to set `forceBase64` option `true` when using the library with socket.io v0.9.x.
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
ss.forceBase64 = true;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
## Documentation
|
|
125
|
+
|
|
126
|
+
### ss(sio)
|
|
127
|
+
|
|
128
|
+
- sio `socket.io Socket` A socket of Socket.IO, both for client and server
|
|
129
|
+
- return `Socket`
|
|
130
|
+
|
|
131
|
+
Look up an existing `Socket` instance based on `sio` (a socket of Socket.IO), or create one if it doesn't exist.
|
|
132
|
+
|
|
133
|
+
### socket.emit(event, [arg1], [arg2], [...])
|
|
134
|
+
|
|
135
|
+
- event `String` The event name
|
|
136
|
+
|
|
137
|
+
Emit an `event` with variable number of arguments including at least a stream.
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
ss(socket).emit('myevent', stream, {name: 'thefilename'}, function() { ... });
|
|
141
|
+
|
|
142
|
+
// send some streams at a time.
|
|
143
|
+
ss(socket).emit('multiple-streams', stream1, stream2);
|
|
144
|
+
|
|
145
|
+
// as members of array or object.
|
|
146
|
+
ss(socket).emit('flexible', [stream1, { foo: stream2 }]);
|
|
147
|
+
|
|
148
|
+
// get streams through the ack callback
|
|
149
|
+
ss(socket).emit('ack', function(stream1, stream2) { ... });
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### socket.on(event, listener)
|
|
153
|
+
|
|
154
|
+
- event `String` The event name
|
|
155
|
+
- listener `Function` The event handler function
|
|
156
|
+
|
|
157
|
+
Add a `listener` for `event`. `listener` will take stream(s) with any data as arguments.
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
ss(socket).on('myevent', function(stream, data, callback) { ... });
|
|
161
|
+
|
|
162
|
+
// access stream options
|
|
163
|
+
ss(socket).on('foo', function(stream) {
|
|
164
|
+
if (stream.options && stream.options.highWaterMark > 1024) {
|
|
165
|
+
console.error('Too big highWaterMark.');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### ss.createStream([options])
|
|
172
|
+
|
|
173
|
+
- options `Object`
|
|
174
|
+
- highWaterMark `Number`
|
|
175
|
+
- encoding `String`
|
|
176
|
+
- decodeStrings `Boolean`
|
|
177
|
+
- objectMode `Boolean`
|
|
178
|
+
- allowHalfOpen `Boolean` if `true`, then the stream won't automatically close when the other endpoint ends. Default to `false`.
|
|
179
|
+
- return `Duplex Stream`
|
|
180
|
+
|
|
181
|
+
Create a new duplex stream. See [the docs](http://nodejs.org/api/stream.html) for the details of stream and `options`.
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
var stream = ss.createStream();
|
|
185
|
+
|
|
186
|
+
// with options
|
|
187
|
+
var stream = ss.createStream({
|
|
188
|
+
highWaterMark: 1024,
|
|
189
|
+
objectMode: true,
|
|
190
|
+
allowHalfOpen: true
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### ss.createBlobReadStream(blob, [options])
|
|
195
|
+
|
|
196
|
+
- options `Object`
|
|
197
|
+
- highWaterMark `Number`
|
|
198
|
+
- encoding `String`
|
|
199
|
+
- objectMode `Boolean`
|
|
200
|
+
- return `Readable Stream`
|
|
201
|
+
|
|
202
|
+
Create a new readable stream for [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and [File](https://developer.mozilla.org/en-US/docs/Web/API/File) on browser. See [the docs](http://nodejs.org/api/stream.html) for the details of stream and `options`.
|
|
203
|
+
|
|
204
|
+
```js
|
|
205
|
+
var stream = ss.createBlobReadStream(new Blob([1, 2, 3]));
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### ss.Buffer
|
|
209
|
+
|
|
210
|
+
[Node Buffer](https://nodejs.org/api/buffer.html) class to use on browser, which is exposed for convenience. On Node environment, you should just use normal `Buffer`.
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
var stream = ss.createStream();
|
|
214
|
+
stream.write(new ss.Buffer([0, 1, 2]));
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
MIT
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
import { Duplex, Readable, DuplexOptions } from 'stream';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Socket.IO socket interface (compatible with both server and client sockets)
|
|
8
|
+
*/
|
|
9
|
+
export interface SocketIOSocketLike {
|
|
10
|
+
on(event: string, callback: (...args: any[]) => void): this;
|
|
11
|
+
emit(event: string, ...args: any[]): this;
|
|
12
|
+
_streamSocket?: any; // Socket instance (using any to avoid circular reference)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Options for creating a Socket instance
|
|
17
|
+
*/
|
|
18
|
+
export interface SocketOptions {
|
|
19
|
+
/**
|
|
20
|
+
* Forces base 64 encoding when emitting. Must be set to true for Socket.IO v0.9 or lower.
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
forceBase64?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Options for creating an IOStream
|
|
28
|
+
*/
|
|
29
|
+
export interface IOStreamOptions extends DuplexOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Allow half-open sockets. If set to false, then the stream will automatically
|
|
32
|
+
* end the writable side when the readable side ends.
|
|
33
|
+
* @default false
|
|
34
|
+
*/
|
|
35
|
+
allowHalfOpen?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Options for creating a BlobReadStream
|
|
40
|
+
*/
|
|
41
|
+
export interface BlobReadStreamOptions {
|
|
42
|
+
/**
|
|
43
|
+
* Use synchronous FileReader API
|
|
44
|
+
* @default false
|
|
45
|
+
*/
|
|
46
|
+
synchronous?: boolean;
|
|
47
|
+
highWaterMark?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Bidirectional stream socket which wraps Socket.IO.
|
|
52
|
+
* Extends EventEmitter and provides stream-based communication over Socket.IO.
|
|
53
|
+
*/
|
|
54
|
+
export class Socket extends EventEmitter {
|
|
55
|
+
/**
|
|
56
|
+
* Base event name for messaging.
|
|
57
|
+
*/
|
|
58
|
+
static readonly event: string;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* List of events that are handled directly by EventEmitter.
|
|
62
|
+
*/
|
|
63
|
+
static readonly events: string[];
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The underlying Socket.IO socket instance.
|
|
67
|
+
*/
|
|
68
|
+
readonly sio: SocketIOSocketLike;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Whether to force base64 encoding.
|
|
72
|
+
*/
|
|
73
|
+
readonly forceBase64: boolean;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Creates a new Socket instance.
|
|
77
|
+
* @param sio - Socket.IO socket instance (server or client)
|
|
78
|
+
* @param options - Socket options
|
|
79
|
+
*/
|
|
80
|
+
constructor(sio: SocketIOSocketLike, options?: SocketOptions);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Emits streams to the corresponding server/client.
|
|
84
|
+
* @param type - Event type
|
|
85
|
+
* @param args - Additional arguments, which may include IOStream instances
|
|
86
|
+
* @returns This Socket instance for chaining
|
|
87
|
+
*/
|
|
88
|
+
emit(type: string, ...args: any[]): this;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Listens for stream events.
|
|
92
|
+
* @param type - Event type
|
|
93
|
+
* @param listener - Event listener function that receives streams and other arguments
|
|
94
|
+
* @returns This Socket instance for chaining
|
|
95
|
+
*/
|
|
96
|
+
on(type: string, listener: (...args: any[]) => void): this;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Listens for stream events (one-time).
|
|
100
|
+
* @param type - Event type
|
|
101
|
+
* @param listener - Event listener function that receives streams and other arguments
|
|
102
|
+
* @returns This Socket instance for chaining
|
|
103
|
+
*/
|
|
104
|
+
once(type: string, listener: (...args: any[]) => void): this;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Removes a listener for stream events.
|
|
108
|
+
* @param type - Event type
|
|
109
|
+
* @param listener - Event listener function
|
|
110
|
+
* @returns This Socket instance for chaining
|
|
111
|
+
*/
|
|
112
|
+
removeListener(type: string, listener: (...args: any[]) => void): this;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Removes all listeners for a specific event type.
|
|
116
|
+
* @param type - Event type
|
|
117
|
+
* @returns This Socket instance for chaining
|
|
118
|
+
*/
|
|
119
|
+
removeAllListeners(type?: string): this;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Cleans up a stream by its ID.
|
|
123
|
+
* @param id - Stream ID
|
|
124
|
+
*/
|
|
125
|
+
cleanup(id: string): void;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Duplex stream for Socket.IO communication.
|
|
130
|
+
* Extends Node.js Duplex stream and can be used for bidirectional data transfer.
|
|
131
|
+
*/
|
|
132
|
+
export class IOStream extends Duplex {
|
|
133
|
+
/**
|
|
134
|
+
* Unique identifier for this stream.
|
|
135
|
+
*/
|
|
136
|
+
readonly id: string;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The Socket instance this stream is associated with.
|
|
140
|
+
*/
|
|
141
|
+
socket: Socket | null;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Whether this stream has been destroyed.
|
|
145
|
+
*/
|
|
146
|
+
readonly destroyed: boolean;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Options passed during construction.
|
|
150
|
+
*/
|
|
151
|
+
readonly options?: IOStreamOptions;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Creates a new IOStream instance.
|
|
155
|
+
* @param options - Stream options
|
|
156
|
+
*/
|
|
157
|
+
constructor(options?: IOStreamOptions);
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Ensures that no more I/O activity happens on this stream.
|
|
161
|
+
* Not necessary in the usual case.
|
|
162
|
+
*/
|
|
163
|
+
destroy(): void;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Readable stream for Blob and File objects on the browser.
|
|
168
|
+
* Extends Node.js Readable stream.
|
|
169
|
+
*/
|
|
170
|
+
export class BlobReadStream extends Readable {
|
|
171
|
+
/**
|
|
172
|
+
* The Blob or File object being read.
|
|
173
|
+
*/
|
|
174
|
+
readonly blob: Blob | File;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Creates a new BlobReadStream instance.
|
|
178
|
+
* @param blob - Blob or File object to read from
|
|
179
|
+
* @param options - Stream options
|
|
180
|
+
*/
|
|
181
|
+
constructor(blob: Blob | File, options?: BlobReadStreamOptions);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Main module function to look up or create a Socket instance.
|
|
186
|
+
* This function also has properties attached to it (Socket, IOStream, etc.)
|
|
187
|
+
*/
|
|
188
|
+
declare function lookup(sio: SocketIOSocketLike, options?: SocketOptions): Socket;
|
|
189
|
+
|
|
190
|
+
declare namespace lookup {
|
|
191
|
+
/**
|
|
192
|
+
* Socket constructor class
|
|
193
|
+
*/
|
|
194
|
+
const Socket: typeof Socket;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* IOStream constructor class
|
|
198
|
+
*/
|
|
199
|
+
const IOStream: typeof IOStream;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* BlobReadStream constructor class
|
|
203
|
+
*/
|
|
204
|
+
const BlobReadStream: typeof BlobReadStream;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Forces base 64 encoding when emitting. Must be set to true for Socket.IO v0.9 or lower.
|
|
208
|
+
* @default false
|
|
209
|
+
*/
|
|
210
|
+
let forceBase64: boolean;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Exposes Node Buffer for browser compatibility.
|
|
214
|
+
*/
|
|
215
|
+
const Buffer: typeof global.Buffer;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Creates a new duplex stream.
|
|
219
|
+
* @param options - Stream options
|
|
220
|
+
* @returns New IOStream instance
|
|
221
|
+
*/
|
|
222
|
+
function createStream(options?: IOStreamOptions): IOStream;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Creates a new readable stream for Blob/File on browser.
|
|
226
|
+
* @param blob - Blob or File object to read from
|
|
227
|
+
* @param options - Stream options
|
|
228
|
+
* @returns New BlobReadStream instance
|
|
229
|
+
*/
|
|
230
|
+
function createBlobReadStream(
|
|
231
|
+
blob: Blob | File,
|
|
232
|
+
options?: BlobReadStreamOptions
|
|
233
|
+
): BlobReadStream;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export = lookup;
|
package/index.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
var util = require('util');
|
|
2
|
+
var Readable = require('readable-stream').Readable;
|
|
3
|
+
var bind = require('component-bind');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
module.exports = BlobReadStream;
|
|
7
|
+
|
|
8
|
+
util.inherits(BlobReadStream, Readable);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Readable stream for Blob and File on browser.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} options
|
|
14
|
+
* @api private
|
|
15
|
+
*/
|
|
16
|
+
function BlobReadStream(blob, options) {
|
|
17
|
+
if (!(this instanceof BlobReadStream)) {
|
|
18
|
+
return new BlobReadStream(blob, options);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Readable.call(this, options);
|
|
22
|
+
|
|
23
|
+
options = options || {};
|
|
24
|
+
this.blob = blob;
|
|
25
|
+
this.slice = blob.slice || blob.webkitSlice || blob.mozSlice;
|
|
26
|
+
this.start = 0;
|
|
27
|
+
this.sync = options.synchronous || false;
|
|
28
|
+
|
|
29
|
+
var fileReader;
|
|
30
|
+
|
|
31
|
+
if (options.synchronous) {
|
|
32
|
+
fileReader = this.fileReader = new FileReaderSync();
|
|
33
|
+
} else {
|
|
34
|
+
fileReader = this.fileReader = new FileReader();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fileReader.onload = bind(this, '_onload');
|
|
38
|
+
fileReader.onerror = bind(this, '_onerror');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
BlobReadStream.prototype._read = function(size) {
|
|
42
|
+
var start = this.start;
|
|
43
|
+
var end = this.start = this.start + size;
|
|
44
|
+
var chunk = this.slice.call(this.blob, start, end);
|
|
45
|
+
|
|
46
|
+
if (chunk.size) {
|
|
47
|
+
if (this.sync) {
|
|
48
|
+
var bufferChunk = new Buffer(new Uint8Array(this.fileReader.readAsArrayBuffer(chunk)));
|
|
49
|
+
this.push(bufferChunk);
|
|
50
|
+
} else {
|
|
51
|
+
this.fileReader.readAsArrayBuffer(chunk);
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
this.push(null);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
BlobReadStream.prototype._onload = function(e) {
|
|
59
|
+
var chunk = new Buffer(new Uint8Array(e.target.result));
|
|
60
|
+
this.push(chunk);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
BlobReadStream.prototype._onerror = function(e) {
|
|
64
|
+
var err = e.target.error;
|
|
65
|
+
this.emit('error', err);
|
|
66
|
+
};
|
|
67
|
+
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
var Socket = require('./socket');
|
|
2
|
+
var IOStream = require('./iostream');
|
|
3
|
+
var BlobReadStream = require('./blob-read-stream');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
exports = module.exports = lookup;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Expose Node Buffer for browser.
|
|
10
|
+
*
|
|
11
|
+
* @api public
|
|
12
|
+
*/
|
|
13
|
+
exports.Buffer = Buffer;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Expose Socket constructor.
|
|
17
|
+
*
|
|
18
|
+
* @api public
|
|
19
|
+
*/
|
|
20
|
+
exports.Socket = Socket;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Expose IOStream constructor.
|
|
24
|
+
*
|
|
25
|
+
* @api public
|
|
26
|
+
*/
|
|
27
|
+
exports.IOStream = IOStream;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Forces base 64 encoding when emitting. Must be set to true for Socket.IO v0.9 or lower.
|
|
31
|
+
*
|
|
32
|
+
* @api public
|
|
33
|
+
*/
|
|
34
|
+
exports.forceBase64 = false;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Look up an existing Socket.
|
|
38
|
+
*
|
|
39
|
+
* @param {socket.io#Socket} socket.io
|
|
40
|
+
* @param {Object} options
|
|
41
|
+
* @return {Socket} Socket instance
|
|
42
|
+
* @api public
|
|
43
|
+
*/
|
|
44
|
+
function lookup(sio, options) {
|
|
45
|
+
options = options || {};
|
|
46
|
+
if (null == options.forceBase64) {
|
|
47
|
+
options.forceBase64 = exports.forceBase64;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!sio._streamSocket) {
|
|
51
|
+
sio._streamSocket = new Socket(sio, options);
|
|
52
|
+
}
|
|
53
|
+
return sio._streamSocket;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Creates a new duplex stream.
|
|
58
|
+
*
|
|
59
|
+
* @param {Object} options
|
|
60
|
+
* @return {IOStream} duplex stream
|
|
61
|
+
* @api public
|
|
62
|
+
*/
|
|
63
|
+
exports.createStream = function(options) {
|
|
64
|
+
return new IOStream(options);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Creates a new readable stream for Blob/File on browser.
|
|
69
|
+
*
|
|
70
|
+
* @param {Blob} blob
|
|
71
|
+
* @param {Object} options
|
|
72
|
+
* @return {BlobReadStream} stream
|
|
73
|
+
* @api public
|
|
74
|
+
*/
|
|
75
|
+
exports.createBlobReadStream = function(blob, options) {
|
|
76
|
+
return new BlobReadStream(blob, options);
|
|
77
|
+
};
|
package/lib/iostream.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
var util = require('util');
|
|
2
|
+
var Duplex = require('readable-stream').Duplex;
|
|
3
|
+
var bind = require('component-bind');
|
|
4
|
+
var uuid = require('./uuid');
|
|
5
|
+
var debug = require('debug')('socket.io-stream:iostream');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module.exports = IOStream;
|
|
9
|
+
|
|
10
|
+
util.inherits(IOStream, Duplex);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Duplex
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} options
|
|
16
|
+
* @api private
|
|
17
|
+
*/
|
|
18
|
+
function IOStream(options) {
|
|
19
|
+
if (!(this instanceof IOStream)) {
|
|
20
|
+
return new IOStream(options);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
IOStream.super_.call(this, options);
|
|
24
|
+
|
|
25
|
+
this.options = options;
|
|
26
|
+
this.id = uuid();
|
|
27
|
+
this.socket = null;
|
|
28
|
+
|
|
29
|
+
// Buffers
|
|
30
|
+
this.pushBuffer = [];
|
|
31
|
+
this.writeBuffer = [];
|
|
32
|
+
|
|
33
|
+
// Op states
|
|
34
|
+
this._readable = false;
|
|
35
|
+
this._writable = false;
|
|
36
|
+
this.destroyed = false;
|
|
37
|
+
|
|
38
|
+
// default to *not* allowing half open sockets
|
|
39
|
+
this.allowHalfOpen = options && options.allowHalfOpen || false;
|
|
40
|
+
|
|
41
|
+
this.on('finish', this._onfinish);
|
|
42
|
+
this.on('end', this._onend);
|
|
43
|
+
this.on('error', this._onerror);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Ensures that no more I/O activity happens on this stream.
|
|
48
|
+
* Not necessary in the usual case.
|
|
49
|
+
*
|
|
50
|
+
* @api public
|
|
51
|
+
*/
|
|
52
|
+
IOStream.prototype.destroy = function() {
|
|
53
|
+
debug('destroy');
|
|
54
|
+
|
|
55
|
+
if (this.destroyed) {
|
|
56
|
+
debug('already destroyed');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.readable = this.writable = false;
|
|
61
|
+
|
|
62
|
+
if (this.socket) {
|
|
63
|
+
debug('clean up');
|
|
64
|
+
this.socket.cleanup(this.id);
|
|
65
|
+
this.socket = null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.destroyed = true;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Local read
|
|
73
|
+
*
|
|
74
|
+
* @api private
|
|
75
|
+
*/
|
|
76
|
+
IOStream.prototype._read = function(size) {
|
|
77
|
+
var push;
|
|
78
|
+
|
|
79
|
+
// We can not read from the socket if it's destroyed obviously ...
|
|
80
|
+
if (this.destroyed) return;
|
|
81
|
+
|
|
82
|
+
if (this.pushBuffer.length) {
|
|
83
|
+
// flush buffer and end if it exists.
|
|
84
|
+
while (push = this.pushBuffer.shift()) {
|
|
85
|
+
if (!push()) break;
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this._readable = true;
|
|
91
|
+
|
|
92
|
+
// Go get data from remote stream
|
|
93
|
+
// Calls
|
|
94
|
+
// ._onread remotely
|
|
95
|
+
// then
|
|
96
|
+
// ._onwrite locally
|
|
97
|
+
this.socket._read(this.id, size);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Read from remote stream
|
|
103
|
+
*
|
|
104
|
+
* @api private
|
|
105
|
+
*/
|
|
106
|
+
IOStream.prototype._onread = function(size) {
|
|
107
|
+
var write = this.writeBuffer.shift();
|
|
108
|
+
if (write) return write();
|
|
109
|
+
|
|
110
|
+
this._writable = true;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Write local data to remote stream
|
|
115
|
+
* Calls
|
|
116
|
+
* remtote ._onwrite
|
|
117
|
+
*
|
|
118
|
+
* @api private
|
|
119
|
+
*/
|
|
120
|
+
IOStream.prototype._write = function(chunk, encoding, callback) {
|
|
121
|
+
var self = this;
|
|
122
|
+
|
|
123
|
+
function write() {
|
|
124
|
+
// We can not write to the socket if it's destroyed obviously ...
|
|
125
|
+
if (self.destroyed) return;
|
|
126
|
+
|
|
127
|
+
self._writable = false;
|
|
128
|
+
self.socket._write(self.id, chunk, encoding, callback);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (this._writable) {
|
|
132
|
+
write();
|
|
133
|
+
} else {
|
|
134
|
+
this.writeBuffer.push(write);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Write the data fetched remotely
|
|
140
|
+
* so that we can now read locally
|
|
141
|
+
*
|
|
142
|
+
* @api private
|
|
143
|
+
*/
|
|
144
|
+
IOStream.prototype._onwrite = function(chunk, encoding, callback) {
|
|
145
|
+
var self = this;
|
|
146
|
+
|
|
147
|
+
function push() {
|
|
148
|
+
self._readable = false;
|
|
149
|
+
var ret = self.push(chunk || '', encoding);
|
|
150
|
+
callback();
|
|
151
|
+
return ret;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (this._readable) {
|
|
155
|
+
push();
|
|
156
|
+
} else {
|
|
157
|
+
this.pushBuffer.push(push);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* When ending send 'end' event to remote stream
|
|
163
|
+
*
|
|
164
|
+
* @api private
|
|
165
|
+
*/
|
|
166
|
+
IOStream.prototype._end = function() {
|
|
167
|
+
if (this.pushBuffer.length) {
|
|
168
|
+
// end after flushing buffer.
|
|
169
|
+
this.pushBuffer.push(bind(this, '_done'));
|
|
170
|
+
} else {
|
|
171
|
+
this._done();
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Remote stream just ended
|
|
177
|
+
*
|
|
178
|
+
* @api private
|
|
179
|
+
*/
|
|
180
|
+
IOStream.prototype._done = function() {
|
|
181
|
+
this._readable = false;
|
|
182
|
+
|
|
183
|
+
// signal the end of the data.
|
|
184
|
+
return this.push(null);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* the user has called .end(), and all the bytes have been
|
|
189
|
+
* sent out to the other side.
|
|
190
|
+
* If allowHalfOpen is false, or if the readable side has
|
|
191
|
+
* ended already, then destroy.
|
|
192
|
+
* If allowHalfOpen is true, then we need to set writable false,
|
|
193
|
+
* so that only the writable side will be cleaned up.
|
|
194
|
+
*
|
|
195
|
+
* @api private
|
|
196
|
+
*/
|
|
197
|
+
IOStream.prototype._onfinish = function() {
|
|
198
|
+
debug('_onfinish');
|
|
199
|
+
// Local socket just finished
|
|
200
|
+
// send 'end' event to remote
|
|
201
|
+
if (this.socket) {
|
|
202
|
+
this.socket._end(this.id);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this.writable = false;
|
|
206
|
+
this._writableState.ended = true;
|
|
207
|
+
|
|
208
|
+
if (!this.readable || this._readableState.ended) {
|
|
209
|
+
debug('_onfinish: ended, destroy %s', this._readableState);
|
|
210
|
+
return this.destroy();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
debug('_onfinish: not ended');
|
|
214
|
+
|
|
215
|
+
if (!this.allowHalfOpen) {
|
|
216
|
+
this.push(null);
|
|
217
|
+
|
|
218
|
+
// just in case we're waiting for an EOF.
|
|
219
|
+
if (this.readable && !this._readableState.endEmitted) {
|
|
220
|
+
this.read(0);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* the EOF has been received, and no more bytes are coming.
|
|
227
|
+
* if the writable side has ended already, then clean everything
|
|
228
|
+
* up.
|
|
229
|
+
*
|
|
230
|
+
* @api private
|
|
231
|
+
*/
|
|
232
|
+
IOStream.prototype._onend = function() {
|
|
233
|
+
debug('_onend');
|
|
234
|
+
this.readable = false;
|
|
235
|
+
this._readableState.ended = true;
|
|
236
|
+
|
|
237
|
+
if (!this.writable || this._writableState.finished) {
|
|
238
|
+
debug('_onend: %s', this._writableState);
|
|
239
|
+
return this.destroy();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
debug('_onend: not finished');
|
|
243
|
+
|
|
244
|
+
if (!this.allowHalfOpen) {
|
|
245
|
+
this.end();
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* When error in local stream
|
|
251
|
+
* notyify remote
|
|
252
|
+
* if err.remote = true
|
|
253
|
+
* then error happened on remote stream
|
|
254
|
+
*
|
|
255
|
+
* @api private
|
|
256
|
+
*/
|
|
257
|
+
IOStream.prototype._onerror = function(err) {
|
|
258
|
+
// check if the error came from remote stream.
|
|
259
|
+
if (!err.remote && this.socket) {
|
|
260
|
+
// notify the error to the corresponding remote stream.
|
|
261
|
+
this.socket._error(this.id, err);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
this.destroy();
|
|
265
|
+
};
|
package/lib/parser.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
var util = require('util');
|
|
2
|
+
var EventEmitter = require('events').EventEmitter;
|
|
3
|
+
var IOStream = require('./iostream');
|
|
4
|
+
var slice = Array.prototype.slice;
|
|
5
|
+
|
|
6
|
+
exports.Encoder = Encoder;
|
|
7
|
+
exports.Decoder = Decoder;
|
|
8
|
+
|
|
9
|
+
util.inherits(Encoder, EventEmitter);
|
|
10
|
+
|
|
11
|
+
function Encoder() {
|
|
12
|
+
EventEmitter.call(this);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Encode streams to placeholder objects.
|
|
17
|
+
*
|
|
18
|
+
* @api public
|
|
19
|
+
*/
|
|
20
|
+
Encoder.prototype.encode = function(v) {
|
|
21
|
+
if (v instanceof IOStream) {
|
|
22
|
+
return this.encodeStream(v);
|
|
23
|
+
} else if (util.isArray(v)) {
|
|
24
|
+
return this.encodeArray(v);
|
|
25
|
+
} else if (v && 'object' == typeof v) {
|
|
26
|
+
return this.encodeObject(v);
|
|
27
|
+
}
|
|
28
|
+
return v;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
Encoder.prototype.encodeStream = function(stream) {
|
|
32
|
+
this.emit('stream', stream);
|
|
33
|
+
|
|
34
|
+
// represent a stream in an object.
|
|
35
|
+
var v = { $stream: stream.id };
|
|
36
|
+
if (stream.options) {
|
|
37
|
+
v.options = stream.options;
|
|
38
|
+
}
|
|
39
|
+
return v;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
Encoder.prototype.encodeArray = function(arr) {
|
|
43
|
+
var v = [];
|
|
44
|
+
for (var i = 0, len = arr.length; i < len; i++) {
|
|
45
|
+
v.push(this.encode(arr[i]));
|
|
46
|
+
}
|
|
47
|
+
return v;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
Encoder.prototype.encodeObject = function(obj) {
|
|
51
|
+
var v = {};
|
|
52
|
+
for (var k in obj) {
|
|
53
|
+
if (obj.hasOwnProperty(k)) {
|
|
54
|
+
v[k] = this.encode(obj[k]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return v;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
util.inherits(Decoder, EventEmitter);
|
|
61
|
+
|
|
62
|
+
function Decoder() {
|
|
63
|
+
EventEmitter.call(this);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Decode placeholder objects to streams.
|
|
68
|
+
*
|
|
69
|
+
* @api public
|
|
70
|
+
*/
|
|
71
|
+
Decoder.prototype.decode = function(v) {
|
|
72
|
+
if (v && v.$stream) {
|
|
73
|
+
return this.decodeStream(v);
|
|
74
|
+
} else if (util.isArray(v)) {
|
|
75
|
+
return this.decodeArray(v);
|
|
76
|
+
} else if (v && 'object' == typeof v) {
|
|
77
|
+
return this.decodeObject(v);
|
|
78
|
+
}
|
|
79
|
+
return v;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Decoder.prototype.decodeStream = function(obj) {
|
|
83
|
+
var stream = new IOStream(obj.options);
|
|
84
|
+
stream.id = obj.$stream;
|
|
85
|
+
this.emit('stream', stream);
|
|
86
|
+
return stream;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Decoder.prototype.decodeArray = function(arr) {
|
|
90
|
+
var v = [];
|
|
91
|
+
for (var i = 0, len = arr.length; i < len; i++) {
|
|
92
|
+
v.push(this.decode(arr[i]));
|
|
93
|
+
}
|
|
94
|
+
return v;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
Decoder.prototype.decodeObject = function(obj) {
|
|
98
|
+
var v = {};
|
|
99
|
+
for (var k in obj) {
|
|
100
|
+
if (obj.hasOwnProperty(k)) {
|
|
101
|
+
v[k] = this.decode(obj[k]);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return v;
|
|
105
|
+
}
|
package/lib/socket.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
var util = require('util');
|
|
2
|
+
var EventEmitter = require('events').EventEmitter;
|
|
3
|
+
var bind = require('component-bind');
|
|
4
|
+
var IOStream = require('./iostream');
|
|
5
|
+
var parser = require('./parser');
|
|
6
|
+
var debug = require('debug')('socket.io-stream:socket');
|
|
7
|
+
var emit = EventEmitter.prototype.emit;
|
|
8
|
+
var on = EventEmitter.prototype.on;
|
|
9
|
+
var slice = Array.prototype.slice;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
exports = module.exports = Socket;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Base event name for messaging.
|
|
16
|
+
*
|
|
17
|
+
* @api public
|
|
18
|
+
*/
|
|
19
|
+
exports.event = '$stream';
|
|
20
|
+
|
|
21
|
+
exports.events = [
|
|
22
|
+
'error',
|
|
23
|
+
'newListener',
|
|
24
|
+
'removeListener'
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
util.inherits(Socket, EventEmitter);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Bidirectional stream socket which wraps Socket.IO.
|
|
31
|
+
*
|
|
32
|
+
* @param {socket.io#Socket} socket.io
|
|
33
|
+
* @api public
|
|
34
|
+
*/
|
|
35
|
+
function Socket(sio, options) {
|
|
36
|
+
if (!(this instanceof Socket)) {
|
|
37
|
+
return new Socket(sio, options);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
EventEmitter.call(this);
|
|
41
|
+
|
|
42
|
+
options = options || {};
|
|
43
|
+
|
|
44
|
+
this.sio = sio;
|
|
45
|
+
this.forceBase64 = !!options.forceBase64;
|
|
46
|
+
this.streams = {};
|
|
47
|
+
this.encoder = new parser.Encoder();
|
|
48
|
+
this.decoder = new parser.Decoder();
|
|
49
|
+
|
|
50
|
+
var eventName = exports.event;
|
|
51
|
+
sio.on(eventName, bind(this, emit));
|
|
52
|
+
sio.on(eventName + '-read', bind(this, '_onread'));
|
|
53
|
+
sio.on(eventName + '-write', bind(this, '_onwrite'));
|
|
54
|
+
sio.on(eventName + '-end', bind(this, '_onend'));
|
|
55
|
+
sio.on(eventName + '-error', bind(this, '_onerror'));
|
|
56
|
+
sio.on('error', bind(this, emit, 'error'));
|
|
57
|
+
sio.on('disconnect', bind(this, '_ondisconnect'));
|
|
58
|
+
|
|
59
|
+
this.encoder.on('stream', bind(this, '_onencode'));
|
|
60
|
+
this.decoder.on('stream', bind(this, '_ondecode'));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Original emit function.
|
|
65
|
+
*
|
|
66
|
+
* @api private
|
|
67
|
+
*/
|
|
68
|
+
Socket.prototype.$emit = emit;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Emits streams to this corresponding server/client.
|
|
72
|
+
*
|
|
73
|
+
* @return {Socket} self
|
|
74
|
+
* @api public
|
|
75
|
+
*/
|
|
76
|
+
Socket.prototype.emit = function(type) {
|
|
77
|
+
if (~exports.events.indexOf(type)) {
|
|
78
|
+
return emit.apply(this, arguments);
|
|
79
|
+
}
|
|
80
|
+
this._stream.apply(this, arguments);
|
|
81
|
+
return this;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
Socket.prototype.on = function(type, listener) {
|
|
85
|
+
if (~exports.events.indexOf(type)) {
|
|
86
|
+
return on.apply(this, arguments);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this._onstream(type, listener);
|
|
90
|
+
return this;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Sends a new stream request.
|
|
95
|
+
*
|
|
96
|
+
* @param {String} event type
|
|
97
|
+
* @api private
|
|
98
|
+
*/
|
|
99
|
+
Socket.prototype._stream = function(type) {
|
|
100
|
+
debug('sending new streams');
|
|
101
|
+
|
|
102
|
+
var self = this;
|
|
103
|
+
var args = slice.call(arguments, 1);
|
|
104
|
+
var ack = args[args.length - 1];
|
|
105
|
+
if ('function' == typeof ack) {
|
|
106
|
+
args[args.length - 1] = function() {
|
|
107
|
+
var args = slice.call(arguments);
|
|
108
|
+
args = self.decoder.decode(args);
|
|
109
|
+
ack.apply(this, args);
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
args = this.encoder.encode(args);
|
|
114
|
+
var sio = this.sio;
|
|
115
|
+
sio.emit.apply(sio, [exports.event, type].concat(args));
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Notifies the read event.
|
|
120
|
+
*
|
|
121
|
+
* @api private
|
|
122
|
+
*/
|
|
123
|
+
Socket.prototype._read = function(id, size) {
|
|
124
|
+
this.sio.emit(exports.event + '-read', id, size);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Requests to write a chunk.
|
|
129
|
+
*
|
|
130
|
+
* @api private
|
|
131
|
+
*/
|
|
132
|
+
Socket.prototype._write = function(id, chunk, encoding, callback) {
|
|
133
|
+
if (Buffer.isBuffer(chunk)) {
|
|
134
|
+
if (this.forceBase64) {
|
|
135
|
+
encoding = 'base64';
|
|
136
|
+
chunk = chunk.toString(encoding);
|
|
137
|
+
} else if (!global.Buffer) {
|
|
138
|
+
// socket.io can't handle Buffer when using browserify.
|
|
139
|
+
if (chunk.toArrayBuffer) {
|
|
140
|
+
chunk = chunk.toArrayBuffer();
|
|
141
|
+
} else {
|
|
142
|
+
chunk = chunk.buffer;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
this.sio.emit(exports.event + '-write', id, chunk, encoding, callback);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
Socket.prototype._end = function(id) {
|
|
150
|
+
this.sio.emit(exports.event + '-end', id);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
Socket.prototype._error = function(id, err) {
|
|
154
|
+
this.sio.emit(exports.event + '-error', id, err.message || err);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Handles a new stream request.
|
|
159
|
+
*
|
|
160
|
+
* @param {String} event type
|
|
161
|
+
* @param {Function} listener
|
|
162
|
+
*
|
|
163
|
+
* @api private
|
|
164
|
+
*/
|
|
165
|
+
Socket.prototype._onstream = function(type, listener) {
|
|
166
|
+
if ('function' != typeof listener) {
|
|
167
|
+
throw TypeError('listener must be a function');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function onstream() {
|
|
171
|
+
debug('new streams');
|
|
172
|
+
var self = this;
|
|
173
|
+
var args = slice.call(arguments);
|
|
174
|
+
var ack = args[args.length - 1];
|
|
175
|
+
if ('function' == typeof ack) {
|
|
176
|
+
args[args.length - 1] = function() {
|
|
177
|
+
var args = slice.call(arguments);
|
|
178
|
+
args = self.encoder.encode(args);
|
|
179
|
+
ack.apply(this, args);
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
args = this.decoder.decode(args);
|
|
184
|
+
listener.apply(this, args);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// for removeListener
|
|
188
|
+
onstream.listener = listener;
|
|
189
|
+
|
|
190
|
+
on.call(this, type, onstream);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
Socket.prototype._onread = function(id, size) {
|
|
194
|
+
debug('read: "%s"', id);
|
|
195
|
+
|
|
196
|
+
var stream = this.streams[id];
|
|
197
|
+
if (stream) {
|
|
198
|
+
stream._onread(size);
|
|
199
|
+
} else {
|
|
200
|
+
debug('ignore invalid stream id');
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
Socket.prototype._onwrite = function(id, chunk, encoding, callback) {
|
|
205
|
+
debug('write: "%s"', id);
|
|
206
|
+
|
|
207
|
+
var stream = this.streams[id];
|
|
208
|
+
if (!stream) {
|
|
209
|
+
callback('invalid stream id: ' + id);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (global.ArrayBuffer && chunk instanceof ArrayBuffer) {
|
|
214
|
+
// make sure that chunk is a buffer for stream
|
|
215
|
+
chunk = new Buffer(new Uint8Array(chunk));
|
|
216
|
+
}
|
|
217
|
+
stream._onwrite(chunk, encoding, callback);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
Socket.prototype._onend = function(id) {
|
|
221
|
+
debug('end: "%s"', id);
|
|
222
|
+
|
|
223
|
+
var stream = this.streams[id];
|
|
224
|
+
if (!stream) {
|
|
225
|
+
debug('ignore non-existent stream id: "%s"', id);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
stream._end();
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
Socket.prototype._onerror = function(id, message) {
|
|
233
|
+
debug('error: "%s", "%s"', id, message);
|
|
234
|
+
|
|
235
|
+
var stream = this.streams[id];
|
|
236
|
+
if (!stream) {
|
|
237
|
+
debug('invalid stream id: "%s"', id);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
var err = new Error(message);
|
|
242
|
+
err.remote = true;
|
|
243
|
+
stream.emit('error', err);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
Socket.prototype._ondisconnect = function() {
|
|
247
|
+
var stream;
|
|
248
|
+
for (var id in this.streams) {
|
|
249
|
+
stream = this.streams[id];
|
|
250
|
+
stream.destroy();
|
|
251
|
+
|
|
252
|
+
// Close streams when the underlaying
|
|
253
|
+
// socket.io connection is closed (regardless why)
|
|
254
|
+
stream.emit('close');
|
|
255
|
+
stream.emit('error', new Error('Connection aborted'));
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
Socket.prototype._onencode = function(stream) {
|
|
260
|
+
if (stream.socket || stream.destroyed) {
|
|
261
|
+
throw new Error('stream has already been sent.');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
var id = stream.id;
|
|
265
|
+
if (this.streams[id]) {
|
|
266
|
+
throw new Error('Encoded stream already exists: ' + id);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
this.streams[id] = stream;
|
|
270
|
+
stream.socket = this;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
Socket.prototype._ondecode = function(stream) {
|
|
274
|
+
var id = stream.id;
|
|
275
|
+
if (this.streams[id]) {
|
|
276
|
+
this._error(id, new Error('Decoded stream already exists: ' + id));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
this.streams[id] = stream;
|
|
281
|
+
stream.socket = this;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
Socket.prototype.cleanup = function(id) {
|
|
285
|
+
delete this.streams[id];
|
|
286
|
+
};
|
|
287
|
+
|
package/lib/uuid.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// UUID function from https://gist.github.com/jed/982883
|
|
2
|
+
// More lightweight than node-uuid
|
|
3
|
+
function b(
|
|
4
|
+
a // placeholder
|
|
5
|
+
){
|
|
6
|
+
return a // if the placeholder was passed, return
|
|
7
|
+
? ( // a random number from 0 to 15
|
|
8
|
+
a ^ // unless b is 8,
|
|
9
|
+
Math.random() // in which case
|
|
10
|
+
* 16 // a random number from
|
|
11
|
+
>> a/4 // 8 to 11
|
|
12
|
+
).toString(16) // in hexadecimal
|
|
13
|
+
: ( // or otherwise a concatenated string:
|
|
14
|
+
[1e7] + // 10000000 +
|
|
15
|
+
-1e3 + // -1000 +
|
|
16
|
+
-4e3 + // -4000 +
|
|
17
|
+
-8e3 + // -80000000 +
|
|
18
|
+
-1e11 // -100000000000,
|
|
19
|
+
).replace( // replacing
|
|
20
|
+
/[018]/g, // zeroes, ones, and eights with
|
|
21
|
+
b // random hex digits
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = b;
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sawa-siemens/socket.io-stream",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "stream for socket.io",
|
|
5
|
+
"author": "Naoyuki Kanezawa <naoyuki.kanezawa@gmail.com>",
|
|
6
|
+
"contributors": [
|
|
7
|
+
{
|
|
8
|
+
"name": "Naoyuki Kanezawa",
|
|
9
|
+
"email": "naoyuki.kanezawa@gmail.com"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"name": "Aaron O'Mullan",
|
|
13
|
+
"email": "aaron.omullan@friendco.de"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "Sawa",
|
|
17
|
+
"email": "sawa.yeung.ext@siemens.com"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"stream",
|
|
22
|
+
"socket.io",
|
|
23
|
+
"binary",
|
|
24
|
+
"file",
|
|
25
|
+
"upload",
|
|
26
|
+
"download"
|
|
27
|
+
],
|
|
28
|
+
"main": "index.js",
|
|
29
|
+
"types": "index.d.ts",
|
|
30
|
+
"browser": "socket.io-stream.js",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"node": "./index.js",
|
|
34
|
+
"browser": "./socket.io-stream.js",
|
|
35
|
+
"default": "./index.js"
|
|
36
|
+
},
|
|
37
|
+
"./package.json": "./package.json"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=0.10.0"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/sawa-siemens/socket.io-stream.git"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"prepublish": "make build",
|
|
48
|
+
"test": "make test"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"component-bind": "~1.0.0",
|
|
52
|
+
"debug": "^4.3.4"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"browserify": "^17.0.0",
|
|
56
|
+
"expect.js": "~0.3.1",
|
|
57
|
+
"socket.io": "^4.8.3",
|
|
58
|
+
"socket.io-client": "^4.8.3"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ss = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
|
2
|
+
|
|
3
|
+
},{}]},{},[1])(1)
|
|
4
|
+
});
|