Skip to content

Commit

Permalink
Merge 7c6db29 into 6ff87b4
Browse files Browse the repository at this point in the history
  • Loading branch information
pboothe authored May 29, 2020
2 parents 6ff87b4 + 7c6db29 commit 51d5366
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 77 deletions.
37 changes: 0 additions & 37 deletions html/ndt7-core.js

This file was deleted.

39 changes: 0 additions & 39 deletions html/ndt7-download.js

This file was deleted.

2 changes: 1 addition & 1 deletion html/ndt7.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!doctype html>
<html lang='en'>
<head>
<script type='text/javascript' src='ndt7-core.js'></script>
<script type='text/javascript' src='js/ndt7.js'></script>
<style>

.row {
Expand Down
64 changes: 64 additions & 0 deletions js/ndt7-download-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* jshint esversion: 6, asi: true, worker: true */
// WebWorker that runs the ndt7 download test

// When run by Node.js, we need to import the websocket libraries.
if (typeof WebSocket === 'undefined') {
global.WebSocket = require('isomorphic-ws');
}

self.onmessage = function (ev) {
'use strict'
// TODO put the choce between secure and insecure here
//let url = new URL(ev.data.href)
//url.protocol = (url.protocol === 'https:') ? 'wss:' : 'ws:'
//url.pathname = '/ndt/v7/download'
const url = ev.data['ws:///ndt/v7/download']
const sock = new WebSocket(url, 'net.measurementlab.ndt.v7')

sock.onclose = function () {
postMessage({
MsgType: "complete"
})
}

sock.onerror = function (ev) {
postMessage({
MsgType: 'error',
Error: ev,
})
}

sock.onopen = function () {
const start = new Date().getTime()
let previous = start
let total = 0

sock.onmessage = function (ev) {
total += (ev.data.hasOwnProperty('size')) ? ev.data.size : ev.data.length
// Perform a client-side measurement 4 times per second.
let now = new Date().getTime()
const every = 250 // ms
if (now - previous > every) {
postMessage({
MsgType: 'measurement',
ClientData: {
ElapsedTime: (now - start) * 1000, // us
NumBytes: total,
MeanClientMbps: total*8 / (now - start) / 1000 // Bytes * 8 bits/byte * 1/(duration in ms) * 1000ms/s * 1 Mb / 1000000 bits = Mb/s
},
Source: 'client',
})
previous = now
}

// Pass along every server-side measurement.
if (typeof ev.data === 'string') {
postMessage({
MsgType: 'measurement',
ServerMessage: ev.data,
Source: 'server',
})
}
}
}
};
24 changes: 24 additions & 0 deletions js/ndt7-nodejs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @fileoverview A command-line NDT7 client for node.js that uses the official
* NDT7 js libraries.
*/

'use strict';

const ndt7 = require('./ndt7');

ndt7.test({
downloadMeasurement: function() {},
serverDiscovery: function() {},
serverChosen: function(server) {
console.log("Testing to:", {
machine: server.machine,
locations: server.location,
});
},
downloadComplete: function(data) {
console.log("Download test is complete:\n\tInstantaneous server bandwidth: ", data.LastServerMeasurement.BBRInfo.BW * 8 / 1000000, "\n\tMean client bandwidth: ", data.LastClientMeasurement.MeanClientMbps)
}
}).then(code => {
process.exit(code);
})
File renamed without changes.
140 changes: 140 additions & 0 deletions js/ndt7.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/* jshint esversion: 6, asi: true */
// ndt7 is a simple ndt7 client API.

// Install workalikes for APIs that are defined in browsers but not in node.js
if ('undefined' === typeof fetch) {
global.fetch = require('node-fetch');
}
if ('undefined' === typeof Worker) {
global.Worker = require('workerjs');
}

const ndt7 = (function() {

// cb creates a default-empty callback function, allowing library users to only need to specify callback functions for the events they care about.
var cb = function(name, callbacks) {
if (typeof(callbacks) != "undefined" && name in callbacks) {
return callbacks[name];
}
return function(arg){ console.log(name, ": ", arg); };
};

var discoverServerURLs = async function(callbacks, config) {
if (config && ('server' in config)) {
return {
'ws:///ndt/v7/download': 'ws://' + config.server + '/ndt/v7/download',
'ws:///ndt/v7/upload': 'ws://' + config.server + '/ndt/v7/upload',
'wss:///ndt/v7/download': 'wss://' + config.server + '/ndt/v7/download',
'wss:///ndt/v7/upload': 'wss://' + config.server + '/ndt/v7/upload'
};
}
// If no server was specified then use a loadbalancer. If no loadbalancer is specified, use the locate service from Measurement Lab.
const lbURL = (config && (loadbalancer in config)) ? config.loadbalancer : new URL('https://locate-dot-mlab-staging.appspot.com/v2beta1/query/ndt/ndt7');
callbacks.serverDiscovery({loadbalancer: lbURL});
const response = await fetch(lbURL);
const js = await response.json();
if (! ('results' in js) ) {
callbacks.error("Could not understand response from " + lbURL + ": " + js);
return {};
}
const choice = js.results[Math.floor(Math.random() * js.results.length)];
callbacks.serverChosen(choice);
return choice.urls;
};

return {
test: async function(userCallbacks, config) {
const callbacks = {
error: cb('error', userCallbacks),
serverDiscovery: cb('serverDiscovery', userCallbacks),
serverChosen: cb('serverChosen', userCallbacks),
downloadStart: cb('downloadStart', userCallbacks),
downloadMeasurement: cb('downloadMeasurement', userCallbacks),
downloadComplete: cb('downloadComplete', userCallbacks),
//startingUpload: function(e){},
//finishingUpload: function(e){},
};

// Starts the asynchronous process of server discovery, allowing other stuff to proceed in the background.
const urlPromise = discoverServerURLs(callbacks, config);
var clientMeasurement, serverMeasurement;


const downloadWorker = new Worker('./ndt7-download-worker.js');
const downloadWorkerPromise = new Promise(resolve => { downloadWorker.resolve = resolve; });
setTimeout(_ => {downloadWorker.terminate(); downloadWorker.resolve()}, 20000) // 20 seconds
downloadWorker.onmessage = function (ev) {
if (ev.data == null || ev.data.MsgType == 'error') {
downloadWorker.terminate();
downloadWorker.resolve();
const errMsg = (ev.data == null) ? 'There was a download error' : ev.data.Error;
callbacks.error(errMsg);
} else if (ev.data.MsgType == 'measurement') {
if (ev.data.Source == 'server') {
serverMeasurement = JSON.parse(ev.data.ServerMessage);
callbacks.downloadMeasurement({
Source: ev.data.Source,
Data: serverMeasurement,
});
} else {
clientMeasurement = ev.data.ClientData;
callbacks.downloadMeasurement({
Source: ev.data.Source,
Data: ev.data.ClientData,
});
}
} else if (ev.data.MsgType == 'complete') {
downloadWorker.terminate()
downloadWorker.resolve()
callbacks.downloadComplete({
LastClientMeasurement: clientMeasurement,
LastServerMeasurement: serverMeasurement,
});
};
};

const urls = await urlPromise;
downloadWorker.postMessage(urls);

// TODO: await the termination of the downloadWorker.
await downloadWorkerPromise;
// Liveness guarantee - once the promise is resolved, .terminate() has
// been called.
return 0;
}
/*,
// run runs the specified test with the specified base URL and calls
// callback to notify the caller of ndt7 events.
run: function(baseURL, testName, callback) {
callback('starting', {Origin: 'client', Test: testName})
let done = false
let worker = new Worker('ndt7-' + testName + '.js')
function finish() {
if (!done) {
done = true
if (callback !== undefined) {
callback('complete', {Origin: 'client', Test: testName})
}
}
}
worker.onmessage = function (ev) {
if (ev.data === null) {
finish()
return
}
callback('measurement', ev.data)
}
// Kill the worker after the timeout. This force the browser to
// close the WebSockets and prevent too-long tests.
setTimeout(function () {
worker.terminate()
finish()
}, 10000)
worker.postMessage({
href: baseURL,
})
}*/
}
}());

module.exports = ndt7;
3 changes: 3 additions & 0 deletions ndt-server.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ func main() {
ndt5WsMux := http.NewServeMux()
ndt5WsMux.HandleFunc("/", defaultHandler)
ndt5WsMux.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("html"))))
ndt5WsMux.Handle("/static/js", http.StripPrefix("/static/js", http.FileServer(http.Dir("js"))))
ndt5WsMux.Handle("/ndt_protocol", ndt5handler.NewWS(*dataDir+"/ndt5"))
ndt5WsServer := &http.Server{
Addr: *ndt5WsAddr,
Expand All @@ -166,6 +167,7 @@ func main() {
ndt7Mux := http.NewServeMux()
ndt7Mux.HandleFunc("/", defaultHandler)
ndt7Mux.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("html"))))
ndt7Mux.Handle("/static/js", http.StripPrefix("/static/js", http.FileServer(http.Dir("js"))))
ndt7Handler := &handler.Handler{
DataDir: *dataDir,
SecurePort: *ndt7Addr,
Expand All @@ -187,6 +189,7 @@ func main() {
ndt5WssMux := http.NewServeMux()
ndt5WssMux.HandleFunc("/", defaultHandler)
ndt5WssMux.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("html"))))
ndt5WssMux.Handle("/static/js", http.StripPrefix("/static/js", http.FileServer(http.Dir("js"))))
ndt5WssMux.Handle("/ndt_protocol", ndt5handler.NewWSS(*dataDir+"/ndt5", *certFile, *keyFile))
ndt5WssServer := &http.Server{
Addr: *ndt5WssAddr,
Expand Down

0 comments on commit 51d5366

Please sign in to comment.