I'm basically following the accepted answer to this question (Is it possible to ping a server from Javascript?)
Update
It seems to work as expected when the domain is 15 characters long (actually, http:// + 15, but 16 or more causes it to bomb. More details at the bottom.
The issue I'm seeing is that if you're using something that seems like a valid domain, for example http://thisisdefinitelynotarealdomainname.com, it returns an error but the code mentioned considers errors okay (because most should be). Looking at the error event, I'm not sure I see where I could get the HTTP response code (i.e., if it's a 404, consider it invalid).
Here is a jsFiddle showing the problem -- they all display "responded". If you look in the console, the invalid domain returns a 404 error, and the two valid ones (if in chrome console, not sure about the others) show that they were interpreted as an image but transferred as text/html -- is there any way to read either the 404 error, or the mime type?
var pinger = function () {
var ping = function (ip, callback) {
if (!this.inUse) {
this.status = 'unchecked';
this.inUse = true;
this.callback = callback;
this.ip = ip;
var _that = this;
this.img = new Image();
this.img.onload = function () {
_that.inUse = false;
_that.callback('responded');
};
this.img.onerror = function (e) {
if (_that.inUse) {
_that.inUse = false;
_that.callback('responded', e);
}
console.log(e);
};
this.start = new Date().getTime();
this.img.src = "http://" + ip + "/?now=" + this.start; // add the current time to work around caching
this.time = setTimeout(function () {
if (_that.inUse) {
_that.inUse = false;
_that.callback('timeout');
}
}, 1500);
}
}
return {
ping: ping
};
}();
(function () {
var output = document.getElementById('output');
var servers = [
'localhost',
'google.com',
'okthisreallydoesntmakeanysense',
'okthisreallydoe',
'thisisashortone',
'thisisabitlonger'
];
servers.forEach(function (server) {
new pinger.ping(server, function (status, e) {
output.innerHTML += server + ': ' + status + '<br />';
});
});
})();
Update
What's even more weird is that it seems to be fine up until 15 characters. I've updated the jsFiddle. See below on ones that respond how I'd expect vs ones that don't. What might cause this?
'localhost',
'google.com',
'okthisreallydoesntmakeanysense', // doesn't work
'okthisreallydoe', // works (15 characters)
'thisisashortone', // works (15 characters)
'thisisabitlonger' // doesn't work (16 characters)
This might help.
function Pinger_ping(ip, callback) {
if(!this.inUse) {
this.inUse = true;
this.callback = callback
this.ip = ip;
var _that = this;
this.img = new Image();
this.img.onload = function() {_that.good();};
this.img.onerror = function() {_that.good();};
this.start = new Date().getTime();
this.img.src = "http://" + ip;
this.timer = setTimeout(function() { _that.bad();}, 1500);
}
}
Let me know if it works
Related
I have a websocket listening on some interfaces so the client could call more than one ip. I don't have a dns for these IPs. This failes even in the try block
window.onload = function() {
ws = "";
try {
ws_connection = "ws://" + lblInfoIP.value + ":9080/websockets";
ws = new WebSocket(ws_connection);
}
catch(err) {
ws_connection = "ws://127.0.01:9080/websockets";
ws = new WebSocket(ws_connection);
}
ws.onmessage = function(msg) { showInfo(msg.data); };
ws.onerror = function(evt){ alert ('Websocket failed with ' + evt.data) };
}
manually using the same ip as the called url works.
How would I correctly handle that ?
Is there somesthing like ws_connection = "ws://" + called_url + "/websockets"; ?
Instead of your try/catch construct, use a simple "or" (||) operation:
window.onload = function () {
let ws = new WebSocket(`ws://${lblInfoIP.value || "127.0.01:9080"}/websockets`)
ws.onmessage = function (msg) { showInfo(msg.data); };
ws.onerror = function (evt) { alert('Websocket failed with ' + evt.data) };
}
In development, set lblInfoIP.value to null or "undefined".
Or use "document.location.host:9080"
I have an app (questionnaire) that uses indexedDB.
We have one database and several stores in it.
Stores have data already stored in them.
At some point a dashboard html file is loaded. In this file I am calling couple of functions:
function init(){
adjustUsedScreenHeight();
db_init();
setInstitutionInstRow();
loadRecommendations();
loadResultsFromDB();
fillEvaluations();
document.addEventListener("deviceready", onDeviceReady, function(e) {console.log(e);});
}
The init() function is called on body onLoad.
setInstitutionInstRow() looks like these:
function setInstitutionInstRow(localId){
//localId = 10;
if (localId == undefined){
console.log("Localid underfined: ");
//open db, open objectstore;
var request = indexedDB.open("kcapp_db", "1.0");
request.onsuccess = function() {
var db = request.result;
var tx = db.transaction ("LOCALINSTITUTIONS", "readonly");
var store = tx.objectStore("LOCALINSTITUTIONS");
tx.oncomplete = function(){
db.close();
}
tx.onerror = function(){
console.log("Transaction error on setInstInstRow");
}
var cursor = store.openCursor();
cursor.onsuccess= function () {
var match = cursor.result;
console.log ("Retrieved item: " + match.value.instid);
// alert("Added new data");
if (match){
setInstituionInstRow(match.value.instid);
console.log("Got localid: " + math.value.instid);
}
else
console.log("localinsid: it is empty " );
};
cursor.onerror = function () {
console.log("Error: " + item.result.errorCode);
}
}
request.onerror = function () {
console.log("Error: " + request.result.errorCode );
}
request.oncomplete = function (){
console.log("The transaction is done: setInstitutionRow()");
}
request.onupgradeneeded = function (){
console.log("Upgrade needed ...");
}
request.onblocked = function(){
console.log("DB is Blocked ...");
}
} else {
instid = localId;
var now = new Date();
//console.log("["+now.getTime()+"]setInstituionInstRow - instid set to "+localId);
//open db, open objectstore;
var request = indexedDB.open("kcapp_db", "1.0");
request.onsuccess = function() {
var db = this.result;
var tx = db.transaction ("INSTITUTIONS", "readonly");
var store = tx.objectStore("INSTITUTIONS");
var item = store.get(localId);
console.log(item);
item.onsuccess= function () {
console.log ("Retrieved item: ");
if (item.length > 0)
var lInstitution = item.result.value;
kitaDisplayValue = lInstitution.krippe;
};
item.onerror = function () {
console.log("Error: " + item.result.errorCode);
}
}
request.onerror = function () {
console.log("Error: " + request.result.errorCode );
}
}
Now the problem is,
var request = indexedDB.open("kcapp_db", "1.0");
the above request is never getting into any onsuccess, oncomplete, onerror states. I debugged with Chrome tools, it never getting into any above states.
Accordingly I am not getting any data from transactions.
And there are no errors in Chrome console.
And here is the request value from Chrome dev:
From above image the readyState: done , which means it should fire an event (success, error, blocked etc). But it is not going into any of them.
I am looking into it, and still can not figure out why it is not working.
Have to mention that the other functions from init() is behaving the same way.
Looking forward to get some help.
You may be using an invalid version parameter to the open function. Try indexedDB.open('kcapp_db', 1); instead.
Like Josh said, your version parameter should be an integer, not a string.
Your request object can get 4 events in response to the open request: success, error, upgradeneeded, or blocked. Add event listeners for all of those (e.g. request.onblocked = ...) and see which one is getting fired.
I had that problem but only with the "onupgradeneeded" event. I fixed it changing the name of the "open" function. At the begining I had a very long name; I changed it for a short one and start working. I don't know if this is the real problem but it was solved at that moment.
My code:
if (this.isSupported) {
this.openRequest = indexedDB.open("OrdenesMant", 1);
/**
* Creación de la base de datos con tablas y claves primarias
*/
this.openRequest.onupgradeneeded = function(oEvent) {
...
Hope it works for you as well.
I'm running nodejs, not as a webserver, but from the command line against a pretty heavily modified version of the example.js which comes with the phantom-cluster package. Server is Ubuntu 13.10 in an AWS instance.
My goal is to "ping" more than 64000 urls to test for 404 or 500 http errors. If there is an error, then log that url with the error for later processing.
Here is my code:
(function() {
var WEBSITES, cluster, enqueueRequests, main, phantomCluster;
var fs = require('fs');
phantomCluster = require("./index");
cluster = require("cluster");
WEBS = [];
function loadUrls(callback)
{
console.log("starting loaded");
var fs = require('fs');
var urls = [];
fs.readFile("/home/ubuntu/phantom-cluster/theurls.txt", 'utf8', function (err, data)
{
if (err) throw err;
var myArray = data.split("\n");
for(i=0;i<myArray.length;i++)
{
urls.push(myArray[i]);
}
callback(null,urls);
})
}
enqueueRequests = function(engine)
{
fulfilled = 0;
loadUrls(function(err,WEBS)
{
console.log(">>" + WEBS.length + " urls to process");
var enqueuer, i, key, _i, _results;
enqueuer = function(request)
{
var item;
item = engine.enqueue(request);
item.on("timeout", function()
{
fs.appendFile("/home/ubuntu/error_log.log", "TIMEOUT: " + request + "\r\n", function (err) {});
});
return item.on("response", function()
{
fulfilled++;
console.log(fulfilled);
});
};
_results = [];
for (i = i = 0;i < 1; i++)
{
_results.push((function()
{
var _results1;
_results1 = [];
for(x=0;x<WEBS.length;x++)
{
_results1.push(enqueuer(WEBS[x]));
}
return _results1;
})());
}
return _results;
});
};
main = function()
{
var engine;
engine = phantomCluster.createQueued(
{
workers: 20,
workerIterations: 1,
phantomBasePort: 54321
});
if (cluster.isMaster)
{
enqueueRequests(engine);
}
engine.on("queueItemReady", function(url)
{
var _this = this;
var retVal;
urlArray = url.split("|");
var phantom = this.ph;
var curPage = phantom.createPage(function(page)
{
page.set('settings.loadImages', false);
page.set('settings.javascriptEnabled', false);
page.set('settings.resourceTimeout', 5000);
page.set('settings.userAgent','Mozilla/5.001 (windows; U; NT4.0; en-US; rv:1.0) Gecko/25250101');
page.set('onError', function(msg, trace)
{
var msgStack = ['ERROR: ' + msg];
if (trace && trace.length)
{
msgStack.push('TRACE:');
trace.forEach(function(t)
{
msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function +'")' : ''));
});
}
console.error(msgStack.join('\n'));
});
page.set('onResourceReceived', function(response)
{
if((response.status == "404") || (response.status == "500"))
{
myUrl = decodeURI(response.url);
if(myUrl == urlArray[0])
{
retVal = response.status + "|" + url;
fs.appendFile("/home/ubuntu/error_log.log", response.status + "|" + url + "\r\n", function (err) {});
return retVal;
}
}
});
page.open(urlArray[0], function(status)
{
_this.next(); // _this is a PhantomQueuedClusterClient object
return _this.queueItemResponse(status);
});
});
});
return engine.start();
};
main();
}).call(this);
The file which is referenced as index.js is here:
https://github.com/dailymuse/phantom-cluster/blob/master/index.js
and I have not modified it at all.
This works great, and sparks up 20 worker processes which go out and get the initial response code for the queued urls.
Here is the problem:
After processing anywhere from 960-990 urls, the whole thing just stops. no error code, no nothing.
I've tried everything I can think of from some sort of node timeout, to an issue with a given url to banging my head against my desk. The first two would return an error when I create a test for it. The third just makes my head hurt.
Anyone have any help or experience working with this?
EDIT I made an update to the code and added the on.response callback and then called the nextTick method to remove the item from the queue. Still have the same issue.
Have you taken a look at link-crawler? It uses phantom-cluster and prerender to do almost exactly what you're looking for.
If all you're looking to do is check HTTP status codes, you don't need a headless browser to do that. Node can do that on it's own using http.request() or something that utilizes promises like request-promise.
Unless you're needing to verify something in the rendering of the pages that you're crawling, there's no need to render the page in a browser, just make HTTP calls to the URLs and introspect their statuses.
After wasting my two days to find out what's going wrong with this script, finally I decide to ask it.
What I am trying to do
I am trying to read a text file from remote server. Then storing all text file updates to an SQLITE database at the time of my Firefox Extension/Addon get loaded.
What I tried
var updatereader = {
start: function () {
//alert('reading update');
var fURL = null;
var ioService = null;
var fURI = null;
var httpChannel = null;
fURL = "http://www.example.com/addon/mlist.txt";
ioService = Components.classes["#mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
fURI = ioService.newURI(fURL, null, null);
httpChannel = ioService.newChannelFromURI(fURI).QueryInterface(Components.interfaces.nsIHttpChannel);
httpChannel.asyncOpen(updatereader.StreamReader, null);
},
onUpdateCompleted: function () {
},
StreamReader:
{
fOutputStream: null,
fPointer: null,
tempFile: "mlist.txt",
onStartRequest: function (aRequest, aContext) {
//alert('onStart');
updatereader.StreamReader.fOutputStream = Components.classes["#mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
updatereader.StreamReader.fPointer = Components.classes["#mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("ProfD", Components.interfaces.nsIFile);
updatereader.StreamReader.fPointer.append(updatereader.StreamReader.tempFile);
updatereader.StreamReader.fOutputStream.init(updatereader.StreamReader.fPointer, 0x02 | 0x20 | 0x08, 0644, 0);
},
onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, aCount) {
//control flow is not entering here - may be here is somehting missing
var sStream = null;
var tempBuffer = null;
sStream = Components.classes["#mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
sStream.init(aInputStream);
tempBuffer = sStream.read(aCount);
updatereader.StreamReader.fOutputStream.write(tempBuffer, aCount);
},
onStopRequest: function (aRequest, aContext, aStatusCode) {
//alert('onStop');
var currentDate = new Date();
if (aStatusCode == 0) {
fileInputStream = Components.classes["#mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
updatereader.StreamReader.fOutputStream.close();
fileInputStream.init(updatereader.StreamReader.fPointer, 0x01, 0, 0);
lineInputStream = fileInputStream.QueryInterface(Components.interfaces.nsILineInputStream);
//pass data to somewhere
var dbH = new dbstore();
dbH.updateData(lineInputStream);
lineInputStream.close();
updatereader.StreamReader.fPointer.remove(false);
updatereader.onUpdateCompleted();
} else {
}
}
}
}
Problem:
Getting nothing in lineInputStream which passes the read data to somewhere else for storing it.
Area of problem:
Program control flow is not entring to this section
onDataAvailable:
Not getting any error.
First of all, there doesn't really seem to be any need to read the file to the disk first (unless it is really, really big).
I'd just use XMLHttpRequest to get the file, which when run from a privileged context (e.g. add-on code, but not a website) can access any and every valid URI.
XMLHttpRequest will simplify almost everything, e.g. no more onDataAvailable, (usually) no more manual text converting, etc.
Also, no need to ever hit the disk during the transfer.
Code would look something like this:
var req = new XMLHttpRequest();
req.open("GET", "http://www.example.com/addon/mlist.txt"); // file:/// would work too, BTW
req.overrideMimeType("text/plain");
req.addEventListener("load", function() {
// Do something with req.responseText
}, false);
req.addEventListener("error", function() {
// Handle error
}, false);
req.send();
If you want to use XMLHttpRequest in a non-window, e.g. js code module or js components, then you need to first initialize a constructor. This is not required for windows, including XUL windows and by that XUL overlays.
// Add XMLHttpRequest constructor, if not already present
if (!('XMLHttpRequest' in this)) {
this.XMLHttpRequest = Components.Constructor("#mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest");
}
SDK users should use the request module, or net/xhr if a lower-level API is required.
PS: If you're still interested in using raw channels, here is a minimal example I coded up in a Scratchpad (to run, open a Scratchpad for a privileged location, e.g. about:newtab).
You shouldn't alert from your own implementation: alert() will spin the event loop and causes reentrant code, which is not supported in this context.
var {
classes: Cc,
interfaces: Ci,
results: Cr,
utils: Cu
} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm")
Cu.import("resource://gre/modules/Services.jsm");
var ConverterStream = Components.Constructor(
"#mozilla.org/intl/converter-input-stream;1",
"nsIConverterInputStream",
"init");
var RC = Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;
function Listener() {
this.content = "";
}
Listener.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamListener]),
onStartRequest: function(req, ctx) {
console.log("start");
},
onDataAvailable: function(req, ctx, stream, offset, count) {
console.log("data", count);
try {
var cs = new ConverterStream(stream, null /* utf-8 */, 4096, RC);
try {
var str = {};
while (cs.readString(4096, str)) {
this.content += str.value;
}
}
finally {
cs.close();
}
}
catch (ex) {
console.error("data", ex.message, ex);
}
},
onStopRequest: function(req, ctx, status) {
console.log("stop", status,
this.content.substr(0, 20), this.content.length);
}
};
var uri = Services.io.newURI("http://example.org", null, null);
Services.io.newChannelFromURI(uri).asyncOpen(new Listener(), null);
I want to make a javascript class with methods which I can call within the class as well as outside of the class. I want to make a "public" method, if you will. I want getTextAreaElement and appendTextArea to be such methods.
I've shown a snippet of best code I could come up with so far. I've also tried defining the methods as prototypes as well as within the class (this.func = ...). But that only allowed me to call the method outside (new Socket().appendTextArea("osgjr89");) but NOT within the class itself! The code snippet below shows the exact opposite implementation where I can't call the method outside of the class but can call it within.
Error:
Uncaught TypeError: Object #Socket has no method 'appendTextArea'
socket.js:
function Socket() {
var socket;
var canvas = document.getElementById('c');
var context = canvas.getContext("2d");
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:9012/websocket");
socket.binaryType = 'arraybuffer';
socket.onopen = onopen;
socket.onmessage = onmessage;
socket.onerror = onerror;
socket.onclose = onclose;
} else {
alert("Your browser does not support Web Socket.");
}
function getTextAreaElement() {
return document.getElementById('responseText');
}
function appendTextArea(newData) {
var el = getTextAreaElement();
el.value = el.value + '\n' + newData + " :)";
}
function onopen(event) {
getTextAreaElement().value = "Web Socket opened!";
}
/*[...]*/
}
main.js (loads after socket.js)
$(document).ready(function() {
var s = new Socket();
s.appendTextArea("osgjr89"); // ERROR!
});
UPDATED socket.js:
function Socket() {
[...]
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:9012/websocket");
socket.binaryType = 'arraybuffer';
socket.onopen = this.onopen;
socket.onmessage = this.onmessage;
socket.onerror = this.onerror;
socket.onclose = this.onclose;
} else {
alert("Your browser does not support Web Socket.");
}
this.getTextAreaElement = function() {
return document.getElementById('responseText');
}
this.appendTextArea = function(newData) {
var el = this.getTextAreaElement();
el.value = el.value + '\n' + newData + " :)";
}
this.onopen = function(event) {
this.getTextAreaElement().value = "Web Socket opened!";
}
[...]
}
All public methods must be declared as properties, not variables/functions. So, you have to change stuff like this:
function getTextAreaElement() {
return document.getElementById('responseText');
}
into
this.getTextAreaElement = function() {
return document.getElementById('responseText');
}
If you do this.func = function() {}, you can call the function inside the Constructor (Socket in your case) using this.func() as well as outside using:
var s = new Socket();
s.func();