aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2012-10-01 16:35:27 -0700
committerAlon Zakai <alonzakai@gmail.com>2012-10-01 16:35:27 -0700
commit6c8ac389060b685dec87faade39841c2799c4d0a (patch)
tree497941020c5863a2a0052ad1088b47c2b0b39424
parent2a4599a92a570a461a60b1eb79df03908a047711 (diff)
add socket send(), and almost working test
-rw-r--r--src/library.js30
-rwxr-xr-xtests/runner.py48
-rw-r--r--tests/socket_relay.py55
-rw-r--r--tests/websockets_bi.c134
-rw-r--r--tests/websockets_bi_side.c56
5 files changed, 306 insertions, 17 deletions
diff --git a/src/library.js b/src/library.js
index 938a3c92..a41887bd 100644
--- a/src/library.js
+++ b/src/library.js
@@ -6391,6 +6391,29 @@ LibraryManager.library = {
console.log('binary!');
}
}
+ info.sendQueue = [];
+ info.senderWaiting = false;
+ info.sender = function(data) {
+ if (data) {
+ info.sendQueue.push(data);
+ } else 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++) {
+ console.log('sending ' + info.sendQueue[i]);
+ info.socket.send(window.btoa(info.sendQueue[i]));
+ }
+ info.sendQueue = [];
+ info.senderWaiting = false;
+ }
return 0;
},
@@ -6413,6 +6436,13 @@ LibraryManager.library = {
return ret;
},
+ send__deps: ['$Sockets'],
+ send: function(fd, buf, len, flags) {
+ var info = Sockets.fds[fd];
+ if (!info) return -1;
+ info.sender(Pointer_stringify(buf, len));
+ },
+
shutdown: function(fd, how) {
var info = Sockets.fds[fd];
if (!info) return -1;
diff --git a/tests/runner.py b/tests/runner.py
index 80926468..7b318412 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -8662,28 +8662,34 @@ elif 'browser' in str(sys.argv):
''')
self.btest('pre_run_deps.cpp', expected='10', args=['--pre-js', 'pre.js'])
+ # Runs a websocket server at a specific port. port is the true tcp socket we forward to, port+1 is the websocket one
class WebsockHarness:
- def __init__(self, port):
+ def __init__(self, port, server_func=None, no_server=False):
self.port = port
+ self.server_func = server_func
+ self.no_server = no_server
def __enter__(self):
self.pids = []
- def server_func(q):
- proc = Popen([path_from_root('tests', 'socket_server.sh'), str(self.port)])
- q.put(proc.pid)
- proc.communicate()
-
- server_queue = multiprocessing.Queue()
- self.server = multiprocessing.Process(target=server_func, args=(server_queue,))
- self.server.start()
- self.pids.append(self.server.pid)
- while True:
- if not server_queue.empty():
- self.pids.append(server_queue.get())
- break
- time.sleep(0.1)
- print '[Socket server on processes %s]' % str(self.pids[-2:])
+ if not self.no_server:
+ def server_func(q):
+ proc = Popen([path_from_root('tests', 'socket_server.sh'), str(self.port)])
+ q.put(proc.pid)
+ proc.communicate()
+
+ server_func = self.server_func or server_func
+
+ server_queue = multiprocessing.Queue()
+ self.server = multiprocessing.Process(target=server_func, args=(server_queue,))
+ self.server.start()
+ self.pids.append(self.server.pid)
+ while True:
+ if not server_queue.empty():
+ self.pids.append(server_queue.get())
+ break
+ time.sleep(0.1)
+ print '[Socket server on processes %s]' % str(self.pids[-2:])
def websockify_func(q):
proc = Popen([path_from_root('third_party', 'websockify', 'other', 'websockify'), '-vvv', str(self.port+1), '127.0.0.1:' + str(self.port)])
@@ -8711,7 +8717,15 @@ elif 'browser' in str(sys.argv):
with self.WebsockHarness(8990):
self.btest('websockets.c', expected='571')
- #def test_websockets_bi(self):
+ def test_zz_websockets_bi(self):
+ def server_func(q):
+ proc = Popen(['python', path_from_root('tests', 'socket_relay.py'), '8990', '8995'])
+ q.put(proc.pid)
+ proc.communicate()
+ with self.WebsockHarness(8990, server_func):
+ with self.WebsockHarness(8995, no_server=True):
+ Popen(['python', EMCC, path_from_root('tests', 'websockets_bi_side.c'), '-o', 'side.html']).communicate()
+ self.btest('websockets_bi.c', expected='2499')
elif 'benchmark' in str(sys.argv):
# Benchmarks. Run them with argument |benchmark|. To run a specific test, do
diff --git a/tests/socket_relay.py b/tests/socket_relay.py
new file mode 100644
index 00000000..e5b3ccef
--- /dev/null
+++ b/tests/socket_relay.py
@@ -0,0 +1,55 @@
+'''
+Listens on 2 ports and relays between them.
+
+Listens to ports A and B. When someone connects to port A, and then
+sends some data to port A, that data is sent to someone who
+connected to socket B. And so forth.
+
+This is different than say socat which will listen to one port
+and then make a connection to another port, and do bidirectional
+communication. We need to actually listen on both ports.
+'''
+
+import os, sys, socket, time, threading
+
+ports = [int(sys.argv[1]), int(sys.argv[2])]
+
+class Listener(threading.Thread):
+ def run(self):
+ self.conn = None
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ global ports
+ port = ports[0]
+ ports = ports[1:]
+ print 'listener binding to ', port
+ s.bind(('127.0.0.1', port))
+ s.listen(1)
+ print 'listener', port, 'waiting for connection'
+ conn, addr = s.accept()
+ self.conn = conn
+ while 1:
+ time.sleep(1)
+ print 'listener', port, 'waiting for data'
+ data = conn.recv(1024)
+ if not data:
+ continue
+ while not self.other.conn:
+ print 'listener', port, 'waiting for other connection in order to send data'
+ time.sleep(1)
+ print 'listener', port, 'sending data', data
+ self.other.conn.send(data)
+
+in_listener = Listener()
+in_listener.daemon = True
+in_listener.start()
+
+out_listener = Listener()
+out_listener.daemon = True
+out_listener.start()
+
+in_listener.other = out_listener
+out_listener.other = in_listener
+
+while 1:
+ time.sleep(1)
+
diff --git a/tests/websockets_bi.c b/tests/websockets_bi.c
new file mode 100644
index 00000000..338d1caf
--- /dev/null
+++ b/tests/websockets_bi.c
@@ -0,0 +1,134 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#if EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
+#define EXPECTED_BYTES 28
+
+int SocketFD;
+
+unsigned int get_all_buf(int sock, char* output, unsigned int maxsize)
+{
+ int bytes;
+ if (ioctl(sock, FIONREAD, &bytes)) return 0;
+ if (bytes == 0) return 0;
+
+ char buffer[1024];
+ int n;
+ unsigned int offset = 0;
+ while((errno = 0, (n = recv(sock, buffer, sizeof(buffer), 0))>0) ||
+ errno == EINTR) {
+ if(n>0)
+ {
+ if (((unsigned int) n)+offset > maxsize) { fprintf(stderr, "too much data!"); exit(EXIT_FAILURE); }
+ memcpy(output+offset, buffer, n);
+ offset += n;
+ }
+ }
+
+ if(n < 0) {
+ fprintf(stderr, "error in get_all_buf!");
+ exit(EXIT_FAILURE);
+ }
+ return offset;
+}
+
+int done = 0;
+
+void iter(void *arg) {
+ /* perform read write operations ... */
+ static char out[1024*2];
+ static int pos = 0;
+ int n = get_all_buf(SocketFD, out+pos, 1024-pos);
+ if (n) printf("read! %d\n", n);
+ pos += n;
+ if (pos >= EXPECTED_BYTES) {
+ int i, sum = 0;
+ for (i=0; i < pos; i++) {
+ printf("%x\n", out[i]);
+ sum += out[i];
+ }
+
+ shutdown(SocketFD, SHUT_RDWR);
+
+ close(SocketFD);
+
+ done = 1;
+
+ printf("sum: %d\n", sum);
+
+ emscripten_cancel_main_loop();
+
+#if EMSCRIPTEN
+ int result = sum;
+ REPORT_RESULT();
+#endif
+ }
+}
+
+int main(void)
+{
+ printf("hello from main page\n");
+
+ struct sockaddr_in stSockAddr;
+ int Res;
+ SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (-1 == SocketFD)
+ {
+ perror("cannot create socket");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&stSockAddr, 0, sizeof(stSockAddr));
+
+ stSockAddr.sin_family = AF_INET;
+ stSockAddr.sin_port = htons(
+#if EMSCRIPTEN
+ 8991
+#else
+ 8990
+#endif
+ );
+ Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr);
+
+ if (0 > Res) {
+ perror("error: first parameter is not a valid address family");
+ close(SocketFD);
+ exit(EXIT_FAILURE);
+ } else if (0 == Res) {
+ perror("char string (second parameter does not contain valid ipaddress)");
+ close(SocketFD);
+ exit(EXIT_FAILURE);
+ }
+
+ if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) {
+ perror("connect failed");
+ close(SocketFD);
+ exit(EXIT_FAILURE);
+
+ }
+
+#if EMSCRIPTEN
+ emscripten_run_script("console.log('adding iframe');"
+ "var iframe = document.createElement('iframe');"
+ "iframe.src = 'side.html';"
+ "document.body.appendChild(iframe);"
+ "console.log('added.');");
+ emscripten_set_main_loop(iter, 0);
+#else
+ while (!done) iter(NULL);
+#endif
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/tests/websockets_bi_side.c b/tests/websockets_bi_side.c
new file mode 100644
index 00000000..52a2c40e
--- /dev/null
+++ b/tests/websockets_bi_side.c
@@ -0,0 +1,56 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <emscripten.h>
+
+#define EXPECTED_BYTES 5
+
+int main(void)
+{
+emscripten_run_script("console.log('hallo from siide')");
+ printf("hello from side page\n");
+
+ struct sockaddr_in stSockAddr;
+ int Res;
+ int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (-1 == SocketFD)
+ {
+ perror("cannot create socket");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&stSockAddr, 0, sizeof(stSockAddr));
+
+ stSockAddr.sin_family = AF_INET;
+ stSockAddr.sin_port = htons(8996);
+ Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr);
+
+ if (0 > Res) {
+ perror("error: first parameter is not a valid address family");
+ close(SocketFD);
+ exit(EXIT_FAILURE);
+ } else if (0 == Res) {
+ perror("char string (second parameter does not contain valid ipaddress)");
+ close(SocketFD);
+ exit(EXIT_FAILURE);
+ }
+
+ if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) {
+ perror("connect failed");
+ close(SocketFD);
+ exit(EXIT_FAILURE);
+ }
+
+ char data[] = "hello from the other siide\n";
+ send(SocketFD, data, sizeof(data), 0);
+
+ return EXIT_SUCCESS;
+}
+