I have a web-socket that is started from javascript, that is connecting to a domain (e.g my-domain):
let socket = new WebSocket("ws://www.my-domain.com/ws");
If I go in the browser and type www.my-domain.com all works fine. However, for short domain name (i.e. without www) (http://my-domain.com) the opening of the websocket fails.
I have tested on Firefox:
Firefox can’t establish a connection to the server at ws://www.my-domain.com/ws.
EDIT: The server is done in GO:
http.HandleFunc("/ws", wsHandler)
http.HandleFunc("/", rootHandler)
panic(http.ListenAndServe(":80", nil))
func (this *server) wsHandler(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Origin") != "http://"+r.Host {
http.Error(w, "Origin not allowed", 403)
return
}
...
Related
Bottom line up front: The Paho MQTT client sucessfully connects to my Mosquitto broker, but immediately disconnects when I try to publish a message or when it receives a message from a topic it's subscribed to. I've tried changing Mosquitto's listening port and authentication settings, and using two different versions of Paho MQTT, and I still have the same problem.
Now let's get into detail.
Intro: I'm making a dashboard for some facial recognition devices that communicate through MQTT. I set up a Mosquitto broker and I've had no problems connecting to it and communicating with the devices using the Paho MQTT client for Python (I made a kind of server to sync the device's info to a database). Now I'm making the web interface, so I added a WebSockets listener to my mosquitto.conf and wrote a script using the Paho MQTT library for Javascript to connect to it, subscribe to topic sgdrf/out, send a simple JSON message to topic sgdrf/in to get the list of online devices, and process the response of the Python server once it arrives.
Problem and attempted solutions: I ran the Django server, loaded the web page and opened the JS console to find that the MQTT client successfully connected to the broker but immediately disconnected when it tried to publish the message to topic sgdrf/in. Here's each line of console output with their explanations:
The message produced by the onSuccess function, which indicates that the client successfully connected to the Mosquitto broker:
Conexión exitosa al broker MQTT.
In the onConnected function, I added console.log(uri) to see the URI used by the client to connect to the broker. I got:
ws://localhost:61613/
After printing uri to console, I made the client subscribe to sgdrf/out and then print 'subscribed' to console:
subscribed
Then I call get_online_devices(mqtt_client), a function which creates a simple JSON string and publishes it to the topic sgdrf/in. But first, it prints the strign to the console so that I can check it (just in case):
{"operator":"GetOnlineDevices","messageId":96792535859850080000,"info":{}}
Then, when the publish method is actually executed, is when I get this error (captured by the onConnectionLost function):
Pérdida de conexión con el broker MQTT: AMQJS0005E Internal error. Error Message: message is not defined, Stack trace: No Error Stack Available (código: 5)
I checked the Mosquitto log file and it only says when a new client was connected and then when it was disconnected because of a socket error (each time for every page reload). Tail of /var/log/mosquitto/mosquitto.log:
1614796149: New connection from 127.0.0.1 on port 61612.
1614796149: New client connected from 127.0.0.1 as mqttx_53195902 (p2, c1, k60, u'admin').
1614796182: Socket error on client sgdrf_dashboard_8499, disconnecting.
1614796325: New client connected from ::ffff:127.0.0.1 as sgdrf_dashboard_1597 (p2, c1, k60, u'admin').
1614796325: Socket error on client sgdrf_dashboard_1597, disconnecting.
1614796336: New client connected from ::ffff:127.0.0.1 as sgdrf_dashboard_6565 (p2, c1, k60, u'admin').
1614796336: Socket error on client sgdrf_dashboard_6565, disconnecting.
1614796931: New client connected from ::ffff:127.0.0.1 as sgdrf_dashboard_9773 (p2, c1, k60, u'admin').
1614796931: Socket error on client sgdrf_dashboard_9773, disconnecting.
1614797168: Saving in-memory database to /var/lib/mosquitto/mosquitto.db.
I tried changing the listening port in mosquitto.conf, and enabling and disabling authentication, but it changes nothing. And obviously I've had to restart Mosquito every time I changed the config file. I don't think the problem is Mosquitto.
I have the same problem whether I use Paho MQTT version 1.1.0 or 1.0.3.
As an experiment, I commented out the call to get_online_devices in my Javascript so that it doesn't try to publish anything, reloaded the page and there was no error, as expected. Then, I used MQTTX to send a JSON message to the sgdrf/out topic to which the MQTT JS client is subscribed to, and it immediately disconnected with the same error message.
Code: At the bottom of the page (index.html) I have the following code (the original code has Django template tags to fill in some values, so this is the actual code received by the browser):
<!-- Paho MQTT -->
<script src="/static/js/paho-mqtt-min.js"></script>
<!-- Scripts -->
<script src="/static/js/dashboard.js"></script>
<script>
function get_online_devices(mqtt_client) {
cmd = {
operator: "GetOnlineDevices",
messageId: generate_random_number_n_exp(20),
info: {}
};
payload_string = JSON.stringify(cmd);
console.log(payload_string)
mqtt_client.publish("sgdrf/in", payload_string);
}
function add_device_to_list(device) {
// Omitted for brevity. It's not being used yet.
}
let mqtt_client = make_mqtt_client("localhost", 61613);
let connection_options = make_connection_options(
"admin",
"CENSORED_PASSWORD"
);
mqtt_client.onConnected = function(reconnect, uri) {
console.log(uri)
mqtt_client.subscribe("sgdrf/out");
console.log('subscribed');
get_online_devices(mqtt_client);
};
mqtt_client.onConnectionLost = mqtt_client_on_connection_lost;
mqtt_client.onMessageDelivered = mqtt_client_on_message_delivered;
mqtt_client.onMessageArrived = function (msg) {
// Omitted for brevity. Checks if the payload is a
// JSON object with the right data and calls
// add_device_to_list for each item of a list in it.
};
$(document).ready(function() {
mqtt_client.connect(connection_options);
$("#reload-device-list-btn").click(function() {
get_online_devices(mqtt_client);
});
});
</script>
The dashboard.js files mentioned above just has some functions that I think will be useful for other pages, so I separated them to a file:
// dashboard.js
function generate_random_number_n_exp(n) {
return parseInt(Math.random() * Math.pow(10, n), 10)
}
function make_mqtt_client(host, port) {
let client_id = "sgdrf_dashboard_" + generate_random_number_n_exp(4);
return new Paho.Client(host, port, '/', client_id);
}
function make_connection_options(user, password) {
let connection_options = {
userName: user,
password: password,
onSuccess: mqtt_client_on_success,
onFailure: mqtt_client_on_failure,
};
return connection_options;
}
function mqtt_client_on_success() {
console.log('Conexión exitosa al broker MQTT.');
}
function mqtt_client_on_failure(error) {
console.log(
'Fallo de conexión con el broker MQTT: ' + error.errorMessage
+ ' (código: ' + error.errorCode + ')'
);
}
function mqtt_client_on_connection_lost (error) {
console.log('Pérdida de conexión con el broker MQTT: ' + error.errorMessage
+ ' (código: ' + error.errorCode + ')'
);
}
function mqtt_client_on_message_delivered(msg) {
let topic = message.destinationName;
let payload = message.payloadString;
console.log("Mensaje enviado a " + topic + ": " + payload);
}
function mqtt_client_on_message_arrived(msg) {
let topic = message.destinationName;
let payload = message.payloadString;
console.log("Mensaje recibido de " + topic + ": " + payload);
}
Here are the contents of my mosquitto.conf file:
per_listener_settings true
listener 61612
allow_anonymous false
password_file /home/s8a/Projects/sgdrf/config/pwdfile.txt
listener 61613
protocol websockets
allow_anonymous false
password_file /home/s8a/Projects/sgdrf/config/pwdfile.txt
It just sets up a TCP listener and a WebSockets listener, both disallow anonymous connections, and authenticate using a pwdfile. As I said before, I have enabled and disabled anonymous connections, and changed the port number to 9001 and back to 61613, and I still have the same error.
Conclusion: I don't know what to do and this project's deadline is next week.
I feel kinda stupid, because it was really a trivial typing mistake. The problem is that the onMessageDelivered and onMessageArrived functions have msg as argument, but I wrote messagein the function body for some reason. That's what the "message is not defined" error meant, message is literally not defined. Anyway I fixed that and now it sends and receives messages without problems.
...
More detailed story: What was not trivial is how I figured it out.
I decided to get my hands dirty and opened the non-minified version of paho-mqtt.js. I looked for "Invalid error" and found where the error constant is defined, and two places where it's used in a catch block. In both catch blocks I noticed that there was a ternary operator checking if (error.hasOwnProperty("stack") == "undefined") but the true and false clauses were inverted, which is why I was getting "No Error Stack Available".
So I inverted the clauses, and indeed I got a stack trace in the console (maybe I should file a bug report to the Paho dev team when I can). The stack trace had my mqtt_client_on_message_delivered function right at the top, so I read it again and suddenly everything made sense. Then I felt stupid for wasting an afternoon on this.
About 4 hours of research...here we go.
I have a C# program that sends and listens for anything coming in a specific Socket. Using the sockets, C# can send stuff to it and can receive from it just fine. Now, going to my JavaScript file, I'm using the WebSocket interface to communicate with C#, but doesn't work (usually times out after a couple of minutes). When the Socket is online, the JavaScript code will take up to about 4 minutes then throw an error saying "WebSocket opening handshake timed out". The thing is I know that it can find because, when the port of the ip doesn't exist the JavaScript file throws an error in the next couple seconds.
Things I've done:
Turn off all firewalls, use both ws and wss at the beginning of the ip and port (ex: wss://xxx.xxx.x.xx:11111), change the port, change the ip to a valid ip still reachable, research for 4 hours.
C#:
IPHostEntry ipHost = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddr = IPAddress.Parse("ip");
IPEndPoint localEndPoint = new IPEndPoint(ipAddr, 11111);
Socket listener = new Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(10);
while (true)
{
Console.WriteLine("Waiting connection...");
Socket clientSocket = listener.Accept();
byte[] bytes = new Byte[1024];
string data = null;
while (true)
{
int numByte = clientSocket.Receive(bytes);
data += Encoding.ASCII.GetString(bytes, 0, numByte);
if (data.IndexOf("<EOF>") > -1)
{
break;
}
}
Console.WriteLine("Text received -> {0} ", data);
byte[] message = Encoding.ASCII.GetBytes("Test Server");
clientSocket.Send(message);
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
JavaScript:
socket = new WebSocket("wss://ip:11111");
socket.onopen = function()
{
alert("Connected!");
}
socket.onerror = function()
{
alert("Connection Failed");
}
The ip is local
Long story short, C# can communicate with itself and JavaScript can find it but can't communicate with it.
Properly complete a handshake. (Or use a library / connection type that does.)
The WebSocket protocol (as original defined in RFC6455 - The WebSocket Protocol) does not open a plain unrestricted socket, in part for security reasons.
Since the handshake is not complete, the client WS request will timeout as the HTTP “Upgrade” response is never received. Until the handshake is complete, the WS will not be active over the underlying TCP connection.
Initiating a WebSocket connection (“the handshake”) is defined in section 4 of the RFC. It is also discussed in How JavaScript works: Deep dive into WebSockets and HTTP/2 with SSE + how to pick the right path.
The client establishes a WebSocket connection through a process known as the WebSocket handshake. This process starts with the client sending a regular HTTP request to the server. An Upgrade header is included in this request which informs the server that the client wishes to establish a WebSocket connection.
..
Now that [after] the handshake is complete the initial HTTP connection is replaced by a WebSocket connection that uses the same underlying TCP/IP connection. At this point, either party can start sending data.
I wrote a simple TCP server-client native application in C. It works as expected (Linux Fedora as well as Cygwin for Windows 10). When I compiled it using EM++ (or EMCC), it built the JS & WASM fine.
$ em++ TcpService.cpp -o TcpService.js -s ASYNCIFY=1
However, while running the server, I see the following error; though it is stuck waiting for arrival of client-data, it never really connects:
$ node TcpService.js
Waiting for arrival of messages ...
TypeError: Cannot read property 'stream' of undefined
TypeError: Cannot read property 'stream' of undefined
...
I narrowed down the problem to the accept(3) call. Note that previously, I would see a stacktrace (not very useful), in addition to the above TypeError. Now that I am compiling the code with "-s ASYNCIFY=1", the stack trace is gone. However, the client program still doesn't connect (neither native build nor its WASM).
The server program uses simple BSD sockets:
void server(void)
{
// port to start the server on
int SERVER_PORT = 8877;
// socket address used for the server
struct sockaddr_in _ServerAddr;
memset(&_ServerAddr, 0, sizeof(_ServerAddr));
_ServerAddr.sin_family = AF_INET;
// htons: host to network short: transforms a value in host byte
// ordering format to a short value in network byte ordering format
_ServerAddr.sin_port = htons(SERVER_PORT);
// htonl: host to network long: same as htons but to long
_ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
// create a TCP socket, creation returns -1 on failure
int listen_sock;
if ((listen_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
throw std::runtime_error("could not create listen socket\n");
}
// bind it to listen to the incoming connections on the created server
// address, will return -1 on error
if ((bind(listen_sock, (struct sockaddr *)&_ServerAddr,
sizeof(_ServerAddr))) < 0) {
throw std::runtime_error("could not bind socket\n");
}
int wait_size = 16; // maximum number of waiting clients, after which
// dropping begins
if (listen(listen_sock, wait_size) < 0) {
throw std::runtime_error("could not open socket for listening\n");
}
// socket address used to store client address
struct sockaddr_in client_address;
int client_address_len = 0;
// run indefinitely
while (true) {
// open a new socket to transmit data per connection
int sock;
printf("Waiting for arrival of messages ...\n");
if ((sock =
accept(listen_sock, (struct sockaddr *)&client_address,
(socklen_t *)&client_address_len)) < 0) {
throw std::runtime_error("could not open a socket to accept data\n");
}
int n = 0;
int len = 0, maxlen = 100;
char buffer[maxlen];
char *pbuffer = buffer;
printf("client connected with ip address: %s\n",
inet_ntoa(client_address.sin_addr));
// keep running as long as the client keeps the connection open
while ((n = recv(sock, pbuffer, maxlen, 0)) > 0) {
pbuffer += n;
maxlen -= n;
len += n;
printf("received: '%s'\n", buffer);
// echo received content back
send(sock, buffer, len, 0);
}
close(sock);
}
close(listen_sock);
}
The client is equally simple:
void sendMessage(void)
{
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
inet_pton(AF_INET, server_name, &server_address.sin_addr);
server_address.sin_port = htons(server_port);
if ((_Sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
printf("could not create socket\n");
}
if (connect(_Sock, (struct sockaddr*)&server_address,
sizeof(server_address)) < 0) {
printf("could not connect to server\n");
return;
}
const char* data_to_send = "test message";
send(_Sock, data_to_send, strlen(data_to_send), 0);
}
Googling for the error "TypeError: Cannot read property 'stream' of undefined" did NOT prove very useful, especially WRT the stream property - most pages are only relevant to AWS, whereas mine is a simple, local test-program.
I also installed the required websockets NPMs before I could get node to run the application.
If you have existing TCP networking code written in C/C++ that utilizes the Posix Sockets API, by default Emscripten attempts to emulate such connections to take place over the WebSocket protocol instead. For this to work, you will need to use something like WebSockify on the server side to enable the TCP server stack to receive incoming WebSocket connections. This emulation is not very complete at the moment, it is likely that you will run into problems out of the box and need to adapt the code to work within the limitations that this emulation provides.
This is the default build mode for POSIX sockets, no linker flags or option settings are needed to enable it.
Full POSIX Sockets over WebSocket Proxy Server
Emscripten provides a native POSIX Sockets proxy server program, located in directory tools/websocket_to_posix_proxy/, that allows full POSIX Sockets API access from a web browser. This support works by proxying all POSIX Sockets API calls from the browser to the Emscripten POSIX Sockets proxy server (via transparent use of WebSockets API), and the proxy server then performs the native TCP/UDP calls on behalf of the page. This allows a web browser page to run full TCP & UDP connections, act as a server to accept incoming connections, and perform host name lookups and reverse lookups. Because all API calls are individually proxied, this support can be slow. This support is mostly useful for developing testing infrastructure and debugging.
The following POSIX sockets functions are proxied in this manner:
socket(), socketpair(), shutdown(), bind(), connect(), listen(), accept(), getsockname(), getpeername(), send(), recv(), sendto(), recvfrom(), sendmsg(), recvmsg(), getsockopt(), setsockopt(), getaddrinfo(), `getnameinfo().
The following POSIX sockets functions are currently not proxied (and will not work):
poll(), close() (use shutdown() instead), select()
To use POSIX sockets proxying, link the application with flags “-lwebsocket.js -s PROXY_POSIX_SOCKETS=1 -s USE_PTHREADS=1 -s PROXY_TO_PTHREAD=1”. That is, POSIX sockets proxying builds on top of the Emscripten WebSockets library, and requires multithreading and proxying the application main() to a pthread.
For an example of how the POSIX Sockets proxy server works in an Emscripten client program, see the file tests/websocket/tcp_echo_client.cpp.
https://emscripten.org/docs/porting/networking.html
I am developing an app for some time and recently I attached to the app an SSL Certificate (it is not self-signed; it is signed by Comodo, so the problem can't occur from here). I implemented long ago a WebSocket endpoint on my Java Glassfish server and I'm using it with javascript. I have been using the WebSocket successfully via http until now, when I moved to https.
Let's have a look at the code snippets I use:
Server Endpoint:
#ServerEndpoint(value = "/ws/chat",
encoders = ChatMessageEncoder.class,
decoders = ChatMessageDecoder.class)
public class ChatEndpoint {
#OnOpen
public void open(final Session session) {
// stuff happenin'
}
#OnMessage
public void onMessage(final Session session) {
// stuff happenin' }
#OnError
public void onError(Throwable t) {
t.printStackTrace();
System.out.println("error");
}
#OnClose
public void onClose() {
}
}
Client Connection:
var wsocketPrivate;
function connectToChatserver() {
var serviceLocation = 'wss://<ip>:8080/ws/chat';
$rootScope.wsocketPrivate = new WebSocket(serviceLocation);
$rootScope.wsocketPrivate.onmessage = onMessageReceived;
};
function onMessageReceived(evt) {
console.log(evt)
};
connectToChatserver();
Not having activated ssl certificate and using var serviceLocation = 'ws://<ip>:8080/ws/chat'; (ws instead of wss) works perfectly fine. When I moved to https, it asked for wss (the browser blocked my ws handshake because it wasn't secure) and moving to wss, the following error occurs:
WebSocket connection to 'wss://<ip>:8080/ws/chat' failed: Error in connection establishment: net::ERR_TIMED_OUT
What am I doing wrong? Can you suggest some tests to find out more information?
Thank you,
Mihai
I finally found where the problem was. I was using an nginx front server as a proxy. The default configuration of that server was blocking my wss for some weird reason.
I'm trying to use a Socket connection to read a file on a remote website. So far, my code:
conn = new Socket;
if( conn.open( 'example.com:80' ) ) {
conn.write( 'GET /indesign-page/ HTTP/1.0' + "\n\n" );
reply = conn.read(999999);
conn.close();
} else {
alert( 'Problem connecting to server' );
}
The socket connects to example.com fine, but the request comes across as this:
GET http://localhost/indesign-page/ HTTP/1.0
when it should be this:
GET http://example.com/indesign-page/ HTTP/1.0
I've tried changing the conn.write parameters to 'GET http://example.com/indesign-page/ ...', but then it comes across as:
GET http://localhosthttp://example.com/indesign-page/ HTTP/1.0
The webserver requires that the host be set correctly to serve correctly.
You need to set the "Host" header.
conn.write( 'GET /indesign-page/ HTTP/1.0' + "Host: example.com\r\n" + "\n\n" );
Because conn.open( 'example.com:80' ) means find example.com's server ip then connect that ip address at 80 port, so the web server does not know that you had resolved example.com before connected to it.
Do you need to use a manual socket object? On Adobe's Community Site there's a mention to this already created FTP Script where you could call a GET or PUT to a file on a FTP server.
Otherwise which OS are you using? If you'll always be on a Mac, you could shell out to an AppleScript command and place the file anywhere you'd like:
var command = 'do shell script "curl http://localhost/indesign-page"';
var response = app.doScript(command, ScriptLanguage.APPLESCRIPT_LANGUAGE);
The nice thing about the AppleScript is that you can execute the command manually using the AppleScript Editor (or Script Editor if you're earlier than 10.6).