0

So i made a small api to start up minecraft servers in screen, which works fine. But now, everytime i start or stop the server, the api crashes somehow because the http server wrote after an end, i cant find anything that ended the server.

I cant seem to find where it ends, its very weird.

I am new to http servers, api's and this stuff so it may be just my dumb fault.

//@author WizzerStudios on Github
//CC 2021 
//Allowed:
//:: Editing
//Disallowed
//:: Redistributing
//:: Claiming as yours 
 
//Importing stuff
var http = require('http');
const { exec } = require("child_process");
var url = require('url');
var https = require('https');
const fs = require('fs');
 
//Console log
console.log("Wizzer API booted.")
 
//Create API / HTTP Server
http.createServer(function (req, res) {
  var q = url.parse(req.url, true).query;
 
  //Check if password is correctly given.
  if(q.password == "Password"){
    //Switch to the action.
    switch(q.action){
      //If action is undefined:
      case undefined:
        res.end("Action not given.");
        break;
      //Get Logs is coming soon.
      case "getlogs":
        if(q.sname == undefined) res.end("No server name given.");
        try {
          const data = fs.readFileSync('serverfiles/' + q.sname + '/logs/latest.log', 'utf8');
          res.end(data);
        } catch (err) {
          console.error(err)
          res.end("Error occured! Server does not exist or isn't available.");    
        }
        break;
      //Start the server using the bash screen command.
      case "stop":
       //If no server name is given, end the connection.
       if(q.sname == undefined) res.end("Server name not specified.");
       //If server exists, run the screen command.
       if (fs.existsSync('./servers/' + q.sname + '.json')) {
         //Check if a screen session with the same name is already running.
         exec('screen -S ' + q.sname + ' -Q select . ; echo $?',
         (error, stdout, stderror) => {
         if (error) {
           console.error("Error: ", error);
           return;
         }
         var out = stdout;
         if(out.includes('0')){
          exec('screen -p 0 -S minecraft-server -X eval `stuff "say TEST MESSAGE..."\\015`', (error, stdout, stderror) => {
            res.end("Server stopped!"); //It says it stopped and crashes here !!!
          });
         } else {
           res.end('Server already stopped!');
           return;
         }});
       } else {
         res.end("Server does not exist!");
         return;
       }
       break;
      case "start":
        //If no server name is given, end the connection.
        if(q.sname == undefined) res.end("Server name not specified.");
        //If server exists, run the screen command.
        if (fs.existsSync('./servers/' + q.sname + '.json')) {
          //Check if a screen session with the same name is already running.
          exec('screen -S ' + q.sname + ' -Q select . ; echo $?',
          (error, stdout, stderror) => {
          if (error) {
            console.error("Error: ", error);
            return;
          }
          var out = stdout;
          if(out.includes('1')){
            exec('screen -S ' + q.sname + ' -dm bash /home/mcserver/api/serverfiles/' + q.sname + '/start.sh', (error, stdout, stderror) => {
              res.end("Server started!"); // Here it says it already stopped!
            });
          } else {
            res.end('Server already started!');
            return;
          }});
        } else {
          res.end("Server does not exist!");
          return;
        }
        break;
      //Create server action.
      case "createServer":
        //If no game is given, end connection.
        if(q.game == undefined) res.end("Game not given");
        //Switch to the game chosen.
        switch(q.game){
          //Minecraft
          case "minecraft":
            if(q.sname == undefined) res.end("Server name not given."); // No server name given, end connection.
            if(q.port == undefined) res.end("Port not given."); // No port given, end connection.
            if(q.ram == undefined) res.end("Please give ram in gigabytes."); // No ram given, end connection.
            if(q.software == undefined) res.end("Server software not given."); // Server software not given, end connection.
            if(q.version == undefined) res.end("Server version not given."); // Version not given, end connection.
            //Check if server already exists:
            var path = './servers/' + q.sname + '.json';
            if (fs.existsSync(path)) {
              res.end("Server Exists.");
              return;
            }
            ram = Number(q.ram);
            ram *= 1024;
            let server = { 
              name: q.sname,
              port: q.port, 
              game: 'Minecraft',
              ram: Number(ram),
              software: q.software,
              version: q.version
            };
            let data = JSON.stringify(server, null, 2);
            fs.mkdir('./serverfiles/' + q.sname, (err) => {
                if (err) {
                    throw err;
                    res.end(err)
                }
            });
            fs.writeFile('./serverfiles/' + q.sname + '/eula.txt', 'eula=true', function (err) {
              if (err) res.end(err);
              if(err) console.log(err);
            });
            fs.writeFile('./serverfiles/' + q.sname + '/start.sh', '#!/bin/bash\njava -Xmx' + Number(ram) + 'M ' + '-Xms' + Number(ram) + 'M -jar /home/mcserver/api/serverfiles/' + q.sname + '/server.jar', function (err) {
              if (err) res.end(err);
              if(err) console.log(err);
            });
            exec('chmod +x ./serverfiles/' + q.sname + '/start.sh');
            fs.writeFile('./serverfiles/' + q.sname + '/server.properties', 'port=' + q.port, function (err) {
              if (err) res.end(err);
              if(err) console.log(err);
            });
            try{
                https.get("https://serverjars.com/api/fetchJar/" + q.software + "/" + q.version, function(response) { response.pipe(fs.createWriteStream("serverfiles/" + q.sname + '/server.jar'))});
            } catch(err){
                res.end("Failed!")
                console.log(err)
            }
            fs.writeFile("servers/" + q.sname + ".json", data, (err) => {});
            res.write("Server Software: " + q.software);
            res.write("\nRam: " + Number(ram));
            res.write("\nServer name: " + q.sname);
            res.write("\nGame: Minecraft");
            res.write("\nPort: " + q.port);
            res.end("\nVersion " + q.version);
        }
    }
    //if(req.url == "/?password=Password&?action=create"){
    //  res.write("game not given")
    //} else if(req.url == "/?password=Password&?action=create&game"){
    //  res.write("Authenticated!")
    //}
  //} else {
    //res.write("WRONG PASSWORD!")
  }
  res.end(); //end the response
}).listen(8080); //the server object listens on port 808
events.js:174
      throw er; // Unhandled 'error' event
      ^

Error [ERR_STREAM_WRITE_AFTER_END]: write after end
    at write_ (_http_outgoing.js:572:17)
    at ServerResponse.write (_http_outgoing.js:567:10)
    at exec (/home/mcserver/api/index.js:86:21)
    at ChildProcess.exithandler (child_process.js:285:7)
    at ChildProcess.emit (events.js:198:13)
    at maybeClose (internal/child_process.js:982:16)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:259:5)
Emitted 'error' event at:
    at writeAfterEndNT (_http_outgoing.js:634:7)
    at process._tickCallback (internal/process/next_tick.js:63:19)
``` is the error.

I do know that i'm writing after the end, but where did it end?

Also, changing res.write to res.end doesn't crash my code, but it doesn't give anything in my browser.
  • 1
    Please explain `because the http server wrote after an end`? What do you mean by "wrote after an end"? Also, `code throws error`, it would be useful to see the error, it's probably full of hints (that's why errors are thrown) – Jeremy Thille Oct 21 '21 at 06:36
  • Added the error ! – Samuel Overduib Oct 21 '21 at 06:43
  • Ah, so that's probably because you `res.end()` and _then_ the code keeps going and you stumble upon a `res.write()`. You can't write after `end()`, hence the error. See this post : https://stackoverflow.com/questions/60714212/error-err-stream-write-after-end-write-after-end. You should use `return res.end()` (or `res.end(); return;`) so as to terminate the function and not keep executing more code. – Jeremy Thille Oct 21 '21 at 06:52
  • Yes, i know that it think i am writing after an end. But where did it end? – Samuel Overduib Oct 21 '21 at 06:59

2 Answers2

0

U have to check that in case u already send some response to not send something again. So each it's better to use:

return res.end();

To stop function.

Bohdan Srdi
  • 144
  • 1
  • 6
  • I do know that i'm writing after the end, but where did it end? Also, changing res.write to res.end doesn't crash my code, but it doesn't give anything in my browser. – Samuel Overduib Oct 21 '21 at 08:03
0

Take out the following line:

  res.end(); //end the response <-----------------
}).listen(8080); //the server object listens on port 8080

You are calling "res.end()" after already calling it inside the function

            res.end("\nVersion " + q.version); // In createServer

calls it then it drops out the switch statements and executes the above res.end() again

ferdil
  • 1,259
  • 11
  • 24