From 5bd6f99e247391b84a94f8c62eb29d88a0dc0986 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Fri, 25 Jul 2014 14:27:54 -0700 Subject: [PATCH 01/25] add net test file, with one test group (connection) --- test/suite/net.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 test/suite/net.js diff --git a/test/suite/net.js b/test/suite/net.js new file mode 100644 index 00000000..053bd506 --- /dev/null +++ b/test/suite/net.js @@ -0,0 +1,33 @@ +//var test = require('ttt'); +// WORKAROUND: https://github.com/tessel/runtime/issues/276 +var test = require("../../node_modules/ttt/ttt.js"), + net = require('net'); + +test('connection', function (t) { + // basic checks + var net = require('net'); + t.ok(net.createConnection, "method available"); + t.ok(net.connect, "method available"); + + // connects + var client = net.connect(80, "ipcalf.com", function () { + t.pass("callback called"); + }); + t.ok(client instanceof net.Socket, "returned socket"); + client.on('connect', function () { + t.pass("socket connected"); + client.write("GET / HTTP/1.1\nHost: ipcalf.com\nAccept: text/plain\n\n"); + }); + client.on('error', function () { + t.fail("socket error"); + }); + + // lives/dies + client.on('data', function (d) { + t.equal(d.slice(0,8).toString(), "HTTP/1.1", "got response"); + client.end(); + }); + client.on('end', function () { + t.ok(true, "socket closed"); + }); +}); From 5de68a70ff2ae5863060b0eeb1d31cab34c6f863 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Fri, 25 Jul 2014 15:50:11 -0700 Subject: [PATCH 02/25] extend net tests to include server, IP format checking --- package.json | 3 +- test/suite/net.js | 106 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 102 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 2006d229..cb4f83a0 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "bindings": "~1.2.0" }, "devDependencies": { + "tap": "git+https://github.com/tcr/node-tap.git#4f96b1", "tape": "~2.3.2", - "tap": "git+https://github.com/tcr/node-tap.git#4f96b1" + "tinytap": "^0.2.0" } } diff --git a/test/suite/net.js b/test/suite/net.js index 053bd506..60323457 100644 --- a/test/suite/net.js +++ b/test/suite/net.js @@ -1,14 +1,72 @@ -//var test = require('ttt'); -// WORKAROUND: https://github.com/tessel/runtime/issues/276 -var test = require("../../node_modules/ttt/ttt.js"), +// TODO: require('tinytap') when https://github.com/tcr/tinytap/issues/4 and https://github.com/tessel/runtime/pull/279 resolved +var test = require("../../node_modules/ttt/ttt.js" || 'ttt'), net = require('net'); -test('connection', function (t) { - // basic checks - var net = require('net'); +test('addresses', function (t) { + // API checks + t.ok(net.isIP); + t.ok(net.isIPv4); + t.ok(net.isIPv6); + + // some samples from http://publib.boulder.ibm.com/infocenter/ts3500tl/v1r0/index.jsp?topic=%2Fcom.ibm.storage.ts3500.doc%2Fopg_3584_IPv4_IPv6_addresses.html + // c.f. http://tools.ietf.org/id/draft-main-ipaddr-text-rep-02.txt for some grammar discussion + + var validIPv4 = ["0.0.0.0", "127.0.0.1", "255.255.255.255", "1.2.3.4"/*, "01.102.103.104"*/], + validIPv6 = [ + "2001:db8:3333:4444:5555:6666:7777:8888", "2001:db8:3333:4444:CCCC:DDDD:EEEE:FFFF", + "::", "2001:db8::", "::1234:5678", "2001:db8::1234:5678", + "2001:0db8:0001:0000:0000:0ab9:C0A8:0102", "2001:db8:1::ab9:C0A8:102", + "2001:db8:3333:4444:5555:6666:1.2.3.4", + "::11.22.33.44", "2001:db8::123.123.123.123", "::1234:5678:91.123.4.56", "::1234:5678:1.2.3.4", "2001:db8::1234:5678:5.6.7.8", + ], + totalBunk = [ + "255.255.255.256", "0xFF.0xFF.0xFF.0xFF", "0.0.A.0", "-1.0.0.0", "123.45.67.89zzz", "01.102.103.104", + "::255.255.255.256", "::FG", "hello world", void 0, 42, "" + ]; + + // isIP + validIPv4.forEach(function (v) { + t.equal(net.isIP(v), 4, v); + }); + validIPv6.forEach(function (v) { + t.equal(net.isIP(v), 6, v); + }); + totalBunk.forEach(function (v) { + t.equal(net.isIP(v), 0, v); + }); + + // isIPv4 + validIPv4.forEach(function (v) { + t.equal(net.isIPv4(v), true, v); + }); + validIPv6.forEach(function (v) { + t.equal(net.isIPv4(v), false, v); + }); + totalBunk.forEach(function (v) { + t.equal(net.isIPv4(v), false, v); + }); + + // isIPv6 + validIPv4.forEach(function (v) { + t.equal(net.isIPv6(v), false, v); + }); + validIPv6.forEach(function (v) { + t.equal(net.isIPv6(v), true, v); + }); + totalBunk.forEach(function (v) { + t.equal(net.isIPv6(v), false, v); + }); + + t.end(); +}); + +test('client-basic', function (t) { + // API checks t.ok(net.createConnection, "method available"); t.ok(net.connect, "method available"); + // see http://nodejs.org/api/net.html#net_net_connect_options_connectionlistener + // connects var client = net.connect(80, "ipcalf.com", function () { t.pass("callback called"); @@ -29,5 +87,41 @@ test('connection', function (t) { }); client.on('end', function () { t.ok(true, "socket closed"); + t.end(); + }); +}); + +test('server-basic', function (t) { + // API checks + t.ok(net.createServer, "method available"); + + // see http://nodejs.org/api/net.html#net_net_createserver_options_connectionlistener + + // listening + var server = net.createServer(function (c) { + t.pass("connection callback called"); + }); + server.listen(0, function () { + t.pass("listening callback called"); + }); + server.on('listening', function () { + t.pass("got listening event"); + t.ok(server.address().port, "port assigned"); + testConnection(server.address().port); + }); + + // connecting + server.on('connection', function (c) { + t.ok(c instanceof net.Socket, "got connection"); + c.on('end', function () { + t.pass("disconnected"); + }); + c.end("«§»"); }); + function testConnection(port) { + net.connect(port).on('data', function (d) { + t.equal(d.toString(), "«§»"); + t.end(); + }); + } }); From 94af33cd790d18915425cd32b7751fb3af798d59 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Fri, 25 Jul 2014 16:03:23 -0700 Subject: [PATCH 03/25] gracefully handle bogus IP candidates --- src/colony/modules/net.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index fff32982..715eff3b 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -61,7 +61,7 @@ function TCPSocket (socket, _secure) { util.inherits(TCPSocket, Stream.Duplex); function isIP (host) { - return host.match(/^[0-9.]+$/); + return (typeof host === 'string') ? host.match(/^[0-9.]+$/) : false; } function isPipeName(s) { From 06a186509ce0228eda336a0574cead07319a3314 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Fri, 25 Jul 2014 16:06:51 -0700 Subject: [PATCH 04/25] properly factor into IPv4/6/either methods --- src/colony/modules/net.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index 715eff3b..d2cb275c 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -60,10 +60,20 @@ function TCPSocket (socket, _secure) { util.inherits(TCPSocket, Stream.Duplex); -function isIP (host) { +function isIPv4 (host) { return (typeof host === 'string') ? host.match(/^[0-9.]+$/) : false; } +function isIPv6 (host) { + return false; +} + +function isIP (host) { + if (isIPv6(host)) return 6; + else if (isIPv4(host)) return 4; + else return 0; +} + function isPipeName(s) { return util.isString(s) && toNumber(s) === false; } @@ -412,6 +422,8 @@ function createServer (onsocket) { */ exports.isIP = isIP; +exports.isIPv4 = isIPv4; +exports.isIPv6 = isIPv6; exports.connect = exports.createConnection = connect; exports.createServer = createServer; exports.Socket = TCPSocket; From 2986cd97b906d6c67acee45713376295c30dafba Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Fri, 25 Jul 2014 16:29:09 -0700 Subject: [PATCH 05/25] =?UTF-8?q?get=20most=20IPv6=20and=20IPv6=20tests=20?= =?UTF-8?q?passing=20via=20StackOverflow=20regexes.=20unfortunately=20a=20?= =?UTF-8?q?few=20IPv4-suffixed-IPv6=20tests=20fail=20so=20now=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/colony/modules/net.js | 6 ++++-- test/suite/net.js | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index d2cb275c..b9d5f8f9 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -61,11 +61,13 @@ function TCPSocket (socket, _secure) { util.inherits(TCPSocket, Stream.Duplex); function isIPv4 (host) { - return (typeof host === 'string') ? host.match(/^[0-9.]+$/) : false; + // via http://stackoverflow.com/a/5284410/179583 + modified to disallow leading 0s + return /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.|$)){4}/.test(host); } function isIPv6 (host) { - return false; + // via http://stackoverflow.com/a/17871737/179583 + return /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/.test(host); } function isIP (host) { diff --git a/test/suite/net.js b/test/suite/net.js index 60323457..395c5f19 100644 --- a/test/suite/net.js +++ b/test/suite/net.js @@ -11,7 +11,7 @@ test('addresses', function (t) { // some samples from http://publib.boulder.ibm.com/infocenter/ts3500tl/v1r0/index.jsp?topic=%2Fcom.ibm.storage.ts3500.doc%2Fopg_3584_IPv4_IPv6_addresses.html // c.f. http://tools.ietf.org/id/draft-main-ipaddr-text-rep-02.txt for some grammar discussion - var validIPv4 = ["0.0.0.0", "127.0.0.1", "255.255.255.255", "1.2.3.4"/*, "01.102.103.104"*/], + var validIPv4 = ["0.0.0.0", "127.0.0.1", "255.255.255.255", "1.2.3.4", "101.203.111.200"/*, "01.102.103.104"*/], validIPv6 = [ "2001:db8:3333:4444:5555:6666:7777:8888", "2001:db8:3333:4444:CCCC:DDDD:EEEE:FFFF", "::", "2001:db8::", "::1234:5678", "2001:db8::1234:5678", @@ -21,7 +21,7 @@ test('addresses', function (t) { ], totalBunk = [ "255.255.255.256", "0xFF.0xFF.0xFF.0xFF", "0.0.A.0", "-1.0.0.0", "123.45.67.89zzz", "01.102.103.104", - "::255.255.255.256", "::FG", "hello world", void 0, 42, "" + "::11.22.33.044", "::255.255.255.256", "::FG", "hello world", void 0, 42, "" ]; // isIP From c53479086f59c3b8e02083f34b187461cfbf54f7 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Fri, 25 Jul 2014 16:45:17 -0700 Subject: [PATCH 06/25] "fix" the IPv6 regex ;-) --- src/colony/modules/net.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index b9d5f8f9..4f6d71f1 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -67,7 +67,17 @@ function isIPv4 (host) { function isIPv6 (host) { // via http://stackoverflow.com/a/17871737/179583 - return /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/.test(host); + var itIs = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/.test(host); + if (!itIs && typeof host === 'string') { + // HACK: regex above doesn't handle all IPv4-suffixed-IPv6 addresses, and, well…do you really blame me for not fixing it? + var parts = host.split(':'); + if (isIPv4(parts[parts.length-1])) { + parts.pop(); + parts.push('FFFF:FFFF'); + itIs = isIPv6(parts.join(':')); + } + } + return itIs; } function isIP (host) { From 6694a214101bf91cb0f1cd497c25fb4ccc346f43 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Mon, 28 Jul 2014 16:03:07 -0700 Subject: [PATCH 07/25] move IP address and misc. helpers out from middle of TCPSocket code --- src/colony/modules/net.js | 68 +++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index 4f6d71f1..e2af72ba 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -17,6 +17,42 @@ var dns = require('dns'); var Stream = require('stream'); var tls = require('tls'); + +/** + * ip/helpers + */ +function isIPv4 (host) { + // via http://stackoverflow.com/a/5284410/179583 + modified to disallow leading 0s + return /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.|$)){4}/.test(host); +} + +function isIPv6 (host) { + // via http://stackoverflow.com/a/17871737/179583 + var itIs = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/.test(host); + if (!itIs && typeof host === 'string') { + // HACK: regex above doesn't handle all IPv4-suffixed-IPv6 addresses, and, well…do you really blame me for not fixing it? + var parts = host.split(':'); + if (isIPv4(parts[parts.length-1])) { + parts.pop(); + parts.push('FFFF:FFFF'); + itIs = isIPv6(parts.join(':')); + } + } + return itIs; +} + +function isIP (host) { + if (isIPv6(host)) return 6; + else if (isIPv4(host)) return 4; + else return 0; +} + +function isPipeName(s) { + return util.isString(s) && toNumber(s) === false; +} + +function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; } + /** * ssl */ @@ -60,38 +96,6 @@ function TCPSocket (socket, _secure) { util.inherits(TCPSocket, Stream.Duplex); -function isIPv4 (host) { - // via http://stackoverflow.com/a/5284410/179583 + modified to disallow leading 0s - return /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.|$)){4}/.test(host); -} - -function isIPv6 (host) { - // via http://stackoverflow.com/a/17871737/179583 - var itIs = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/.test(host); - if (!itIs && typeof host === 'string') { - // HACK: regex above doesn't handle all IPv4-suffixed-IPv6 addresses, and, well…do you really blame me for not fixing it? - var parts = host.split(':'); - if (isIPv4(parts[parts.length-1])) { - parts.pop(); - parts.push('FFFF:FFFF'); - itIs = isIPv6(parts.join(':')); - } - } - return itIs; -} - -function isIP (host) { - if (isIPv6(host)) return 6; - else if (isIPv4(host)) return 4; - else return 0; -} - -function isPipeName(s) { - return util.isString(s) && toNumber(s) === false; -} - -function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; } - function normalizeConnectArgs(args) { var options = {}; From 2127273566908fd0359bde44d6cd4a3c60dc4f9e Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Mon, 28 Jul 2014 16:25:47 -0700 Subject: [PATCH 08/25] close socket when stream finished, fire end event whenever __listen loop ceases. updates #310, for posix version at least --- src/colony/modules/net.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index e2af72ba..58604d52 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -82,6 +82,11 @@ function TCPSocket (socket, _secure) { this._queueEnd = false; var self = this; + self.on('finish', function () { + // this is called when writing is ended + // TODO: support allowHalfOpen (if firmware can?) + self.close(); + }) self._closehandler = function (buf) { var socket = buf.readUInt32LE(0); if (socket == self.socket) { @@ -227,6 +232,10 @@ TCPSocket.prototype.__listen = function () { var self = this; this.__listenid = setTimeout(function loop () { self.__listenid = null; + // ~HACK: set a watchdog to fire end event if not re-polled + var failsafeEnd = setImmediate(function () { + self.emit('end'); + }); if (self._sending) { return; @@ -258,6 +267,7 @@ TCPSocket.prototype.__listen = function () { } self.__listenid = setTimeout(loop, 10); + clearImmediate(failsafeEnd); }, 10); }; @@ -354,6 +364,7 @@ TCPSocket.prototype.destroy = TCPSocket.prototype.close = function () { if (self.__listenid != null) { clearInterval(self.__listenid); self.__listenid = null + self.emit('end') } if (self.socket != null) { From 24429057c2474910fa58d2b23960e7daef0dbb90 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Mon, 28 Jul 2014 16:39:07 -0700 Subject: [PATCH 09/25] temporary workaround for https://github.com/tessel/runtime/issues/311 --- test/suite/net.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/suite/net.js b/test/suite/net.js index 395c5f19..05c260fe 100644 --- a/test/suite/net.js +++ b/test/suite/net.js @@ -119,7 +119,9 @@ test('server-basic', function (t) { c.end("«§»"); }); function testConnection(port) { - net.connect(port).on('data', function (d) { + //net.connect(port).on('data', function (d) { + // WORKAROUND: https://github.com/tessel/runtime/issues/311 + net.connect(port, "localhost").on('data', function (d) { t.equal(d.toString(), "«§»"); t.end(); }); From 8157595a61dbd0eab10212bfb4fe63dd26d10211 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Mon, 28 Jul 2014 16:40:07 -0700 Subject: [PATCH 10/25] net.Server should emit 'connection', not 'socket', events --- src/colony/modules/net.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index 58604d52..4612aff6 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -428,7 +428,7 @@ TCPServer.prototype.listen = function (port, ip) { var clientsocket = new TCPSocket(client); clientsocket.connected = true; clientsocket.__listen(); - self.emit('socket', clientsocket); + self.emit('connection', clientsocket); } setTimeout(poll, 10); @@ -439,7 +439,7 @@ TCPServer.prototype.listen = function (port, ip) { function createServer (onsocket) { var server = new TCPServer(tm.tcp_open()); - onsocket && server.on('socket', onsocket); + onsocket && server.on('connection', onsocket); return server; }; From 0356a79964fb602acb8227ea14bf9e22f22bb3b3 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Mon, 28 Jul 2014 17:12:15 -0700 Subject: [PATCH 11/25] have net.Server emit listen event, handle optional arguments to .listen method --- src/colony/modules/net.js | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index 4612aff6..d3186b22 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -408,7 +408,28 @@ function TCPServer (socket) { util.inherits(TCPServer, TCPSocket); -TCPServer.prototype.listen = function (port, ip) { +TCPServer.prototype.listen = function (port, host, backlog, cb) { + if (typeof port === 'string') { + throw Error("UNIX sockets not supported"); + } else if (port === 0) { + // TODO: actually fix https://github.com/tessel/runtime/issues/329 + port = 1024 + Math.round(64311*Math.random()); + } + + if (typeof host === 'function') { + cb = host; + host = null; // NOTE: would be INADDR_ANY, but we ignore… + backlog = 511; // NOTE: also ignored + } else if (typeof host === 'number') { + backlog = host; + host = null; + } + + if (typeof backlog === 'function') { + backlog = 511; + cb = backlog; + } + var self = this; var res = tm.tcp_listen(this.socket, port); if (res < 0) { @@ -416,7 +437,8 @@ TCPServer.prototype.listen = function (port, ip) { } self._port = port; - self._address = ip; + self._address = host; + self.emit('listening'); function poll(){ if(self.socket == null){ return false; } From ee266e997131eafd04a84cad358bb2ecb7a95e77 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Mon, 28 Jul 2014 17:21:49 -0700 Subject: [PATCH 12/25] actually register listening cb, fire event async so sync-registered listeners can catch --- src/colony/modules/net.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index d3186b22..8d03c454 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -430,6 +430,8 @@ TCPServer.prototype.listen = function (port, host, backlog, cb) { cb = backlog; } + if (cb) this.once('listening', cb); + var self = this; var res = tm.tcp_listen(this.socket, port); if (res < 0) { @@ -438,7 +440,9 @@ TCPServer.prototype.listen = function (port, host, backlog, cb) { self._port = port; self._address = host; - self.emit('listening'); + setImmediate(function () { + self.emit('listening'); + }); function poll(){ if(self.socket == null){ return false; } From b6b0c684cc12bc8018d557897cc19f6ef91c4f97 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Tue, 29 Jul 2014 18:02:50 -0700 Subject: [PATCH 13/25] basic JS-side automatic port binding. does not handle potential conflicts with ephemeral port on CC3k side, cannot find info on that range --- src/colony/modules/net.js | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index 8d03c454..650e49e6 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -101,6 +101,18 @@ function TCPSocket (socket, _secure) { util.inherits(TCPSocket, Stream.Duplex); +TCPSocket._portsUsed = Object.create(null); + +TCPSocket._requestPort = function (port) { + // NOTE: only supports _automatic_ port assignment; we track (but not *check*) manually requested ports + if (port === 0) { + port = 1024; // NOTE: could optimize, e.g. by starting from last-granted or assuming only 7 sockets… + while (port in TCPSocket._portsUsed) ++port; + } + TCPSocket._portsUsed[port] = true; + return port; +}; + function normalizeConnectArgs(args) { var options = {}; @@ -129,7 +141,7 @@ TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { var host = args[0].host; var cb = args[1]; - self._port = port; + self._port = +port; self._address = host; if (cb) { @@ -152,7 +164,7 @@ TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { var unsplitIp = ip; ip = ip.split('.').map(Number); - var ret = tm.tcp_connect(self.socket, ip[0], ip[1], ip[2], ip[3], Number(port)); + var ret = tm.tcp_connect(self.socket, ip[0], ip[1], ip[2], ip[3], self._port); if (ret >= 1) { // we're not connected to the internet throw new Error("Lost connection"); @@ -411,9 +423,6 @@ util.inherits(TCPServer, TCPSocket); TCPServer.prototype.listen = function (port, host, backlog, cb) { if (typeof port === 'string') { throw Error("UNIX sockets not supported"); - } else if (port === 0) { - // TODO: actually fix https://github.com/tessel/runtime/issues/329 - port = 1024 + Math.round(64311*Math.random()); } if (typeof host === 'function') { @@ -430,16 +439,16 @@ TCPServer.prototype.listen = function (port, host, backlog, cb) { cb = backlog; } + this._port = TCPSocket._requestPort(port); + this._address = host; if (cb) this.once('listening', cb); - var self = this; - var res = tm.tcp_listen(this.socket, port); + var res = tm.tcp_listen(this.socket, this._port); if (res < 0) { - throw "Error listening on TCP socket (port " + port + ", ip " + ip + ")" + throw "Error listening on TCP socket (port " + this._port + ", ip " + host + ")" } - - self._port = port; - self._address = host; + + var self = this; setImmediate(function () { self.emit('listening'); }); From dd2fed5208cccdbec9c3f3f04a61c253aaa7dd2d Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Tue, 29 Jul 2014 18:15:36 -0700 Subject: [PATCH 14/25] don't throw on listen error, fixes #332 --- src/colony/modules/net.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index 650e49e6..82eae2e0 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -443,16 +443,15 @@ TCPServer.prototype.listen = function (port, host, backlog, cb) { this._address = host; if (cb) this.once('listening', cb); - var res = tm.tcp_listen(this.socket, this._port); - if (res < 0) { - throw "Error listening on TCP socket (port " + this._port + ", ip " + host + ")" - } - - var self = this; - setImmediate(function () { + var self = this, + res = tm.tcp_listen(this.socket, this._port); + if (res < 0) setImmediate(function () { + self.emit('error', new Error("Listen on TCP socket failed ("+res+")")); + }); else setImmediate(function () { self.emit('listening'); + poll(); }); - + function poll(){ if(self.socket == null){ return false; } var _ = tm.tcp_accept(self.socket) @@ -468,8 +467,6 @@ TCPServer.prototype.listen = function (port, host, backlog, cb) { setTimeout(poll, 10); } - - poll(); }; function createServer (onsocket) { From 8082d81630b2478abcdee8c81f0d5143488b9d28 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Wed, 30 Jul 2014 14:31:09 -0700 Subject: [PATCH 15/25] adds a test for port binding behavior, fixes #331 --- test/suite/net.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/suite/net.js b/test/suite/net.js index 05c260fe..dcc79b1e 100644 --- a/test/suite/net.js +++ b/test/suite/net.js @@ -127,3 +127,23 @@ test('server-basic', function (t) { }); } }); + +test('server-binding', function (t) { + var firstServer = net.createServer(), + otherServer = net.createServer(), + conflicting = net.createServer(); + firstServer.listen(0, function () { + var firstPort = firstServer.address().port; + t.ok(firstPort, "assigned a port"); + otherServer.listen(0, function () { + t.notEqual(otherServer.address().port, firstPort, "assigned a different port"); + }); + conflicting.listen(firstPort, function () { + t.fail("this should not be called!"); + }); + conflicting.on('error', function (e) { + t.ok(e, "got error as expected"); + t.end(); + }); + }); +}); From 719d4c6676278245d2c8006ac2a2da6267ada03f Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Wed, 30 Jul 2014 14:55:10 -0700 Subject: [PATCH 16/25] avoid throwing and emit errors instead (except for two usage asserts), including a refactor of connect routine to support emitting socket open errors. updates #262 (will need to confirm event propagation through HTTP module) --- src/colony/modules/net.js | 42 ++++++++++++++++++++------------------- test/suite/net.js | 7 +++++++ 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index 82eae2e0..0f26dadd 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -75,13 +75,25 @@ function ensureSSLCtx () { function TCPSocket (socket, _secure) { Stream.Duplex.call(this); - this.socket = socket; + + if (socket === null) { + if (_secure) ensureSSLCtx(); + this.socket = tm.tcp_open(); + } else { + this.socket = socket; + } this._secure = _secure; this._outgoing = []; this._sending = false; this._queueEnd = false; var self = this; + if (this.socket < 0) { + setImmediate(function () { + self.emit('error', new Error("ENOENT: Cannot open another socket.")); + }); + return; + } self.on('finish', function () { // this is called when writing is ended // TODO: support allowHalfOpen (if firmware can?) @@ -167,12 +179,12 @@ TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { var ret = tm.tcp_connect(self.socket, ip[0], ip[1], ip[2], ip[3], self._port); if (ret >= 1) { // we're not connected to the internet - throw new Error("Lost connection"); + return self.emit('error', new Error("Lost connection")); } if (ret < 0) { tm.tcp_close(self.socket); // -57 if (retries > 3) { - throw new Error('ENOENT Cannot connect to ' + ip.join('.') + ' Got: err'+ret); + return self.emit('error', new Error('ENOENT Cannot connect to ' + ip.join('.') + ' Got: err'+ret)); } else { retries++; setTimeout(function(){ @@ -194,11 +206,11 @@ TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { , ret = _[1] if (ret != 0) { if (ret == -517) { - throw new Error('CERT_HAS_EXPIRED'); + return self.emit('error', new Error('CERT_HAS_EXPIRED')); } else if (ret == -516) { - throw new Error('CERT_NOT_YET_VALID'); + return self.emit('error', new Error('CERT_NOT_YET_VALID')); } else { - throw new Error('Could not validate SSL request (error ' + ret + ')'); + return self.emit('error', new Error('Could not validate SSL request (error ' + ret + ')')); } } @@ -222,7 +234,7 @@ TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { } if (!tls.checkServerIdentity(host, cert)) { - throw new Error('Hostname/IP doesn\'t match certificate\'s altnames'); + return self.emit('error', new Error('Hostname/IP doesn\'t match certificate\'s altnames')); } self._ssl = ssl; @@ -340,7 +352,7 @@ TCPSocket.prototype.__send = function (cb) { } if (ret == null) { - throw new Error('Never sent data over socket'); + return self.emit('error', new Error('Never sent data over socket')); } else if (ret == -2) { // cc3000 ran out of buffers. wait until a buffer clears up to send this packet. setTimeout(function() { @@ -352,8 +364,7 @@ TCPSocket.prototype.__send = function (cb) { // EWOULDBLOCK / EAGAIN setTimeout(send, 100); } else if (ret < 0) { - // Error. - throw new Error(-ret); + return self.emit('error', new Error("Socket write failed unexpectedly! ("+ret+")")); } else { // Next buffer. self._sending = false; @@ -395,16 +406,7 @@ TCPSocket.prototype.setTimeout = function () { /* noop */ }; TCPSocket.prototype.setNoDelay = function () { /* noop */ }; function connect (port, host, callback, _secure) { - if (_secure) { - ensureSSLCtx(); - } - - var sock = tm.tcp_open(); - if (sock == -1) { - throw 'ENOENT: Cannot connect to new socket.' - } - - var client = new TCPSocket(sock, _secure); + var client = new TCPSocket(null, _secure); client.connect(port, host, callback); return client; }; diff --git a/test/suite/net.js b/test/suite/net.js index dcc79b1e..a91f147c 100644 --- a/test/suite/net.js +++ b/test/suite/net.js @@ -147,3 +147,10 @@ test('server-binding', function (t) { }); }); }); + +test('client-errors', function (t) { + net.connect(1, "0.0.0.0").on('error', function (e) { + t.ok(e, "got expected error"); + t.end(); + }); +}); From e1f51639ce824ee13885a31b40e9d40043ae634a Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Wed, 30 Jul 2014 15:10:24 -0700 Subject: [PATCH 17/25] fix whitespace --- src/colony/modules/net.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index 0f26dadd..b1c40b88 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -455,8 +455,7 @@ TCPServer.prototype.listen = function (port, host, backlog, cb) { }); function poll(){ - if(self.socket == null){ return false; } - var _ = tm.tcp_accept(self.socket) + var _ = tm.tcp_accept(self.socket) , client = _[0] , ip = _[1]; From 53d284eedcc44b14c5e2c52d39c2d4e92eaac55c Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Wed, 30 Jul 2014 15:11:13 -0700 Subject: [PATCH 18/25] stop server from continued polling after socket is closed, fixes #333 --- src/colony/modules/net.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index b1c40b88..c3f5ea08 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -455,6 +455,9 @@ TCPServer.prototype.listen = function (port, host, backlog, cb) { }); function poll(){ + // stop polling if we get closed + if (self.socket === null) return; + var _ = tm.tcp_accept(self.socket) , client = _[0] , ip = _[1]; From fb0204cf3c2c308fae167c9d66c7269b8f756d37 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Wed, 30 Jul 2014 16:25:01 -0700 Subject: [PATCH 19/25] default host when connecting, fixes #311 --- src/colony/modules/net.js | 8 ++++---- test/suite/net.js | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index c3f5ea08..a7f7ab5a 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -149,11 +149,11 @@ function normalizeConnectArgs(args) { TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { var self = this; var args = normalizeConnectArgs(arguments); - var port = args[0].port; - var host = args[0].host; + var port = +args[0].port; + var host = args[0].host || "127.0.0.1"; var cb = args[1]; - self._port = +port; + self._port = port; self._address = host; if (cb) { @@ -176,7 +176,7 @@ TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { var unsplitIp = ip; ip = ip.split('.').map(Number); - var ret = tm.tcp_connect(self.socket, ip[0], ip[1], ip[2], ip[3], self._port); + var ret = tm.tcp_connect(self.socket, ip[0], ip[1], ip[2], ip[3], port); if (ret >= 1) { // we're not connected to the internet return self.emit('error', new Error("Lost connection")); diff --git a/test/suite/net.js b/test/suite/net.js index a91f147c..e6fd06ea 100644 --- a/test/suite/net.js +++ b/test/suite/net.js @@ -119,9 +119,7 @@ test('server-basic', function (t) { c.end("«§»"); }); function testConnection(port) { - //net.connect(port).on('data', function (d) { - // WORKAROUND: https://github.com/tessel/runtime/issues/311 - net.connect(port, "localhost").on('data', function (d) { + net.connect(port).on('data', function (d) { t.equal(d.toString(), "«§»"); t.end(); }); From c3780029748180dae9e93c9b2369e43f7a1fa420 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Wed, 30 Jul 2014 17:01:43 -0700 Subject: [PATCH 20/25] implement net.Socket.prototype.setTimeout --- src/colony/modules/net.js | 24 ++++++++++++++++++++++-- test/suite/net.js | 13 +++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index a7f7ab5a..79031b26 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -161,6 +161,7 @@ TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { } setImmediate(function () { + self._restartTimeout(); if (isIP(host)) { doConnect(host); } else { @@ -168,6 +169,7 @@ TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { if (err) { return self.emit('error', err); } + self._restartTimeout(); doConnect(ips[0]); }) } @@ -240,6 +242,7 @@ TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { self._ssl = ssl; } + self._restartTimeout(); self.__listen(); self.connected = true; self.emit('connect'); @@ -286,6 +289,7 @@ TCPSocket.prototype.__listen = function () { } if (buf.length) { + self._restartTimeout(); self.push(buf); // TODO: stop polling if this returns false } @@ -308,7 +312,7 @@ var WRITE_PACKET_SIZE = 1024; TCPSocket.prototype._write = function (buf, encoding, cb) { var self = this; - + if (!Buffer.isBuffer(buf)) { buf = new Buffer(buf); } @@ -366,6 +370,7 @@ TCPSocket.prototype.__send = function (cb) { } else if (ret < 0) { return self.emit('error', new Error("Socket write failed unexpectedly! ("+ret+")")); } else { + self._restartTimeout(); // Next buffer. self._sending = false; self.__send(cb); @@ -402,7 +407,22 @@ TCPSocket.prototype.destroy = TCPSocket.prototype.close = function () { }); }; -TCPSocket.prototype.setTimeout = function () { /* noop */ }; +TCPSocket.prototype.setTimeout = function (msecs, cb) { + this._timeout = msecs; + this._restartTimeout(); + if (cb) { + if (msecs) this.once('timeout', cb); + else this.removeListener('timeout', cb); // not documented, but node.js does this + } +}; +TCPSocket.prototype._restartTimeout = function () { + var self = this; + clearTimeout(self._timeoutWatchdog); + this._timeoutWatchdog = (self._timeout) ? setTimeout(function () { + self.emit('timeout'); + }, self._timeout) : null; +} + TCPSocket.prototype.setNoDelay = function () { /* noop */ }; function connect (port, host, callback, _secure) { diff --git a/test/suite/net.js b/test/suite/net.js index e6fd06ea..97591853 100644 --- a/test/suite/net.js +++ b/test/suite/net.js @@ -152,3 +152,16 @@ test('client-errors', function (t) { t.end(); }); }); + +test('client-timeout', function (t) { + var client = net.connect(80, "ipcalf.com", function () { + client.setTimeout(100, function () { + t.pass("timeout callback called"); + }); + client.on('timeout', function () { + t.pass("timeout event fired"); + client.destroy(); + t.end(); + }); + }); +}); From 68b0d129110520a06f5c88c05a33d23e6c05c0c1 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Wed, 30 Jul 2014 17:08:07 -0700 Subject: [PATCH 21/25] add warning and note about setNoDelay --- src/colony/modules/net.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index 79031b26..ece183b7 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -423,7 +423,11 @@ TCPSocket.prototype._restartTimeout = function () { }, self._timeout) : null; } -TCPSocket.prototype.setNoDelay = function () { /* noop */ }; + +// NOTE: CC3K may not support? http://e2e.ti.com/support/wireless_connectivity/f/851/p/349461/1223801.aspx#1223801 +TCPSocket.prototype.setNoDelay = function (val) { + if (val) console.warn("Ignoring call to setNoDelay. TCP_NODELAY socket option not supported."); +}; function connect (port, host, callback, _secure) { var client = new TCPSocket(null, _secure); From 037789618d68d35feb72670bcec596c6ddaa0f79 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Wed, 30 Jul 2014 17:14:09 -0700 Subject: [PATCH 22/25] handle options to createServer, warn if allowHalfOpen requested --- src/colony/modules/net.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index ece183b7..baaa9438 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -149,8 +149,10 @@ function normalizeConnectArgs(args) { TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { var self = this; var args = normalizeConnectArgs(arguments); - var port = +args[0].port; - var host = args[0].host || "127.0.0.1"; + var opts = args[0]; + if (opts.allowHalfOpen) console.warn("Ignoring allowHalfOpen option."); + var port = +opts.port; + var host = opts.host || "127.0.0.1"; var cb = args[1]; self._port = port; @@ -497,7 +499,12 @@ TCPServer.prototype.listen = function (port, host, backlog, cb) { } }; -function createServer (onsocket) { +function createServer (opts, onsocket) { + if (typeof opts === 'function') { + onsocket = opts; + opts = null; + } + if (opts && opts.allowHalfOpen) console.warn("Ignoring allowHalfOpen option."); var server = new TCPServer(tm.tcp_open()); onsocket && server.on('connection', onsocket); return server; From 5faf6dd9d87f9c38e9d9f6326421f4878a86bcd9 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Wed, 30 Jul 2014 17:14:09 -0700 Subject: [PATCH 23/25] handle options to createServer, warn if allowHalfOpen requested --- src/colony/modules/net.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index baaa9438..09ae380e 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -76,7 +76,11 @@ function ensureSSLCtx () { function TCPSocket (socket, _secure) { Stream.Duplex.call(this); - if (socket === null) { + if (typeof socket === 'object') { + this.socket = socket.fd; + // TODO: respect readable/writable flags + if (socket.allowHalfOpen) console.warn("Ignoring allowHalfOpen option."); + } else if (socket == null) { if (_secure) ensureSSLCtx(); this.socket = tm.tcp_open(); } else { From b489d344474fc9984bcf783531b85b87c063457d Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Thu, 31 Jul 2014 16:39:44 -0700 Subject: [PATCH 24/25] =?UTF-8?q?[caution:=20tm=5Fnet=20change=20will=20ne?= =?UTF-8?q?ed=20mirroring=20in=20firmware]=20basic=20pass=20at=20local/rem?= =?UTF-8?q?ote=20address/port=20properties,=20move=20.address()=20to=20TCP?= =?UTF-8?q?Server,=20clean=20up=20some=20of=20the=20C=20code,=20mostly=20f?= =?UTF-8?q?ixes=20#338=20(localAddress=20always=200.0.0.0=20though?= =?UTF-8?q?=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/colony/lua_tm.c | 15 ++++++------ src/colony/modules/net.js | 50 ++++++++++++++++++++++++--------------- src/posix/tm_net.c | 29 ++++++++++------------- src/tm.h | 4 ++-- 4 files changed, 53 insertions(+), 45 deletions(-) diff --git a/src/colony/lua_tm.c b/src/colony/lua_tm.c index 76dc9c70..f46c61c9 100644 --- a/src/colony/lua_tm.c +++ b/src/colony/lua_tm.c @@ -188,13 +188,10 @@ static int l_tm_tcp_close (lua_State* L) static int l_tm_tcp_connect (lua_State* L) { tm_socket_t socket = (tm_socket_t) lua_tonumber(L, 1); - uint8_t ip0 = (uint8_t) lua_tonumber(L, 2); - uint8_t ip1 = (uint8_t) lua_tonumber(L, 3); - uint8_t ip2 = (uint8_t) lua_tonumber(L, 4); - uint8_t ip3 = (uint8_t) lua_tonumber(L, 5); - uint16_t port = (uint16_t) lua_tonumber(L, 6); + uint32_t addr = (uint32_t) lua_tonumber(L, 2); + uint16_t port = (uint16_t) lua_tonumber(L, 3); - lua_pushnumber(L, tm_tcp_connect(socket, ip0, ip1, ip2, ip3, port)); + lua_pushnumber(L, tm_tcp_connect(socket, addr, port)); return 1; } @@ -247,11 +244,13 @@ static int l_tm_tcp_listen (lua_State* L) static int l_tm_tcp_accept (lua_State* L) { uint32_t addr; + uint16_t port; tm_socket_t socket = (tm_socket_t) lua_tonumber(L, 1); - lua_pushnumber(L, tm_tcp_accept(socket, &addr)); + lua_pushnumber(L, tm_tcp_accept(socket, &addr, &port)); lua_pushnumber(L, addr); - return 2; + lua_pushnumber(L, port); + return 3; } #ifdef ENABLE_TLS diff --git a/src/colony/modules/net.js b/src/colony/modules/net.js index 09ae380e..c5e0b68b 100644 --- a/src/colony/modules/net.js +++ b/src/colony/modules/net.js @@ -159,8 +159,11 @@ TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { var host = opts.host || "127.0.0.1"; var cb = args[1]; - self._port = port; - self._address = host; + self.remotePort = port; + self.remoteAddress = host; + // TODO: proper value for these? + self.localPort = 0; + self.localAddress = "0.0.0.0"; if (cb) { self.once('connect', cb); @@ -176,15 +179,16 @@ TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { return self.emit('error', err); } self._restartTimeout(); - doConnect(ips[0]); + self.remoteAddress = ips[0]; + doConnect(self.remoteAddress); }) } var retries = 0; function doConnect(ip) { - var unsplitIp = ip; - ip = ip.split('.').map(Number); + var addr = ip.split('.').map(Number); + addr = (addr[0] << 24) + (addr[1] << 16) + (addr[2] << 8) + addr[3]; - var ret = tm.tcp_connect(self.socket, ip[0], ip[1], ip[2], ip[3], port); + var ret = tm.tcp_connect(self.socket, addr, port); if (ret >= 1) { // we're not connected to the internet return self.emit('error', new Error("Lost connection")); @@ -192,13 +196,13 @@ TCPSocket.prototype.connect = function (/*options | [port], [host], [cb]*/) { if (ret < 0) { tm.tcp_close(self.socket); // -57 if (retries > 3) { - return self.emit('error', new Error('ENOENT Cannot connect to ' + ip.join('.') + ' Got: err'+ret)); + return self.emit('error', new Error('ENOENT Cannot connect to ' + ip + ' Got: err'+ret)); } else { retries++; setTimeout(function(){ // wait for tcp socket to actually close self.socket = tm.tcp_open(); - doConnect(unsplitIp); + doConnect(ip); }, 100); return; } @@ -305,13 +309,8 @@ TCPSocket.prototype.__listen = function () { }, 10); }; -TCPSocket.prototype.address = function () { - return { - port: this._port, - family: 'IPv4', - address: this._address - }; -}; +TCPSocket.prototype.localFamily = 'IPv4'; +TCPSocket.prototype.remoteFamily = 'IPv4'; // Maximum packet size CC can handle. var WRITE_PACKET_SIZE = 1024; @@ -471,12 +470,12 @@ TCPServer.prototype.listen = function (port, host, backlog, cb) { cb = backlog; } - this._port = TCPSocket._requestPort(port); - this._address = host; + this.localPort = TCPSocket._requestPort(port); + this.localAddress = host || "0.0.0.0"; if (cb) this.once('listening', cb); var self = this, - res = tm.tcp_listen(this.socket, this._port); + res = tm.tcp_listen(this.socket, this.localPort); if (res < 0) setImmediate(function () { self.emit('error', new Error("Listen on TCP socket failed ("+res+")")); }); else setImmediate(function () { @@ -490,11 +489,16 @@ TCPServer.prototype.listen = function (port, host, backlog, cb) { var _ = tm.tcp_accept(self.socket) , client = _[0] - , ip = _[1]; + , addr = _[1] + , port = _[2]; if (client >= 0) { var clientsocket = new TCPSocket(client); clientsocket.connected = true; + clientsocket.localAddress = self.localAddress; // TODO: https://forums.tessel.io/t/get-ip-address-of-tessel-in-code/203 + clientsocket.localPort = self.localPort; + clientsocket.remoteAddress = [addr >>> 24, (addr >>> 16) & 0xFF, (addr >>> 8) & 0xFF, addr & 0xFF].join('.'); + clientsocket.remotePort = port; clientsocket.__listen(); self.emit('connection', clientsocket); } @@ -503,6 +507,14 @@ TCPServer.prototype.listen = function (port, host, backlog, cb) { } }; +TCPServer.prototype.address = function () { + return { + port: this.localPort, + family: this.localFamily, + address: this.localAddress + }; +}; + function createServer (opts, onsocket) { if (typeof opts === 'function') { onsocket = opts; diff --git a/src/posix/tm_net.c b/src/posix/tm_net.c index df4036bb..5b0f25d0 100644 --- a/src/posix/tm_net.c +++ b/src/posix/tm_net.c @@ -125,11 +125,11 @@ int tm_tcp_close (tm_socket_t sock) // return close(sock); } -int tm_tcp_connect (tm_socket_t sock, uint8_t ip0, uint8_t ip1, uint8_t ip2, uint8_t ip3, uint16_t port) +int tm_tcp_connect (tm_socket_t sock, uint32_t addr, uint16_t port) { struct sockaddr_in server; - server.sin_addr.s_addr = htonl(ip0 << 24 | ip1 << 16 | ip2 << 8 | ip3); // inet_addr("74.125.235.20"); server.sin_family = AF_INET; + server.sin_addr.s_addr = htonl(addr); server.sin_port = htons(port); // printf("server: %p, %d, %d\n", server.sin_addr.s_addr, server.sin_family, server.sin_port); return connect(sock, (struct sockaddr *) &server, sizeof(server)); @@ -166,19 +166,15 @@ int tm_tcp_listen (tm_socket_t sock, uint16_t port) { // CC3000_START; - struct sockaddr localSocketAddr; - localSocketAddr.sa_family = AF_INET; - localSocketAddr.sa_data[0] = (port & 0xFF00) >> 8; //ascii_to_char(0x01, 0x01); - localSocketAddr.sa_data[1] = (port & 0x00FF); //ascii_to_char(0x05, 0x0c); - localSocketAddr.sa_data[2] = 0; - localSocketAddr.sa_data[3] = 0; - localSocketAddr.sa_data[4] = 0; - localSocketAddr.sa_data[5] = 0; + struct sockaddr_in localSocketAddr; + localSocketAddr.sin_family = AF_INET; + localSocketAddr.sin_port = htons(port); + localSocketAddr.sin_addr.s_addr = 0; // Bind socket // TM_COMMAND('w', "Binding local socket..."); int sockStatus; - if ((sockStatus = bind(sock, &localSocketAddr, sizeof(struct sockaddr))) != 0) { + if ((sockStatus = bind(sock, (struct sockaddr *) &localSocketAddr, sizeof(localSocketAddr))) != 0) { // TM_COMMAND('w', "binding failed: %d", sockStatus); // CC3000_END; return -1; @@ -204,11 +200,12 @@ int tm_tcp_listen (tm_socket_t sock, uint16_t port) // Returns -1 on error or no socket. // Returns -2 on pending connection. // Returns >= 0 for socket descriptor. -tm_socket_t tm_tcp_accept (tm_socket_t sock, uint32_t *ip) +tm_socket_t tm_tcp_accept (tm_socket_t sock, uint32_t *addr, uint16_t *port) { - struct sockaddr addrClient; - socklen_t addrlen; - int res = accept(sock, &addrClient, &addrlen); - *ip = ((struct sockaddr_in *) &addrClient)->sin_addr.s_addr; + struct sockaddr_in addrClient; + socklen_t addrlen = sizeof(addrClient); + int res = accept(sock, (struct sockaddr *) &addrClient, &addrlen); + *addr = ntohl(addrClient.sin_addr.s_addr); + *port = ntohs(addrClient.sin_addr.s_addr); return res; } \ No newline at end of file diff --git a/src/tm.h b/src/tm.h index f778fe98..6d062ecd 100644 --- a/src/tm.h +++ b/src/tm.h @@ -123,12 +123,12 @@ int tm_udp_send (int ulSocket, uint8_t ip0, uint8_t ip1, uint8_t ip2, uint8_t ip tm_socket_t tm_tcp_open (); int tm_tcp_close (); -int tm_tcp_connect (tm_socket_t sock, uint8_t ip0, uint8_t ip1, uint8_t ip2, uint8_t ip3, uint16_t port); +int tm_tcp_connect (tm_socket_t sock, uint32_t addr, uint16_t port); int tm_tcp_write (tm_socket_t sock, const uint8_t *buf, size_t buflen); int tm_tcp_read (tm_socket_t sock, uint8_t *buf, size_t buflen); int tm_tcp_readable (tm_socket_t sock); int tm_tcp_listen (tm_socket_t sock, uint16_t port); -tm_socket_t tm_tcp_accept (tm_socket_t sock, uint32_t *ip); +tm_socket_t tm_tcp_accept (tm_socket_t sock, uint32_t *addr, uint16_t *port); // DNS From e416432d76c7e87cb23f44cb007b7a5b5465bab9 Mon Sep 17 00:00:00 2001 From: Nathan Vander Wilt Date: Tue, 5 Aug 2014 12:53:11 -0700 Subject: [PATCH 25/25] =?UTF-8?q?use=20tinytap=20which=20is=20the=20only?= =?UTF-8?q?=20small=20test=20runner=20that=20"works",=20https://github.com?= =?UTF-8?q?/tcr/tinytap/issues/4=20or=20no=20https://github.com/tcr/tinyta?= =?UTF-8?q?p/issues/4=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/suite/net.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/suite/net.js b/test/suite/net.js index 97591853..2075ce1a 100644 --- a/test/suite/net.js +++ b/test/suite/net.js @@ -1,5 +1,5 @@ -// TODO: require('tinytap') when https://github.com/tcr/tinytap/issues/4 and https://github.com/tessel/runtime/pull/279 resolved -var test = require("../../node_modules/ttt/ttt.js" || 'ttt'), +// NOTE: see https://github.com/tcr/tinytap/issues/4 — not all tests get applied?! +var test = require('tinytap'), net = require('net'); test('addresses', function (t) {