Node.js "write after end" error
Asked Answered
C

2

9

Starting off with a basic node app, and I can't figure out how to beat this "write after end" error, even with the callbacks suggested on several sites.

index.js:

var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");

var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;

server.start(router.route, handle);

server.js:

var http = require("http");
var url = require("url");

function start(route, handle) {
  function onRequest(request, response) {
    var postData ="";
    var pathname = url.parse(request.url).pathname;
    console.log("Request for " + pathname + " received.");

    route(handle, pathname, response);
   }

  http.createServer(onRequest).listen(8888);
  console.log("Server has started.");
}

exports.start = start;

router.js:

function route(handle, pathname, response) {
  console.log("About to route a request for " + pathname);
  if (typeof handle[pathname] === 'function') {
    handle[pathname](response);
  } 
  else {
    console.log("No request handler found for " + pathname);
    response.writeHead(404, {"Content-Type": "text/plain"});
    response.write("404 Not found");
    response.end();
  }
}

exports.route = route;

and now the culprit, requestHandlers.js:

function start(response) {
  console.log("Request handler 'start' was called.");

  var body = '<html>'+
    '<head>'+
    '<meta http-equiv="Content-Type" content="text/html; '+
    'charset=UTF-8" />'+
    '</head>'+
    '<body>'+
    '<div>Hello Start</div>' +
    '</body>'+
    '</html>';

    response.writeHead(200, {"Content-Type": "text/html"});
    response.write(body);
    response.end();
}

function upload(response) {
    console.log("Request handler 'upload' was called");
    response.writeHead(200, {"Content-Type": "text/plain"}); 
    response.write("Hello Upload");
    response.end();
}

exports.start = start;
exports.upload = upload;

And here's the stack trace:

C02RF8L7FVH5:HelloWorld francop$ node index.js Server has started. Request for / received. About to route a request for / Request handler 'start' was called. About to route a request for / Request handler 'start' was called. events.js:141 throw er; // Unhandled 'error' event ^

Error: write after end at ServerResponse.OutgoingMessage.write (_http_outgoing.js:430:15) at Object.start (/Users/francop/Documents/Apps/HelloWorld/requestHandlers.js:18:14) at route (/Users/francop/Documents/Apps/HelloWorld/router.js:5:21) at IncomingMessage. (/Users/francop/Documents/Apps/HelloWorld/server.js:19:6) at emitNone (events.js:67:13) at IncomingMessage.emit (events.js:166:7) at endReadableNT (_stream_readable.js:921:12) at nextTickCallbackWith2Args (node.js:442:9) at process._tickCallback (node.js:356:17)

The only answers I found anywhere were "It's async so use a callback" like so:

response.write(body, function(err) {
    response.end();
});

...Which throws the exact same error.

Any help with this would be amazing. The few sparse answers I've found online don't help at all.

Thanks!

Campbell answered 1/9, 2016 at 1:32 Comment(0)
A
5

Return your body in response.end() instead of response.write()

function start (response) {
    console.log("Request handler 'start' was called.");
    var body = '<html>'+
    '<head>'+
    '<meta http-equiv="Content-Type" content="text/html; '+
    'charset=UTF-8" />'+
    '</head>'+
    '<body>'+
    '<div>Hello Start</div>' +
    '</body>'+
    '</html>';

    response.writeHead(200, {"Content-Type": "text/html"});
    response.end(body);
}

Edit 1:

The first time response.write() is called, it will send the buffered header information and the first body to the client. The second time response.write() is called, Node.js assumes you're going to be streaming data, and sends that separately. That is, the response is buffered up to the first chunk of body.

So, if you want to send only one chunk, you must use response.end(body), and if you want to send multiple chunks you must end you called with the response.end() after multiple response.write().

Example single chunk:

function (chunk){
    response.end(chunk);
}

Example multiple chunks:

function (chunkArray){
    // loop on chunks 
    for(var i=0; i < chunkArray.length; i++){
        response.write(chunkArray[i]);                
    }

    // end the response with empty params
    response.end();
}
Ameline answered 1/9, 2016 at 1:43 Comment(3)
Thanks David! Why do I need to do this? I see the response.write('whatever'); response.end() used all over the place in the resources I'm learning from and on help sites.Campbell
Please see Edit #1.Ameline
I had the same error: Turns out I was calling res.end() twiceWinkelman
M
1

This is an example from "The Node Beginner"

I have reposted the files below because as of the time of this post there is no separation on your post between "router.js" and "requestHanders.js".

I was encountering the same problem when I ran this in node v8 currently runs with out incident node v9.5.0. I am not presenting this as a solution to the problem just saying it worked for me. Perhaps this was an issue resolved in updated version I can't say for sure.

Also your error stream reads "requestHandlers.js:18:14" you might get better results if you confirm what line 18 is on your code (in the future). Without spaces line 18 is the line before declaration of upload function. When in fact this error was triggered by "response.end();"

index.js

var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");

var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;

server.start(router.route, handle);

router.js

function route(handle, pathname, response) {
  console.log("About to route a request for " + pathname);
  if (typeof handle[pathname] === 'function') {
    handle[pathname](response);
  } 
  else {
    console.log("No request handler found for " + pathname);
    response.writeHead(404, {"Content-Type": "text/plain"});
    response.write("404 Not found");
    response.end();
  }
}

exports.route = route;

server.js

var http = require("http");
var url = require("url");

function start(route, handle) {
  function onRequest(request, response) {
    var postData ="";
    var pathname = url.parse(request.url).pathname;
    console.log("Request for " + pathname + " received.");

    route(handle, pathname, response);
   }

  http.createServer(onRequest).listen(8888);
  console.log("Server has started.");
}

exports.start = start;

requestHandlers.js

function start(response) {
  console.log("Request handler 'start' was called.");

  var body = '<html>'+
    '<head>'+
    '<meta http-equiv="Content-Type" content="text/html; '+
    'charset=UTF-8" />'+
    '</head>'+
    '<body>'+
    '<div>Hello Start</div>' +
    '</body>'+
    '</html>';

    response.writeHead(200, {"Content-Type": "text/html"});
    response.write(body);
    response.end();
}

function upload(response) {
    console.log("Request handler 'upload' was called");
    response.writeHead(200, {"Content-Type": "text/plain"}); 
    response.write("Hello Upload");
    response.end();
}

exports.start = start;
exports.upload = upload;
Matta answered 13/2, 2018 at 9:43 Comment(1)
Thank you for the tips! It is interesting to learn this issue has disappeared on later versions.Campbell

© 2022 - 2024 — McMap. All rights reserved.