aboutsummaryrefslogtreecommitdiff
path: root/src/library.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/library.js')
-rw-r--r--src/library.js271
1 files changed, 256 insertions, 15 deletions
diff --git a/src/library.js b/src/library.js
index 594ba931..69642151 100644
--- a/src/library.js
+++ b/src/library.js
@@ -382,12 +382,14 @@ LibraryManager.library = {
// You can also call this with a typed array instead of a url. It will then
// do preloading for the Image/Audio part, as if the typed array were the
// result of an XHR that you did manually.
- createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror) {
+ createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile) {
Browser.ensureObjects();
var fullname = FS.joinPath([parent, name], true);
function processData(byteArray) {
function finish(byteArray) {
- FS.createDataFile(parent, name, byteArray, canRead, canWrite);
+ if (!dontCreateFile) {
+ FS.createDataFile(parent, name, byteArray, canRead, canWrite);
+ }
if (onload) onload();
removeRunDependency('cp ' + fullname);
}
@@ -1286,7 +1288,7 @@ LibraryManager.library = {
return 0;
case {{{ cDefine('F_SETOWN') }}}:
case {{{ cDefine('F_GETOWN') }}}:
- // These are for sockets. We don't have them implemented (yet?).
+ // These are for sockets. We don't have them fully implemented yet.
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
default:
@@ -2498,8 +2500,8 @@ LibraryManager.library = {
while ((curr < max_ || isNaN(max_)) && next > 0) {
if (!(next in __scanString.whiteSpace) && // stop on whitespace
(type == 's' ||
- ((type === 'd' || type == 'u') && ((next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0)) ||
- (first && next == '-'.charCodeAt(0)))) ||
+ ((type === 'd' || type == 'u' || type == 'i') && ((next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0)) ||
+ (first && next == '-'.charCodeAt(0)))) ||
(type === 'x' && (next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0) ||
next >= 'a'.charCodeAt(0) && next <= 'f'.charCodeAt(0) ||
next >= 'A'.charCodeAt(0) && next <= 'F'.charCodeAt(0)))) &&
@@ -2518,7 +2520,7 @@ LibraryManager.library = {
var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}};
argIndex += Runtime.getNativeFieldSize('void*');
switch (type) {
- case 'd': case 'u':
+ case 'd': case 'u': case 'i':
if (half) {
{{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i16') }}};
} else {
@@ -6398,14 +6400,18 @@ LibraryManager.library = {
ntohl: 'htonl',
ntohs: 'htons',
- inet_pton__deps: ['__setErrNo', '$ERRNO_CODES'],
+ inet_addr: function(ptr) {
+ var b = Pointer_stringify(ptr).split(".");
+ if (b.length !== 4) return -1; // we return -1 for error, and otherwise a uint32. this helps inet_pton differentiate
+ return (Number(b[0]) | (Number(b[1]) << 8) | (Number(b[2]) << 16) | (Number(b[3]) << 24)) >>> 0;
+ },
+
+ inet_pton__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_addr'],
inet_pton: function(af, src, dst) {
// int af, const char *src, void *dst
if ((af ^ {{{ cDefine("AF_INET") }}}) !== 0) { ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); return -1; }
- var b = Pointer_stringify(src).split(".");
- if (b.length !== 4) return 0;
- var ret = Number(b[0]) | (Number(b[1]) << 8) | (Number(b[2]) << 16) | (Number(b[3]) << 24);
- if (isNaN(ret)) return 0;
+ var ret = _inet_addr(src);
+ if (ret == -1 || isNaN(ret)) return 0;
setValue(dst, ret, 'i32');
return 1;
},
@@ -6423,7 +6429,62 @@ LibraryManager.library = {
},
// ==========================================================================
- // sockets
+ // netdb.h
+ // ==========================================================================
+
+ // All we can do is alias names to ips. you give this a name, it returns an
+ // "ip" that we later know to use as a name. There is no way to do actual
+ // name resolving clientside in a browser.
+ // we do the aliasing in 172.29.*.*, giving us 65536 possibilities
+ // note: lots of leaking here!
+ __hostent_struct_layout: Runtime.generateStructInfo([
+ ['i8*', 'h_name'],
+ ['i8**', 'h_aliases'],
+ ['i32', 'h_addrtype'],
+ ['i32', 'h_length'],
+ ['i8**', 'h_addr_list'],
+ ]),
+ gethostbyname__deps: ['__hostent_struct_layout'],
+ gethostbyname: function(name) {
+ name = Pointer_stringify(name);
+ if (!_gethostbyname.id) {
+ _gethostbyname.id = 1;
+ _gethostbyname.table = {};
+ }
+ var id = _gethostbyname.id++;
+ assert(id < 65535);
+ var fakeAddr = 172 | (29 << 8) | ((id & 0xff) << 16) | ((id & 0xff00) << 24);
+ _gethostbyname.table[id] = name;
+ // generate hostent
+ var ret = _malloc(___hostent_struct_layout.__size__);
+ var nameBuf = _malloc(name.length+1);
+ writeStringToMemory(name, nameBuf);
+ setValue(ret+___hostent_struct_layout.h_name, nameBuf, 'i8*');
+ var aliasesBuf = _malloc(4);
+ setValue(aliasesBuf, 0, 'i8*');
+ setValue(ret+___hostent_struct_layout.h_aliases, aliasesBuf, 'i8**');
+ setValue(ret+___hostent_struct_layout.h_addrtype, {{{ cDefine("AF_INET") }}}, 'i32');
+ setValue(ret+___hostent_struct_layout.h_length, 4, 'i32');
+ var addrListBuf = _malloc(12);
+ setValue(addrListBuf, addrListBuf+8, 'i32*');
+ setValue(addrListBuf+4, 0, 'i32*');
+ setValue(addrListBuf+8, fakeAddr, 'i32');
+ setValue(ret+___hostent_struct_layout.h_addr_list, addrListBuf, 'i8**');
+ return ret;
+ },
+
+ gethostbyname_r__deps: ['gethostbyname'],
+ gethostbyname_r: function(name, hostData, buffer, bufferSize, hostEntry, errnum) {
+ var data = _gethostbyname(name);
+ _memcpy(hostData, data, ___hostent_struct_layout.__size__);
+ _free(data);
+ setValue(errnum, 0, 'i32');
+ return 0;
+ },
+
+ // ==========================================================================
+ // sockets. Note that the implementation assumes all sockets are always
+ // nonblocking
// ==========================================================================
$Sockets__deps: ['__setErrNo', '$ERRNO_CODES'],
@@ -6437,6 +6498,15 @@ LibraryManager.library = {
['i32', 'sin_addr'],
['i64', 'sin_zero'],
]),
+ msghdr_layout: Runtime.generateStructInfo([
+ ['*', 'msg_name'],
+ ['i32', 'msg_namelen'],
+ ['*', 'msg_iov'],
+ ['i32', 'msg_iovlen'],
+ ['*', 'msg_control'],
+ ['i32', 'msg_controllen'],
+ ['i32', 'msg_flags'],
+ ]),
},
socket__deps: ['$Sockets'],
@@ -6448,7 +6518,7 @@ LibraryManager.library = {
return fd;
},
- connect__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs'],
+ connect__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs', 'gethostbyname'],
connect: function(fd, addr, addrlen) {
var info = Sockets.fds[fd];
if (!info) return -1;
@@ -6456,24 +6526,68 @@ LibraryManager.library = {
info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32');
info.port = _ntohs(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16'));
info.host = __inet_ntop_raw(info.addr);
- info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['arraybuffer']);
+ // Support 'fake' ips from gethostbyname
+ var parts = info.host.split('.');
+ if (parts[0] == '172' && parts[1] == '29') {
+ var low = Number(parts[2]);
+ var high = Number(parts[3]);
+ info.host = _gethostbyname.table[low + 0xff*high];
+ assert(info.host, 'problem translating fake ip ' + parts);
+ }
+ console.log('opening ws://' + info.host + ':' + info.port);
+ info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['base64']);
info.socket.binaryType = 'arraybuffer';
info.buffer = new Uint8Array(Sockets.BUFFER_SIZE);
info.bufferWrite = info.bufferRead = 0;
info.socket.onmessage = function (event) {
var data = event.data;
+#if SOCKET_DEBUG
+ Module.print(['onmessage', window.location, event.data, window.atob(data)]);
+#endif
if (typeof data == 'string') {
var binaryString = window.atob(data);
var len = binaryString.length;
+#if SOCKET_DEBUG
+ var out = [];
+#endif
for (var i = 0; i < len; i++) {
- info.buffer[info.bufferWrite++] = binaryString.charCodeAt(i);
+ var curr = binaryString.charCodeAt(i);
+ info.buffer[info.bufferWrite++] = curr;
+#if SOCKET_DEBUG
+ out.push(curr);
+#endif
if (info.bufferWrite == Sockets.BUFFER_SIZE) info.bufferWrite = 0;
if (info.bufferWrite == info.bufferRead) throw 'socket buffer overflow';
}
+#if SOCKET_DEBUG
+ Module.print(['onmessage data:', len, ':', out]);
+#endif
} else {
console.log('binary!');
}
}
+ info.sendQueue = [];
+ info.senderWaiting = false;
+ info.sender = function(data) {
+ if (data) {
+ info.sendQueue.push(data);
+ } else {
+ info.senderWaiting = false; // we are a setTimeout callback
+ if (info.sendQueue.length == 0) return;
+ }
+ if (info.socket.readyState != info.socket.OPEN) {
+ if (!info.senderWaiting) {
+ console.log('waiting for socket in order to send');
+ setTimeout(info.sender, 100);
+ info.senderWaiting = true;
+ }
+ return;
+ }
+ for (var i = 0; i < info.sendQueue.length; i++) {
+ info.socket.send(window.btoa(info.sendQueue[i]));
+ }
+ info.sendQueue = [];
+ }
return 0;
},
@@ -6486,6 +6600,9 @@ LibraryManager.library = {
return 0; // should this be -1 like the spec says?
}
var ret = 0;
+#if SOCKET_DEBUG
+ Module.print('pre-recv: ' + [len, info.bufferWrite, info.bufferRead]);
+#endif
while (info.bufferWrite != info.bufferRead && len > 0) {
// write out a byte
{{{ makeSetValue('buf++', '0', 'info.buffer[info.bufferRead++]', 'i8') }}};
@@ -6493,9 +6610,105 @@ LibraryManager.library = {
len--;
ret++;
}
+#if SOCKET_DEBUG
+ Module.print('recv: ' + ret + ' : ' + Array.prototype.slice.call(HEAPU8.subarray(buf-len, buf)));
+#endif
+ return ret;
+ },
+
+ send__deps: ['$Sockets'],
+ send: function(fd, buf, len, flags) {
+ var info = Sockets.fds[fd];
+ if (!info) return -1;
+#if SOCKET_DEBUG
+ Module.print('send: ' + Array.prototype.slice.call(HEAPU8.subarray(buf, buf+len)));
+#endif
+ info.sender(Pointer_stringify(buf, len));
+ return len;
+ },
+
+ sendmsg__deps: ['$Sockets', 'connect'],
+ sendmsg: function(fd, msg, flags) {
+ var info = Sockets.fds[fd];
+ if (!info) return -1;
+ // if we are not connected, use the address info in the message
+ if (!info.connected) {
+ var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
+ assert(name, 'sendmsg on non-connected socket, and no name/address in the message');
+ _connect(fd, name, {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}});
+ }
+ var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
+ var data = '';
+ for (var i = 0; i < num; i++) {
+ var currNum = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov+8*i' + '+4', 'i32') }}};
+ if (!currNum) continue;
+ var currBuf = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov+8*i', 'i8*') }}};
+#if SOCKET_DEBUG
+ Module.print('sendmsg part ' + i + ' : ' + currNum + ' : ' + Array.prototype.slice.call(HEAPU8.subarray(currBuf, currBuf+currNum)));
+#endif
+ data += Pointer_stringify(currBuf, currNum);
+ }
+ info.sender(data);
+ return data.length;
+ },
+
+ recvmsg__deps: ['$Sockets', 'connect', 'recv', '__setErrNo', '$ERRNO_CODES'],
+ recvmsg: function(fd, msg, flags) {
+#if SOCKET_DEBUG
+ Module.print('recvmsg!');
+#endif
+ var info = Sockets.fds[fd];
+ if (!info) return -1;
+ // if we are not connected, use the address info in the message
+ if (!info.connected) {
+#if SOCKET_DEBUG
+ Module.print('recvmsg connecting');
+#endif
+ var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
+ assert(name, 'sendmsg on non-connected socket, and no name/address in the message');
+ _connect(fd, name, {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}});
+ }
+ var bytes = info.bufferWrite - info.bufferRead;
+ if (bytes < 0) bytes += Sockets.BUFFER_SIZE;
+#if SOCKET_DEBUG
+ Module.print('recvmsg bytes: ' + bytes);
+#endif
+ if (bytes == 0) {
+ ___setErrNo(ERRNO_CODES.EWOULDBLOCK);
+ return -1;
+ }
+ var ret = bytes;
+ var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
+ var data = '';
+ for (var i = 0; i < num && bytes > 0; i++) {
+ var currNum = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov+8*i' + '+4', 'i32') }}};
+#if SOCKET_DEBUG
+ Module.print('recvmsg loop ' + [i, num, bytes, currNum]);
+#endif
+ if (!currNum) continue;
+ currNum = Math.min(currNum, bytes); // XXX what should happen when we partially fill a buffer..?
+ bytes -= currNum;
+ var currBuf = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov+8*i', 'i8*') }}};
+#if SOCKET_DEBUG
+ Module.print('recvmsg call recv ' + currNum);
+#endif
+ assert(_recv(fd, currBuf, currNum, 0) == currNum);
+ }
return ret;
},
+ recvfrom__deps: ['$Sockets', 'connect', 'recv'],
+ recvfrom: function(fd, buf, len, flags, addr, addrlen) {
+ var info = Sockets.fds[fd];
+ if (!info) return -1;
+ // if we are not connected, use the address info in the message
+ if (!info.connected) {
+ //var name = {{{ makeGetValue('addr', '0', '*') }}};
+ _connect(fd, addr, addrlen);
+ }
+ return _recv(fd, buf, len, flags);
+ },
+
shutdown: function(fd, how) {
var info = Sockets.fds[fd];
if (!info) return -1;
@@ -6514,6 +6727,34 @@ LibraryManager.library = {
return 0;
},
+ setsockopt: function(d, level, optname, optval, optlen) {
+ console.log('ignoring setsockopt command');
+ return 0;
+ },
+
+ bind__deps: ['connect'],
+ bind: function(fd, addr, addrlen) {
+ return _connect(fd, addr, addrlen);
+ },
+
+ listen: function(fd, backlog) {
+ return 0;
+ },
+
+ accept: function(fd, addr, addrlen) {
+ // TODO: webrtc queued incoming connections, etc.
+ // For now, the model is that bind does a connect, and we "accept" that one connection,
+ // which has host:port the same as ours. We also return the same socket fd.
+ var info = Sockets.fds[fd];
+ if (!info) return -1;
+ if (addr) {
+ setValue(addr + Sockets.sockaddr_in_layout.sin_addr, info.addr, 'i32');
+ setValue(addr + Sockets.sockaddr_in_layout.sin_port, info.port, 'i32');
+ setValue(addrlen, Sockets.sockaddr_in_layout.__size__, 'i32');
+ }
+ return fd;
+ },
+
// ==========================================================================
// emscripten.h
// ==========================================================================