├── .gitignore
├── .npmignore
├── .travis.yml
├── test
├── chrome-app
│ ├── window.html
│ ├── background.js
│ └── manifest.json
├── client
│ └── dgram.js
├── dgram.js
└── helper.js
├── LICENSE
├── package.json
├── README.md
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | bundle.js
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .travis.yml
2 | test/
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - lts/*
4 |
--------------------------------------------------------------------------------
/test/chrome-app/window.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/test/chrome-app/background.js:
--------------------------------------------------------------------------------
1 | /* global chrome */
2 |
3 | chrome.app.runtime.onLaunched.addListener(function () {
4 | chrome.app.window.create('window.html', {
5 | bounds: {
6 | width: 100,
7 | height: 100
8 | }
9 | })
10 | })
11 |
--------------------------------------------------------------------------------
/test/chrome-app/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 |
4 | "name": "Hello World!",
5 | "description": "My first Chrome App.",
6 | "version": "0.1.0",
7 |
8 | "app": {
9 | "background": {
10 | "scripts": ["background.js"]
11 | }
12 | },
13 |
14 | "permissions": [
15 | "*://*/*"
16 | ],
17 |
18 | "sockets": {
19 | "udp": {
20 | "bind": "*",
21 | "send": "*"
22 | }
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/test/client/dgram.js:
--------------------------------------------------------------------------------
1 | const dgram = require('../../')
2 |
3 | const PORT = Number(process.env.PORT)
4 |
5 | const sock = dgram.createSocket('udp4')
6 | sock.setMulticastTTL(2)
7 |
8 | // If any errors are emitted, log them
9 | sock.on('error', function (err) {
10 | console.error(err.stack)
11 | })
12 |
13 | sock.send('beep', 0, 'beep'.length, PORT, '127.0.0.1')
14 |
15 | sock.on('message', function (data, rInfo) {
16 | if (data.toString() === 'boop') {
17 | sock.send('pass1', 0, 'pass1'.length, rInfo.port, rInfo.address)
18 | sock.send('pass2', rInfo.port, rInfo.address)
19 | } else {
20 | sock.send('fail', 0, 'fail'.length, rInfo.port, rInfo.address)
21 | }
22 | })
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Feross Aboukhadijeh & John Hiesey
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | 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, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/test/dgram.js:
--------------------------------------------------------------------------------
1 | const dgram = require('dgram')
2 | const helper = require('./helper')
3 | const portfinder = require('portfinder')
4 | const test = require('tape')
5 |
6 | test('UDP works (echo test)', function (t) {
7 | portfinder.getPort(function (err, port) {
8 | t.error(err, 'Found free ports')
9 | const socket = dgram.createSocket('udp4')
10 | let child
11 |
12 | socket.on('listening', function () {
13 | const env = { PORT: port }
14 | helper.browserify('dgram.js', env, function (err) {
15 | t.error(err, 'Clean browserify build')
16 | child = helper.launchBrowser()
17 | })
18 | })
19 |
20 | let i = 0
21 | socket.on('message', function (message, remote) {
22 | if (i === 0) {
23 | t.equal(message.toString(), 'beep', 'Got beep')
24 | const boop = Buffer.from('boop')
25 | socket.send(boop, 0, boop.length, remote.port, remote.address)
26 | } else if (i === 1) {
27 | t.equal(message.toString(), 'pass1', 'Boop was received')
28 | } else if (i === 2) {
29 | t.equal(message.toString(), 'pass2', 'Omitting `offset` and `length` works')
30 | child.kill()
31 | socket.close()
32 | t.end()
33 | } else {
34 | t.fail('UDP client sent unexpected message')
35 | }
36 | i += 1
37 | })
38 |
39 | socket.bind(port)
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chrome-dgram",
3 | "description": "Use the Node `dgram` API in Chrome Apps",
4 | "version": "3.0.6",
5 | "author": {
6 | "name": "Feross Aboukhadijeh",
7 | "email": "feross@feross.org",
8 | "url": "https://feross.org"
9 | },
10 | "bugs": {
11 | "url": "https://github.com/feross/chrome-dgram/issues"
12 | },
13 | "contributors": [
14 | "John Hiesey "
15 | ],
16 | "dependencies": {
17 | "inherits": "^2.0.4",
18 | "run-series": "^1.1.9"
19 | },
20 | "devDependencies": {
21 | "browserify": "^17.0.0",
22 | "envify": "^4.1.0",
23 | "once": "1.x",
24 | "portfinder": "^1.0.28",
25 | "standard": "*",
26 | "tape": "^5.0.1"
27 | },
28 | "homepage": "https://github.com/feross/chrome-dgram",
29 | "keywords": [
30 | "chrome app",
31 | "chrome.socket",
32 | "socket api",
33 | "dgram",
34 | "udp",
35 | "wrapper",
36 | "client",
37 | "server"
38 | ],
39 | "license": "MIT",
40 | "main": "index.js",
41 | "repository": {
42 | "type": "git",
43 | "url": "git://github.com/feross/chrome-dgram.git"
44 | },
45 | "scripts": {
46 | "test": "standard",
47 | "test-browser": "tape test/*.js"
48 | },
49 | "funding": [
50 | {
51 | "type": "github",
52 | "url": "https://github.com/sponsors/feross"
53 | },
54 | {
55 | "type": "patreon",
56 | "url": "https://www.patreon.com/feross"
57 | },
58 | {
59 | "type": "consulting",
60 | "url": "https://feross.org/support"
61 | }
62 | ]
63 | }
64 |
--------------------------------------------------------------------------------
/test/helper.js:
--------------------------------------------------------------------------------
1 | const browserify = require('browserify')
2 | const cp = require('child_process')
3 | const envify = require('envify/custom')
4 | const fs = require('fs')
5 | const once = require('once')
6 | const path = require('path')
7 | const os = require('os')
8 |
9 | let CHROME = process.env.CHROME
10 |
11 | // locate default chromes for os
12 | switch (os.platform()) {
13 | case 'win32' :
14 | if (process.arch === 'x64') {
15 | CHROME = '"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"'
16 | } else {
17 | CHROME = '"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"'
18 | }
19 | break
20 | case 'darwin' :
21 | CHROME = '/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome'
22 | break
23 | case 'linux' :
24 | CHROME = '/opt/google/chrome/chrome'
25 | break
26 | default :
27 | console.log('Defaulting to process.env.CHROME `%s`', process.env.CHROME)
28 | break
29 | }
30 |
31 | const BUNDLE_PATH = path.join(__dirname, 'chrome-app/bundle.js')
32 |
33 | exports.browserify = function (filename, env, cb) {
34 | if (!env) env = {}
35 | if (!cb) cb = function () {}
36 | cb = once(cb)
37 |
38 | const b = browserify()
39 | b.add(path.join(__dirname, 'client', filename))
40 | b.transform(envify(env))
41 |
42 | b.bundle()
43 | .pipe(fs.createWriteStream(BUNDLE_PATH))
44 | .on('close', cb)
45 | .on('error', cb)
46 | }
47 |
48 | exports.launchBrowser = function () {
49 | // supply full path because windows
50 | const app = path.join(__dirname, '/chrome-app')
51 |
52 | let command = CHROME + ' --load-and-launch-app=' + app
53 | if (os.platform() === 'darwin' || os.platform() === 'linux') {
54 | command += ' > /dev/null 2>&1'
55 | }
56 | const env = { cwd: path.join(__dirname, '..') }
57 |
58 | return cp.exec(command, env, function (err) {
59 | if (err) throw err
60 | })
61 | }
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # chrome-dgram [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url]
2 |
3 | [travis-image]: https://img.shields.io/travis/feross/chrome-dgram/master.svg
4 | [travis-url]: https://travis-ci.org/feross/chrome-dgram
5 | [npm-image]: https://img.shields.io/npm/v/chrome-dgram.svg
6 | [npm-url]: https://npmjs.org/package/chrome-dgram
7 | [downloads-image]: https://img.shields.io/npm/dm/chrome-dgram.svg
8 | [downloads-url]: https://npmjs.org/package/chrome-dgram
9 | [standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg
10 | [standard-url]: https://standardjs.com
11 |
12 | ### Use the Node `dgram` API in Chrome Apps
13 |
14 | This module lets you use the Node.js [dgram](http://nodejs.org/api/dgram.html) (UDP) API in [Chrome Packaged Apps](http://developer.chrome.com/apps/about_apps.html).
15 |
16 | Instead of learning the quirks of Chrome's `chrome.sockets` API for networking in Chrome Apps just **use the higher-level node API you're familiar with**. Then, compile your code with [browserify](https://github.com/substack/node-browserify) and you're all set!
17 |
18 | This module is used by [webtorrent](https://github.com/feross/webtorrent).
19 |
20 | ## install
21 |
22 | ```
23 | npm install chrome-dgram
24 | ```
25 |
26 | ## methods
27 |
28 | Use node's `dgram` API, including all parameter list shorthands and variations.
29 |
30 | Example UDP client/bind:
31 |
32 | ```js
33 | var dgram = require('chrome-dgram')
34 |
35 | var sock = dgram.createSocket('udp4')
36 |
37 | sock.send('beep', 0, 'beep'.length, 1337, '127.0.0.1')
38 |
39 | sock.on('message', function (data, rInfo) {
40 | console.log('Got data from ' + rInfo.address + ':' + rInfo.port)
41 | console.log(data)
42 | })
43 | ```
44 |
45 | See nodejs.org for full API documentation: [dgram](http://nodejs.org/api/dgram.html)
46 |
47 | ## contribute
48 |
49 | To run tests, use `npm test`. The tests will run TCP and UDP servers and launch a few different Chrome Packaged Apps with browserified client code. The tests currently require Chrome Canary on Mac. If you're on Windows or Linux, feel free to send a pull request to fix this limitation.
50 |
51 | ## license
52 |
53 | MIT. Copyright (c) [Feross Aboukhadijeh](http://feross.org) & John Hiesey.
54 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /*! chrome-dgram. MIT License. Feross Aboukhadijeh */
2 | /* global chrome */
3 |
4 | /**
5 | * UDP / Datagram Sockets
6 | * ======================
7 | *
8 | * Datagram sockets are available through require('chrome-dgram').
9 | */
10 |
11 | exports.Socket = Socket
12 |
13 | const EventEmitter = require('events').EventEmitter
14 | const inherits = require('inherits')
15 | const series = require('run-series')
16 |
17 | const BIND_STATE_UNBOUND = 0
18 | const BIND_STATE_BINDING = 1
19 | const BIND_STATE_BOUND = 2
20 |
21 | // Track open sockets to route incoming data (via onReceive) to the right handlers.
22 | const sockets = {}
23 |
24 | // Thorough check for Chrome App since both Edge and Chrome implement dummy chrome object
25 | if (
26 | typeof chrome === 'object' &&
27 | typeof chrome.runtime === 'object' &&
28 | typeof chrome.runtime.id === 'string' &&
29 | typeof chrome.sockets === 'object' &&
30 | typeof chrome.sockets.udp === 'object'
31 | ) {
32 | chrome.sockets.udp.onReceive.addListener(onReceive)
33 | chrome.sockets.udp.onReceiveError.addListener(onReceiveError)
34 | }
35 |
36 | function onReceive (info) {
37 | if (info.socketId in sockets) {
38 | sockets[info.socketId]._onReceive(info)
39 | } else {
40 | console.error('Unknown socket id: ' + info.socketId)
41 | }
42 | }
43 |
44 | function onReceiveError (info) {
45 | if (info.socketId in sockets) {
46 | sockets[info.socketId]._onReceiveError(info.resultCode)
47 | } else {
48 | console.error('Unknown socket id: ' + info.socketId)
49 | }
50 | }
51 |
52 | /**
53 | * dgram.createSocket(type, [callback])
54 | *
55 | * Creates a datagram Socket of the specified types. Valid types are `udp4`
56 | * and `udp6`.
57 | *
58 | * Takes an optional callback which is added as a listener for message events.
59 | *
60 | * Call socket.bind if you want to receive datagrams. socket.bind() will bind
61 | * to the "all interfaces" address on a random port (it does the right thing
62 | * for both udp4 and udp6 sockets). You can then retrieve the address and port
63 | * with socket.address().address and socket.address().port.
64 | *
65 | * @param {string} type Either 'udp4' or 'udp6'
66 | * @param {function} listener Attached as a listener to message events.
67 | * Optional
68 | * @return {Socket} Socket object
69 | */
70 | exports.createSocket = function (type, listener) {
71 | return new Socket(type, listener)
72 | }
73 |
74 | inherits(Socket, EventEmitter)
75 |
76 | /**
77 | * Class: dgram.Socket
78 | *
79 | * The dgram Socket class encapsulates the datagram functionality. It should
80 | * be created via `dgram.createSocket(type, [callback])`.
81 | *
82 | * Event: 'message'
83 | * - msg Buffer object. The message
84 | * - rinfo Object. Remote address information
85 | * Emitted when a new datagram is available on a socket. msg is a Buffer and
86 | * rinfo is an object with the sender's address information and the number
87 | * of bytes in the datagram.
88 | *
89 | * Event: 'listening'
90 | * Emitted when a socket starts listening for datagrams. This happens as soon
91 | * as UDP sockets are created.
92 | *
93 | * Event: 'close'
94 | * Emitted when a socket is closed with close(). No new message events will
95 | * be emitted on this socket.
96 | *
97 | * Event: 'error'
98 | * - exception Error object
99 | * Emitted when an error occurs.
100 | */
101 | function Socket (options, listener) {
102 | const self = this
103 | EventEmitter.call(self)
104 | if (typeof options === 'string') options = { type: options }
105 | if (options.type !== 'udp4') throw new Error('Bad socket type specified. Valid types are: udp4')
106 |
107 | if (typeof listener === 'function') self.on('message', listener)
108 |
109 | self._destroyed = false
110 | self._bindState = BIND_STATE_UNBOUND
111 | self._bindTasks = []
112 | }
113 |
114 | /**
115 | * socket.bind(port, [address], [callback])
116 | *
117 | * For UDP sockets, listen for datagrams on a named port and optional address.
118 | * If address is not specified, the OS will try to listen on all addresses.
119 | * After binding is done, a "listening" event is emitted and the callback(if
120 | * specified) is called. Specifying both a "listening" event listener and
121 | * callback is not harmful but not very useful.
122 | *
123 | * A bound datagram socket keeps the node process running to receive
124 | * datagrams.
125 | *
126 | * If binding fails, an "error" event is generated. In rare case (e.g. binding
127 | * a closed socket), an Error may be thrown by this method.
128 | *
129 | * @param {number} port
130 | * @param {string} address Optional
131 | * @param {function} callback Function with no parameters, Optional. Callback
132 | * when binding is done.
133 | */
134 | Socket.prototype.bind = function (port, address, callback) {
135 | const self = this
136 | if (typeof address === 'function') {
137 | callback = address
138 | address = undefined
139 | }
140 |
141 | if (!address) address = '0.0.0.0'
142 |
143 | if (!port) port = 0
144 |
145 | if (self._bindState !== BIND_STATE_UNBOUND) throw new Error('Socket is already bound')
146 |
147 | self._bindState = BIND_STATE_BINDING
148 |
149 | if (typeof callback === 'function') self.once('listening', callback)
150 |
151 | chrome.sockets.udp.create(function (createInfo) {
152 | self.id = createInfo.socketId
153 |
154 | sockets[self.id] = self
155 |
156 | const bindFns = self._bindTasks.map(function (t) { return t.fn })
157 |
158 | series(bindFns, function (err) {
159 | if (err) return self.emit('error', err)
160 | chrome.sockets.udp.bind(self.id, address, port, function (result) {
161 | if (result < 0) {
162 | self.emit('error', new Error('Socket ' + self.id + ' failed to bind. ' +
163 | chrome.runtime.lastError.message))
164 | return
165 | }
166 | chrome.sockets.udp.getInfo(self.id, function (socketInfo) {
167 | if (!socketInfo.localPort || !socketInfo.localAddress) {
168 | self.emit('error', new Error('Cannot get local port/address for Socket ' + self.id))
169 | return
170 | }
171 |
172 | self._port = socketInfo.localPort
173 | self._address = socketInfo.localAddress
174 |
175 | self._bindState = BIND_STATE_BOUND
176 | self.emit('listening')
177 |
178 | self._bindTasks.forEach(function (t) {
179 | t.callback()
180 | })
181 | })
182 | })
183 | })
184 | })
185 | }
186 |
187 | /**
188 | * Internal function to receive new messages and emit `message` events.
189 | */
190 | Socket.prototype._onReceive = function (info) {
191 | const self = this
192 |
193 | const buf = Buffer.from(new Uint8Array(info.data))
194 | const rinfo = {
195 | address: info.remoteAddress,
196 | family: 'IPv4',
197 | port: info.remotePort,
198 | size: buf.length
199 | }
200 | self.emit('message', buf, rinfo)
201 | }
202 |
203 | Socket.prototype._onReceiveError = function (resultCode) {
204 | const self = this
205 | self.emit('error', new Error('Socket ' + self.id + ' receive error ' + resultCode))
206 | }
207 |
208 | /**
209 | * socket.send(buf, offset, length, port, address, [callback])
210 | *
211 | * For UDP sockets, the destination port and IP address must be
212 | * specified. A string may be supplied for the address parameter, and it will
213 | * be resolved with DNS. An optional callback may be specified to detect any
214 | * DNS errors and when buf may be re-used. Note that DNS lookups will delay
215 | * the time that a send takes place, at least until the next tick. The only
216 | * way to know for sure that a send has taken place is to use the callback.
217 | *
218 | * If the socket has not been previously bound with a call to bind, it's
219 | * assigned a random port number and bound to the "all interfaces" address
220 | * (0.0.0.0 for udp4 sockets, ::0 for udp6 sockets).
221 | *
222 | * @param {Buffer|Arrayish|string} buf Message to be sent
223 | * @param {number} offset Offset in the buffer where the message starts. Optional.
224 | * @param {number} length Number of bytes in the message. Optional.
225 | * @param {number} port destination port
226 | * @param {string} address destination IP
227 | * @param {function} callback Callback when message is done being delivered.
228 | * Optional.
229 | *
230 | * Valid combinations:
231 | * send(buffer, offset, length, port, address, callback)
232 | * send(buffer, offset, length, port, address)
233 | * send(buffer, offset, length, port)
234 | * send(bufferOrList, port, address, callback)
235 | * send(bufferOrList, port, address)
236 | * send(bufferOrList, port)
237 | *
238 | */
239 | Socket.prototype.send = function (buffer, offset, length, port, address, callback) {
240 | const self = this
241 |
242 | let list
243 |
244 | if (address || (port && typeof port !== 'function')) {
245 | buffer = sliceBuffer(buffer, offset, length)
246 | } else {
247 | callback = port
248 | port = offset
249 | address = length
250 | }
251 |
252 | if (!Array.isArray(buffer)) {
253 | if (typeof buffer === 'string') {
254 | list = [Buffer.from(buffer)]
255 | } else if (!(buffer instanceof Buffer)) {
256 | throw new TypeError('First argument must be a buffer or a string')
257 | } else {
258 | list = [buffer]
259 | }
260 | } else if (!(list = fixBufferList(buffer))) {
261 | throw new TypeError('Buffer list arguments must be buffers or strings')
262 | }
263 |
264 | port = port >>> 0
265 | if (port === 0 || port > 65535) {
266 | throw new RangeError('Port should be > 0 and < 65536')
267 | }
268 |
269 | // Normalize callback so it's always a function
270 | if (typeof callback !== 'function') {
271 | callback = function () {}
272 | }
273 |
274 | if (self._bindState === BIND_STATE_UNBOUND) self.bind(0)
275 |
276 | // If the socket hasn't been bound yet, push the outbound packet onto the
277 | // send queue and send after binding is complete.
278 | if (self._bindState !== BIND_STATE_BOUND) {
279 | // If the send queue hasn't been initialized yet, do it, and install an
280 | // event handler that flishes the send queue after binding is done.
281 | if (!self._sendQueue) {
282 | self._sendQueue = []
283 | self.once('listening', function () {
284 | // Flush the send queue.
285 | for (let i = 0; i < self._sendQueue.length; i++) {
286 | self.send.apply(self, self._sendQueue[i])
287 | }
288 | self._sendQueue = undefined
289 | })
290 | }
291 | self._sendQueue.push([buffer, offset, length, port, address, callback])
292 | return
293 | }
294 |
295 | const ab = Buffer.concat(list).buffer
296 |
297 | chrome.sockets.udp.send(self.id, ab, address, port, function (sendInfo) {
298 | if (sendInfo.resultCode < 0) {
299 | const err = new Error('Socket ' + self.id + ' send error ' + sendInfo.resultCode)
300 | callback(err)
301 | self.emit('error', err)
302 | } else {
303 | callback(null)
304 | }
305 | })
306 | }
307 |
308 | function sliceBuffer (buffer, offset, length) {
309 | if (typeof buffer === 'string') {
310 | buffer = Buffer.from(buffer)
311 | } else if (!(buffer instanceof Buffer)) {
312 | throw new TypeError('First argument must be a buffer or string')
313 | }
314 |
315 | offset = offset >>> 0
316 | length = length >>> 0
317 |
318 | // assuming buffer is browser implementation (`buffer` package on npm)
319 | let buf = buffer.buffer
320 | if (buffer.byteOffset || buffer.byteLength !== buf.byteLength) {
321 | buf = buf.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)
322 | }
323 | if (offset || length !== buffer.length) {
324 | buf = buf.slice(offset, length)
325 | }
326 |
327 | return Buffer.from(buf)
328 | }
329 |
330 | function fixBufferList (list) {
331 | const newlist = new Array(list.length)
332 |
333 | for (let i = 0, l = list.length; i < l; i++) {
334 | const buf = list[i]
335 | if (typeof buf === 'string') {
336 | newlist[i] = Buffer.from(buf)
337 | } else if (!(buf instanceof Buffer)) {
338 | return null
339 | } else {
340 | newlist[i] = buf
341 | }
342 | }
343 |
344 | return newlist
345 | }
346 |
347 | /**
348 | * Close the underlying socket and stop listening for data on it.
349 | */
350 | Socket.prototype.close = function () {
351 | const self = this
352 | if (self._destroyed) return
353 |
354 | delete sockets[self.id]
355 | chrome.sockets.udp.close(self.id)
356 | self._destroyed = true
357 |
358 | self.emit('close')
359 | }
360 |
361 | /**
362 | * Returns an object containing the address information for a socket. For UDP
363 | * sockets, this object will contain address, family and port.
364 | *
365 | * @return {Object} information
366 | */
367 | Socket.prototype.address = function () {
368 | const self = this
369 | return {
370 | address: self._address,
371 | port: self._port,
372 | family: 'IPv4'
373 | }
374 | }
375 |
376 | Socket.prototype.setBroadcast = function (flag) {
377 | // No chrome.sockets equivalent
378 | }
379 |
380 | Socket.prototype.setTTL = function (ttl) {
381 | // No chrome.sockets equivalent
382 | }
383 |
384 | // NOTE: Multicast code is untested. Pull requests accepted for bug fixes and to
385 | // add tests!
386 |
387 | /**
388 | * Sets the IP_MULTICAST_TTL socket option. TTL stands for "Time to Live," but
389 | * in this context it specifies the number of IP hops that a packet is allowed
390 | * to go through, specifically for multicast traffic. Each router or gateway
391 | * that forwards a packet decrements the TTL. If the TTL is decremented to 0
392 | * by a router, it will not be forwarded.
393 | *
394 | * The argument to setMulticastTTL() is a number of hops between 0 and 255.
395 | * The default on most systems is 1.
396 | *
397 | * NOTE: The Chrome version of this function is async, whereas the node
398 | * version is sync. Keep this in mind.
399 | *
400 | * @param {number} ttl
401 | * @param {function} callback CHROME-SPECIFIC: Called when the configuration
402 | * operation is done.
403 | */
404 | Socket.prototype.setMulticastTTL = function (ttl, callback) {
405 | const self = this
406 | if (!callback) callback = function () {}
407 | if (self._bindState === BIND_STATE_BOUND) {
408 | setMulticastTTL(callback)
409 | } else {
410 | self._bindTasks.push({
411 | fn: setMulticastTTL,
412 | callback
413 | })
414 | }
415 |
416 | function setMulticastTTL (callback) {
417 | chrome.sockets.udp.setMulticastTimeToLive(self.id, ttl, callback)
418 | }
419 | }
420 |
421 | /**
422 | * Sets or clears the IP_MULTICAST_LOOP socket option. When this option is
423 | * set, multicast packets will also be received on the local interface.
424 | *
425 | * NOTE: The Chrome version of this function is async, whereas the node
426 | * version is sync. Keep this in mind.
427 | *
428 | * @param {boolean} flag
429 | * @param {function} callback CHROME-SPECIFIC: Called when the configuration
430 | * operation is done.
431 | */
432 | Socket.prototype.setMulticastLoopback = function (flag, callback) {
433 | const self = this
434 | if (!callback) callback = function () {}
435 | if (self._bindState === BIND_STATE_BOUND) {
436 | setMulticastLoopback(callback)
437 | } else {
438 | self._bindTasks.push({
439 | fn: setMulticastLoopback,
440 | callback
441 | })
442 | }
443 |
444 | function setMulticastLoopback (callback) {
445 | chrome.sockets.udp.setMulticastLoopbackMode(self.id, flag, callback)
446 | }
447 | }
448 |
449 | /**
450 | * Tells the kernel to join a multicast group with IP_ADD_MEMBERSHIP socket
451 | * option.
452 | *
453 | * If multicastInterface is not specified, the OS will try to add membership
454 | * to all valid interfaces.
455 | *
456 | * NOTE: The Chrome version of this function is async, whereas the node
457 | * version is sync. Keep this in mind.
458 | *
459 | * @param {string} multicastAddress
460 | * @param {string} [multicastInterface] Optional
461 | * @param {function} callback CHROME-SPECIFIC: Called when the configuration
462 | * operation is done.
463 | */
464 | Socket.prototype.addMembership = function (multicastAddress,
465 | multicastInterface,
466 | callback) {
467 | const self = this
468 | if (!callback) callback = function () {}
469 | chrome.sockets.udp.joinGroup(self.id, multicastAddress, callback)
470 | }
471 |
472 | /**
473 | * Opposite of addMembership - tells the kernel to leave a multicast group
474 | * with IP_DROP_MEMBERSHIP socket option. This is automatically called by the
475 | * kernel when the socket is closed or process terminates, so most apps will
476 | * never need to call this.
477 | *
478 | * NOTE: The Chrome version of this function is async, whereas the node
479 | * version is sync. Keep this in mind.
480 | *
481 | * If multicastInterface is not specified, the OS will try to drop membership
482 | * to all valid interfaces.
483 | *
484 | * @param {[type]} multicastAddress
485 | * @param {[type]} multicastInterface Optional
486 | * @param {function} callback CHROME-SPECIFIC: Called when the configuration
487 | * operation is done.
488 | */
489 | Socket.prototype.dropMembership = function (multicastAddress,
490 | multicastInterface,
491 | callback) {
492 | const self = this
493 | if (!callback) callback = function () {}
494 | chrome.sockets.udp.leaveGroup(self.id, multicastAddress, callback)
495 | }
496 |
497 | Socket.prototype.unref = function () {
498 | // No chrome.sockets equivalent
499 | }
500 |
501 | Socket.prototype.ref = function () {
502 | // No chrome.sockets equivalent
503 | }
504 |
--------------------------------------------------------------------------------