Commit 86725f9b authored by Joel Martin's avatar Joel Martin

wswrapper: add dup2, fix select w/ NULL timeout.

- add dup2 functionality. This requires adding a ref cnt to the
  _WS_connections structure so that we only free the structure once
  all dup'd referenced are closed. Also, refactor malloc and free of
  connection structure into _WS_alloc and _WS_free.
- allow select to accept a NULL timeout value which means sleep
  forever instead of segfaulting.
- fix some compile warnings related to ppoll definition.
- move some WebSockets related html test pages into utils and symlink
  them from tests.
parent 6a883409
<html>
<head>
<title>WebSockets Echo Test</title>
<script src="include/base64.js"></script>
<script src="include/util.js"></script>
<script src="include/webutil.js"></script>
<!-- Uncomment to activate firebug lite -->
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
</head>
<body>
Host: <input id='host' style='width:100'>&nbsp;
Port: <input id='port' style='width:50'>&nbsp;
Encrypt: <input id='encrypt' type='checkbox'>&nbsp;
<input id='connectButton' type='button' value='Start' style='width:100px'
onclick="connect();">&nbsp;
<br>
Log:<br>
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
</body>
<script>
var ws, host = null, port = null,
msg_cnt = 0, send_cnt = 1, echoDelay = 500,
echo_ref;
function message(str) {
console.log(str);
cell = $D('messages');
cell.innerHTML += msg_cnt + ": " + str + "\n";
cell.scrollTop = cell.scrollHeight;
msg_cnt++;
}
Array.prototype.pushStr = function (str) {
var n = str.length;
for (var i=0; i < n; i++) {
this.push(str.charCodeAt(i));
}
}
function send_msg() {
if (ws.bufferedAmount > 0) {
console.log("Delaying send");
return;
}
var str = "Message #" + send_cnt, arr = [];
arr.pushStr(str)
ws.send(Base64.encode(arr));
message("Sent message: '" + str + "'");
send_cnt++;
}
function update_stats() {
$D('sent').innerHTML = sent;
$D('received').innerHTML = received;
$D('errors').innerHTML = errors;
}
function init_ws() {
console.log(">> init_ws");
console.log("<< init_ws");
}
function connect() {
var host = $D('host').value,
port = $D('port').value,
scheme = "ws://", uri;
console.log(">> connect");
if ((!host) || (!port)) {
console.log("must set host and port");
return;
}
if (ws) {
ws.close();
}
if ($D('encrypt').checked) {
scheme = "wss://";
}
uri = scheme + host + ":" + port;
message("connecting to " + uri);
ws = new WebSocket(uri);
ws.onmessage = function(e) {
//console.log(">> WebSockets.onmessage");
var arr = Base64.decode(e.data), str = "", i;
for (i = 0; i < arr.length; i++) {
str = str + String.fromCharCode(arr[i]);
}
message("Received message '" + str + "'");
//console.log("<< WebSockets.onmessage");
};
ws.onopen = function(e) {
console.log(">> WebSockets.onopen");
echo_ref = setInterval(send_msg, echoDelay);
console.log("<< WebSockets.onopen");
};
ws.onclose = function(e) {
console.log(">> WebSockets.onclose");
if (echo_ref) {
clearInterval(echo_ref);
echo_ref = null;
}
console.log("<< WebSockets.onclose");
};
ws.onerror = function(e) {
console.log(">> WebSockets.onerror");
if (echo_ref) {
clearInterval(echo_ref);
echo_ref = null;
}
console.log("<< WebSockets.onerror");
};
$D('connectButton').value = "Stop";
$D('connectButton').onclick = disconnect;
console.log("<< connect");
}
function disconnect() {
console.log(">> disconnect");
if (ws) {
ws.close();
}
if (echo_ref) {
clearInterval(echo_ref);
}
$D('connectButton').value = "Start";
$D('connectButton').onclick = connect;
console.log("<< disconnect");
}
/* If no builtin websockets then load web_socket.js */
if (window.WebSocket) {
VNC_native_ws = true;
} else {
VNC_native_ws = false;
console.log("Loading web-socket-js flash bridge");
var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
extra += "<script src='include/web-socket-js/web_socket.js'><\/script>";
document.write(extra);
}
window.onload = function() {
console.log("onload");
if (!VNC_native_ws) {
console.log("initializing web-socket-js flash bridge");
WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
WebSocket.__initialize();
}
var url = document.location.href;
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
}
</script>
</html>
../utils/wsecho.html
\ No newline at end of file
<html>
<head>
<title>WebSockets Test</title>
<script src="include/base64.js"></script>
<script src="include/util.js"></script>
<script src="include/webutil.js"></script>
<!-- Uncomment to activate firebug lite -->
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
</head>
<body>
Host: <input id='host' style='width:100'>&nbsp;
Port: <input id='port' style='width:50'>&nbsp;
Encrypt: <input id='encrypt' type='checkbox'>&nbsp;
Send Delay (ms): <input id='sendDelay' style='width:50' value="100">&nbsp;
<input id='connectButton' type='button' value='Start' style='width:100px'
onclick="connect();">&nbsp;
<br><br>
<table border=1>
<tr>
<th align="right">Packets sent:</th>
<td align="right"><div id='sent'>0</div></td>
</tr><tr>
<th align="right">Good Packets Received:</th>
<td align="right"><div id='received'>0</div></td>
</tr><tr>
<th align="right">Errors (Bad Packets Received:)</th>
<td align="right"><div id='errors'>0</div></td>
</tr>
</table>
<br>
Errors:<br>
<textarea id="error" style="font-size: 9;" cols=80 rows=25></textarea>
</body>
<script>
function error(str) {
console.error(str);
cell = $D('error');
cell.innerHTML += errors + ": " + str + "\n";
cell.scrollTop = cell.scrollHeight;
}
var host = null, port = null, sendDelay = 0;
var ws = null, update_ref = null, send_ref = null;
var sent = 0, received = 0, errors = 0;
var max_send = 2000;
var recv_seq = 0, send_seq = 0;
Array.prototype.pushStr = function (str) {
var n = str.length;
for (var i=0; i < n; i++) {
this.push(str.charCodeAt(i));
}
}
function add (x,y) {
return parseInt(x,10)+parseInt(y,10);
}
function check_respond(data) {
//console.log(">> check_respond");
var decoded, first, last, str, length, chksum, nums, arr;
decoded = Base64.decode(data);
first = String.fromCharCode(decoded.shift());
last = String.fromCharCode(decoded.pop());
if (first != "^") {
errors++;
error("Packet missing start char '^'");
return;
}
if (last != "$") {
errors++;
error("Packet missing end char '$'");
return;
}
arr = decoded.map(function(num) {
return String.fromCharCode(num);
} ).join('').split(':');
seq = arr[0];
length = arr[1];
chksum = arr[2];
nums = arr[3];
//console.log(" length:" + length + " chksum:" + chksum + " nums:" + nums);
if (seq != recv_seq) {
errors++;
error("Expected seq " + recv_seq + " but got " + seq);
recv_seq = parseInt(seq,10) + 1; // Back on track
return;
}
recv_seq++;
if (nums.length != length) {
errors++;
error("Expected length " + length + " but got " + nums.length);
return;
}
//real_chksum = nums.reduce(add);
real_chksum = 0;
for (var i=0; i < nums.length; i++) {
real_chksum += parseInt(nums.charAt(i), 10);
}
if (real_chksum != chksum) {
errors++
error("Expected chksum " + chksum + " but real chksum is " + real_chksum);
return;
}
received++;
//console.log(" Packet checks out: length:" + length + " chksum:" + chksum);
//console.log("<< check_respond");
}
function send() {
if (ws.bufferedAmount > 0) {
console.log("Delaying send");
return;
}
var length = Math.floor(Math.random()*(max_send-9)) + 10; // 10 - max_send
var numlist = [], arr = [];
for (var i=0; i < length; i++) {
numlist.push( Math.floor(Math.random()*10) );
}
//chksum = numlist.reduce(add);
chksum = 0;
for (var i=0; i < numlist.length; i++) {
chksum += parseInt(numlist[i], 10);
}
var nums = numlist.join('');
arr.pushStr("^" + send_seq + ":" + length + ":" + chksum + ":" + nums + "$")
send_seq ++;
ws.send(Base64.encode(arr));
sent++;
}
function update_stats() {
$D('sent').innerHTML = sent;
$D('received').innerHTML = received;
$D('errors').innerHTML = errors;
}
function init_ws() {
console.log(">> init_ws");
var scheme = "ws://";
if ($D('encrypt').checked) {
scheme = "wss://";
}
var uri = scheme + host + ":" + port;
console.log("connecting to " + uri);
ws = new WebSocket(uri);
ws.onmessage = function(e) {
//console.log(">> WebSockets.onmessage");
check_respond(e.data);
//console.log("<< WebSockets.onmessage");
};
ws.onopen = function(e) {
console.log(">> WebSockets.onopen");
send_ref = setInterval(send, sendDelay);
console.log("<< WebSockets.onopen");
};
ws.onclose = function(e) {
console.log(">> WebSockets.onclose");
clearInterval(send_ref);
console.log("<< WebSockets.onclose");
};
ws.onerror = function(e) {
console.log(">> WebSockets.onerror");
console.log(" " + e);
console.log("<< WebSockets.onerror");
};
console.log("<< init_ws");
}
function connect() {
console.log(">> connect");
host = $D('host').value;
port = $D('port').value;
sendDelay = parseInt($D('sendDelay').value, 10);
if ((!host) || (!port)) {
console.log("must set host and port");
return;
}
if (ws) {
ws.close();
}
init_ws();
update_ref = setInterval(update_stats, 1);
$D('connectButton').value = "Stop";
$D('connectButton').onclick = disconnect;
console.log("<< connect");
}
function disconnect() {
console.log(">> disconnect");
if (ws) {
ws.close();
}
clearInterval(update_ref);
update_stats(); // Final numbers
recv_seq = 0;
send_seq = 0;
$D('connectButton').value = "Start";
$D('connectButton').onclick = connect;
console.log("<< disconnect");
}
/* If no builtin websockets then load web_socket.js */
if (window.WebSocket) {
VNC_native_ws = true;
} else {
VNC_native_ws = false;
console.log("Loading web-socket-js flash bridge");
var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
extra += "<script src='include/web-socket-js/web_socket.js'><\/script>";
document.write(extra);
}
window.onload = function() {
console.log("onload");
if (!VNC_native_ws) {
console.log("initializing web-socket-js flash bridge");
WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
WebSocket.__initialize();
}
var url = document.location.href;
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
}
</script>
</html>
../utils/wstest.html
\ No newline at end of file
<html>
<head>
<title>WebSockets Echo Test</title>
<script src="include/base64.js"></script>
<script src="include/util.js"></script>
<script src="include/webutil.js"></script>
<!-- Uncomment to activate firebug lite -->
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
</head>
<body>
Host: <input id='host' style='width:100'>&nbsp;
Port: <input id='port' style='width:50'>&nbsp;
Encrypt: <input id='encrypt' type='checkbox'>&nbsp;
<input id='connectButton' type='button' value='Start' style='width:100px'
onclick="connect();">&nbsp;
<br>
Log:<br>
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
</body>
<script>
var ws, host = null, port = null,
msg_cnt = 0, send_cnt = 1, echoDelay = 500,
echo_ref;
function message(str) {
console.log(str);
cell = $D('messages');
cell.innerHTML += msg_cnt + ": " + str + "\n";
cell.scrollTop = cell.scrollHeight;
msg_cnt++;
}
Array.prototype.pushStr = function (str) {
var n = str.length;
for (var i=0; i < n; i++) {
this.push(str.charCodeAt(i));
}
}
function send_msg() {
if (ws.bufferedAmount > 0) {
console.log("Delaying send");
return;
}
var str = "Message #" + send_cnt, arr = [];
arr.pushStr(str)
ws.send(Base64.encode(arr));
message("Sent message: '" + str + "'");
send_cnt++;
}
function update_stats() {
$D('sent').innerHTML = sent;
$D('received').innerHTML = received;
$D('errors').innerHTML = errors;
}
function init_ws() {
console.log(">> init_ws");
console.log("<< init_ws");
}
function connect() {
var host = $D('host').value,
port = $D('port').value,
scheme = "ws://", uri;
console.log(">> connect");
if ((!host) || (!port)) {
console.log("must set host and port");
return;
}
if (ws) {
ws.close();
}
if ($D('encrypt').checked) {
scheme = "wss://";
}
uri = scheme + host + ":" + port;
message("connecting to " + uri);
ws = new WebSocket(uri);
ws.onmessage = function(e) {
//console.log(">> WebSockets.onmessage");
var arr = Base64.decode(e.data), str = "", i;
for (i = 0; i < arr.length; i++) {
str = str + String.fromCharCode(arr[i]);
}
message("Received message '" + str + "'");
//console.log("<< WebSockets.onmessage");
};
ws.onopen = function(e) {
console.log(">> WebSockets.onopen");
echo_ref = setInterval(send_msg, echoDelay);
console.log("<< WebSockets.onopen");
};
ws.onclose = function(e) {
console.log(">> WebSockets.onclose");
if (echo_ref) {
clearInterval(echo_ref);
echo_ref = null;
}
console.log("<< WebSockets.onclose");
};
ws.onerror = function(e) {
console.log(">> WebSockets.onerror");
if (echo_ref) {
clearInterval(echo_ref);
echo_ref = null;
}
console.log("<< WebSockets.onerror");
};
$D('connectButton').value = "Stop";
$D('connectButton').onclick = disconnect;
console.log("<< connect");
}
function disconnect() {
console.log(">> disconnect");
if (ws) {
ws.close();
}
if (echo_ref) {
clearInterval(echo_ref);
}
$D('connectButton').value = "Start";
$D('connectButton').onclick = connect;
console.log("<< disconnect");
}
/* If no builtin websockets then load web_socket.js */
if (window.WebSocket) {
VNC_native_ws = true;
} else {
VNC_native_ws = false;
console.log("Loading web-socket-js flash bridge");
var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
extra += "<script src='include/web-socket-js/web_socket.js'><\/script>";
document.write(extra);
}
window.onload = function() {
console.log("onload");
if (!VNC_native_ws) {
console.log("initializing web-socket-js flash bridge");
WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
WebSocket.__initialize();
}
var url = document.location.href;
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
}
</script>
</html>
<html>
<head>
<title>WebSockets Test</title>
<script src="include/base64.js"></script>
<script src="include/util.js"></script>
<script src="include/webutil.js"></script>
<!-- Uncomment to activate firebug lite -->
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
</head>
<body>
Host: <input id='host' style='width:100'>&nbsp;
Port: <input id='port' style='width:50'>&nbsp;
Encrypt: <input id='encrypt' type='checkbox'>&nbsp;
Send Delay (ms): <input id='sendDelay' style='width:50' value="100">&nbsp;
<input id='connectButton' type='button' value='Start' style='width:100px'
onclick="connect();">&nbsp;
<br><br>
<table border=1>
<tr>
<th align="right">Packets sent:</th>
<td align="right"><div id='sent'>0</div></td>
</tr><tr>
<th align="right">Good Packets Received:</th>
<td align="right"><div id='received'>0</div></td>
</tr><tr>
<th align="right">Errors (Bad Packets Received:)</th>
<td align="right"><div id='errors'>0</div></td>
</tr>
</table>
<br>
Errors:<br>
<textarea id="error" style="font-size: 9;" cols=80 rows=25></textarea>
</body>
<script>
function error(str) {
console.error(str);
cell = $D('error');
cell.innerHTML += errors + ": " + str + "\n";
cell.scrollTop = cell.scrollHeight;
}
var host = null, port = null, sendDelay = 0;
var ws = null, update_ref = null, send_ref = null;
var sent = 0, received = 0, errors = 0;
var max_send = 2000;
var recv_seq = 0, send_seq = 0;
Array.prototype.pushStr = function (str) {
var n = str.length;
for (var i=0; i < n; i++) {
this.push(str.charCodeAt(i));
}
}
function add (x,y) {
return parseInt(x,10)+parseInt(y,10);
}
function check_respond(data) {
//console.log(">> check_respond");
var decoded, first, last, str, length, chksum, nums, arr;
decoded = Base64.decode(data);
first = String.fromCharCode(decoded.shift());
last = String.fromCharCode(decoded.pop());
if (first != "^") {
errors++;
error("Packet missing start char '^'");
return;
}
if (last != "$") {
errors++;
error("Packet missing end char '$'");
return;
}
arr = decoded.map(function(num) {
return String.fromCharCode(num);
} ).join('').split(':');
seq = arr[0];
length = arr[1];
chksum = arr[2];
nums = arr[3];
//console.log(" length:" + length + " chksum:" + chksum + " nums:" + nums);
if (seq != recv_seq) {
errors++;
error("Expected seq " + recv_seq + " but got " + seq);
recv_seq = parseInt(seq,10) + 1; // Back on track
return;
}
recv_seq++;
if (nums.length != length) {
errors++;
error("Expected length " + length + " but got " + nums.length);
return;
}
//real_chksum = nums.reduce(add);
real_chksum = 0;
for (var i=0; i < nums.length; i++) {
real_chksum += parseInt(nums.charAt(i), 10);
}
if (real_chksum != chksum) {
errors++
error("Expected chksum " + chksum + " but real chksum is " + real_chksum);
return;
}
received++;
//console.log(" Packet checks out: length:" + length + " chksum:" + chksum);
//console.log("<< check_respond");
}
function send() {
if (ws.bufferedAmount > 0) {
console.log("Delaying send");
return;
}
var length = Math.floor(Math.random()*(max_send-9)) + 10; // 10 - max_send
var numlist = [], arr = [];
for (var i=0; i < length; i++) {
numlist.push( Math.floor(Math.random()*10) );
}
//chksum = numlist.reduce(add);
chksum = 0;
for (var i=0; i < numlist.length; i++) {
chksum += parseInt(numlist[i], 10);
}
var nums = numlist.join('');
arr.pushStr("^" + send_seq + ":" + length + ":" + chksum + ":" + nums + "$")
send_seq ++;
ws.send(Base64.encode(arr));
sent++;
}
function update_stats() {
$D('sent').innerHTML = sent;
$D('received').innerHTML = received;
$D('errors').innerHTML = errors;
}
function init_ws() {
console.log(">> init_ws");
var scheme = "ws://";
if ($D('encrypt').checked) {
scheme = "wss://";
}
var uri = scheme + host + ":" + port;
console.log("connecting to " + uri);
ws = new WebSocket(uri);
ws.onmessage = function(e) {
//console.log(">> WebSockets.onmessage");
check_respond(e.data);
//console.log("<< WebSockets.onmessage");
};
ws.onopen = function(e) {
console.log(">> WebSockets.onopen");
send_ref = setInterval(send, sendDelay);
console.log("<< WebSockets.onopen");
};
ws.onclose = function(e) {
console.log(">> WebSockets.onclose");
clearInterval(send_ref);
console.log("<< WebSockets.onclose");
};
ws.onerror = function(e) {
console.log(">> WebSockets.onerror");
console.log(" " + e);
console.log("<< WebSockets.onerror");
};
console.log("<< init_ws");
}
function connect() {
console.log(">> connect");
host = $D('host').value;
port = $D('port').value;
sendDelay = parseInt($D('sendDelay').value, 10);
if ((!host) || (!port)) {
console.log("must set host and port");
return;
}
if (ws) {
ws.close();
}
init_ws();
update_ref = setInterval(update_stats, 1);
$D('connectButton').value = "Stop";
$D('connectButton').onclick = disconnect;
console.log("<< connect");
}
function disconnect() {
console.log(">> disconnect");
if (ws) {
ws.close();
}
clearInterval(update_ref);
update_stats(); // Final numbers
recv_seq = 0;
send_seq = 0;
$D('connectButton').value = "Start";
$D('connectButton').onclick = connect;
console.log("<< disconnect");
}
/* If no builtin websockets then load web_socket.js */
if (window.WebSocket) {
VNC_native_ws = true;
} else {
VNC_native_ws = false;
console.log("Loading web-socket-js flash bridge");
var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
extra += "<script src='include/web-socket-js/web_socket.js'><\/script>";
document.write(extra);
}
window.onload = function() {
console.log("onload");
if (!VNC_native_ws) {
console.log("initializing web-socket-js flash bridge");
WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
WebSocket.__initialize();
}
var url = document.location.href;
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
}
</script>
</html>
...@@ -11,11 +11,12 @@ ...@@ -11,11 +11,12 @@
* Limitations: * Limitations:
* - multi-threaded programs may not work * - multi-threaded programs may not work
* - programs using ppoll or epoll will not work correctly * - programs using ppoll or epoll will not work correctly
* - doesn't support fopencookie, streams, putc, etc.
*/ */
#define DO_MSG 1 #define DO_MSG 1
//#define DO_DEBUG 1 #define DO_DEBUG 1
//#define DO_TRACE 1 #define DO_TRACE 1
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -78,6 +79,55 @@ int _WS_subtract_time (result, x, y, ts) ...@@ -78,6 +79,55 @@ int _WS_subtract_time (result, x, y, ts)
return x->tv_sec <= y->tv_sec; return x->tv_sec <= y->tv_sec;
} }
int _WS_alloc(int fd) {
if (_WS_connections[fd]) {
RET_ERROR(ENOMEM, "Memory already allocated for fd %d\n", fd);
}
if (! (_WS_connections[fd] = malloc(sizeof(_WS_connection)))) {
RET_ERROR(ENOMEM, "Could not allocate interposer memory\n");
}
_WS_connections[fd]->rcarry_cnt = 0;
_WS_connections[fd]->rcarry[0] = '\0';
_WS_connections[fd]->newframe = 1;
_WS_connections[fd]->refcnt = 1;
/* Add to search list for select/pselect */
_WS_fds[_WS_nfds] = fd;
_WS_nfds++;
return 0;
}
int _WS_free(int fd) {
int i;
_WS_connection * wsptr;
wsptr = _WS_connections[fd];
if (wsptr) {
TRACE(">> _WS_free(%d)\n", fd);
wsptr->refcnt--;
if (wsptr->refcnt <= 0) {
free(wsptr);
DEBUG("freed memory for fd %d\n", fd);
}
_WS_connections[fd] = NULL;
/* Remove from the search list for select/pselect */
for (i = 0; i < _WS_nfds; i++) {
if (_WS_fds[i] == fd) {
break;
}
}
if (_WS_nfds - i - 1 > 0) {
memmove(_WS_fds + i, _WS_fds + i + 1, _WS_nfds - i - 1);
}
_WS_nfds--;
MSG("finished interposing on fd %d\n", fd);
TRACE("<< _WS_free(%d)\n", fd);
}
}
/* /*
* WebSocket handshake routines * WebSocket handshake routines
...@@ -619,8 +669,10 @@ int _WS_select(int mode, int nfds, fd_set *readfds, ...@@ -619,8 +669,10 @@ int _WS_select(int mode, int nfds, fd_set *readfds,
} }
TRACE(">> _WS_select(%d, %d, _, _, _, _)\n", mode, nfds); TRACE(">> _WS_select(%d, %d, _, _, _, _)\n", mode, nfds);
memcpy(&savetv, timeptr, sizeof(savetv)); if (timeptr) {
gettimeofday(&starttv, NULL); memcpy(&savetv, timeptr, sizeof(savetv));
gettimeofday(&starttv, NULL);
}
/* If we have carry-over return it right away */ /* If we have carry-over return it right away */
FD_ZERO(&carryfds); FD_ZERO(&carryfds);
...@@ -649,9 +701,11 @@ int _WS_select(int mode, int nfds, fd_set *readfds, ...@@ -649,9 +701,11 @@ int _WS_select(int mode, int nfds, fd_set *readfds,
} }
do { do {
TRACE(" _WS_select(%d, %d, _, _, _, _) tv/ts: %ld:%ld\n", mode, nfds, if (timeptr) {
((struct timeval *) timeptr)->tv_sec, TRACE(" _WS_select tv/ts: %ld:%ld\n",
((struct timeval *) timeptr)->tv_usec); ((struct timeval *) timeptr)->tv_sec,
((struct timeval *) timeptr)->tv_usec);
}
if (mode == 0) { if (mode == 0) {
ret = (int) func0(nfds, readfds, writefds, exceptfds, ret = (int) func0(nfds, readfds, writefds, exceptfds,
timeptr); timeptr);
...@@ -689,6 +743,10 @@ int _WS_select(int mode, int nfds, fd_set *readfds, ...@@ -689,6 +743,10 @@ int _WS_select(int mode, int nfds, fd_set *readfds,
* them were really ready (empty frames) then we select again. But * them were really ready (empty frames) then we select again. But
* first restore original values less passage of time. * first restore original values less passage of time.
*/ */
if (! timeptr) {
/* No timeout, spin forever */
continue;
}
memcpy(readfds, &savefds, sizeof(savefds)); memcpy(readfds, &savefds, sizeof(savefds));
gettimeofday(&nowtv, NULL); gettimeofday(&nowtv, NULL);
/* Amount of time that has passed */ /* Amount of time that has passed */
...@@ -871,7 +929,7 @@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) ...@@ -871,7 +929,7 @@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
_WS_listen_fd = sockfd; _WS_listen_fd = sockfd;
TRACE("<< bind, interposing on port: %d (fd %d)\n", envport, sockfd); TRACE("<< bind, listening for WebSockets connections on port: %d (fd %d)\n", envport, sockfd);
return ret; return ret;
} }
...@@ -902,21 +960,13 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) ...@@ -902,21 +960,13 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
if (_WS_nfds >= WS_MAX_FDS) { if (_WS_nfds >= WS_MAX_FDS) {
RET_ERROR(ENOMEM, "Too many interposer fds\n"); RET_ERROR(ENOMEM, "Too many interposer fds\n");
} }
if (! (_WS_connections[fd] = malloc(sizeof(_WS_connection)))) { if (_WS_alloc(fd) < 0) {
RET_ERROR(ENOMEM, "Could not allocate interposer memory\n"); return -1;
} }
_WS_connections[fd]->rcarry_cnt = 0;
_WS_connections[fd]->rcarry[0] = '\0';
_WS_connections[fd]->newframe = 1;
/* Add to search list for select/pselect */
_WS_fds[_WS_nfds] = fd;
_WS_nfds++;
ret = _WS_handshake(fd); ret = _WS_handshake(fd);
if (ret < 0) { if (ret < 0) {
free(_WS_connections[fd]); _WS_free(fd);
_WS_connections[fd] = NULL;
errno = EPROTO; errno = EPROTO;
TRACE("<< accept(%d, _, _): ret %d\n", sockfd, ret); TRACE("<< accept(%d, _, _): ret %d\n", sockfd, ret);
return ret; return ret;
...@@ -929,61 +979,45 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) ...@@ -929,61 +979,45 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
int close(int fd) int close(int fd)
{ {
int i;
static void * (*func)(); static void * (*func)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "close"); if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "close");
if (_WS_connections[fd]) { TRACE("close(%d) called\n", fd);
TRACE(">> close(%d)\n", fd);
free(_WS_connections[fd]);
_WS_connections[fd] = NULL;
/* Remove from the search list for select/pselect */ _WS_free(fd);
for (i = 0; i < _WS_nfds; i++) {
if (_WS_fds[i] == fd) {
break;
}
}
if (_WS_nfds - i - 1 > 0) {
memmove(_WS_fds + i, _WS_fds + i + 1, _WS_nfds - i - 1);
}
_WS_nfds--;
MSG("finished interposing on fd %d (freed memory)\n", fd);
TRACE("<< close(%d)\n", fd);
}
return (int) func(fd); return (int) func(fd);
} }
ssize_t read(int fd, void *buf, size_t count) ssize_t read(int fd, void *buf, size_t count)
{ {
//TRACE("read(%d, _, %d) called\n", fd, count); TRACE("read(%d, _, %d) called\n", fd, count);
return (ssize_t) _WS_recv(0, fd, buf, count, 0); return (ssize_t) _WS_recv(0, fd, buf, count, 0);
} }
ssize_t write(int fd, const void *buf, size_t count) ssize_t write(int fd, const void *buf, size_t count)
{ {
//TRACE("write(%d, _, %d) called\n", fd, count); TRACE("write(%d, _, %d) called\n", fd, count);
return (ssize_t) _WS_send(0, fd, buf, count, 0); return (ssize_t) _WS_send(0, fd, buf, count, 0);
} }
ssize_t recv(int sockfd, void *buf, size_t len, int flags) ssize_t recv(int sockfd, void *buf, size_t len, int flags)
{ {
//TRACE("recv(%d, _, %d, %d) called\n", sockfd, len, flags); TRACE("recv(%d, _, %d, %d) called\n", sockfd, len, flags);
return (ssize_t) _WS_recv(1, sockfd, buf, len, flags); return (ssize_t) _WS_recv(1, sockfd, buf, len, flags);
} }
ssize_t send(int sockfd, const void *buf, size_t len, int flags) ssize_t send(int sockfd, const void *buf, size_t len, int flags)
{ {
//TRACE("send(%d, _, %d, %d) called\n", sockfd, len, flags); TRACE("send(%d, _, %d, %d) called\n", sockfd, len, flags);
return (ssize_t) _WS_send(1, sockfd, buf, len, flags); return (ssize_t) _WS_send(1, sockfd, buf, len, flags);
} }
int select(int nfds, fd_set *readfds, fd_set *writefds, int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout) fd_set *exceptfds, struct timeval *timeout)
{ {
//TRACE("select(%d, _, _, _, _) called\n", nfds); TRACE("select(%d, _, _, _, _) called\n", nfds);
return _WS_select(0, nfds, readfds, writefds, exceptfds, return _WS_select(0, nfds, readfds, writefds, exceptfds,
(void *) timeout, NULL); (void *) timeout, NULL);
} }
...@@ -1007,5 +1041,43 @@ int ppoll(struct pollfd *fds, nfds_t nfds, ...@@ -1007,5 +1041,43 @@ int ppoll(struct pollfd *fds, nfds_t nfds,
const struct timespec *timeout, const sigset_t *sigmask) const struct timespec *timeout, const sigset_t *sigmask)
{ {
TRACE("ppoll(_, %ld, _, _) called\n", nfds); TRACE("ppoll(_, %ld, _, _) called\n", nfds);
return _WS_poll(0, fds, nfds, 0, timeout, sigmask); return _WS_poll(0, fds, nfds, 0, (struct timespec *)timeout,
(sigset_t *)sigmask);
}
int dup2(int oldfd, int newfd) {
int ret;
static void * (*func)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "dup2");
TRACE("dup2(%d, %d) called\n", oldfd, newfd);
ret = (int) func(oldfd, newfd);
if (! _WS_connections[oldfd]) {
return ret;
}
if (ret < 0) {
return ret;
}
if (oldfd == newfd) {
return newfd;
}
/* dup2 behavior is to close newfd if it's open */
if (_WS_connections[newfd]) {
_WS_free(newfd);
}
/* oldfd and newfd are now descriptors for the same socket,
* re-use the same context memory area */
_WS_connections[newfd] = _WS_connections[oldfd];
_WS_connections[newfd]->refcnt++;
/* Add to search list for select/pselect */
_WS_fds[_WS_nfds] = newfd;
_WS_nfds++;
return ret;
} }
...@@ -61,6 +61,7 @@ typedef struct { ...@@ -61,6 +61,7 @@ typedef struct {
int rcarry_cnt; int rcarry_cnt;
char rcarry[3]; char rcarry[3];
int newframe; int newframe;
int refcnt;
} _WS_connection; } _WS_connection;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment