So I tried to solve it on my own and googled a lot but I couldn't really solve the problem although I think it shouldn't be that difficult. So what I am doing (or trying to do) is, I establish a simple telnet connection to a server and simulate the beginning of an SMTP session to verify if an email exist (if that gives me correct results or not is another question)
My code to do that looks like this:
const net = require('net');
async function TCPConnection(smtpServer, emailDomain, senderEmail) {
const client = new net.Socket();
client.setEncoding("utf-8");
let completeData = '';
client.connect({port: 25, host: smtpServer}, () => {
console.log("TCP Connection established with the Server.");
})
client.write("ehlo " + emailDomain + "\r\n");
client.write("mail from:<" + senderEmail + ">\r\n");
client.write("rcpt to:<" + senderEmail + ">\r\n");
client.on('data', function (data) {
console.log(data);
});
function dataReceived(data){
completeData += data;
}
client.on('end', function () {
console.log('Requested an end to the TCP connection');
});
}
TCPConnection('aspmx.l.google.com', 'mailtrap.io', 'new-recipient#mailtrap.io');
The responses from the server logged to console looks like this:
TCP Connection established with the Server.
220 mx.google.com ESMTP m17si1129948edf.309 - gsmtp
250-mx.google.com at your service, [217.84.87.66]
250-SIZE 157286400
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
250 2.1.0 OK m17si1129948edf.309 - gsmtp
550-5.1.1 The email account that you tried to reach does not exist. Please try
550-5.1.1 double-checking the recipient's email address for typos or
550-5.1.1 unnecessary spaces. Learn more at
550 5.1.1 https://support.google.com/mail/?p=NoSuchUser m17si1129948edf.309 - gsmtp
So what I want to achieve is to get the complete data (that is logged to the console right now) out of the callback or TCPConnection function. Like for example with "return completeData;" at the end of the TCPConnection function.
The reasyon why I want to do this is, that this function (TCPConnection) gets called over an HTTP Request. And I want to send the result of this back to client. The complete function could look like this (but obviously not working so far):
app.post('/tcpConnection', (req, res) => {
let smtpServer = req.body.smptServer;
let domain = req.body.domain;
let senderEmail = req.body.senderEmail;
async function sendResponse(){
let response = await TCPConnection(smtpServer,domain,senderEmail);
await res.status(200).json(result);
}
})
I read the solution for this could be to do what I want to do with my data after the "end" event got emitted. But my problem with that is, that after my three "write-commands" the server doesn't end the connection immediately. And if I would write this:
client.write("ehlo " + emailDomain + "\r\n");
client.write("mail from:<" + senderEmail + ">\r\n");
client.write("rcpt to:<" + senderEmail + ">\r\n");
client.end();
It is ending the connection before the second and third command gets a response. I tried to "wait" for the reponses with "await client.write..." but that is obviously not gonna work for not sure what reasons.
I hope you get a gist of what I am trying to get done if not feel free to ask. Thank you!
So I think I have done it myself or at least get a solution that is working fine in most cases. What I am doing is that I end the socket in the callback of the third write command like this:
client.write("rcpt to:<" + senderEmail + ">\r\n","utf-8",()=>{
console.log("third command written");
client.end();
});
By doing this, the server "should get enough time" to answer all commands.
Ealier solution was to count the number of responses in the callback function of the 'data' event, like this:
client.on('data', function (data) {
counterResponses++;
console.log("Response Nr: " + counterResponses + '\r\n' + data);
completeData.push(data);
if (counterResponses === 4){
console.log("4 responses received end connection");
client.end();
}
});
Since I wanted to end the socket after receiving 4 responses to my commands I used an if-statement to check the number of responses. But the problem with this is, that sometimes multiple responses could get send as one. For example, the responses could either look like this:
Response Nr: 1
220 mtaproxy104.free.mail.ir2.yahoo.com ESMTP ready
Response Nr: 2
250-mtaproxy104.free.mail.ir2.yahoo.com
250-PIPELINING
250-SIZE 41943040
250-8BITMIME
250 STARTTLS
But also like this:
Response Nr: 1
220 mtaproxy104.free.mail.ir2.yahoo.com ESMTP ready
250-mtaproxy104.free.mail.ir2.yahoo.com
250-PIPELINING
250-SIZE 41943040
250-8BITMIME
250 STARTTLS
So 2 responses get send as one. This makes it hard to evaluate if I got all the responses I needed to get. That's why I just end the socket after the last write-command.
In the client.on('end') function I then send the completeData back to the client.
client.on('end', function () {
console.log('Requested an end to the TCP connection');
console.log(completeData);
res.status(200).json(completeData);
});
Only thing that still bothers me is that my array (where I push the data with each emitted 'data'-event) is variable in its length. This is gonna make it a bit difficult to verify, if the email was found or not. But since this should always be the last response, it should work in the most cases if I just test if the last entry of the array either has the statuscode '250' or '550' in it.
I hope this helps at least somebody. Let me know if it helped you or if you have any questions about it. Also still open for better solutions, since I am sure there are better ways to do this.
Related
I have this code, whenever the user goes to this certain endpoint, it is supposed to emit a message to a python client, which then gets some data and then returns it back as a callback so I can show the users the data.
This is the server-side code (NodeJS):
app.get('/hueapi/lights', verifyToken, (req,res) => {
const bridgeIDFromApp = req.header('bridgeID');
const socketID = socketRefDic[bridgeIDFromApp]['socketID'];
io.to(socketID).emit('getAllLights', 'getAllLights', function(data){
res.send(data); // The callback function that shows the data given by the python client
});
});
It just sends a simple 'getAllLights' message to the python client in question and then runs the function which provides the data.
This is the client-side code (python):
def getAllLights(data):
lightData = requests.get('http://localhost:4000/lights/')
return lightData
Am I doing the call back wrong or? I just want to send the data straight back to the user after retrieving it.
EDIT:
I am now using io.to(...).emit(...) instead of io.send(...).emit(...) yet I am still getting the error saying I'm broadcasting, yet I'm not, am I?
I don't think that the ack method will work for you unless it is implemented on the python side as well. The reason that you are still getting the broadcasting error is because io.to does not return a socket it returns a room which does broadcast.
Probably easier to just have a separate endpoint on the client side. Which your python code doesn't even attempt from what I see. The python code should still be able to write to the socket.
So to implement your own ack function you would simply write your ack message to the socket. If you need it to be statefully namespaced then you would have to include an address for the python code to reference with your getAllLights message.
Node:
app.get('/hueapi/lights', verifyToken, (req,res) => {
const bridgeIDFromApp = req.header('bridgeID');
const socketID = socketRefDic[bridgeIDFromApp]['socketID'];
const uniqAck = "some unique endpoint path";
const socket = getSocketByID(socketID);
socket.on(uniqAck, (data) => res.send);
socket.emit('getAllLights', 'getAllLights:'+uniqAck);
});
Python:
def getAllLights(data):
lightData = requests.get('http://localhost:4000/lights/');
return (lightData, split(data, ":")[1]); // split if its not already done by this point.
// capture 'value' from getAllLights when it is called...
socket.emit(value[1], value[0]);
while playing around with mqtt.js of node.js i figured out that the value of client.connected seem to be wrong or i misunderstand something.
I did this tutorial http://thejackalofjavascript.com/getting-started-mqtt/ and everything works fine.
I tried to get more into mqtt, so i looked up on https://www.npmjs.com/package/mqtt#mqttclientconnected , Description of client.connected:
Boolean : set to true if the client is connected. false otherwise.
In order to look if it works, i modified the sourcecode for the firstline just like this
var mqtt = require('mqtt')
client = mqtt.connect('mqtt://localhost');
if (client.connected == true) {
console.log("Connection successful.")
}
else{
console.log("Something went wrong.")
}
client.subscribe('presence');
console.log('Client publishing.. ');
client.publish('presence', 'Client 1 is alive.. Test Ping! ' + Date());
client.end();
I just added the if-clause. When executing the script, I always receive the message, that something went wrong, ergo: connected --> false.
But why?
The problem is that the mqtt.connect() call is asynchronous. So it returns before it has actually connected. This means as you test the mqtt.connected straight after calling mqtt.connect() it will still be false because it's not actually finished connecting yet.
You need to register event handlers to be told when the client has actually been connected.
var mqtt = require('mqtt')
var client = mqtt.connect('mqtt://localhost');
client.on('connected',function(){
client.subscribe('presence');
console.log('Client publishing.. ');
client.publish('presence', 'Client 1 is alive.. Test Ping! ' + Date());
});
client.on('message',function(topic,message){
console.log("received message on " + topic);
console.log(message.toString());
client.end();
});
This code connects to a broker, subscribes to the topic presence then publishes a message to that same topic. When it receives a message on the presence topic it prints it out then exits.
I have a node application handling some ZeroMQ events coming from another application utilizing the Node-ZMQ bindings found here: https://github.com/JustinTulloss/zeromq.node
The issue I am running into is one of the operations from an event takes a long time to process and this appears to be blocking any other event from being processed during this time. Although the application is not currently clustered, doing so would only afford a few more threads and doesn't really solve the issue. I am wondering if there is a way of allowing for these async calls to not block other incoming requests while they process, and how I might go about implementing them.
Here is a highly condensed/contrived code example of what I am doing currently:
var zmq = require('zmq');
var zmqResponder = zmq.socket('rep');
var Client = require('node-rest-client').Client;
var client = new Client();
zmqResponder.on('message', function (msg, data) {
var parsed = JSON.parse(msg);
logging.info('ZMQ Request received: ' + parsed.event);
switch (parsed.event) {
case 'create':
//Typically short running process, not an issue
case 'update':
//Long running process this is the issue
serverRequest().then(function(response){
zmqResponder.send(JSON.stringify(response));
});
}
});
function serverRequest(){
var deferred = Q.defer();
client.get(function (data, response) {
if (response.statusCode !== 200) {
deferred.reject(data.data);
} else {
deferred.resolve(data.data);
}
});
return deferred.promise;
}
EDIT** Here's a gist of the code: https://gist.github.com/battlecow/cd0c2233e9f197ec0049
I think, through the comment thread, I've identified your issue. REQ/REP has a strict synchronous message order guarantee... You must receive-send-receive-send-etc. REQ must start with send and REP must start with receive. So, you're only processing one message at a time because the socket types you've chosen enforce that.
If you were using a different, non-event-driven language, you'd likely get an error telling you what you'd done wrong when you tried to send or receive twice in a row, but node lets you do it and just queues the subsequent messages until it's their turn in the message order.
You want to change REQ/REP to DEALER/ROUTER and it'll work the way you expect. You'll have to change your logic slightly for the ROUTER socket to get it to send appropriately, but everything else should work the same.
Rough example code, using the relevant portions of the posted gist:
var zmqResponder = zmq.socket('router');
zmqResponder.on('message', function (msg, data) {
var peer_id = msg[0];
var parsed = JSON.parse(msg[1]);
switch (parsed.event) {
case 'create':
// build parsedResponse, then...
zmqResponder.send([peer_id, JSON.stringify(parsedResponse)]);
break;
}
});
zmqResponder.bind('tcp://*:5668', function (err) {
if (err) {
logging.error(err);
} else {
logging.info("ZMQ awaiting orders on port 5668");
}
});
... you need to grab the peer_id (or whatever you want to call it, in ZMQ nomenclature it's the socket ID of the socket you're sending from, think of it as an "address" of sorts) from the first frame of the message you receive, and then use send it as the first frame of the message you send back.
By the way, I just noticed in your gist you are both connect()-ing and bind()-ing on the same socket (zmq.js lines 52 & 143, respectively). Don't do that. Inferring from other clues, you just want to bind() on this side of the process.
So I have a node server, and I have it kicking off a stored proc for one particular internal tool. However, it seems it is dying part way through (seems to be once dynamic sql is used).
So it does a bunch of general sql commands, but seems to be dying when we hit:
set #cmd = N'Some stuff'
print #cmd
exec(#cmd)
my js server code is:
var connection2 = new sql.Connection(config2, function(err){
console.log("Connection 2 error - "+ err);
var request2 = connection2.request();
var spName = "nameOfSP";
console.log("---calling sp----");
request2.execute(spName,function(response){
console.log("SP response " + response);
})
});
I am getting this error:
SP response RequestError: Timeout: Request failed to complete in 15000ms
However, I am getting confirmation that the steps up until that dynamic sql is getting process almost instantaneously.
I've tried adding:
connectionTimeout: 30000
to my config, but it didn't seem to effect anything, which is making me feel like I'm not implementing that correctly. The documentation for this doesn't show much for this part.
Any help would be greatly appreciated.
Thanks!
I was specifying connectionTimeout, rather than requestTimeout.
This fixed it.
I have this small piece of code. I run it, and then connect with a Telnet client.
var net = require('net');
//create server
var server = net.createServer(function (conn) {
console.log('New connection!')
//Message to the one who connects.
conn.write (
'\n > Welcome to NODE-CHAT!'
+ '\n > ' + count + ' other people are connected at this time.'
+ '\n > Please write your name and press Enter: '
);
//When a connected user writes...
conn.on('data', function (data) {
console.log(data);
});
conn.setEncoding('utf8');
});
//Listen
server.listen(3000, function () {
console.log('Server listening on *:3000');
})
When connected i get the Welcome messages as expected... Then if i write something in the telnet client, it will immediately trigger the conn.on listener... Lets say i write "hi you" the output in the console will be:
h
i
y
o
u
I would like this messages to sent when it is "finished" instead whenever i type character. I guess i could store the data from the conn.on trigger in a string, and output this string when the '\n' character is typed.
But I'm wondering if this is the right way to do it. Is there a way to change what trigger the conn.on? Maybe so it will only trigger (that is output in this case... ) on certain characters?... namely the \n char.
TCP is a stream of data so you should not make any assumptions about how much data you will receive by calling .read() or when listening for data events.
If your input is newline delimited, then you will have to buffer until you see a newline. There could also be multiple newlines in one chunk passed to your data event handlers too.
I don't see any problems in your code. The behavior you describe is related to the Telnet client that sends every keystroke, it does not wait for you to hit enter. If you are on Linux try sending data with wget o open a browser and type
http://localhost:3000/hiyou
and see if you get a complete string instead of one character.