html5 / js websocket: setting event handlers *after* connecting? - javascript

A lot of HTML5 websocket example code does something like this:
websocket = new WebSocket('ws://example.com');
websocket.onopen = MyOpenHandler;
websocket.onerror = MyErrorHandler;
Of course with the intention of MyOpenHandler being called upon connecting, or MyErrorHandler if the connection fails.
Exactly when does the actual connection takes place and is the above event handling approach guaranteed to work, even if the connection happens or fails immediately?
What I mean is, wouldn't an approach like this make more sense:
websocket = new WebSocket;
websocket.onopen = MyOpenEventHandler;
websocket.onerror = MyErrorHandler;
websocket.connect('ws://example.com');
I.e. connect after the event handlers have been set, to be 100% sure they are being called when appropriate.
Or am I just being paranoia here, and is the reference implementation (the topmost example) actually correct?
Addendum
I did some additional testing with the following code:
var websocket = new WebSocket("ws://localhost:8000"); // this connection will fail
alert("Created the websocket, now let's set the event handler");
websocket.onerror = function(e) { alert("Could not connect") }
This fails, the "Could not connect" alert does NOT appear. However if I remove that former alert (the one on line 2), it does.
This kinda worries me. Apparently it's possible the connection already failed before I got the chance to set the onerror event handler, thus the handler never gets called. How can I be 100% certain that the connection is guaranteed to take place after I set the appropriate handlers?

https://www.w3.org/TR/2011/WD-websockets-20110419/#the-websocket-interface
I believe that number 6 below answers your question.
When the WebSocket() constructor is invoked, the UA must run these steps:
Parse a WebSocket URL's components from the url argument, to obtain host, port, resource name, and secure. If this fails, throw a SYNTAX_ERR exception and abort these steps.
If port is a port to which the user agent is configured to block access, then throw a SECURITY_ERR exception. (User agents typically block access to well-known ports like SMTP.)
Access to ports 80 and 443 should not be blocked, including the unlikely cases when secure is false but port is 443 or secure is true but port is 80.
If protocols is absent, let protocols be an empty array.
Otherwise, if protocols is present and a string, let protocols instead be an array consisting of just that string.
If any of the values in protocols occur more than once or contain characters with Unicode code points less than U+0021 or greater than U+007E (i.e. the space character or any characters that are not printable ASCII characters), then throw a SYNTAX_ERR exception and abort these steps.
Let origin be the ASCII serialization of the origin of the script that invoked the WebSocket() constructor, converted to ASCII lowercase.
Return a new WebSocket object, and continue these steps in the background (without blocking scripts).
Establish a WebSocket connection to a host host, on port port (if one was specified), from origin, with the flag secure, with resource name as the resource name, with protocols as the (possibly empty) list of protocols, and with the defer cookies flag set.
UPDATE
https://html.spec.whatwg.org/multipage/web-sockets.html#feedback-from-the-protocol
When the WebSocket connection is established, the user agent must queue a task to run these steps:
Change the readyState attribute's value to OPEN (1).
Change the extensions attribute's value to the extensions in use, if is not the null value. [WSP]
Change the protocol attribute's value to the subprotocol in use, if is not the null value. [WSP]
Fire an event named open at the WebSocket object.
NOTE
Since the algorithm above is queued as a task, there is no race condition between the WebSocket connection being established and the script setting up an event listener for the open event.

Related

Digest Authentication with fetch-API broken with redirects

I am looking at the digest-fetch.js over the fetch API, and I am finding some problems. The issue is that in my scenario a POST call is made, responded first with 401 with the challenge and then retried with the authentication header, then succeeds. So far, so good.
But our server responds deliberately with 303, giving a different URI (on same server) to pick up the final result.
The fetch API does not allow me to manually handle the redirect (which I totally don't get why this would be considered "insecure" and hence hidden) but when the core fetch API will retry that, it gets a 401 again from our server. At that point it will fail.
I think this is because the fetch API re-uses the headers, namely the Authorization header, as a constant string. That same header is used for the follow-up GET request on the 303 redirection. The server barks that the nc has not been increased.
As [https://en.wikipedia.org/wiki/Digest_access_authentication] says:
[...] the client may make another request, reusing the server nonce value (the server only issues a new nonce for each "401" response) but providing a new client nonce (cnonce). For subsequent requests, the hexadecimal request counter (nc) must be greater than the last value it used [...]
I consider this a bug in digest-fetch but worse in the Fetch API (of Chrome and Firefox anyway).
I have tried not to provide a String header, but an object with a toString method, hoping that this would be called every time a request is made. So, instead of:
options.headers.Authorization = digest;
I did:
options.headers.Authorization = {
toString: function() {
alert(new Error("Gotcha! " + digest));
return digest;
}
}
Yet sadly, that gets called only once. I think out of the core fetch API function, where I am afraid a string is stored, and the toString() method will not again be called.
Any workaround this problem?

Not able to visit a url in cypress test, but working fine otherwise

I am trying to visit a URL through cypress test but is giving me the below error. If I copy and past the same URL in a different browser it works fine.
Also, if I copy-paste the URL into a new tab in the same browser in which cypress is running the automated test it again shows 464. So I am concluding the is an issue with the automated browser.
Looks like the following relates to your problem.
Load balancer troubleshooting
HTTP 464
The load balancer received an incoming request protocol that is incompatible with the version config of the target group protocol.
Possible causes:
The request protocol is an HTTP/1.1, while the target group protocol version is a gRPC or HTTP/2.
The request protocol is a gRPC, while the target group protocol version is an HTTP/1.1.
The request protocol is an HTTP/2 and the request is not POST, while target group protocol version is a gRPC.
You should fix the issue rather than try to avoid it with flags to ignore it.
You can set the failOnStatusCode as false so that cypress doesn't fail on getting a response code of 464.
cy.visit('https://example.com', {failOnStatusCode: false})
You can also try with retryOnStatusCodeFailure. Definition - Whether Cypress should automatically retry status code errors under the hood. Cypress will retry a request up to 4 times if this is set to true.
cy.visit('https://example.com', {retryOnStatusCodeFailure: true})
Or with retryOnNetworkFailure. Definition - Whether Cypress should automatically retry transient network errors under the hood. Cypress will retry a request up to 4 times if this is set to true.
cy.visit('https://example.com', {retryOnNetworkFailure: true})
In case you want to pass two options together you can do like this:
cy.visit('https://example.com', {
failOnStatusCode: false,
retryOnStatusCodeFailure: true,
})

How to set Security Mode to Transport

Trying to make a basic SOAP call to test a vendor's API and see their data response (Documentation doesn't have it.)
When I try, I am getting a response of
The message with Action '' cannot be processed at the receiver, due to
a ContractFilter mismatch at the EndpointDispatcher. This may be
because of either a contract mismatch (mismatched Actions between
sender and receiver) or a binding/security mismatch between the sender
and the receiver. Check that sender and receiver have the same
contract and the same binding (including security requirements, e.g.
Message, Transport, None).
Which the API Provider said is due to my Security Mode being set to Transport - I am not sure what that means in the terms of using A) POSTMAN B) A generic Node script to query using Request/Popsicle etc.
If you need to have Transport security, usually all that means is switching to tls via https versus standard http to make your requests (though there are other, less common forms of Transport security). For mode libraries and utilities, this will happen automatically if you just specify the url with https.

Receiving responses to responses from messages broadcast to 255.255.255.255 on Node.js

I am sending broadcast to zero network [255.255.255.255] (e.g. DHCP or any other specific protocol, which cannot use any specific broadcast address and limited to local only) from the specific interface and expecting to get response.
So as part as result of Bind() for dgram callback, i am trying to add my local ip to this broadcast group
socket.setMulticastTTL(128);
socket.setBroadcast(true);
socket.addMembership(BROAD_ADDRESS,ip);
socket.setMulticastLoopback(true);
and getting error.
throw errnoException(err, 'addMembership');
^
Error: addMembership EINVAL
I would assume, that since this is local address it is already a part of zero network group (like in Windows). But the problem is that without using addMembership call, i can see response traffic coming to the interface, but NodeJS client is not able to receive it.
Everything works fine (i am able to send messages and get responses, when using any other broadcast address), but not this specific one.
This also not works with network specific addresses (e.g. for 10.1.10.0/24 network with broadcast to 10.1.10.255)
So, how to receive responses to broadcast messages to zero network on linux?

Javascript doesn't catch error in WebSocket instantiation

My socket currently throws net::ERR_CONNECTION_REFUSED because the server isn't running, which I want it to do at the moment.
The problem is that the following piece of code doesn't catch the error. In the console I see an exception on line 2 (with net::ERR_CONNECTION_REFUSED) which I believe shouldn't happen as it's within a try statement.
1 try {
2 ws = new WebSocket('ws://'+ host + ':' + port + '/');
3 }
4 catch (err) {
5 console.log('This never prints');
6 }
7 ws.onerror = function (error) {
8 console.log(error);
9 };
So my question is why is it not being caught?
What I ultimately want is the error message to be displayed elsewhere, but I can't catch it, and line 8 prints an "event" object which doesn't mention net::ERR_CONNECTION_REFUSED, so I'm not sure how to get the error message out.
The WebSocket's connection-time error causes a dispatched event, not a thrown value. This is because throw operations must be synchronous. In order to handle all connection-time errors as thrown errors, the WebSocket constructor would need to completely suspend all script execution and UI interaction until the entire WebSocket handshake had completed. Instead, the connection process runs asynchronously, thereby allowing the browser thread to continue working while the WebSocket connection initializes in the background. Because of the connection's asynchronous nature, the WebSocket must report errors via error events, since the synchronous new WebSocket operation has already finished by the time the asynchronous connection task encounters an error.
The ERR_CONNECTION_REFUSED message you see is purely for the benefit of developers; it is not accessible to the script in any way. It does not have any representation within the JavaScript environment. It's just a red-colored message that appears in your console to inform you, the human looking at the browser, about an error.
The error handler event is the correct place to respond to failure, but the lack of script-readable connection-time error information is by design. From the WHATWG spec for the WebSocket API:
User agents must not convey any failure information to scripts in a way that would allow a script to distinguish the following situations:
A server whose host name could not be resolved.
A server to which packets could not successfully be routed.
A server that refused the connection on the specified port.
A server that failed to correctly perform a TLS handshake (e.g., the server certificate can't be verified).
A server that did not complete the opening handshake (e.g. because it was not a WebSocket server).
A WebSocket server that sent a correct opening handshake, but that specified options that caused the client to drop the connection (e.g. the server specified a subprotocol that the client did not offer).
A WebSocket server that abruptly closed the connection after successfully completing the opening handshake.
[...] Allowing a script to distinguish these cases would allow a script to probe the user's local network in preparation for an attack.
The browser is deliberately omitting any useful information as required by the spec. The spec authors are concerned that access to this information could allow a malicious Web page to gain information about your network, so they require browsers report all connection-time errors in an indistinguishable way.
I tried creating fiddle with it
and I am able to see line printed.
host='localhost';
port=100;
try {
ws = new WebSocket('ws://'+ host + ':' + port + '/');
}
catch (err) {
console.log('This never prints');
}
ws.onerror = function (error) {
console.log(error);
};
https://jsfiddle.net/xzumgag0/
Chrome based browsers: In my case I just wanted to get rid of the error message from the console. In which case you can enter this regex into the console filter:
-/Websocket\sconnection.*failed/

Categories

Resources