Source: class/OrigamiServer.js

const http = require('http')
const { removeTrailingSlash } = require('../shared/removeTrailingSlash')
const { RequestContext } = require('./RequestContext')
const { RequestHandler } = require('./RequestHandler')
const { RequestResponse } = require('./RequestResponse')

/**
 * An Origami server.
 * 
 * @constructor
 * @param {number} port - The port of this server
 * @param {Origami} parent - The app which instantiated this server
 */
class OrigamiServer {
    constructor(port, parent) {
        this.port = port
        this.Parent = parent

        this.handler = new RequestHandler()
    }

    /**
     * Create an HTTP server and listen.
     */
    listen = function() {
        this.Server = http.createServer(async (req, res) => {
            let parsedUrl = removeTrailingSlash(req.url)
            let { route, includes, query, middles } = this.Parent.routes.getRoute(parsedUrl, req.method)

            let body = []
            //console.log('Returned route', route)
            //console.log('Returned includes', includes)

            if(!route || !route.route) {
                res.writeHead(404, { 'Content-Type': 'text/plain' });
                res.write('404 Not Found')
                res.end();

                return
            }

            req.on('data', (chunk) => {

                body.push(chunk)

            }).on('end', async () => {

                body = Buffer.concat(body)

                try {
                    //Setup contexts
                    let ctx =  new RequestContext(this,
                        {
                            includes: includes,
                            body: body,
                            headers: req.headers,
                            method: req.method,
                            queryString: query,
                            _requestBase: req,
                        }
                    )
                    let out = new RequestResponse(this, res)

                    // Execute before middlewares
                    for(let fn of middles.before) {
                        let new_ctx, new_res = await fn(ctx, out)

                        if(new_ctx instanceof RequestContext) ctx = new_ctx
                        if(new_res instanceof RequestResponse) out = new_res
                    }

                    // Execute the route resolver
                    let resolved = await route.route.resolver(ctx, out)

                    let { head, status, write } = this.handler.proc(resolved)

                    if(res.writableEnded) return; // We shouldn't try to write headers, body etc. 
                                                  // if the response has already been ended by other means 
                                                  // e.g. with the StreamedResponse API

                    // Write the things to be written and end the response.
                    res.writeHead(status, head)
                    res.write(write)
                    res.end();

                    // Execute after middlewares
                    for(let fn of middles.after) {
                        await fn(ctx)
                    }
                } catch(e) {
                    if(e.constructor.name == 'RequestError') {
                        res.writeHead(e.status, {})
                        res.write(JSON.stringify({'code': e.code, 'message': e.message}))
                        res.end();

                        return
                    }

                    console.error("\x1b[31m", `origami: An error occurred while resolving ${route.route.path}\n`, "\x1b[37m", e)

                    res.writeHead(500, {})
                    res.write('Internal Server Error')
                    res.end();
                }

            })

            return
        })

        this.Server.listen(this.port)
    }
}

module.exports.OrigamiServer = OrigamiServer