How should a closed WebSocket behave when send is called? - javascript

In the MDN documentation for WebSocket.send it says:
Exceptions thrown
INVALID_STATE_ERR
The connection is not currently OPEN.
An exception is indeed thrown when in CONNECTING state, but if the socket is closed (state is CLOSED) then I don't get any exception. This happens for all the browsers I've tried.
I've tried reading the spec but couldn't seem to find anything relevant.
Here's some reproducing code:
Clicking Create also sends before the socket is open (exception thrown)
After socket is open click Send (no exception is thrown)
Click Close and then Send (no exception is thrown, this surprises me)
var s = null;
function log(msg) {
var d = document.createElement('div');
d.textContent = (new Date().toLocaleTimeString()) +": " + msg;
document.getElementById('out').appendChild(d);
}
function create() {
log("Creating");
s = new WebSocket("wss://echo.websocket.org");
s.onopen = function() {
log("Socket opened");
}
s.onmessage = function(e) {
log("Got: " + e.data);
}
send("before open");
}
function send(msg) {
try {
log("Sending: " + msg);
s.send(msg);
log("Send didn't throw");
}
catch(e) {
log("Exception: " + e.message);
}
}
function close() {
log("Closing");
s.close();
}
document.getElementById('create').addEventListener('click', create, true);
document.getElementById('send').addEventListener('click', send.bind(null, 'manually'), true);
document.getElementById('close').addEventListener('click', close, true);
<button id="create">Create</button>
<button id="send">Send</button>
<button id="close">Close</button>
<div id="out"/>

Related

Can you use Microsoft Graph API in JavaScript to get list items?

I'm using the samples for the MSAL and converting them to use MS Graph to read SharePoint but when it comes to reading list items it seems I am getting permissions issues.
To make sure I have my syntax correct, I use the Graph Explorer with my AD account and I am able to read list items and confirm the URI is correct. I am also able to read and get an array of lists. But as soon as I try to get the list items for a list nothing is returned.
The base code is here https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-javascript-spa
Here's the code I converted from the sample. If you update the variables and register in Azure you should be able to run against your SPO site.
<!DOCTYPE html>
<html>
<head>
<title>Quickstart for MSAL JS</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.4/bluebird.min.js"></script>
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/msal.js"></script>
</head>
<body>
<h2>Welcome to MSAL.js Quickstart</h2><br />
<h4 id="WelcomeMessage"></h4>
<button id="SignIn" onclick="signIn()">Sign In</button><br /><br />
<button id="btnAllLists" onclick="GetWithEndPoint()">Get All Lists</button><br /><br />
<button id="btnListItems" onclick="GetWithEndPoint()">Get List Items</button><br /><br />
<button id="btnListItemsAllFields" onclick="GetWithEndPoint()">Get List Items All Fields</button><br /><br />
<pre id="json"></pre>
<script>
var config = {
portalname: "yourportalname",
sitename: "yoursitename",
listid: "guidofalist"
}
var msalConfig = {
auth: {
clientId: "azureclientguid",
authority: "https://login.microsoftonline.com/yourportal.onmicrosoft.com"
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
var graphConfig = {
graphMeEndpoint: "https://graph.microsoft.com/v1.0/me",
spShowAllListsEp: "https://graph.microsoft.com/v1.0/sites/" + config.portalname + ".sharepoint.com:/sites/" + config.sitename + ":/lists",
spShowListItemsEp: "https://graph.microsoft.com/v1.0/sites/" + config.portalname + ".sharepoint.com:/sites/" + config.sitename + ":/lists/" + config.listid + "/items",
spShowListItemsAllFieldsEp: "https://graph.microsoft.com/v1.0/sites/" + config.portalname + ".sharepoint.com:/sites/" + config.sitename + ":/lists/" + config.listid + "/items?expand=fields",
};
// this can be used for login or token request, however in more complex situations this can have diverging options
var requestObj = {
scopes: ["user.read"]
};
var myMSALObj = new Msal.UserAgentApplication(msalConfig);
// Register Callbacks for redirect flow
myMSALObj.handleRedirectCallback(authRedirectCallBack);
function callMSGraph(theUrl, accessToken, callback) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200)
callback(JSON.parse(this.responseText));
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
xmlHttp.setRequestHeader('Authorization', 'Bearer ' + accessToken);
xmlHttp.send();
}
function signIn() {
myMSALObj.loginPopup(requestObj).then(function (loginResponse) {
//Login Success
showWelcomeMessage();
acquireTokenPopupAndCallMSGraph();
}).catch(function (error) {
console.log(error);
});
}
function acquireTokenPopupAndCallMSGraph() {
//Always start with acquireTokenSilent to obtain a token in the signed in user from cache
myMSALObj.acquireTokenSilent(requestObj).then(function (tokenResponse) {
callMSGraph(graphConfig.graphMeEndpoint, tokenResponse.accessToken, graphAPICallback);
}).catch(function (error) {
console.log(error);
// Upon acquireTokenSilent failure (due to consent or interaction or login required ONLY)
// Call acquireTokenPopup(popup window)
if (requiresInteraction(error.errorCode)) {
myMSALObj.acquireTokenPopup(requestObj).then(function (tokenResponse) {
callMSGraph(graphConfig.graphMeEndpoint, tokenResponse.accessToken, graphAPICallback);
}).catch(function (error) {
console.log(error);
});
}
});
}
function graphAPICallback(data) {
document.getElementById("json").innerHTML = JSON.stringify(data, null, 2);
}
function showWelcomeMessage() {
var divWelcome = document.getElementById('WelcomeMessage');
divWelcome.innerHTML = 'Welcome ' + myMSALObj.getAccount().userName + "to Microsoft Graph API";
var loginbutton = document.getElementById('SignIn');
loginbutton.innerHTML = 'Sign Out';
loginbutton.setAttribute('onclick', 'signOut();');
var btn1 = document.getElementById('btnAllLists');
btn1.setAttribute('onclick', "GetWithEndPoint('" + graphConfig.spShowAllListsEp + "');");
var btn2 = document.getElementById('btnListItems');
btn2.setAttribute('onclick', "GetWithEndPoint('" + graphConfig.spShowListItemsEp + "');");
var btn3 = document.getElementById('btnListItemsAllFields');
btn3.setAttribute('onclick', "GetWithEndPoint('" + graphConfig.spShowListItemsAllFieldsEp + "');");
}
//This function can be removed if you do not need to support IE
function acquireTokenRedirectAndCallMSGraph() {
//Always start with acquireTokenSilent to obtain a token in the signed in user from cache
myMSALObj.acquireTokenSilent(requestObj).then(function (tokenResponse) {
callMSGraph(graphConfig.graphMeEndpoint, tokenResponse.accessToken, graphAPICallback);
}).catch(function (error) {
console.log(error);
// Upon acquireTokenSilent failure (due to consent or interaction or login required ONLY)
// Call acquireTokenRedirect
if (requiresInteraction(error.errorCode)) {
myMSALObj.acquireTokenRedirect(requestObj);
}
});
}
function authRedirectCallBack(error, response) {
if (error) {
console.log(error);
}
else {
if (response.tokenType === "access_token") {
callMSGraph(graphConfig.graphEndpoint, response.accessToken, graphAPICallback);
} else {
console.log("token type is:" + response.tokenType);
}
}
}
function requiresInteraction(errorCode) {
if (!errorCode || !errorCode.length) {
return false;
}
return errorCode === "consent_required" ||
errorCode === "interaction_required" ||
errorCode === "login_required";
}
function signOut() {
myMSALObj.logout();
}
// Browser check variables
var ua = window.navigator.userAgent;
var msie = ua.indexOf('MSIE ');
var msie11 = ua.indexOf('Trident/');
var msedge = ua.indexOf('Edge/');
var isIE = msie > 0 || msie11 > 0;
var isEdge = msedge > 0;
//If you support IE, our recommendation is that you sign-in using Redirect APIs
//If you as a developer are testing using Edge InPrivate mode, please add "isEdge" to the if check
// can change this to default an experience outside browser use
var loginType = isIE ? "REDIRECT" : "POPUP";
if (loginType === 'POPUP') {
if (myMSALObj.getAccount()) {// avoid duplicate code execution on page load in case of iframe and popup window.
showWelcomeMessage();
acquireTokenPopupAndCallMSGraph();
}
}
else if (loginType === 'REDIRECT') {
document.getElementById("SignIn").onclick = function () {
myMSALObj.loginRedirect(requestObj);
};
if (myMSALObj.getAccount() && !myMSALObj.isCallback(window.location.hash)) {// avoid duplicate code execution on page load in case of iframe and popup window.
showWelcomeMessage();
acquireTokenRedirectAndCallMSGraph();
}
} else {
console.error('Please set a valid login type');
}
</script>
<script>
function GetWithEndPoint(endpointString) {
myMSALObj.acquireTokenSilent(requestObj).then(function (tokenResponse) {
callMSGraph(endpointString, tokenResponse.accessToken, graphAPICallback);
}).catch(function (error) {
console.log(error);
if (requiresInteraction(error.errorCode)) {
myMSALObj.acquireTokenPopup(requestObj).then(function (tokenResponse) {
callMSGraph(endpointString, tokenResponse.accessToken, graphAPICallback);
}).catch(function (error) {
console.log(error);
});
}
});
}
</script>
</body>
</html>
Clicking either button that returns list items throws this message which I understand to mean is permissions.
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(microsoft.graph.list)('myid')/items",
"value": []
}
My expectation is that I would get the same results from the Graph Explorer. But this indicates I don't have permission. I've tried a few different lists and the results are always the same. I can get a list of all the lists. But trying to get the items from a list fails.
Can we not use the Graph API with JS to get list items?
Here are the Azure delegated permissions from Azure which I think should be all I need to get list items.
But this indicates I don't have permission
That's right, empty results usually indicates one of the following permissions (delegated permissions in your case) are missing for Get Items endpoint:
Sites.Read.All - read items in all site collections
Sites.ReadWrite.All - edit or delete items in all site collections

How to save incoming textfile from client from websocket connection

I'm trying to implement a websocket server in node.js without using any framework.
Sending messages from client to server is working fine. But now I tried to send a text file from client to server. I can see the content on the server side by using console.log in the terminal.
But:
how can i get the file information ? (name, created/edited date, etc. ?)
how can i save the file ?
Client code:
(function () {
'use strict';
var output, ws;
//Display logging information in the document
function log(s) {
var p = document.createElement("p");
p.style.wordWrap = "break-word";
p.textContent = s;
output.appendChild(p);
//Also log information on the javascript console
window.console.log(s);
}
//Send a message on the Websocket
function sendMessage(msg) {
console.log(ws.binaryType);
ws.send(msg);
console.log("Message sent");
}
//Initialize WebSocket connection and event handlers
function setup() {
output = document.getElementById("output");
ws = new window.WebSocket("ws://localhost:9999/");
//Listen for the connection open event then call the sendMessage function
ws.onopen = function () {
console.log("Connected");
document.getElementById('fl').onchange = function() {
sendMessage(document.querySelector('input[type="file"]').files[0]);
};
sendMessage("Hello Galileo!");
}
//Listen for the close connection event
ws.onclose = function (e) {
if(this.readyState == 2){
console.log('Connection is closing (Closing Handshake).')
}
else if(this.readyState == 3){
console.log('Connection closed. Has been closed or could not be opened.')
}
else{
console.log('Unhandled ReadyState: ',this.readyState);
}
console.log("Disconnected: " +
' reason:' + e.reason +
' was clean:' + e.wasClean +
' code:' + e.code);
}
//Listen for connection errors
ws.onerror = function (e) {
console.log("Error: " + e);
}
//Listen for new messages arriving at the client
ws.onmessage = function (e) {
console.log("Message received: " + e.data);
//Close the socket once one message has arrived
ws.close();
}
}
//Start running the example
setup();
})();
HTML Code
<!DOCTYPE html>
<html>
<head>
<title>Websocket Echo Client</title>
<meta charset="utf-8"/>
</head>
<body>
<h2>Websocket Echo Client</h2>
<div id="output"></div>
<input type="file" id="fl"/>
<script src="websocket.js"></script>
</body>
</html>
Server code
switch (opcode) {
case opcodes.TEXT:
this.payload = payload.toString("utf8");
winston.log('info','Text:\r\n', this.payload);
break;
case opcodes.BINARY:
console.log('info','File:\r\n', payload.toString("utf8"));
As far as I know the payload you're receiving on the server side does not contain the meta data about the file. I believe the File object is treated as a normal Blob with some extra meta data and the ws.send is only handling it like a Blob (it has no special handling for File).
The meta data can be accessed using
document.querySelector('input[type="file"]').files[0].name
document.querySelector('input[type="file"]').files[0].size
document.querySelector('input[type="file"]').files[0].type
And then send separately.

Setting up websockets server

this is my first time using websockets and I am having trouble getting my server to work. Below is the simple em-websocket server I copied from this github page. It's basically unchanged right now so it looks like so:
require 'em-websocket'
EM.run {
EM::WebSocket.run(:host => "0.0.0.0", :port => 8080) do |ws|
ws.onopen { |handshake|
puts "WebSocket connection open"
# Access properties on the EM::WebSocket::Handshake object, e.g.
# path, query_string, origin, headers
# Publish message to the client
ws.send "Hello Client, you connected to #{handshake.path}"
}
ws.onclose { puts "Connection closed" }
ws.onmessage { |msg|
puts "Recieved message: #{msg}"
ws.send("Pong: #{msg}")
}
end
}
I then copied the html from this simple echo test, just changing the url from ws://echo.websocket.org to ws://localhost:3000/websocket.rb (the .rb file is located in the app directory of my Rails project).
When I run the test from that page, I get the following results:
CONNECTED
SENT: WebSocket rocks
RESPONSE: [["client_connected",{"id":null,"channel":null,"data":{"connection_id":70297110490160},"success":null,"result":null,"server_token":null}]]
DISCONNECTED
Since the data in this test is hard-coded, I had expected the SENT and RESPONSE messages to be the same. However, I get the confusing message that begins with client_connected but contains nothing but null data.
Can anyone tell me what is going on? Am I connecting to my "server" (the websockets.rb file) and if so do I simply need to make some adjustments in the code, such as create a reply method? Again, this is my first time attempting to use this technology so I apologize if I'm missing anything in this post.
For good measure, here is the code that is running in the echo test.
<script language="javascript" type="text/javascript">
var wsUri = "ws://localhost:3000/websocket.rb";
var output;
function init() {
output = document.getElementById("output");
testWebSocket();
}
function testWebSocket() {
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) {
onOpen(evt)
};
websocket.onclose = function(evt) {
onClose(evt)
};
websocket.onmessage = function(evt) {
onMessage(evt)
};
websocket.onerror = function(evt) {
onError(evt) };
}
function onOpen(evt) {
writeToScreen("CONNECTED");
doSend("WebSocket rocks");
}
function onClose(evt) {
writeToScreen("DISCONNECTED");
}
function onMessage(evt) {
writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
websocket.close();
}
function onError(evt) {
writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
}
function doSend(message) {
writeToScreen("SENT: " + message); websocket.send(message);
}
function writeToScreen(message) {
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message; output.appendChild(pre);
}
window.addEventListener("load", init, false);
</script>
<h2>WebSocket Test</h2>
<div id="output"></div>
</html>

why Websocket onmessage not firing

this my code from books "The Definitive Guide to HTML5 websocket".
....
<div id="output"></div>
<script>
function setup() {
output = document.getElementById("output");
ws = new WebSocket("ws://localhost:7777");
ws.onopen = function(e) {
log("Connected");
sendMessage("Hello Websocket!");
}
ws.onclose = function(e){
log("Disconnected: " + e.reason);
}
ws.onerror = function(e){
log("Error ");
}
ws.onmessage = function(e) {
log("Message received: " + e.data);
ws.close();
}
}
function sendMessage(msg){
ws.send(msg);
log("Message Sent");
}
function log(s){
var p = document.createElement("p");
p.style.wordWrap = "break-word";
p.textContent = s;
output.appendChild(p);
console.log(s);
}
setup();
</script>
but, when i'm running it in localhost.. the output just like this
Connected
Message Sent
and stop until that. i knew event onmessage is not firing, but i dont know why. what could possibly be the problem? thanks
onmessage will only fire when the server sends a message to the client, not when the client is sending a message to the server (which is what you're doing).
If your server is sending back a message and that isn't being picked up by your client, you're going to need to provide a bit more context (server implementation, etc).

XMLRequest ResponseText is blank

I've got a JavaScript function that I want to report an alert message to the users if it successfully updates the database, or if it has an error.
In the main X.JSP file I have:
function startRequest(pChange)
{
//alert("startRequest");
createXmlHttpRequest();
//alert("sending message");
//var u1=document.f1.user.value;
//alert("Running startRequest for: " + pChange.id);
//xmlHttp.open("GET","updateEntry.jsp&pID=pChange.id&pStatus=pChange.status&pAddress=pChange.address&pDate=pChange.date&pNotes=pChange.note&pAssigned=pChange.assigned" ,true)
xmlHttp.open("GET","updateEntry.jsp?pID=" + pChange.id + "&pAddress=" +pChange.address + "&pStatus=" + pChange.status +"&pNote=" + pChange.notes +"&pAssigned=" +pChange.assigned ,true)
//alert(xmlHttp.responseText);
xmlHttp.onreadystatechange=handleStateChange;
xmlHttp.send(null);
}
function handleStateChange()
{
//alert("handleStateChange");
var message = xmlHttp.responseText;
alert("Return Code:" + message);
if(xmlHttp.readyState==4)
{
if(xmlHttp.status==200)
{
//alert("test2");
//alert("recieved Message");
var message = xmlHttp.responseText;
alert(message);
}
}
else
{
alert("Error loading page"+ xmlHttp.status +
":"+xmlHttp.statusText);
}
}
I then run a method in updateEntry.jsp that does a number of things, but of interest is this section:
if(nId.equals("NMI")||nId.equals("MI")||nId.equals("NI")||nId.equals("SA")||nId.equals("S"))
{
org.hibernate.Query query2 = session2.createQuery("update Leads set Status = :nstatus where Id = :nid");
query2.setParameter("nid", nId);
query2.setParameter("nstatus", nstatus);
query2.executeUpdate();
out.println("Update successfully with: " + nstatus);
// Actual contact insertion will happen at this step
session2.flush();
session2.close();
}
else
{
out.println("Status must be: NMI, MI, NI, SA or S");
}
My understanding is that this should only create a single alert, if the function completes successfully. Instead it creates like 9 alerts all of which are blank. What am I doing wrong? I'm seeing both the "Return Code: " message and a blank " " message, (two different sections of code) but both output blank message variables.
If the readystate is not 4, it does not mean it is an error. Ajax has multiple states that inform the clientside about what is happening. Your code says that those connection states are all errors.
if(xmlHttp.readyState==4)
{
if(xmlHttp.status==200)
{
//alert("test2");
//alert("recieved Message");
var message = xmlHttp.responseText;
alert(message);
} <-- your else should most likely be up here
}
else <-- this is incorrect
{
alert("Error loading page"+ xmlHttp.status +
":"+xmlHttp.statusText);
}
Read the document at MDN - Ajax Getting Started

Categories

Resources