294
326
if (typeof XMLHttpRequest !== 'undefined') {
295
327
if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc';
296
328
// Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse.
297
var LazyUint8Array = function(chunkSize, length) {
298
this.length = length;
299
this.chunkSize = chunkSize;
329
var LazyUint8Array = function() {
330
this.lengthKnown = false;
300
331
this.chunks = []; // Loaded chunks. Index is the chunk number
302
333
LazyUint8Array.prototype.get = function(idx) {
303
334
if (idx > this.length-1 || idx < 0) {
304
335
return undefined;
306
var chunkOffset = idx % chunkSize;
307
var chunkNum = Math.floor(idx / chunkSize);
337
var chunkOffset = idx % this.chunkSize;
338
var chunkNum = Math.floor(idx / this.chunkSize);
308
339
return this.getter(chunkNum)[chunkOffset];
310
341
LazyUint8Array.prototype.setDataGetter = function(getter) {
311
342
this.getter = getter;
315
var xhr = new XMLHttpRequest();
316
xhr.open('HEAD', url, false);
318
if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
319
var datalength = Number(xhr.getResponseHeader("Content-length"));
321
var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
345
LazyUint8Array.prototype.cacheLength = function() {
347
var xhr = new XMLHttpRequest();
348
xhr.open('HEAD', url, false);
350
if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
351
var datalength = Number(xhr.getResponseHeader("Content-length"));
353
var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
322
354
#if SMALL_XHR_CHUNKS
323
var chunkSize = 1024; // Chunk size in bytes
355
var chunkSize = 1024; // Chunk size in bytes
325
var chunkSize = 1024*1024; // Chunk size in bytes
357
var chunkSize = 1024*1024; // Chunk size in bytes
327
if (!hasByteServing) chunkSize = datalength;
329
// Function to get a range from the remote URL.
330
var doXHR = (function(from, to) {
331
if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
332
if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
334
// TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
335
var xhr = new XMLHttpRequest();
336
xhr.open('GET', url, false);
337
if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
339
// Some hints to the browser that we want binary data.
340
if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
341
if (xhr.overrideMimeType) {
342
xhr.overrideMimeType('text/plain; charset=x-user-defined');
346
if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
347
if (xhr.response !== undefined) {
348
return new Uint8Array(xhr.response || []);
350
return intArrayFromString(xhr.responseText || '', true);
354
var lazyArray = new LazyUint8Array(chunkSize, datalength);
355
lazyArray.setDataGetter(function(chunkNum) {
356
var start = chunkNum * lazyArray.chunkSize;
357
var end = (chunkNum+1) * lazyArray.chunkSize - 1; // including this byte
358
end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
359
if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
360
lazyArray.chunks[chunkNum] = doXHR(start, end);
362
if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
363
return lazyArray.chunks[chunkNum];
360
if (!hasByteServing) chunkSize = datalength;
362
// Function to get a range from the remote URL.
363
var doXHR = (function(from, to) {
364
if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
365
if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
367
// TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
368
var xhr = new XMLHttpRequest();
369
xhr.open('GET', url, false);
370
if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
372
// Some hints to the browser that we want binary data.
373
if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
374
if (xhr.overrideMimeType) {
375
xhr.overrideMimeType('text/plain; charset=x-user-defined');
379
if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
380
if (xhr.response !== undefined) {
381
return new Uint8Array(xhr.response || []);
383
return intArrayFromString(xhr.responseText || '', true);
386
var lazyArray = this;
387
lazyArray.setDataGetter(function(chunkNum) {
388
var start = chunkNum * chunkSize;
389
var end = (chunkNum+1) * chunkSize - 1; // including this byte
390
end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
391
if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
392
lazyArray.chunks[chunkNum] = doXHR(start, end);
394
if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
395
return lazyArray.chunks[chunkNum];
398
this._length = datalength;
399
this._chunkSize = chunkSize;
400
this.lengthKnown = true;
403
var lazyArray = new LazyUint8Array();
404
Object.defineProperty(lazyArray, "length", {
406
if(!this.lengthKnown) {
412
Object.defineProperty(lazyArray, "chunkSize", {
414
if(!this.lengthKnown) {
417
return this._chunkSize;
365
421
var properties = { isDevice: false, contents: lazyArray };
367
423
var properties = { isDevice: false, url: url };
6592
6717
// ==========================================================================
6594
6719
$ERRNO_CODES: {
6653
ENOTRECOVERABLE: 131,
6664
EPROTONOSUPPORT: 93,
6826
EPROTONOSUPPORT: 123,
6827
ESOCKTNOSUPPORT: 124,
6844
ENOTRECOVERABLE: 141,
6677
6848
$ERRNO_MESSAGES: {
6850
1: 'Not super-user',
6678
6851
2: 'No such file or directory',
6852
3: 'No such process',
6853
4: 'Interrupted system call',
6855
6: 'No such device or address',
6856
7: 'Arg list too long',
6857
8: 'Exec format error',
6858
9: 'Bad file number',
6860
11: 'No more processes',
6861
12: 'Not enough core',
6679
6862
13: 'Permission denied',
6680
98: 'Address already in use',
6681
99: 'Cannot assign requested address',
6682
97: 'Address family not supported by protocol',
6683
11: 'Resource temporarily unavailable',
6684
114: 'Operation already in progress',
6685
9: 'Bad file descriptor',
6687
16: 'Device or resource busy',
6688
125: 'Operation canceled',
6689
10: 'No child processes',
6690
103: 'Software caused connection abort',
6691
111: 'Connection refused',
6692
104: 'Connection reset by peer',
6693
35: 'Resource deadlock avoided',
6694
89: 'Destination address required',
6695
33: 'Numerical argument out of domain',
6696
122: 'Disk quota exceeded',
6864
15: 'Block device required',
6865
16: 'Mount device busy',
6697
6866
17: 'File exists',
6699
27: 'File too large',
6700
113: 'No route to host',
6701
43: 'Identifier removed',
6702
84: 'Invalid or incomplete multibyte or wide character',
6703
115: 'Operation now in progress',
6704
4: 'Interrupted system call',
6867
18: 'Cross-device link',
6868
19: 'No such device',
6869
20: 'Not a directory',
6870
21: 'Is a directory',
6705
6871
22: 'Invalid argument',
6706
5: 'Input/output error',
6707
106: 'Transport endpoint is already connected',
6708
21: 'Is a directory',
6709
40: 'Too many levels of symbolic links',
6872
23: 'Too many open files in system',
6710
6873
24: 'Too many open files',
6874
25: 'Not a typewriter',
6875
26: 'Text file busy',
6876
27: 'File too large',
6877
28: 'No space left on device',
6879
30: 'Read only file system',
6711
6880
31: 'Too many links',
6712
90: 'Message too long',
6713
72: 'Multihop attempted',
6714
36: 'File name too long',
6715
100: 'Network is down',
6716
102: 'Network dropped connection on reset',
6717
101: 'Network is unreachable',
6718
23: 'Too many open files in system',
6719
105: 'No buffer space available',
6720
61: 'No data available',
6721
19: 'No such device',
6722
8: 'Exec format error',
6723
37: 'No locks available',
6724
67: 'Link has been severed',
6725
12: 'Cannot allocate memory',
6726
42: 'No message of desired type',
6727
92: 'Protocol not available',
6728
28: 'No space left on device',
6882
33: 'Math arg out of domain of func',
6883
34: 'Math result not representable',
6884
35: 'No message of desired type',
6885
36: 'Identifier removed',
6886
37: 'Channel number out of range',
6887
38: 'Level 2 not synchronized',
6888
39: 'Level 3 halted',
6889
40: 'Level 3 reset',
6890
41: 'Link number out of range',
6891
42: 'Protocol driver not attached',
6892
43: 'No CSI structure available',
6893
44: 'Level 2 halted',
6894
45: 'Deadlock condition',
6895
46: 'No record locks available',
6896
50: 'Invalid exchange',
6897
51: 'Invalid request descriptor',
6898
52: 'Exchange full',
6900
54: 'Invalid request code',
6902
56: 'File locking deadlock error',
6903
57: 'Bad font file fmt',
6904
60: 'Device not a stream',
6905
61: 'No data (for no delay io)',
6906
62: 'Timer expired',
6729
6907
63: 'Out of streams resources',
6730
60: 'Device not a stream',
6731
38: 'Function not implemented',
6732
107: 'Transport endpoint is not connected',
6733
20: 'Not a directory',
6734
39: 'Directory not empty',
6735
131: 'State not recoverable',
6736
88: 'Socket operation on non-socket',
6737
95: 'Operation not supported',
6738
25: 'Inappropriate ioctl for device',
6739
6: 'No such device or address',
6740
45: 'Op not supported on transport endpoint',
6741
75: 'Value too large for defined data type',
6743
1: 'Operation not permitted',
6908
64: 'Machine is not on the network',
6909
65: 'Package not installed',
6910
66: 'The object is remote',
6911
67: 'The link has been severed',
6912
68: 'Advertise error',
6913
69: 'Srmount error',
6914
70: 'Communication error on send',
6745
6915
71: 'Protocol error',
6746
93: 'Protocol not supported',
6747
91: 'Protocol wrong type for socket',
6748
34: 'Numerical result out of range',
6749
30: 'Read-only file system',
6751
3: 'No such process',
6752
116: 'Stale NFS file handle',
6753
62: 'Timer expired',
6754
110: 'Connection timed out',
6755
26: 'Text file busy',
6756
18: 'Invalid cross-device link'
6916
74: 'Multihop attempted',
6917
75: 'Inode is remote (not really error)',
6918
76: 'Cross mount point (not really error)',
6919
77: 'Trying to read unreadable message',
6920
79: 'Inappropriate file type or format',
6921
80: 'Given log. name not unique',
6922
81: 'f.d. invalid for this operation',
6923
82: 'Remote address changed',
6924
83: 'Can\t access a needed shared lib',
6925
84: 'Accessing a corrupted shared lib',
6926
85: '.lib section in a.out corrupted',
6927
86: 'Attempting to link in too many libs',
6928
87: 'Attempting to exec a shared library',
6929
88: 'Function not implemented',
6930
89: 'No more files',
6931
90: 'Directory not empty',
6932
91: 'File or path name too long',
6933
92: 'Too many symbolic links',
6934
95: 'Operation not supported on transport endpoint',
6935
96: 'Protocol family not supported',
6936
104: 'Connection reset by peer',
6937
105: 'No buffer space available',
6938
106: 'Address family not supported by protocol family',
6939
107: 'Protocol wrong type for socket',
6940
108: 'Socket operation on non-socket',
6941
109: 'Protocol not available',
6942
110: 'Can\'t send after socket shutdown',
6943
111: 'Connection refused',
6944
112: 'Address already in use',
6945
113: 'Connection aborted',
6946
114: 'Network is unreachable',
6947
115: 'Network interface is not configured',
6948
116: 'Connection timed out',
6949
117: 'Host is down',
6950
118: 'Host is unreachable',
6951
119: 'Connection already in progress',
6952
120: 'Socket already connected',
6953
121: 'Destination address required',
6954
122: 'Message too long',
6955
123: 'Unknown protocol',
6956
124: 'Socket type not supported',
6957
125: 'Address not available',
6959
127: 'Socket is already connected',
6960
128: 'Socket is not connected',
6966
134: 'Not supported',
6967
135: 'No medium (in tape drive)',
6968
136: 'No such host or network path',
6969
137: 'Filename exists with different case',
6971
139: 'Value too large for defined data type',
6972
140: 'Operation canceled',
6973
141: 'State not recoverable',
6974
142: 'Previous owner died',
6975
143: 'Streams pipe error',
6758
__setErrNo__postset: '___setErrNo(0);',
6978
__setErrNo__deps: ['__errno_state'],
6979
__setErrNo__postset: '___errno_state = Runtime.staticAlloc(4); {{{ makeSetValue("___errno_state", 0, 0, "i32") }}};',
6759
6980
__setErrNo: function(value) {
6760
6981
// For convenient setting and returning of errno.
6761
if (!___setErrNo.ret) ___setErrNo.ret = allocate([0], 'i32', ALLOC_STATIC);
6762
{{{ makeSetValue('___setErrNo.ret', '0', 'value', 'i32') }}}
6982
{{{ makeSetValue('___errno_state', '0', 'value', 'i32') }}}
6765
6985
__errno_location__deps: ['__setErrNo'],
6766
6986
__errno_location: function() {
6767
return ___setErrNo.ret;
6987
return ___errno_state;
6769
6989
__errno: '__errno_location',
7082
7314
['i32', 'msg_controllen'],
7083
7315
['i32', 'msg_flags'],
7088
connect: function(info) {
7089
console.log('opening ws://' + info.host + ':' + info.port);
7090
info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['binary']);
7091
info.socket.binaryType = 'arraybuffer';
7093
var i32Temp = new Uint32Array(1);
7094
var i8Temp = new Uint8Array(i32Temp.buffer);
7097
info.hasData = function() { return info.inQueue.length > 0 }
7099
var partialBuffer = null; // in datagram mode, inQueue contains full dgram messages; this buffers incomplete data. Must begin with the beginning of a message
7102
info.socket.onmessage = function(event) {
7103
assert(typeof event.data !== 'string' && event.data.byteLength); // must get binary data!
7104
var data = new Uint8Array(event.data); // make a typed array view on the array buffer
7106
Module.print(['onmessage', data.length, '|', Array.prototype.slice.call(data)]);
7109
info.inQueue.push(data);
7111
// we added headers with message sizes, read those to find discrete messages
7112
if (partialBuffer) {
7113
// append to the partial buffer
7114
var newBuffer = new Uint8Array(partialBuffer.length + data.length);
7115
newBuffer.set(partialBuffer);
7116
newBuffer.set(data, partialBuffer.length);
7117
// forget the partial buffer and work on data
7119
partialBuffer = null;
7122
while (currPos+4 < data.length) {
7123
i8Temp.set(data.subarray(currPos, currPos+4));
7124
var currLen = i32Temp[0];
7125
assert(currLen > 0);
7126
if (currPos + 4 + currLen > data.length) {
7127
break; // not enough data has arrived
7131
Module.print(['onmessage message', currLen, '|', Array.prototype.slice.call(data.subarray(currPos, currPos+currLen))]);
7133
info.inQueue.push(data.subarray(currPos, currPos+currLen));
7136
// If data remains, buffer it
7137
if (currPos < data.length) {
7138
partialBuffer = data.subarray(currPos);
7142
function send(data) {
7143
// TODO: if browser accepts views, can optimize this
7145
Module.print('sender actually sending ' + Array.prototype.slice.call(data));
7147
// ok to use the underlying buffer, we created data and know that the buffer starts at the beginning
7148
info.socket.send(data.buffer);
7151
var intervalling = false, interval;
7152
function trySend() {
7153
if (info.socket.readyState != info.socket.OPEN) {
7154
if (!intervalling) {
7155
intervalling = true;
7156
console.log('waiting for socket in order to send');
7157
interval = setInterval(trySend, 100);
7161
for (var i = 0; i < outQueue.length; i++) {
7164
outQueue.length = 0;
7166
intervalling = false;
7167
clearInterval(interval);
7170
info.sender = function(data) {
7172
// add a header with the message size
7173
var header = new Uint8Array(4);
7174
i32Temp[0] = data.length;
7176
outQueue.push(header);
7178
outQueue.push(new Uint8Array(data));
7188
emscripten_set_network_backend__deps: ['$Sockets'],
7189
emscripten_set_network_backend: function(backend) {
7190
Sockets.backend = backend;
7320
/* WebRTC sockets supports several options on the Module object.
7322
* Module['host']: true if this peer is hosting, false otherwise
7323
* Module['webrtc']['broker']: hostname for the p2p broker that this peer should use
7324
* Module['webrtc']['session']: p2p session for that this peer will join, or undefined if this peer is hosting
7325
* Module['webrtc']['hostOptions']: options to pass into p2p library if this peer is hosting
7326
* Module['webrtc']['onpeer']: function(peer, route), invoked when this peer is ready to connect
7327
* Module['webrtc']['onconnect']: function(peer), invoked when a new peer connection is ready
7328
* Module['webrtc']['ondisconnect']: function(peer), invoked when an existing connection is closed
7329
* Module['webrtc']['onerror']: function(error), invoked when an error occurs
7193
7331
socket__deps: ['$Sockets'],
7194
7332
socket: function(family, type, protocol) {
7195
var fd = Sockets.nextFd++;
7333
var fd = FS.createFileHandle({
7336
inQueue: new CircularBuffer(INCOMING_QUEUE_LENGTH),
7337
header: new Uint16Array(2),
7196
7341
assert(fd < 64); // select() assumes socket fd values are in 0..63
7197
7342
var stream = type == {{{ cDefine('SOCK_STREAM') }}};
7198
7343
if (protocol) {
7199
7344
assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if stream, must be tcp
7201
if (Sockets.backend == Sockets.BACKEND_WEBRTC) {
7202
assert(!stream); // If WebRTC, we can only support datagram, not stream
7347
// Open the peer connection if we don't have it already
7348
if (null == Sockets.peer) {
7349
var host = Module['host'];
7350
var broker = Module['webrtc']['broker'];
7351
var session = Module['webrtc']['session'];
7352
var peer = new Peer(broker);
7353
var listenOptions = Module['webrtc']['hostOptions'] || {};
7354
peer.onconnection = function(connection) {
7355
console.log('connected');
7357
/* If this peer is connecting to the host, assign 10.0.0.1 to the host so it can be
7358
reached at a known address.
7360
// Assign 10.0.0.1 to the host
7361
if (session && session === connection['route']) {
7362
addr = 0x0100000a; // 10.0.0.1
7364
addr = Sockets.addrPool.shift();
7366
connection['addr'] = addr;
7367
Sockets.connections[addr] = connection;
7368
connection.ondisconnect = function() {
7369
console.log('disconnect');
7370
// Don't return the host address (10.0.0.1) to the pool
7371
if (!(session && session === Sockets.connections[addr]['route'])) {
7372
Sockets.addrPool.push(addr);
7374
delete Sockets.connections[addr];
7376
if (Module['webrtc']['ondisconnect'] && 'function' === typeof Module['webrtc']['ondisconnect']) {
7377
Module['webrtc']['ondisconnect'](peer);
7380
connection.onerror = function(error) {
7381
if (Module['webrtc']['onerror'] && 'function' === typeof Module['webrtc']['onerror']) {
7382
Module['webrtc']['onerror'](error);
7385
connection.onmessage = function(label, message) {
7386
if ('unreliable' === label) {
7387
handleMessage(addr, message.data);
7391
if (Module['webrtc']['onconnect'] && 'function' === typeof Module['webrtc']['onconnect']) {
7392
Module['webrtc']['onconnect'](peer);
7395
peer.onpending = function(pending) {
7396
console.log('pending from: ', pending['route'], '; initiated by: ', (pending['incoming']) ? 'remote' : 'local');
7398
peer.onerror = function(error) {
7399
console.error(error);
7401
peer.onroute = function(route) {
7402
if (Module['webrtc']['onpeer'] && 'function' === typeof Module['webrtc']['onpeer']) {
7403
Module['webrtc']['onpeer'](peer, route);
7406
function handleMessage(addr, message) {
7408
Module.print("received " + message.byteLength + " raw bytes");
7410
var header = new Uint16Array(message, 0, 2);
7411
if (Sockets.portmap[header[1]]) {
7412
Sockets.portmap[header[1]].inQueue.push([addr, message]);
7414
console.log("unable to deliver message: ", addr, header[1], message);
7417
window.onbeforeunload = function() {
7418
var ids = Object.keys(Sockets.connections);
7419
ids.forEach(function(id) {
7420
Sockets.connections[id].close();
7423
Sockets.peer = peer;
7426
var INCOMING_QUEUE_LENGTH = 64;
7428
function CircularBuffer(max_length) {
7429
var buffer = new Array(++ max_length);
7435
push: function(element) {
7436
buffer[tail ++] = element;
7437
length = Math.min(++ length, max_length - 1);
7438
tail = tail % max_length;
7439
if (tail === head) {
7440
head = (head + 1) % max_length;
7443
shift: function(element) {
7444
if (length < 1) return undefined;
7446
var element = buffer[head];
7448
head = (head + 1) % max_length;
7451
length: function() {
7460
mkport__deps: ['$Sockets'],
7461
mkport: function() {
7462
for(var i = 0; i < Sockets.maxport; ++ i) {
7463
var port = Sockets.nextport ++;
7464
Sockets.nextport = (Sockets.nextport > Sockets.maxport) ? 1 : Sockets.nextport;
7465
if (!Sockets.portmap[port]) {
7469
assert(false, 'all available ports are in use!');
7472
connect: function() {
7473
// Stub: connection-oriented sockets are not supported yet.
7476
bind__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs', 'mkport'],
7477
bind: function(fd, addr, addrlen) {
7478
var info = FS.streams[fd];
7479
if (!info) return -1;
7481
info.port = _ntohs(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16'));
7482
// info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32');
7485
info.port = _mkport();
7487
info.addr = Sockets.localAddr; // 10.0.0.254
7488
info.host = __inet_ntop_raw(info.addr);
7489
info.close = function() {
7490
Sockets.portmap[info.port] = undefined;
7492
Sockets.portmap[info.port] = info;
7493
console.log("bind: ", info.host, info.port);
7497
sendmsg__deps: ['$Sockets', 'bind', '_inet_ntop_raw', 'ntohs'],
7498
sendmsg: function(fd, msg, flags) {
7499
var info = FS.streams[fd];
7500
if (!info) return -1;
7501
// if we are not connected, use the address info in the message
7506
var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
7507
assert(name, 'sendmsg on non-connected socket, and no name/address in the message');
7508
var port = _ntohs(getValue(name + Sockets.sockaddr_in_layout.sin_port, 'i16'));
7509
var addr = getValue(name + Sockets.sockaddr_in_layout.sin_addr, 'i32');
7510
var connection = Sockets.connections[addr];
7511
// var host = __inet_ntop_raw(addr);
7513
if (!(connection && connection.connected)) {
7514
___setErrNo(ERRNO_CODES.EWOULDBLOCK);
7518
var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
7519
var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
7521
Module.print('sendmsg vecs: ' + num);
7524
for (var i = 0; i < num; i++) {
7525
totalSize += {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
7527
var data = new Uint8Array(totalSize);
7529
for (var i = 0; i < num; i++) {
7530
var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
7532
Module.print('sendmsg curr size: ' + currNum);
7534
if (!currNum) continue;
7535
var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}};
7536
data.set(HEAPU8.subarray(currBuf, currBuf+currNum), ret);
7540
info.header[0] = info.port; // src port
7541
info.header[1] = port; // dst port
7543
Module.print('sendmsg port: ' + info.header[0] + ' -> ' + info.header[1]);
7544
Module.print('sendmsg bytes: ' + data.length + ' | ' + Array.prototype.slice.call(data));
7546
var buffer = new Uint8Array(info.header.byteLength + data.byteLength);
7547
buffer.set(new Uint8Array(info.header.buffer));
7548
buffer.set(data, info.header.byteLength);
7550
connection.send('unreliable', buffer.buffer);
7553
recvmsg__deps: ['$Sockets', 'bind', '__setErrNo', '$ERRNO_CODES', 'htons'],
7554
recvmsg: function(fd, msg, flags) {
7555
var info = FS.streams[fd];
7556
if (!info) return -1;
7557
// if we are not connected, use the address info in the message
7559
console.log('recvmsg on unbound socket');
7560
assert(false, 'cannot receive on unbound socket');
7562
if (info.inQueue.length() == 0) {
7563
___setErrNo(ERRNO_CODES.EWOULDBLOCK);
7567
var entry = info.inQueue.shift();
7568
var addr = entry[0];
7569
var message = entry[1];
7570
var header = new Uint16Array(message, 0, info.header.length);
7571
var buffer = new Uint8Array(message, info.header.byteLength);
7573
var bytes = buffer.length;
7575
Module.print('recvmsg port: ' + header[1] + ' <- ' + header[0]);
7576
Module.print('recvmsg bytes: ' + bytes + ' | ' + Array.prototype.slice.call(buffer));
7579
var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
7580
{{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_addr', 'addr', 'i32') }}};
7581
{{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_port', '_htons(header[0])', 'i16') }}};
7584
var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
7585
var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
7587
for (var i = 0; i < num && bytes > 0; i++) {
7588
var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
7590
Module.print('recvmsg loop ' + [i, num, bytes, currNum]);
7592
if (!currNum) continue;
7593
currNum = Math.min(currNum, bytes); // XXX what should happen when we partially fill a buffer..?
7595
var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}};
7597
Module.print('recvmsg call recv ' + currNum);
7599
HEAPU8.set(buffer.subarray(bufferPos, bufferPos + currNum), currBuf);
7600
bufferPos += currNum;
7605
shutdown: function(fd, how) {
7606
var info = FS.streams[fd];
7607
if (!info) return -1;
7609
FS.removeFileHandle(fd);
7612
ioctl: function(fd, request, varargs) {
7613
var info = FS.streams[fd];
7614
if (!info) return -1;
7616
if (info.hasData()) {
7617
bytes = info.inQueue[0].length;
7619
var dest = {{{ makeGetValue('varargs', '0', 'i32') }}};
7620
{{{ makeSetValue('dest', '0', 'bytes', 'i32') }}};
7624
setsockopt: function(d, level, optname, optval, optlen) {
7625
console.log('ignoring setsockopt command');
7629
accept: function(fd, addr, addrlen) {
7630
// TODO: webrtc queued incoming connections, etc.
7631
// For now, the model is that bind does a connect, and we "accept" that one connection,
7632
// which has host:port the same as ours. We also return the same socket fd.
7633
var info = FS.streams[fd];
7634
if (!info) return -1;
7636
setValue(addr + Sockets.sockaddr_in_layout.sin_addr, info.addr, 'i32');
7637
setValue(addr + Sockets.sockaddr_in_layout.sin_port, info.port, 'i32');
7638
setValue(addrlen, Sockets.sockaddr_in_layout.__size__, 'i32');
7643
select: function(nfds, readfds, writefds, exceptfds, timeout) {
7644
// readfds are supported,
7645
// writefds checks socket open status
7646
// exceptfds not supported
7647
// timeout is always 0 - fully async
7650
var errorCondition = 0;
7652
function canRead(info) {
7653
return info.inQueue.length() > 0;
7656
function canWrite(info) {
7660
function checkfds(nfds, fds, can) {
7666
var srcLow = {{{ makeGetValue('fds', 0, 'i32') }}};
7667
var srcHigh = {{{ makeGetValue('fds', 4, 'i32') }}};
7668
nfds = Math.min(64, nfds); // fd sets have 64 bits
7670
for (var fd = 0; fd < nfds; fd++) {
7671
var mask = 1 << (fd % 32), int = fd < 32 ? srcLow : srcHigh;
7673
// index is in the set, check if it is ready for read
7674
var info = FS.streams[fd];
7675
if (info && can(info)) {
7677
fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask);
7683
{{{ makeSetValue('fds', 0, 'dstLow', 'i32') }}};
7684
{{{ makeSetValue('fds', 4, 'dstHigh', 'i32') }}};
7688
var totalHandles = checkfds(nfds, readfds, canRead) + checkfds(nfds, writefds, canWrite);
7689
if (errorCondition) {
7690
___setErrNo(ERRNO_CODES.EBADF);
7693
return totalHandles;
7697
socket__deps: ['$Sockets'],
7698
socket: function(family, type, protocol) {
7699
var stream = type == {{{ cDefine('SOCK_STREAM') }}};
7701
assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if SOCK_STREAM, must be tcp
7703
var fd = FS.createFileHandle({
7205
7704
connected: false,
7708
assert(fd < 64); // select() assumes socket fd values are in 0..63
7211
connect__deps: ['$Sockets', '_inet_ntop_raw', 'htons', 'gethostbyname'],
7712
connect__deps: ['$FS', '$Sockets', '_inet_ntop_raw', 'ntohs', 'gethostbyname'],
7212
7713
connect: function(fd, addr, addrlen) {
7213
var info = Sockets.fds[fd];
7714
var info = FS.streams[fd];
7214
7715
if (!info) return -1;
7215
7716
info.connected = true;
7216
7717
info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32');