DEV Community

Cover image for Fake C++ Compiler with Node.js - Server - Part 2
Gurigraphics
Gurigraphics

Posted on

1

Fake C++ Compiler with Node.js - Server - Part 2

C++

Let's make it find the entrypoint and endpoint automatically.

1) Create data.txt with these symbols at the beginning and at the end

<?? hello world ??>
Enter fullscreen mode Exit fullscreen mode

2) Generate resources.res
3) Generate main.exe.

Server

1) Start

mkdir nodecpp
cd nodecpp
npm init -y
npm i express express-fileupload
npm i cors fs path buffer-indexof
Enter fullscreen mode Exit fullscreen mode

2) path_join.js

const path = require('path')

const path_join = function( file, folder ){
  if( folder ) return path.join(__dirname, `/${folder}/${ file }`)
  return path.join(__dirname, `/${ file }`)
}

module.exports = path_join; 
Enter fullscreen mode Exit fullscreen mode

3) server.js

var cors = require('cors')
const path = require('path')
const express = require('express')
const fileUpload = require('express-fileupload')

const app = express()
const port = 8000

var corsOptions = {
  origin: '*',
  optionsSuccessStatus: 200
}

app.use(fileUpload())
app.use(express.static(__dirname + '/www'));

var router_home = require("./routes/home")
var router_uploads = require("./routes/uploads")
var router_downloads = require("./routes/downloads")

app.use("/",          router_home)
app.use("/uploads",   router_uploads)
app.use("/downloads", router_downloads)

app.listen(port, () => {
  console.log(`Server listening on port ${port}`)
})
Enter fullscreen mode Exit fullscreen mode

Routes

4) Generate routes folder

mkdir routes
Enter fullscreen mode Exit fullscreen mode

5) routes/home.js

const express = require("express")
const router = express.Router()

const controller_home = require("../controllers/home")

router.get("/", controller_home.index);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

6) routes/uploads.js

const express = require("express")
const router = express.Router()

const controller_uploads = require("../controllers/uploads")

router.post("/", controller_uploads.uploadFile, controller_uploads.upload_txt, controller_uploads.upload_exe)

module.exports = router

Enter fullscreen mode Exit fullscreen mode

7) routes/downloads.js

const express = require("express")
const router = express.Router()

const controller_downloads = require("../controllers/downloads")

router.get("/", controller_downloads.getInfo);
router.get("/:filename", controller_downloads.getFile);

module.exports = router;

Enter fullscreen mode Exit fullscreen mode

Controllers

Generate controllers folder

mkdir controllers
Enter fullscreen mode Exit fullscreen mode

8) controllers/home.js

const path_join = require('../path_join')

exports.index = async function (req, res, next) {

  res.sendFile( path_join( "www/index.html" ) )
}

Enter fullscreen mode Exit fullscreen mode

9) controllers/uploads.js

var service_uploads = require('../services/uploads')   
var service_compiler = require('../services/compiler') 

exports.uploadFile = async function ( req, res, next ){

  if( req.files && Object.keys(req.files).length !== 0 ){

    const uploadedFile = req.files.uploadFile
    const filename = uploadedFile.name

    if( !uploadedFile.name.includes(".exe") && !uploadedFile.name.includes(".txt") ){

      return res.status(500).json({ error: 'Only EXE or TXT file are accepted' })
    }

    next()

  }else{

    res.status(400).json({ error: 'No file name' })
  } 
}

exports.upload_txt = async function ( req, res, next ){

    const uploadedFile = req.files.uploadFile
    const filename = uploadedFile.name

    if( uploadedFile.name.includes(".exe") ){

       next()

    }else{

        try{

          service_uploads.uploadFile(uploadedFile, uploadedFile.name, function( err ){

            if( err ){

              console.log(err);
              res.status(404).json({ error: 'No such file or directory' })

            }else{

               res.status(200).json({ ok: 'TXT received!' })        
            }
          })

        }catch( err ){

          console.log( err )
          res.status(500).json({ error: 'Server upload file error' })
        }
    }    
}

exports.upload_exe = async function ( req, res, next ){

    const uploadedFile = req.files.uploadFile
    const filename = uploadedFile.name
    const new_filename = "new_"+filename

    try{

      service_uploads.uploadFile(uploadedFile, uploadedFile.name, function( err ){

        if( err ){

          console.log(err);
          res.status(404).json({ error: 'No such file or directory' })

        }else{

          var result = service_compiler.compile( filename, new_filename )

          if( result == "ok" ){

           res.redirect(`downloads?filename=${new_filename}`)
           //res.redirect(`downloads/${new_filename}`)

          }else{

            res.status(404).json({ error: result })
          }          
        }
      })

    }catch( err ){

      console.log( err )
      res.status(500).json({ error: 'Server upload file error' })
    }
}

Enter fullscreen mode Exit fullscreen mode

10) controllers/downloads.js

var service_downloads = require('../services/downloads')    

exports.getInfo = async function ( req, res, next ){

  if( req.query.filename ){ 

    try{

      var download_info = service_downloads.getInfo( req )

      res.json( download_info )

    }catch( err ){

      console.log( err )
      res.status(500).json({ error: 'Server info file error' })
    }

  }else{

    res.status(400).send("No file name")
  } 
}

exports.getFile = async function ( req, res, next ){

  if( req.params.filename ){ 

    try{

      service_downloads.getFile( req.params.filename, res, function( err ){

        if( err ){

          console.log(err);
          res.status(404).json({ error: 'No such file or directory' })
        }

      })

    }catch( err ){

      console.log( err )
      res.status(500).json({ error: 'Server download file error' })
    }

  }else{

    res.status(400).json({ error: 'No file name' })
  } 
}

Enter fullscreen mode Exit fullscreen mode

Services

Generate services folder

mkdir services 
Enter fullscreen mode Exit fullscreen mode

11) services/uploads.js

const path_join = require('../path_join')

exports.uploadFile = function( uploadedFile, filename, callback ) {

  var uploadPath = path_join(filename, "uploads")

  uploadedFile.mv(uploadPath, async function (err) {

    if( err ){
      console.log(err);
      callback("Error uploading file");
    }else{
      callback(null); // No error
    }
  })
}

Enter fullscreen mode Exit fullscreen mode

12) services/downloads.js

const path_join = require('../path_join')

exports.getInfo = function( req ){

  var folder = "downloads";
  var host = req.get('host')
  var protocol = req.protocol
  var filename = req.query.filename

  var download_link = `${protocol}://${host}/${folder}/${filename}`;

  return {
    filename: filename,
    download_link: download_link
  }
}

exports.getFile = function( filename, res, callback ) {

  var file_path = path_join( filename, "uploads" )

  res.download( file_path, function( err ) {

    if( err ){
      console.log(err);
      callback("Error download file");
    }else{
      callback(null); // No error
    } 
  })
}

Enter fullscreen mode Exit fullscreen mode

13) services/compiler.js

var path_join = require('../path_join')

var slicer = require('./compiler/slicer')
const getIndex = require('./compiler/getIndex');
var merge3binary = require('./compiler/merge3binary')
var replaceBinaryText = require('./compiler/replaceBinaryText')

exports.compile = function( filename, new_filename ){

  var folder = "uploads"

  var input_path = path_join( filename, folder )
  var output_path = path_join( new_filename, folder )

  var header_path = path_join( "main_header.exe", folder )
  var middle_path = path_join( "main_middle.exe", folder )  
  var footer_path = path_join( "main_footer.exe", folder )

  var data_path = path_join( "data.txt", folder )
  var new_path = path_join( "main_middle_updated.exe", folder )

  var indexes = getIndex( input_path )

  if( indexes.error ){
    return indexes.error
  }

  var adress_start = indexes[0]
  var adress_end = indexes[1]

  // -------- Generate 3 slices
  slicer( input_path, header_path, "0x00000", adress_start)
  slicer( input_path, middle_path, adress_start, adress_end)
  slicer( input_path, footer_path, adress_end, "end")


  // -------- Update resources region
  replaceBinaryText( data_path, middle_path, new_path )


  // Merge 3 binary
  merge3binary(header_path, new_path, footer_path, output_path)

  return "ok"
}

Enter fullscreen mode Exit fullscreen mode

Compiler

Generate compiler folder inside services

mkdir compiler
Enter fullscreen mode Exit fullscreen mode

14) compiler/getIndex.js

const fs = require('fs');
const BufferIndexOf = require('buffer-indexof');

const getIndex = function( exe_path ){

  const input_data = fs.readFileSync( exe_path )

  const buffer = Buffer.from( input_data )
  const startString = Buffer.from('<??', 'utf-8')
  const endString = Buffer.from('??>', 'utf-8')

  const start = BufferIndexOf(buffer, startString)
  const endSize = BufferIndexOf(buffer, endString, start + startString.length)
  const end = endSize + endString.length

  const entryPoint = start.toString(16)
  const endPoint = end.toString(16)

  if (start === -1 || end === -1) {
    return { error: "<?? and ??> not found" }
    console.log("<?? and ??> not found")
  } else {
    console.log("Start:"+entryPoint)
    console.log("End:"+endPoint)
    return [entryPoint, endPoint]
  }
} 

module.exports = getIndex; 
Enter fullscreen mode Exit fullscreen mode

15) compiler/slicer.js

const fs = require('fs')

const slicer = function( exe_path, result_path, entry_start, entry_end ){

  var file_data;

  // Set positions
  const file_start = parseInt( entry_start, 16 );
  const file_end = parseInt( entry_end, 16 );

  // Get exe
  const input_data = fs.readFileSync( exe_path );

  // Slice
  if( entry_end == "end" ){
    file_data = input_data.slice( file_start )
  }else{
    file_data = input_data.slice( file_start, file_end )
  }  

  // Write
  fs.writeFileSync( result_path, file_data );

  console.log('Binary file sucessfull created!');
} 

module.exports = slicer; 

Enter fullscreen mode Exit fullscreen mode

16) compiler/replaceBinaryText.js

const fs = require('fs')

const replaceBinaryText = function( data_path, input_path, output_path ){

  // Get bin
  const exe = fs.readFileSync( input_path );
  const min_size = exe.length;

  // Get text
  const input_data = fs.readFileSync( data_path );
  const data_size = input_data.length;

  if( data_size > min_size ){

    console.log(`File Error. Maximum size is ${min_size}`);

  }else{

    // Fill
    const padding_buffer = Buffer.alloc(min_size - data_size, 0x00);

    // Concat
    const output_buffer = Buffer.concat([input_data, padding_buffer]);

    // Write
    fs.writeFileSync(output_path, output_buffer, 'binary');

    console.log('Binary file sucessfull created!');
  }
}

module.exports = replaceBinaryText; 

Enter fullscreen mode Exit fullscreen mode

17) compiler/merge3binary.js

const fs = require('fs')

const merge3binary = function( header, middle, footer, output_path ){

  const header_data = fs.readFileSync( header );
  const middle_data = fs.readFileSync( middle );
  const footer_data = fs.readFileSync( footer );

  const merged_buffer = Buffer.concat([header_data, middle_data, footer_data]);

  fs.writeFileSync(output_path, merged_buffer, 'binary');

  console.log('Binary file sucessfull created!');
}

module.exports = merge3binary; 

Enter fullscreen mode Exit fullscreen mode

Compiler

Generate folders: uploads and www

mkdir uploads 
mkdir www
Enter fullscreen mode Exit fullscreen mode

18) www/index.html

<!DOCTYPE html>
<html lang='en'>
  <head>
    <meta name='viewport' content='width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no'>
    <meta charset='utf-8'>
    <title>Fake Compiler C++</title>
    <link rel='stylesheet' src='style.css'>
  </head>
  <body>
    <div id='root'>

<h1>Fake Compiler C++</h1>

<form id="txt" method="post" enctype="multipart/form-data" action="/uploads">
   <input type="hidden" name="msgtype" value="2"/>
   <input type="file" name="uploadFile" accept=".txt"/>           
   <button type="submit" value="Upload" >Browse File</button>
</form>

   </div>
      <script type='javascript' src='main.js'></script>
  </body>
</html>

Enter fullscreen mode Exit fullscreen mode

19) Run

node server.js
Enter fullscreen mode Exit fullscreen mode
# Download fileInfo

http://localhost:8000/downloads?filename=new_main.exe

# Download file

http://localhost:8000/downloads/new_main.exe

# Post file

var post_txt = async() => {

  const formData = new FormData(txt);

  try {
    const response = await fetch('/uploads', {
      method: 'POST',
      body: formData
    });

    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}
Enter fullscreen mode Exit fullscreen mode

20) Demo

Fake C++ Compiler with Node.js - Frontend - Part 3

https://dev.to/gurigraphics/fake-c-compiler-with-nodejs-frontend-part-3-1m18

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (0)

nextjs tutorial video

Youtube Tutorial Series 📺

So you built a Next.js app, but you need a clear view of the entire operation flow to be able to identify performance bottlenecks before you launch. But how do you get started? Get the essentials on tracing for Next.js from @nikolovlazar in this video series 👀

Watch the Youtube series