<commit_before>var mime = require("mime");
var express = require("express");

module.exports = expressStaticGzip;

/**
 * Generates a middleware function to serve static files. It is build on top of the express.static middleware.
 * It extends the express.static middleware with the capability to serve (previously) gziped files. For this
 * it asumes, the gziped files are next to the original files.
 * @param {string} rootFolder: folder to staticly serve files from
 * @param {{enableBrotli:boolean, customCompressions:[{encodingName:string,fileExtension:string}], indexFromEmptyFile:boolean}} options: options to change module behaviour  
 * @returns express middleware function
 */
function expressStaticGzip(rootFolder, options) {
    options = options || {};
    if (typeof (options.indexFromEmptyFile) === "undefined") options.indexFromEmptyFile = true;

    //create a express.static middleware to handle serving files 
    var defaultStatic = express.static(rootFolder, options),
        compressions = [],
        files = {};

    //read compressions from options
    setupCompressions();

    //if at least one compression has been added, lookup files
    if (compressions.length > 0) {
        findAllCompressionFiles(require("fs"), rootFolder);
    }

    return function middleware(req, res, next) {
        changeUrlFromEmptyToIndexHtml(req);

        //get browser's' supported encodings
        var acceptEncoding = req.header("accept-encoding");

        //test if any compression is available 
        var matchedFile = files[req.url];
        if (matchedFile) {
            //as long as there is any compression available for this file, add the Vary Header (used for caching proxies)
            res.setHeader("Vary", "Accept-Encoding");

            //use the first matching compression to serve a compresed file
            var compression = findAvailableCompressionForFile(matchedFile.compressions, acceptEncoding);
            if (compression) {
                convertToCompressedRequest(req, res, compression);
            }
        }

        //allways call the default static file provider
        defaultStatic(req, res, next);
    };

    /**
     * Reads the options into a list of available compressions.
     */
    function setupCompressions() {
        //register all provided compressions
        if (options.customCompressions && options.customCompressions.length > 0) {
            for (var i = 0; i < options.customCompressions.length; i++) {
                var customCompression = options.customCompressions[i];
                registerCompression(customCompression.encodingName, customCompression.fileExtension);
            }
        }

        //enable brotli compression
        if (options.enableBrotli) {
            registerCompression("br", "br");
        }

        //gzip compression is enabled by default
        registerCompression("gzip", "gz");
    }

    /**
     * Changes the url and adds required headers to serve a compressed file.
     * @param {Object} req
     * @param {Object} res
     */
    function convertToCompressedRequest(req, res, compression) {
        var type = mime.lookup(req.url);
        var charset = mime.charsets.lookup(type);

        req.url = req.url + compression.fileExtension;
        res.setHeader("Content-Encoding", compression.encodingName);
        res.setHeader("Content-Type", type + (charset ? "; charset=" + charset : ""));
    }

    /**
     * In case it's enabled in the options and the requested url does not request a specific file, "index.html" will be appended.
     * @param {Object} req
     */
    function changeUrlFromEmptyToIndexHtml(req) {
        if (options.indexFromEmptyFile && req.url.endsWith("/")) {
            req.url += "index.html";
        }
    }

    /**
     * Searches for the first matching compression available from the given compressions.
     * @param {[Compression]} compressionList
     * @param {string} acceptedEncoding
     * @returns<commit_msg>Use req.path to allow for cache busting headers<commit_after>var mime = require("mime");
var express = require("express");

module.exports = expressStaticGzip;

/**
 * Generates a middleware function to serve static files. It is build on top of the express.static middleware.
 * It extends the express.static middleware with the capability to serve (previously) gziped files. For this
 * it asumes, the gziped files are next to the original files.
 * @param {string} rootFolder: folder to staticly serve files from
 * @param {{enableBrotli:boolean, customCompressions:[{encodingName:string,fileExtension:string}], indexFromEmptyFile:boolean}} options: options to change module behaviour  
 * @returns express middleware function
 */
function expressStaticGzip(rootFolder, options) {
    options = options || {};
    if (typeof (options.indexFromEmptyFile) === "undefined") options.indexFromEmptyFile = true;

    //create a express.static middleware to handle serving files 
    var defaultStatic = express.static(rootFolder, options),
        compressions = [],
        files = {};

    //read compressions from options
    setupCompressions();

    //if at least one compression has been added, lookup files
    if (compressions.length > 0) {
        findAllCompressionFiles(require("fs"), rootFolder);
    }

    return function middleware(req, res, next) {
        changeUrlFromEmptyToIndexHtml(req);

        //get browser's' supported encodings
        var acceptEncoding = req.header("accept-encoding");

        //test if any compression is available 
        var matchedFile = files[req.path];
        if (matchedFile) {
            //as long as there is any compression available for this file, add the Vary Header (used for caching proxies)
            res.setHeader("Vary", "Accept-Encoding");

            //use the first matching compression to serve a compresed file
            var compression = findAvailableCompressionForFile(matchedFile.compressions, acceptEncoding);
            if (compression) {
                convertToCompressedRequest(req, res, compression);
            }
        }

        //allways call the default static file provider
        defaultStatic(req, res, next);
    };

    /**
     * Reads the options into a list of available compressions.
     */
    function setupCompressions() {
        //register all provided compressions
        if (options.customCompressions && options.customCompressions.length > 0) {
            for (var i = 0; i < options.customCompressions.length; i++) {
                var customCompression = options.customCompressions[i];
                registerCompression(customCompression.encodingName, customCompression.fileExtension);
            }
        }

        //enable brotli compression
        if (options.enableBrotli) {
            registerCompression("br", "br");
        }

        //gzip compression is enabled by default
        registerCompression("gzip", "gz");
    }

    /**
     * Changes the url and adds required headers to serve a compressed file.
     * @param {Object} req
     * @param {Object} res
     */
    function convertToCompressedRequest(req, res, compression) {
        var type = mime.lookup(req.path);
        var charset = mime.charsets.lookup(type);
        var search = req.url.split('?').splice(1).join('?');

        if (search !== "") {
            search = "?" + search;
        }

        req.url = req.path + compression.fileExtension + search;
        res.setHeader("Content-Encoding", compression.encodingName);
        res.setHeader("Content-Type", type + (charset ? "; charset=" + charset : ""));
    }

    /**
     * In case it's enabled in the options and the requested url does not request a specific file, "index.html" will be appended.
     * @param {Object} req
     */
    function changeUrlFromEmptyToIndexHtml(req) {
        if (options.indexFromEmptyFile && req.url.endsWith("/")) {
            req.url += "index.html";
        }
    }

    /**
     * Searches for the first matching compression available from the given compressions.
     * @param {[Compression]} compressionList
     * @param {string} acceptedEncoding
     * @returns
{'fraction_non_alphanumeric': 0.10046614327772325, 'fraction_numerical': 0.0008586849852796859, 'mean_word_length': 2.7658198614318708, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1125682', 'n_tokens_mistral': 2142, 'n_tokens_neox': 2054, 'n_words': 666}