~ubuntu-branches/ubuntu/vivid/emscripten/vivid

« back to all changes in this revision

Viewing changes to src/library_sockfs.js

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2013-09-20 22:44:35 UTC
  • mfrom: (4.1.1 sid)
  • Revision ID: package-import@ubuntu.com-20130920224435-apuwj4fsl3fqv1a6
Tags: 1.5.6~20130920~6010666-1
* New snapshot release
* Update the list of supported architectures to the same as libv8
  (Closes: #723129)
* emlibtool has been removed from upstream.
* Fix warning syntax-error-in-dep5-copyright
* Refresh of the patches

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
mergeInto(LibraryManager.library, {
 
2
  $SOCKFS__postset: '__ATINIT__.push({ func: function() { SOCKFS.root = FS.mount(SOCKFS, {}, null); } });',
 
3
  $SOCKFS__deps: ['$FS'],
 
4
  $SOCKFS: {
 
5
    mount: function(mount) {
 
6
      return FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0);
 
7
    },
 
8
    nextname: function() {
 
9
      if (!SOCKFS.nextname.current) {
 
10
        SOCKFS.nextname.current = 0;
 
11
      }
 
12
      return 'socket[' + (SOCKFS.nextname.current++) + ']';
 
13
    },
 
14
    createSocket: function(family, type, protocol) {
 
15
      var streaming = type == {{{ cDefine('SOCK_STREAM') }}};
 
16
      if (protocol) {
 
17
        assert(streaming == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if SOCK_STREAM, must be tcp
 
18
      }
 
19
 
 
20
      // create our internal socket structure
 
21
      var sock = {
 
22
        family: family,
 
23
        type: type,
 
24
        protocol: protocol,
 
25
        server: null,
 
26
        peers: {},
 
27
        pending: [],
 
28
        recv_queue: [],
 
29
#if SOCKET_WEBRTC
 
30
#else
 
31
        sock_ops: SOCKFS.websocket_sock_ops
 
32
#endif
 
33
      };
 
34
 
 
35
      // create the filesystem node to store the socket structure
 
36
      var name = SOCKFS.nextname();
 
37
      var node = FS.createNode(SOCKFS.root, name, {{{ cDefine('S_IFSOCK') }}}, 0);
 
38
      node.sock = sock;
 
39
 
 
40
      // and the wrapping stream that enables library functions such
 
41
      // as read and write to indirectly interact with the socket
 
42
      var stream = FS.createStream({
 
43
        path: name,
 
44
        node: node,
 
45
        flags: FS.modeStringToFlags('r+'),
 
46
        seekable: false,
 
47
        stream_ops: SOCKFS.stream_ops
 
48
      });
 
49
 
 
50
      // map the new stream to the socket structure (sockets have a 1:1
 
51
      // relationship with a stream)
 
52
      sock.stream = stream;
 
53
 
 
54
      return sock;
 
55
    },
 
56
    getSocket: function(fd) {
 
57
      var stream = FS.getStream(fd);
 
58
      if (!stream || !FS.isSocket(stream.node.mode)) {
 
59
        return null;
 
60
      }
 
61
      return stream.node.sock;
 
62
    },
 
63
    // node and stream ops are backend agnostic
 
64
    stream_ops: {
 
65
      poll: function(stream) {
 
66
        var sock = stream.node.sock;
 
67
        return sock.sock_ops.poll(sock);
 
68
      },
 
69
      ioctl: function(stream, request, varargs) {
 
70
        var sock = stream.node.sock;
 
71
        return sock.sock_ops.ioctl(sock, request, varargs);
 
72
      },
 
73
      read: function(stream, buffer, offset, length, position /* ignored */) {
 
74
        var sock = stream.node.sock;
 
75
        var msg = sock.sock_ops.recvmsg(sock, length);
 
76
        if (!msg) {
 
77
          // socket is closed
 
78
          return 0;
 
79
        }
 
80
#if USE_TYPED_ARRAYS == 2
 
81
        buffer.set(msg.buffer, offset);
 
82
#else
 
83
        for (var i = 0; i < size; i++) {
 
84
          buffer[offset + i] = msg.buffer[i];
 
85
        }
 
86
#endif
 
87
        return msg.buffer.length;
 
88
      },
 
89
      write: function(stream, buffer, offset, length, position /* ignored */) {
 
90
        var sock = stream.node.sock;
 
91
        return sock.sock_ops.sendmsg(sock, buffer, offset, length);
 
92
      },
 
93
      close: function(stream) {
 
94
        var sock = stream.node.sock;
 
95
        sock.sock_ops.close(sock);
 
96
      }
 
97
    },
 
98
    // backend-specific stream ops
 
99
    websocket_sock_ops: {
 
100
      //
 
101
      // peers are a small wrapper around a WebSocket to help in
 
102
      // emulating dgram sockets
 
103
      //
 
104
      // these functions aren't actually sock_ops members, but we're
 
105
      // abusing the namespace to organize them
 
106
      //
 
107
      createPeer: function(sock, addr, port) {
 
108
        var ws;
 
109
 
 
110
        if (typeof addr === 'object') {
 
111
          ws = addr;
 
112
          addr = null;
 
113
          port = null;
 
114
        }
 
115
 
 
116
        if (ws) {
 
117
          // for sockets that've already connected (e.g. we're the server)
 
118
          // we can inspect the _socket property for the address
 
119
          if (ws._socket) {
 
120
            addr = ws._socket.remoteAddress;
 
121
            port = ws._socket.remotePort;
 
122
          }
 
123
          // if we're just now initializing a connection to the remote,
 
124
          // inspect the url property
 
125
          else {
 
126
            var result = /ws[s]?:\/\/([^:]+):(\d+)/.exec(ws.url);
 
127
            if (!result) {
 
128
              throw new Error('WebSocket URL must be in the format ws(s)://address:port');
 
129
            }
 
130
            addr = result[1];
 
131
            port = parseInt(result[2], 10);
 
132
          }
 
133
        } else {
 
134
          // create the actual websocket object and connect
 
135
          try {
 
136
            var url = 'ws://' + addr + ':' + port;
 
137
#if SOCKET_DEBUG
 
138
            console.log('connect: ' + url);
 
139
#endif
 
140
            // the node ws library API is slightly different than the browser's
 
141
            var opts = ENVIRONMENT_IS_NODE ? {} : ['binary'];
 
142
            ws = new WebSocket(url, opts);
 
143
            ws.binaryType = 'arraybuffer';
 
144
          } catch (e) {
 
145
            throw new FS.ErrnoError(ERRNO_CODES.EHOSTUNREACH);
 
146
          }
 
147
        }
 
148
 
 
149
#if SOCKET_DEBUG
 
150
        Module.print('websocket adding peer: ' + addr + ':' + port);
 
151
#endif
 
152
 
 
153
        var peer = {
 
154
          addr: addr,
 
155
          port: port,
 
156
          socket: ws,
 
157
          dgram_send_queue: []
 
158
        };
 
159
 
 
160
        SOCKFS.websocket_sock_ops.addPeer(sock, peer);
 
161
        SOCKFS.websocket_sock_ops.handlePeerEvents(sock, peer);
 
162
 
 
163
        // if this is a bound dgram socket, send the port number first to allow
 
164
        // us to override the ephemeral port reported to us by remotePort on the
 
165
        // remote end.
 
166
        if (sock.type === {{{ cDefine('SOCK_DGRAM') }}} && typeof sock.sport !== 'undefined') {
 
167
#if SOCKET_DEBUG
 
168
          Module.print('websocket queuing port message (port ' + sock.sport + ')');
 
169
#endif
 
170
          peer.dgram_send_queue.push(new Uint8Array([
 
171
              255, 255, 255, 255,
 
172
              'p'.charCodeAt(0), 'o'.charCodeAt(0), 'r'.charCodeAt(0), 't'.charCodeAt(0),
 
173
              ((sock.sport & 0xff00) >> 8) , (sock.sport & 0xff)
 
174
          ]));
 
175
        }
 
176
 
 
177
        return peer;
 
178
      },
 
179
      getPeer: function(sock, addr, port) {
 
180
        return sock.peers[addr + ':' + port];
 
181
      },
 
182
      addPeer: function(sock, peer) {
 
183
        sock.peers[peer.addr + ':' + peer.port] = peer;
 
184
      },
 
185
      removePeer: function(sock, peer) {
 
186
        delete sock.peers[peer.addr + ':' + peer.port];
 
187
      },
 
188
      handlePeerEvents: function(sock, peer) {
 
189
        var first = true;
 
190
 
 
191
        var handleOpen = function () {
 
192
#if SOCKET_DEBUG
 
193
          Module.print('websocket handle open');
 
194
#endif
 
195
          try {
 
196
            var queued = peer.dgram_send_queue.shift();
 
197
            while (queued) {
 
198
#if SOCKET_DEBUG
 
199
              Module.print('websocket sending queued data (' + queued.byteLength + ' bytes): ' + [Array.prototype.slice.call(new Uint8Array(queued))]);
 
200
#endif
 
201
              peer.socket.send(queued);
 
202
              queued = peer.dgram_send_queue.shift();
 
203
            }
 
204
          } catch (e) {
 
205
            // not much we can do here in the way of proper error handling as we've already
 
206
            // lied and said this data was sent. shut it down.
 
207
            peer.socket.close();
 
208
          }
 
209
        };
 
210
 
 
211
        var handleMessage = function(data) {
 
212
          assert(typeof data !== 'string' && data.byteLength !== undefined);  // must receive an ArrayBuffer
 
213
          data = new Uint8Array(data);  // make a typed array view on the array buffer
 
214
 
 
215
#if SOCKET_DEBUG
 
216
          Module.print('websocket handle message (' + data.byteLength + ' bytes): ' + [Array.prototype.slice.call(data)]);
 
217
#endif
 
218
 
 
219
          // if this is the port message, override the peer's port with it
 
220
          var wasfirst = first;
 
221
          first = false;
 
222
          if (wasfirst &&
 
223
              data.length === 10 &&
 
224
              data[0] === 255 && data[1] === 255 && data[2] === 255 && data[3] === 255 &&
 
225
              data[4] === 'p'.charCodeAt(0) && data[5] === 'o'.charCodeAt(0) && data[6] === 'r'.charCodeAt(0) && data[7] === 't'.charCodeAt(0)) {
 
226
            // update the peer's port and it's key in the peer map
 
227
            var newport = ((data[8] << 8) | data[9]);
 
228
            SOCKFS.websocket_sock_ops.removePeer(sock, peer);
 
229
            peer.port = newport;
 
230
            SOCKFS.websocket_sock_ops.addPeer(sock, peer);
 
231
            return;
 
232
          }
 
233
 
 
234
          sock.recv_queue.push({ addr: peer.addr, port: peer.port, data: data });
 
235
        };
 
236
 
 
237
        if (ENVIRONMENT_IS_NODE) {
 
238
          peer.socket.on('open', handleOpen);
 
239
          peer.socket.on('message', function(data, flags) {
 
240
            if (!flags.binary) {
 
241
              return;
 
242
            }
 
243
            handleMessage((new Uint8Array(data)).buffer);  // copy from node Buffer -> ArrayBuffer
 
244
          });
 
245
          peer.socket.on('error', function() {
 
246
            // don't throw
 
247
          });
 
248
        } else {
 
249
          peer.socket.onopen = handleOpen;
 
250
          peer.socket.onmessage = function(event) {
 
251
            handleMessage(event.data);
 
252
          };
 
253
        }
 
254
      },
 
255
 
 
256
      //
 
257
      // actual sock ops
 
258
      //
 
259
      poll: function(sock) {
 
260
        if (sock.type === {{{ cDefine('SOCK_STREAM') }}} && sock.server) {
 
261
          // listen sockets should only say they're available for reading
 
262
          // if there are pending clients.
 
263
          return sock.pending.length ? ({{{ cDefine('POLLRDNORM') }}} | {{{ cDefine('POLLIN') }}}) : 0;
 
264
        }
 
265
 
 
266
        var mask = 0;
 
267
        var dest = sock.type === {{{ cDefine('SOCK_STREAM') }}} ?  // we only care about the socket state for connection-based sockets
 
268
          SOCKFS.websocket_sock_ops.getPeer(sock, sock.daddr, sock.dport) :
 
269
          null;
 
270
 
 
271
        if (sock.recv_queue.length ||
 
272
            !dest ||  // connection-less sockets are always ready to read
 
273
            (dest && dest.socket.readyState === dest.socket.CLOSING) ||
 
274
            (dest && dest.socket.readyState === dest.socket.CLOSED)) {  // let recv return 0 once closed
 
275
          mask |= ({{{ cDefine('POLLRDNORM') }}} | {{{ cDefine('POLLIN') }}});
 
276
        }
 
277
 
 
278
        if (!dest ||  // connection-less sockets are always ready to write
 
279
            (dest && dest.socket.readyState === dest.socket.OPEN)) {
 
280
          mask |= {{{ cDefine('POLLOUT') }}};
 
281
        }
 
282
 
 
283
        if ((dest && dest.socket.readyState === dest.socket.CLOSING) ||
 
284
            (dest && dest.socket.readyState === dest.socket.CLOSED)) {
 
285
          mask |= {{{ cDefine('POLLHUP') }}};
 
286
        }
 
287
 
 
288
        return mask;
 
289
      },
 
290
      ioctl: function(sock, request, arg) {
 
291
        switch (request) {
 
292
          case {{{ cDefine('FIONREAD') }}}:
 
293
            var bytes = 0;
 
294
            if (sock.recv_queue.length) {
 
295
              bytes = sock.recv_queue[0].data.length;
 
296
            }
 
297
            {{{ makeSetValue('arg', '0', 'bytes', 'i32') }}};
 
298
            return 0;
 
299
          default:
 
300
            return ERRNO_CODES.EINVAL;
 
301
        }
 
302
      },
 
303
      close: function(sock) {
 
304
        // if we've spawned a listen server, close it
 
305
        if (sock.server) {
 
306
          try {
 
307
            sock.server.close();
 
308
          } catch (e) {
 
309
          }
 
310
          sock.server = null;
 
311
        }
 
312
        // close any peer connections
 
313
        var peers = Object.keys(sock.peers);
 
314
        for (var i = 0; i < peers.length; i++) {
 
315
          var peer = sock.peers[peers[i]];
 
316
          try {
 
317
            peer.socket.close();
 
318
          } catch (e) {
 
319
          }
 
320
          SOCKFS.websocket_sock_ops.removePeer(sock, peer);
 
321
        }
 
322
        return 0;
 
323
      },
 
324
      bind: function(sock, addr, port) {
 
325
        if (typeof sock.saddr !== 'undefined' || typeof sock.sport !== 'undefined') {
 
326
          throw new FS.ErrnoError(ERRNO_CODES.EINVAL);  // already bound
 
327
        }
 
328
        sock.saddr = addr;
 
329
        sock.sport = port || _mkport();
 
330
        // in order to emulate dgram sockets, we need to launch a listen server when
 
331
        // binding on a connection-less socket
 
332
        // note: this is only required on the server side
 
333
        if (sock.type === {{{ cDefine('SOCK_DGRAM') }}}) {
 
334
          // close the existing server if it exists
 
335
          if (sock.server) {
 
336
            sock.server.close();
 
337
            sock.server = null;
 
338
          }
 
339
          // swallow error operation not supported error that occurs when binding in the
 
340
          // browser where this isn't supported
 
341
          try {
 
342
            sock.sock_ops.listen(sock, 0);
 
343
          } catch (e) {
 
344
            if (!(e instanceof FS.ErrnoError)) throw e;
 
345
            if (e.errno !== ERRNO_CODES.EOPNOTSUPP) throw e;
 
346
          }
 
347
        }
 
348
      },
 
349
      connect: function(sock, addr, port) {
 
350
        if (sock.server) {
 
351
          throw new FS.ErrnoError(ERRNO_CODS.EOPNOTSUPP);
 
352
        }
 
353
 
 
354
        // TODO autobind
 
355
        // if (!sock.addr && sock.type == {{{ cDefine('SOCK_DGRAM') }}}) {
 
356
        // }
 
357
 
 
358
        // early out if we're already connected / in the middle of connecting
 
359
        if (typeof sock.daddr !== 'undefined' && typeof sock.dport !== 'undefined') {
 
360
          var dest = SOCKFS.websocket_sock_ops.getPeer(sock, sock.daddr, sock.dport);
 
361
          if (dest) {
 
362
            if (dest.socket.readyState === dest.socket.CONNECTING) {
 
363
              throw new FS.ErrnoError(ERRNO_CODES.EALREADY);
 
364
            } else {
 
365
              throw new FS.ErrnoError(ERRNO_CODES.EISCONN);
 
366
            }
 
367
          }
 
368
        }
 
369
 
 
370
        // add the socket to our peer list and set our
 
371
        // destination address / port to match
 
372
        var peer = SOCKFS.websocket_sock_ops.createPeer(sock, addr, port);
 
373
        sock.daddr = peer.addr;
 
374
        sock.dport = peer.port;
 
375
 
 
376
        // always "fail" in non-blocking mode
 
377
        throw new FS.ErrnoError(ERRNO_CODES.EINPROGRESS);
 
378
      },
 
379
      listen: function(sock, backlog) {
 
380
        if (!ENVIRONMENT_IS_NODE) {
 
381
          throw new FS.ErrnoError(ERRNO_CODES.EOPNOTSUPP);
 
382
        }
 
383
        if (sock.server) {
 
384
           throw new FS.ErrnoError(ERRNO_CODES.EINVAL);  // already listening
 
385
        }
 
386
        var WebSocketServer = require('ws').Server;
 
387
        var host = sock.saddr;
 
388
#if SOCKET_DEBUG
 
389
        console.log('listen: ' + host + ':' + sock.sport);
 
390
#endif
 
391
        sock.server = new WebSocketServer({
 
392
          host: host,
 
393
          port: sock.sport
 
394
          // TODO support backlog
 
395
        });
 
396
 
 
397
        sock.server.on('connection', function(ws) {
 
398
#if SOCKET_DEBUG
 
399
          console.log('received connection from: ' + ws._socket.remoteAddress + ':' + ws._socket.remotePort);
 
400
#endif
 
401
          if (sock.type === {{{ cDefine('SOCK_STREAM') }}}) {
 
402
            var newsock = SOCKFS.createSocket(sock.family, sock.type, sock.protocol);
 
403
 
 
404
            // create a peer on the new socket
 
405
            var peer = SOCKFS.websocket_sock_ops.createPeer(newsock, ws);
 
406
            newsock.daddr = peer.addr;
 
407
            newsock.dport = peer.port;
 
408
 
 
409
            // push to queue for accept to pick up
 
410
            sock.pending.push(newsock);
 
411
          } else {
 
412
            // create a peer on the listen socket so calling sendto
 
413
            // with the listen socket and an address will resolve
 
414
            // to the correct client
 
415
            SOCKFS.websocket_sock_ops.createPeer(sock, ws);
 
416
          }
 
417
        });
 
418
        sock.server.on('closed', function() {
 
419
          sock.server = null;
 
420
        });
 
421
        sock.server.on('error', function() {
 
422
          // don't throw
 
423
        });
 
424
      },
 
425
      accept: function(listensock) {
 
426
        if (!listensock.server) {
 
427
          throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
 
428
        }
 
429
        var newsock = listensock.pending.shift();
 
430
        newsock.stream.flags = listensock.stream.flags;
 
431
        return newsock;
 
432
      },
 
433
      getname: function(sock, peer) {
 
434
        var addr, port;
 
435
        if (peer) {
 
436
          if (sock.daddr === undefined || sock.dport === undefined) {
 
437
            throw new FS.ErrnoError(ERRNO_CODES.ENOTCONN);
 
438
          }
 
439
          addr = sock.daddr;
 
440
          port = sock.dport;
 
441
        } else {
 
442
          // TODO saddr and sport will be set for bind()'d UDP sockets, but what
 
443
          // should we be returning for TCP sockets that've been connect()'d?
 
444
          addr = sock.saddr || 0;
 
445
          port = sock.sport || 0;
 
446
        }
 
447
        return { addr: addr, port: port };
 
448
      },
 
449
      sendmsg: function(sock, buffer, offset, length, addr, port) {
 
450
        if (sock.type === {{{ cDefine('SOCK_DGRAM') }}}) {
 
451
          // connection-less sockets will honor the message address,
 
452
          // and otherwise fall back to the bound destination address
 
453
          if (addr === undefined || port === undefined) {
 
454
            addr = sock.daddr;
 
455
            port = sock.dport;
 
456
          }
 
457
          // if there was no address to fall back to, error out
 
458
          if (addr === undefined || port === undefined) {
 
459
            throw new FS.ErrnoError(ERRNO_CODES.EDESTADDRREQ);
 
460
          }
 
461
        } else {
 
462
          // connection-based sockets will only use the bound
 
463
          addr = sock.daddr;
 
464
          port = sock.dport;
 
465
        }
 
466
 
 
467
        // find the peer for the destination address
 
468
        var dest = SOCKFS.websocket_sock_ops.getPeer(sock, addr, port);
 
469
 
 
470
        // early out if not connected with a connection-based socket
 
471
        if (sock.type === {{{ cDefine('SOCK_STREAM') }}}) {
 
472
          if (!dest || dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) {
 
473
            throw new FS.ErrnoError(ERRNO_CODES.ENOTCONN);
 
474
          } else if (dest.socket.readyState === dest.socket.CONNECTING) {
 
475
            throw new FS.ErrnoError(ERRNO_CODES.EAGAIN);
 
476
          }
 
477
        }
 
478
 
 
479
        // create a copy of the incoming data to send, as the WebSocket API
 
480
        // doesn't work entirely with an ArrayBufferView, it'll just send
 
481
        // the entire underlying buffer
 
482
        var data;
 
483
        if (buffer instanceof Array || buffer instanceof ArrayBuffer) {
 
484
          data = buffer.slice(offset, offset + length);
 
485
        } else {  // ArrayBufferView
 
486
          data = buffer.buffer.slice(buffer.byteOffset + offset, buffer.byteOffset + offset + length);
 
487
        }
 
488
 
 
489
        // if we're emulating a connection-less dgram socket and don't have
 
490
        // a cached connection, queue the buffer to send upon connect and
 
491
        // lie, saying the data was sent now.
 
492
        if (sock.type === {{{ cDefine('SOCK_DGRAM') }}}) {
 
493
          if (!dest || dest.socket.readyState !== dest.socket.OPEN) {
 
494
            // if we're not connected, open a new connection
 
495
            if (!dest || dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) {
 
496
              dest = SOCKFS.websocket_sock_ops.createPeer(sock, addr, port);
 
497
            }
 
498
#if SOCKET_DEBUG
 
499
            Module.print('websocket queuing (' + length + ' bytes): ' + [Array.prototype.slice.call(new Uint8Array(data))]);
 
500
#endif
 
501
            dest.dgram_send_queue.push(data);
 
502
            return length;
 
503
          }
 
504
        }
 
505
 
 
506
        try {
 
507
#if SOCKET_DEBUG
 
508
          Module.print('websocket send (' + length + ' bytes): ' + [Array.prototype.slice.call(new Uint8Array(data))]);
 
509
#endif
 
510
          // send the actual data
 
511
          dest.socket.send(data);
 
512
          return length;
 
513
        } catch (e) {
 
514
          throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
 
515
        }
 
516
      },
 
517
      recvmsg: function(sock, length) {
 
518
        // http://pubs.opengroup.org/onlinepubs/7908799/xns/recvmsg.html
 
519
        if (sock.type === {{{ cDefine('SOCK_STREAM') }}} && sock.server) {
 
520
          // tcp servers should not be recv()'ing on the listen socket
 
521
          throw new FS.ErrnoError(ERRNO_CODES.ENOTCONN);
 
522
        }
 
523
 
 
524
        var queued = sock.recv_queue.shift();
 
525
        if (!queued) {
 
526
          if (sock.type === {{{ cDefine('SOCK_STREAM') }}}) {
 
527
            var dest = SOCKFS.websocket_sock_ops.getPeer(sock, sock.daddr, sock.dport);
 
528
 
 
529
            if (!dest) {
 
530
              // if we have a destination address but are not connected, error out
 
531
              throw new FS.ErrnoError(ERRNO_CODES.ENOTCONN);
 
532
            }
 
533
            else if (dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) {
 
534
              // return null if the socket has closed
 
535
              return null;
 
536
            }
 
537
            else {
 
538
              // else, our socket is in a valid state but truly has nothing available
 
539
              throw new FS.ErrnoError(ERRNO_CODES.EAGAIN);
 
540
            }
 
541
          } else {
 
542
            throw new FS.ErrnoError(ERRNO_CODES.EAGAIN);
 
543
          }
 
544
        }
 
545
 
 
546
        // queued.data will be an ArrayBuffer if it's unadulterated, but if it's
 
547
        // requeued TCP data it'll be an ArrayBufferView
 
548
        var queuedLength = queued.data.byteLength || queued.data.length;
 
549
        var queuedOffset = queued.data.byteOffset || 0;
 
550
        var queuedBuffer = queued.data.buffer || queued.data;
 
551
        var bytesRead = Math.min(length, queuedLength);
 
552
        var res = {
 
553
          buffer: new Uint8Array(queuedBuffer, queuedOffset, bytesRead),
 
554
          addr: queued.addr,
 
555
          port: queued.port
 
556
        };
 
557
 
 
558
#if SOCKET_DEBUG
 
559
        Module.print('websocket read (' + bytesRead + ' bytes): ' + [Array.prototype.slice.call(res.buffer)]);
 
560
#endif
 
561
 
 
562
        // push back any unread data for TCP connections
 
563
        if (sock.type === {{{ cDefine('SOCK_STREAM') }}} && bytesRead < queuedLength) {
 
564
          var bytesRemaining = queuedLength - bytesRead;
 
565
#if SOCKET_DEBUG
 
566
          Module.print('websocket read: put back ' + bytesRemaining + ' bytes');
 
567
#endif
 
568
          queued.data = new Uint8Array(queuedBuffer, queuedOffset + bytesRead, bytesRemaining);
 
569
          sock.recv_queue.unshift(queued);
 
570
        }
 
571
 
 
572
        return res;
 
573
      }
 
574
    }
 
575
  }
 
576
});
 
 
b'\\ No newline at end of file'