I have just got started with Node.js and I am trying to write a simple http client that just sends a post request to a server.
var req = http.request(
{
host : 'localhost',
port: 3000,
url : '/',
method:'POST'
},function(res){
console.log('res status - ' + res.statusCode);
res.on('data', function(){}); //<--------
}
);
(I have omitted the code that writes to request and calls req.end()). I have observed if the last line is commented out and the client doesn't read the response, the client doesn't terminate. What is the reason behind this ?
This behaviour is because of following Node implementation:
Issuing http.request() creates an object http.ClientRequest with below behaviour
If no 'response' handler(i.e 2nd argument of request method) is added, then the response will be entirely discarded. However, if you add a 'response' event handler, then you must consume the data from the response object, either by calling response.read() whenever there is a 'readable' event, or by adding a 'data' handler, or by calling the .resume() method. Until the data is consumed, the 'end' event will not fire. Also, until the data is read it will consume memory that can eventually lead to a 'process out of memory' error.
Related
Consider the following code:
const http = require('http')
const req = http.request({hostname: 'www.example.com'}, res=>{
console.log('Response received')
res.on('data',data=>{
console.log('Received: '+data)
})
res.on('end',()=>{
console.log('Connection ended')
})
})
req.end()
As expected, it prints:
Response received
Received data
Connection ended
But when I remove the event listener for 'data', like this:
const http = require('http')
const req = http.request({hostname: 'www.example.com'}, res=>{
console.log('Response received')
res.on('end',()=>{
console.log('Connection ended')
})
})
req.end()
For some reason, it only prints:
Response received
Why is this? Does this mean the connection stays open?
Also, why does not setting an event listener even affect the behavior? Thanks in advance for any help.
Streams in node.js (which a HTTP Response is) start in "paused" mode. They wait for someone to read them, either through calls to read or by changing into "flowing" mode in which they continuously emit data events. Attaching an handler to the data event automatically sets them to flowing but since you never did this, the stream just waits, forever.
You can call res.resume() to set flowing mode without attaching an handler to the event. It'll still read and emit data, but there's nothing listening for that event so the data is just lost.
I am writing a bare minimal node.js server to understand how it works. From what I can tell this line:
res.writeHead(200);
does nothing. If I remove it I get the exact same behavior from the browser. Does it have any purpose? Is it OK to remove it?
// https://nodejs.dev/learn/the-nodejs-http-module
const http = require('http');
const server = http.createServer(handler);
server.listen(3000, () => {
console.log('node: http server: listening on port: ' + 3000);
});
function handler(request, response) {
res.writeHead(200);
res.end('hello world\n');
}
Is it some how related to http headers?
The default http status is 200, so you do not have to tell the response object that the status is 200. If you don't set it, the response will automatically be 200. You can remove res.writeHead(200); and similarly, you don't need the express version of that which would be res.status(200).
The other thing that res.writeHeader(200) does is cause the headers to be written out in the response stream at that point. You also don't need to call that yourself because when you do res.send(...), the headers will automatically be sent first (if they haven't already been sent). In fact, the res.headersSent property keeps track of whether the headers have been sent yet. If not, they are sent as soon as you call any method that starts sending the body (like res.send() or res.write(), etc...).
Is it OK to remove it?
Yes, in this context it is OK to remove it.
I am sending a post request this way(the baseURL is correct, the path /api/backend/valuePairs exists on the server).
sendValues(valuepairList:{x:number;fx:number}[]): Observable<boolean> {
const headers = new Headers({'Content-Type': 'application/json'});
const options = new RequestOptions({headers: headers});
console.log("sending to server: ",JSON.stringify(valuepairList)); //this seems ok too
return this.http.post(this.baseUrl + '/api/backend/valuePairs', {'data': JSON.stringify(valuepairList)}, options)
.map(FunctionapproximationService.extractData)
.catch(FunctionapproximationService.handleError);
}
But looking in Chrome at the website in Inspect mode / Networks nothing is sent during the execution of this function.(It is executed, the logging inside the function appears). Does someone has idea about what I am doing wrong? Thank you.
Nothing is sent because observables are lazy by default. You need to subscribe to them in order to invoke the HTTP request.
sendValues(args).subscribe(result => console.log(result))
The function passed to subscribe function will be called when the response arrives. It also accepts other functions for error handling and one that is called when the stream is completed, but I leave that to you to figure out from the documentation.
In RxJS worl, .subscribe() is basically like calling a usual JS function with parans: (). To execute a function, you need to call it. To execute an observable, you need to subscribe to it.
I'm using the npm request library and am running into an issue where the request is never sent if I call express's res.send() after calling request. I realize the request callback won't fire if I close the connection, but I'm not even seeing the request being sent in the first place.
This code is being executed on RunKit (formerly TonicDev), an online code editor that allows code execution via endpoints. I'm not seeing this issue on my local machine, so it seems like it may have to do with RunKit. Anyone have any ideas as to what's going on here or how I might work around this?
You can execute the code yourself by going to:
https://runkit.com/gragland/58056bc6e9d9ed00130c84d5 and clicking the endpoint link at the top.
// Helper to return a RunKit compatible express app (runkit.com/tonic/express-endpoint)
var tonicExpress = require("#runkit/tonic/express-endpoint/1.0.0")
// Provide the exports object to the tonicExpress helper
var app = tonicExpress(module.exports)
var request = require('request')
app.get("/", function(req, res){
var request_number = 9
request({
// To see if request is sent go to: https://requestb.in/1coqbqn1?inspect
url: 'http://requestb.in/1coqbqn1',
method: 'POST',
json: {
request_number: request_number,
message: 'hello'
}
})
// The line below has to be commented out for the above request to be sent
// I don't care about the request callback() firing, I just want the request to be sent
res.send('Done')
})
I'm having trouble to understand why my callback function needs 2 parameters, i.e. one for the request and one for the response?
For instance i bind a callback function to my server:
server.on("request", doThis(req, resp));
In my opinion he needs only one parameter (req for example here) to store the req information (GET, POST, url, etc.). Why does he need a second for response? I write the information in resp. (i.e. the server, so my other scripts) and not the client.
Every time a request is coming in, the callback function is invoked and so the req parameter is set. Am i wrong? But why do I need the response parameter? My server needs it when he is responding but not when I'm reading/saving the request informations?
The Response parameter is what's generally used to send back a response.
A request comes in, you get the request's data in the req (first) param and you then use the res (second) param to send back a response like:
server.on('message', function(req, res){
res.send('hello your ip is: ' + req.client.ip);
})
This is all dependant on your framework but in expressjs this is how it works (more or less).
To answer your question, you don't need it - you can simply not issue it as a parameter (although it will still be accessible) if you don't plan on responding (which is weird and quite uncommon)
server.on('message', function(req){
console.log('someone requested "message"')
})
Generally speaking, you would always send back a response although the end user might never visually see it, it's just to confirm that the action has been completed successfully.
server.on('save', function(req, res){
saveFile(req.file)
res.sendStatus(200)
})
Additionally, you could check if the process completed successfully - if it did you'd send back a success message, otherwise send back an error message.
server.on('save', function(req, res){
saveFile(req.file, function(error){
if(error) res.sendStatus(500)
res.sendStatus(200)
})
})