I'm writing a application using sockets, but can't seem to get the initial handshake to work. I'm using WebSockets + React on my front-end, running on PORT 8080, and Node.js socket on the backend running on PORT 5000.
The front-end handshake is done through my component like so:
componentDidMount(){
this.socket = new WebSocket('ws://localhost:5000', ['json']);
this.socket.onerror = err => {
console.log(err)
}
this.socket.onmessage = e => {
let res = JSON.parse(e.data);
console.log(e, res);
let copyArr = [...this.state.message]
copyArr.push(res);
this.setState({
message: copyArr
});
}
}
On my Node server, I do:
const server = http.createServer();
server.on('upgrade', (req, socket) => {
if(req.headers['upgrade'] !== "websocket"){
socket.end('HTTP/1.1 400 Bad Request');
return;
}
const acceptKey = req.headers['sec-websocket-key'];
const acceptHash = generateValue(acceptKey);
console.log('accepkey', acceptKey, 'hash', acceptHash);
const resHeaders = [ 'HTTP/1.1 101 Web Socket Protocol Handshake', 'Upgrade: WebSocket', 'Connection: Upgrade', `Sec-WebSocket-Accept: ${acceptHash}` ];
console.log(resHeaders);
let protocols = req.headers['sec-websocket-protocol'];
protocols = !protocols ? [] : protocols.split(',').map(name => name.trim());
if(protocols.includes('json')){
console.log('json here');
resHeaders.push(`Sec-WebSocket-Protocol: json`);
}
socket.write(resHeaders.join('\r\n') + '\r\n\r\n');
})
function generateValue(key){
return crypto
.createHash('sha1')
.update(key + '258EAFA5-E914–47DA-95CA-C5AB0DC85B11', 'binary')
.digest('base64');
}
When my React component mounts, it tries to establish the initial handshake but fails with the error: WebSocket connection to 'ws://localhost:5000/' failed: Error during WebSocket handshake: Incorrect 'Sec-WebSocket-Accept' header value. I've checked using Chrome developer tool and found this
While on the backend, when logging the request accept-key header, and response headers, I saw this:
So, unless I'm mistaken about these headers, it seems that the request and response accept-key header somehow changes when making it's way from the client to the server, and vice versa. How is this happening? Or have I misunderstood what's going on. Why exactly is the initial handshake not working?
There is a en dash – instead of hyphen - in 258EAFA5-E914–47DA-95CA-C5AB0DC85B11 after E914
So replace it with hyphen -
reference https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-WebSocket-Accept
I believe the generateValue function is wrong, you pass binary as the inputData encoding which is a keyword for latin1 according to the docs. But I believe it is UTF-8 string, not latin1, so the result hash is wrong. So just try to use update(key + '258EAFA5-E914–47DA-95CA-C5AB0DC85B11', 'utf8') or even without the second utf8 argument since it is a default.
Related
I have this error from smtp gmail when i try to send my message.
I use TLS socket on nodejs to connect to smtp, then i have a class which creates me commands which i send.
secure.on('secureConnect', () => {
//secure.setEncoding('base64');
let caretka = `\r\n`;
console.log(secure.authorized);
console.log('IN SECURE');
//secure.write('EHLO hailmail\r\n');
**com**.forEach(command => {
secure.write(command);
});
secure.write('quit\r\n');
secure.on('data', chunk => {
const response = chunk.toString();
console.log(`Response in secure: ${response}`);
})
secure.on('keylog', (line) => {
console.log(`Line in secure: ${line}`);
})
secure.on('close', () => {
console.log('closed');
})
});
My class:
class creatingCommandsForSMTP {
constructor(msg) {
this.helo = `EHLO hailmail\r\n`;
this.mailFrom = `MAIL FROM: <${msg.from}>\r\n`;
this.recipient = `RCPT TO: <${msg.recipient}>\r\n`;
this.commandData = `DATA`;
//this.dataFrom = `FROM: <${msg.from}>\r\n`;
//this.dataTo = `TO: <${msg.recipient}>\r\n`;
//this.dataSubject = `SUBJECT: ${msg.subject}\r\n`;
//this.dataText = `${msg.text}\r\n`;
//this.dot = `.\r\n`;
//this.commandQuit = 'QUIT\r\n';
}
getValue(command) {
return this[command]
}
};
So the problem is when i send Mail from and Rcpt to i always get OK code from server,
but after command 'DATA\r\n' i have this response:
'451 4.5.0 SMTP protocol violation, see RFC 2821 k12-20020adff5cc000000b0021e4c3b2966si4569901wrp.1037 - gsmtp '
I tried to search RFC 2821, but there is no information about rules inside the DATA command
EDIT: The last tries gave me a code 'Go Ahead' which means that server is ready to recieve data, then i send 'FROM: ' and get the same code '451 SMTP protocol violation'
Many mail servers deliberately reject anything that does not follow the RFCs to the letter, in the aim of rejecting bots and spammers. The code snippet is obviously incomplete, but I suspect that the error you are seeing is caused because the commands you are sending do not wait for a response before sending the next. This is classic bot behaviour.
If you change your main command loop to something like the following, you should avoid this problem (psuedocode):
loop_through_commands {
send command
receive response
if error blah
}
Goal: Use Server-Sent Events in my Angular App with Express Backend
Problem: Server-Sent Events do not reach the client
Backend Code
router.get('/options/:a/:b/:c', async (req, res) => {
console.log('options endpoint called..', req.params.a,req.params.b,req.params.c);
// ---- SERVER SENT EVENTS ---- //
// Setting Headers
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader('Connection', 'keep-alive');
res.flushHeaders(); // flush the headers to establish SSE with client
for(let i=0;i<10;i++){
console.log("should now send this response...banane")
res.write('banane!')
}
});
Client Side Code
export class NetworkingService {
BASE_URL;
OPTIONS = 'options'
searchResultSubject: Subject<SearchResults>;
constructor(private http: HttpClient, private ls: LoggingService, private _zone: NgZone) { // shortened }
getOptions(a: string, b: string, c: string) {
this.ls.log("get options endpoint about to be triggered")
this.getServerSentEvent(this.BASE_URL + this.OPTIONS + "/" + a + "/" + b + "/" + c).subscribe(resp => {
this.ls.log("got from server: ", resp)
});
return this.searchResultSubject;
}
getServerSentEvent(url: string): Observable<any> {
console.log('creating an eventsource...')
return new Observable(observer => {
const eventSource = this.getEventSource(url);
eventSource.onopen = event => {
console.log('opening connection', eventSource.readyState)
};
eventSource.onmessage = event => {
console.log('event from server..')
this._zone.run(() => {
observer.next(event);
});
};
eventSource.onerror = error => {
console.log('error from server..')
this._zone.run(() => {
observer.error(error);
});
};
});
}
private getEventSource(url: string): EventSource {
return new EventSource(url);
}
}
Server Side log output (as expected)
options endpoint called.. A B C
should now send this response...banane
should now send this response...banane
should now send this response...banane
should now send this response...banane
should now send this response...banane
should now send this response...banane
should now send this response...banane
should now send this response...banane
should now send this response...banane
should now send this response...banane
Client Side log output (NOT as expected)
get options endpoint about to be triggered
creating an eventsource...
opening connection 1
...and then nothing.
What have I tried?
I fail to see how I differ from these: so-question, tutorial, tutorial
In the Networks Tab in Dev Tools I see a Status 200, type eventsource line entry with the correct headers. But only one!
I think I am making a really obvious mistake, since it is ALMOST working and seems to be straightforward from the examples.
My Angular is 10.1.6 and express is 4.17.1
I am new to interacting directly with ngZone is there a potential error?
The problem persists even when I comment out the compression library or use res.flush(), as suggested here.
I was having the same problem, I was not getting the response on the client.
After a number of changes it seems to be working.
First I set the headers:
response.setHeader('Cache-Control', 'no-cache');
response.setHeader('Content-Type', 'text/event-stream');
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Connection', 'keep alive');
response.setHeader('X-Accel-Buffering', 'no');
response.flushHeaders(); // flush headers to establish SSE with client
If you are using the compress middleware, it is necessary that when sending the data to the client, you put
response.flush();
example
response.write(`event: ${event}\ndata: ${data}\n\n`);
response.flush();
It seems that the client receives messages from the generated event,
that is, if you send the client the following response
response.write(`event: some_event\ndata: some_data\n\n`);
the client should have a code similar to this:
const eventSource = new EventSource(url);
eventSource.addEventListener('some_event', listener => {
console.log(listener);
...
}, false);
I hope to be helpful
What worked for me was similar to what daacdev said, but not entirely.
Serverside I used:
res.setHeader('Cache-Control', 'no-cache, no-transform'); // Notice the no-transform!
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders(); // Flush the headers to establish SSE with client
// Do some stuff...
// Like sending an event with data
res.write('event: fooEvent\n'); // Note the 1 newline after the event name
res.write(`data: ${JSON.stringify({ foo: 'bar' })}\n\n`); // Note the 2 newlines after the data
// And when you're done, end it
res.end();
And clientside, we have eventListeners for event types:
const eventSource = new EventSource('/api/foo');
eventSource.addEventListener('fooEvent', event => {
console.log('Got fooEvent: ', event.data); // event.data has your data
eventSource.close(); // Close the eventSource if the client is done
});
You can leave out the event type altogether and only send 'data' in your res.write(). In which case your client listens for event type-less messages like this:
eventSource.onmessage = event => {
console.log('Got some data: ', event.data); // event.data has your data
eventSource.close(); // Close the eventSource if the client is done
Noteworthy:
The Cache-Control header needed 'no-transform' aside from 'no-cache'. Before I did that, the client would only get the messages after the server ended the whole thing
I did not need to flush after every write
You can't only send an event type. It always needs data to follow it. So if you simply want to send a message, do something like:
res.write('event: doubleProcesses\n');
res.write('data: doubleProcesses\n\n');
You must adhere to the defined structure: the optional 'event' (followed by a colon, a name, and 1 newline) and the required 'data' (followed by a colon, your data as a string, and 2 newlines). You cannot simply put anything in your res.write.
That last point is for sure what you were missing.
Docs here:
https://developer.mozilla.org/en-US/docs/Web/API/EventSource
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
For anyone who landed here after searching similar issues, what our problem turns out to be is that our node.js app is being hosted on Azure windows, and it is using IIS under the hood. The handler iisnode has a default setting of buffering response, which is the reason why response from our server was never received at the client side. All we had to do was to edit the web.config files according to this Microsoft guide
https://learn.microsoft.com/en-us/azure/app-service/app-service-web-nodejs-best-practices-and-troubleshoot-guide
pay attention to the
flushResponse section
I'm a total beginner in Using Node-Red and nodeJS. Im trying to write nodejs code in an "daemon node" for handling my payload before sending it by MQTT to my nodejs Server. My Problem is: I can't get my payload from stdin.
I tried everything i found online about reading from stdin, but I didn't find a solution.
"use strict"
let mqtt = require("mqtt");
let client = mqtt.connect("mqtt://192.168.178.36");
let obj = process.stdin;
console.log(obj);
client.on('connect', () => {
console.log("Sending...")
client.publish("test/reader01", "Reader01: " + (new Date()).toString() + "\n" + obj);
client.end();
});
The program you can see here sends the aktual Date to the server an prints out a string including a net.socket object on the console, but i can't get the payload from my stream.
I picked up this previous working app (Angular2) and find that it is not working (Angular4) as expected now.
Module was used (it may not matter):
import { HttpModule, JsonpModule } from '#angular/http';
Module being used now:
import { HttpClientModule} from '#angular/common/http';
Trying to get a list of records from the backend (Node.js, Express, and MongoDB) as below.
listResource(resType: string, parameters: string[]) {
console.log("***listResource");
let headers = new HttpHeaders().set("X-CustomHeader", "custom header value");
headers.append('Content-Type', 'application/fhir+json');
headers.append("'Access-Control-Allow-Origin", "*");
headers.append("Accept", "application/fhir+json");
headers.append("USER_KEY", "QIF83Fjoe4sYxdQsah3h"); //TOUCHSTONE KEY
let urlString = this.baseUrl + resType + "/list"; // + queryString;
console.log("List resource URL string: [" + urlString + "]");
return (this.httpClient.get(urlString, { headers })
.map((res: Response) => res.json()))
.catch((error: any) => Observable.throw(error.json().error || 'Server error from Observable http.get call')); //...errors if any
}
when my component is loaded, the above listResource will be called as below.
ngOnInit() {
//Get the initial 25 latest patient
//this.progressBar = true;
this.currentPage = 0;
this.patientList = [];
this.globalSvc.gPatient = [];
console.log("***OnInit");
this.restSvc.listResource("Patient", ["identifier=*", "family=*", "given=*"]).subscribe(
data => {
console.log("Response data: " + JSON.stringify(data));
},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
console.log('An error occurred:', err.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
}
});
}
Below is the output from Chrome console. Of course, I don't get any good response. It seems to me the Chrome browser sends CORS option and the server responds correctly, then the browser doesn't send the actual GET.
If I send the REST API request from PostMan directly which doesn't have CORS, I get the expected good response from the server. Hence, it seems to me the server is ok.
Questions:
Any idea how to debug or fix it?
Will this relate to CORS on both Angular client and Node.js server?
The ${err.status} and ${err.error} are "undefined" in Chrome console. How can I find the actual error?
console.log(Backend returned code ${err.status}, body was: ${err.error});
Update 1 based on Grey's suggestion on the immutable header and const.
The GET is returning data now.
headers.append() does not alter the headers, it returns a new Headers (because Headers is immutable).
So, instead of
let headers = new HttpHeaders().set("X-CustomHeader", "custom header value");
headers.append('Content-Type', 'application/fhir+json');
headers.append("'Access-Control-Allow-Origin", "*");
headers.append("Accept", "application/fhir+json");
headers.append("USER_KEY", "QIF83Fjoe4sYxdQsah3h"); //TOUCHSTONE KEY
you need to do something like:
let headers = new HttpHeaders().set("X-CustomHeader", "custom header value")
.append('Content-Type', 'application/fhir+json')
.append("'Access-Control-Allow-Origin", "*")
.append("Accept", "application/fhir+json")
.append("USER_KEY", "QIF83Fjoe4sYxdQsah3h");
Oh, and that should actually be const headers =, rather than let headers =
I have a NodeJS API web server (let's call it WS1) that receives RESTful HTTP requests from clients, and to respond needs to first query another local server (let's call it WS2).
The flow is pretty much like this:
WS1 receives an HTTP request from a client and parses it.
WS1 sends a request to WS2 for some information.
When WS1 receives the response from WS2, it finishes processing the original request and sends a response back to the client.
Until now all communication between WS1 and WS2 has been done through HTTP requests, since the two machines are on the same local network.
To speed things up though I'm considering to start using zmq instead. I've looked at the patterns they show on the docs, but still haven't figured out a concurrency problem.
WS1 can send many requests per second to WS2, and there's no guarantee that WS2 replies in the same order as it receives the requests, since some async operations can internally take longer than others.
So, using zmq with NodeJS, how do I make sure that when WS1 receives a message from WS2 it knows to what original client request it belongs to? Is there a built-in mechanism to take care of it?
Thanks!
0MQ is an interesting tool set that helps abstract socket communication. There are mechanism (should you choose the correct socket types) that allow the server to respond to the right client, and it is handled within the confines of 0mq.
The basic API types are:
PUSH-PULL
PUB-SUB
REQUEST-REPLY
IF you want to be able to have one machine respond to the originator, then I believe you want REQ-REP api type.
then you need to consider the multi-plexing on each side to get the connectors correct. But keep it one to one for simplicity sake at first:
Sample Client (from http://zguide.zeromq.org/js:rrclient
// Hello World client in Node.js
// Connects REQ socket to tcp://localhost:5559
// Sends "Hello" to server, expects "World" back
var zmq = require('zmq')
, requester = zmq.socket('req');
requester.connect('tcp://localhost:5559');
var replyNbr = 0;
requester.on('message', function(msg) {
console.log('got reply', replyNbr, msg.toString());
replyNbr += 1;
});
for (var i = 0; i < 10; ++i) {
requester.send("Hello");
}
sample server (from http://zguide.zeromq.org/js:rrserver)
// Hello World server in Node.js
// Connects REP socket to tcp://*:5560
// Expects "Hello" from client, replies with "World"
var zmq = require('zmq')
, responder = zmq.socket('rep');
responder.connect('tcp://localhost:5560');
responder.on('message', function(msg) {
console.log('received request:', msg.toString());
setTimeout(function() {
responder.send("World");
}, 1000);
});
The routing of the reply back to the client is handled automatically by 0MQ. it is part of the message (although I don't remember if you see the address buffer in these examples - it maybe abstracted away). Here is what the request envelope looks like:
it is the first frame, which allows 0MQ to be able to reply to the correct client.
Once that is running you can then consider 1..* *..1 and ... All it really does is require you to change the socket types to DEALER and ROUTER where appropriate.
I ended up implementing some sort of "middleware" to support this functionality with zmq.
In the example below for simplicity I've used Express with Node >= v4.0.0 (supporting native JS promises), but you can obviously substitute it with any HTTP server you like (these days I prefer Koa) and promises library you prefer. This is the code for the two servers.
WS1 (requester)
var zmq = require('zmq');
var mem = {};
var requester = zmq.socket('req');
requester.on("message", function(reply) {
reply = reply.toString().split('*');
mem[reply.pop()](reply);
});
requester.connect("tcp://localhost:5555");
var app = require('express')();
app.get('/', function (req, res) {
var id = Date.now() + Math.random();
new Promise(function (resolve, reject) {
mem[id] = function (reply) {
reply[0] === 'success' ? resolve(reply[1]) : reject(reply[1]);
}
})
.then(function (data) {
res.send(data);
})
.catch(function (err) {
console.log(err);
res.send(500);
})
requester.send(id + '*' + message);
});
var server = app.listen(3000);
WS2 (responder)
var zmq = require('zmq');
var responder = zmq.socket('rep');
responder.on('message', function(message) {
message = message.split('*');
var reqId = message[0];
// Do whatever async stuff you need with message[1]
// Then at the end of your callbacks you'll have something like this
if (err) {
responder.send('err' + '*' + JSON.stringify(err) + '*' + reqId);
} else {
responder.send('success' + '*' + JSON.stringify(yourData) + '*' + reqId);
}
});
responder.bind('tcp://*:5555');