In Node.js, suppose serving a request takes time. So upon receipt of the request the server wants to response back with "I revived your request and I will get back to you soon". Then once the processing the request is over, the server wants to, this time, get back to the client with the actual response . How can we do that?
The code snippet below issues error ( connection closed or something ).
'use strict';
var http = require('http');
var numberOfRequests = 0;
http.createServer(function (request, responce) {
request.n = numberOfRequests;
console.log('Request number ' + numberOfRequests + ' recioved!');
responce.writeHead(200);
responce.write("soon we will get back to you for your request# " + request.n);
responce.end();
setTimeout(function () {
responce.writeHead(200);
responce.write("Responce to your request# " + request.n);
console.log("Responce to the request# " + request.n);
responce.end();
}, 5000);
numberOfRequests++;
}
).listen(8080);
console.log('listening ...');
You can't send two responses to the same request. You can only send one fully formed response per request. That's the http specification.
The only work-arounds for your issue that I know of are:
Use http in a flushed streaming mode. Here, you send part of the response, flush it out so you know it is sent and you have a special type of client on the other end that is reading partial responses and interpreting them (not the usual way that http responses are read and not what a browser does on its own).
Use a websocket or socket.io connection to update the client with progress before you finally send the actual http response. The user would connect a socket.io connection, then make a long running http request, then receive regular progress on the socket.io connection, then receive the http response when it was done. This can be done simply in any web page without much difficulty. The only main problem to solve is you have to install a method of associated a webSocket or socket.io connection with an incoming http connection (which can usually be done via a session cookie) so you know which webSocket or socket.io connection is associated with the http request that just arrives.
Use some other server-push scheme to "push" the second response to the client.
Implement client polling. Send an initial http response that instructs the client to check back in NN ms to see if there is more to the response. When the client checks back again NN ms later, you either have the final response for them and send it or you respond again tell them to to check back again in NN ms, etc...
You are looking for Server-Sent-Events (SSE), of course you can send chunked data, just don't call res.end() until the final piece of data is sent. And by the way, you can only send headers one time. See this example:
res.writeHead(200);
res.write("The first piece of data");
setTimeout(()=>{
res.write("This piece of data will be sent in 1 second.");
res.end("Ok, all data has be send, now close the connection.");
}, 1000);
Related
I'm making a chat application that uses long polling with express (I'm aware websockets are better for this, I just wanted to make something specifically with long polling).
This is the code that the client sends a GET request to when it's waiting for a new chat message:
app.get('/api',(req,res)=>{
listeners.push(res)
})
It pushes the response objects to an array, and once a new chat message is available it calls .send() on the responses. When the client recieves the response, it sends a GET request again, waiting for new messages.
Here's the code for when someone sends a message (simplified)
app.post('/api', (req,res)=>{
listeners.forEach(listener=>{
listener.json(req.body)
})
listeners = []
res.status(200).end()
})
I noticed that when I open two tabs of my site, only the 1st tab's request gets put in the array at first. That makes the 2nd tab not receive the 1st chat message. The 2nd tab's request only gets pushed into the array after the 1st chat message. So when the second chat message is sent it works fine. This means that the 2nd tab is always one request behind.
Here's an example in case that wasn't clear:
Client 1 connects
Client 2 connects
---
Client X sends a message:
Client 1's request 1 receives the message
Client 2's request 1 is still pending
---
Client X sends another message
Client 1's request 2 receives the message
Client 2's request 1 receives the message
---
Client X sends another message
Client 1's request 3 receives the message
Client 2's request 2 receives the message
...
Another thing I noticed is that when I restart the server after client 1 and 2 connect, client 2's request get pushed into the array after the restart.
Could someone explain this behavior? Thanks in advance.
This sounds like the browser is hoping to use a cached request so it waits for a previous request with the exact same URL to see if it can use the cached result rather than firing the same request again.
You can test this hypothesis by adding a unique query parameter to every request as that will disable any attempts at caching since they would all be different URLs.
Or, if these requests are sent using the fetch() api in the browser, you can directly tell that API to not use caching with the cache: 'no-cache' option as in:
const response = await fetch(url, {cache: 'no-cache'});
So, basically, I have this code :
let socket = new WebSocket('ws://localhost:8080/server.php');
socket.onopen = () => {
console.log('connection established !');
}
I use this code to establish a connection for a Real-Time Quiz. But after going to the Sources page of my inspector, I can see the whole javascript code in my browser, including ws://localhost:8080/server.php. Is it dangerous to show it (unintentionally) ? If someones creates a script and puts in it the same url (not localhost, it's just an example), can he receive/send data to the server ?
yes,it is dangerous. u can:
verify the client http request header for example 'Origin'. make sure the client website is the right client website.
use a TSL websocket service, visit to the server over SSL. So the protocol is changing to: wss://
give the client a request token, put this token in header or in post data, the server verify this token.
check the request times of a client in limited time. make sure a specific client won't request too frequently
I am making a chat program.
I am using an Nginx server and NodeJS.
I have setup a websocket via ssl and that works fine.
I have decided to use cookies for authentication.
There are two functions which are crucial:
mconnection.prototype.make_server_https=function(){
console.log('Make server https');
var cthis=this;
var server_https=modules.https.createServer({
key: this.ssl_key,
cert:this.ssl_cert,
ca:this.ssl_ca
},(request,response)=>{
console.log('### CreateServer ###');
console.log('CreateServer, Request:');
console.log(request);
console.log('CreateServer, Response:');
console.log(response);
console.log('######');
and
mconnection.prototype.make_server_websocket=function(){
var server_websocket=new modules.ws.Server({server:this.server_https});
var cookie = require("cookie");
var cthis=this;
//whenever a new client connects with the server.
server_websocket.on('connection', function(client_socket, request){
console.log('### On Connection ###');
console.log('OnConnection, Client Socket:');
console.log(client_socket);
console.log('OnConnection, Request:');
console.log(request);
console.log('######');
If I do state the port number in the client url,function make_server_https gets run and inside there i can access the cookie and set it via the response object.
but in the original url,function make_server_websocket gets run, and there i have access to the client_socket on the server. But there it seems i dont have access to the cookies.
I need to client_websocket to start the connection with this given client. And I need to tie it somehow with the cookies login information.
But i never have both at the same time so i dont get how i could connect them to make the login happen.
I am probably misunderstanding something, any help in the right direction would really be appreciated.
you have to serve you index page from node server using GET then when the request reaches backend you will have response object which can then be used to SET-COOKIE if not set from backend.
And after GET request is complete COOKIE will be added in browser, when next request is made for websocket connection COOKIE will be added to the request in REQUEST HEADERS by the browser which will be available in backend through request object.
And if you decide to use it in login system then you can SET-COOKIE on successfull login.
i got it. its an event called on headers, not on connection. and there i can just push onto the headers.
I was trying to implement a NODE JS get method where I could encode in the url parameters and send back responses like in Server Sent Events.
For example, when I used:
curl -D- 'http://localhost:8030/update'
The server would return a message, and then keep the connection opened to return more messages (like Push).
I was using require('connect'), I tried with require('express') but can't get it working.
Here's part of my code:
var http = require('http');
var connect = require('express');
var app = connect();
app.use(bodyParser.urlencoded({ extended: false }))
.use(bodyParser.json()) // JSON
.use(cors(corsOpts))
.get('/update', updateMiddleware);
var server = http.createServer(app);
server.listen("twserver.alunos.dcc.fc.up.pt", 8030);
function updateMiddleware(req, res) {
res.setHeader('Connection', 'keep-alive');
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.writeHead(200);
setTimeout(function() {
res.write("this is an event");
res.flushHeaders();
}, 1000);
setTimeout(function() {
res.write("this is another event");
}, 2000);
// should print without ending
}
EDIT: I found it was working, but only in chrome. In terminal, I only receive it after waiting a long time, and messages come like in chunks.
You can't use a single HTTP request to listen for multiple event data. If you are really stuck with HTTP (i.e. WebSocket or WebRTC is not an option) then the technique you are looking for is called long polling. This basically works this way:
Client sends request to server
Server waits until an event happens (or until a specific but not too long timeout, so the client application does not throw a timeout error for the request)
Server responses with a complete http response, containing the details of the event
Client receives the event details and immediately sends another request to listen to further events.
This method really takes advantage of HTTP Keep-Alive
EDIT:
For me it looks like your code does not follow the protocol of server sent events. Here is a tutorial: Server-Sent Events in nodejs.
Following another tutorial on MDN about Server-Sent Events, the structure of the messages should be the following:
: this is a test stream
data: some text
data: another message
data: with two lines
Note that the data to be sent must be followed by a double new-line \n\n.
In general, http endpoints in Express aren't supposed to do things like that. If you want live event data, the best way is to use a web socket.
That being said, this thread has an example on how to force Express to do this.
socket.io or Webrtc is the best choice
My web application uses javascript to communicate with a server by HTTP requests. However the server software is changed so that instead of using HTTP requests, it uses WebSocket communication.
Rewriting the the entire web application to use open communication (i.e. WebSockets) instead of a request-respond mechanism entirely will be an incredibly tedious job, and I don't really have anything to gain on using WebSockets for the web application.
What I want to be able to achieve is to add a javascript module that communicates by WebSockets, but still in a request-respond manner. I guess this means that I have to "pause" the javascript until the server responds to my WebSocket messages? Because with HTTP requests my web application waited until the request was responded to.
So I guess it boils down to: How can I make javascript wait until the server sends a WebSocket message?
Cheers
EDIT: To specify, my Javascript code uses HTTP requests throughout the source code via a function I named "get(URL)". I want to modify this function so that it sends a WebSocket message, waits for the response and returns the response. Any ideas?
If I understand you correctly from the comments, you do in fact not desire to "pause" your Javascript, but merely to replace async ajax calls with websocket communication.
You would emit a message to the server, with a pre-defined key as it is in the server. You could then have some sort of payload in the message if you desire, and then have a callback function with the data you receive. Following is a sample where I did something similar for a simple chat application I created.
My client code:
socket.emit('join', options, function(res){
_printToChat(res);
});
Server code:
socket.on('join', function(roomname, fn){
socket.join(roomname);
fn('You joined ' + roomname);
});
This sends a messge with the key "join", and the callback is called with the response from server. In this case res in the client would be "You joined " + roomname.
You would likely do something like:
Client code:
socket.emit('getSomeResource', options, function(res){
// do something with response in res variable.
});
Server code:
socket.on('getSomeResource', function(yourOptions, fn){
fn(resourceToReturn);
});