<commit_before>// create api
require('./api');
var M = process.mono;
var net = require('net');
var startApp = require('./spawner').start;

function pipe (app, socket, buffer) {
    
    // the host of an application is the ip address where the application runs
    var appSocket = net.connect(app.port, app.host);
    appSocket.setKeepAlive(true);
    appSocket.on('error', function (err) {
        
        M.cache.rm(app.host);
        send(socket, 500, M.error.APP_SOCKET_RELOAD_MESSAGE);
    });
    
    // down stream
    appSocket.pipe(socket);
    // up stream
    socket.pipe(appSocket);
    
    appSocket.write(buffer);
    
    socket.resume();
}

// handle socket
function server (socket) {
    
    // set up piping on first data event
    socket.once('data', function (buffer) {
        
        // TODO test proxy for memory leaks
        socket.pause();
        
        // Info: read more from buffer, if there are a lot of 414 errors
        var host = buffer.toString('ascii', 0, 1000);
        
        // is the URL to long?
        if (host.indexOf('\n') > 2048) {
            return send(socket, 414, 'Request-URL Too Long.');
        }
        
        // get host
        if (!(host = host.match(/host\: *([a-z0-9\-_\.]+)(:[0-9]{2,5})?/i))) {
            return send(socket, 400, 'No Host found in headers.');
        }
        host = host[1];
        
        // get application from cache
        var app = M.cache.get(host);
        if (app === null) {
            return send(socket, 503, 'Application is starting...');
        }
        
        // proxy request
        if (app) {
            return pipe(app, socket, buffer);
        }
        
        // mark application as "starting"
        M.cache.save(host, null);
        
        // start app
        startApp(host, function (err, application) {
            
            // handle error
            if (err) {
                
                // remove app from cache
                M.cache.rm(host);
                
                // end socket connection
                if (err === 503) {
                    return send(socket, 503, '503 Application ' + application + ' unavailable');
                }
                
                // set appropriate status code
                var statusCode = 500;
                if (err.code === M.error.APP_NOT_FOUND) {
                    statusCode = 404;
                }

                // send error
                return send(socket, statusCode, err.message);
            }
            
            // save app in cache
            M.cache.save(host, application);
            
            // pipe socket to application
            pipe(application, socket, buffer);
        });
    });
    
    // handle socket errors
    socket.on('error', function (err) {
        console.log(err.message);
    });
}

function send (socket, status, msg) {
    
    socket.resume();
    socket.end(
        'HTTP/1.1 ' + status + '\r\n' +
        'Date: ' + new Date().toString() + '\r\n' +
        'Server: Mono Web Server\r\n' +
        'Content-Length: ' + msg.length + '\r\n' +
        'Connection: close\r\n' +
        'Content-Type: text/plain; charset=utf-8\r\n' +
        '\r\n' + msg
    );
}

function killApps (err) {
    
    var apps = M.cache.getAll();
    for (var host in apps) {
        
        if (apps[host] && apps[host].pid) {
            process.kill(apps[host].pid);
        }
    }
    
    if (err) {
        console.error(err.message);
        console.error(err.stack);
        return process.exit(1);
    }
    
    process.exit();
}

// on user term/int or exception, kill all application processes and exit
process.on('SIGTERM', killApps);
process.on('SIGINT', killApps);
process.on('uncaughtException', killApps);

// handle api errors
M.on('error', function (err) {
    throw new Error(err);
});

// start proxy server
M.on('ready', function () {
    net.createServer(server).listen(M.config.port, M.config.host);
});
<commit_msg>TODO test proxys memory usage<commit_after>/*

TODO test proxy for memory leaks

*/
require('./api');
var M = process.mono;
var net = require('net');
var startApp = require('./spawner').start;

function pipe (app, socket, buffer) {
    
    // the host of an application is the ip address where the application runs
    var appSocket = net.connect(app.port, app.host);
    appSocket.setKeepAlive(true);
    appSocket.on('error', function (err) {
        
        M.cache.rm(app.host);
        send(socket, 500, M.error.APP_SOCKET_RELOAD_MESSAGE);
    });
    
    // down stream
    appSocket.pipe(socket);
    // up stream
    socket.pipe(appSocket);
    
    appSocket.write(buffer);
    
    socket.resume();
}

// handle socket
function server (socket) {
    
    // set up piping on first data event
    socket.once('data', function (buffer) {
        
        socket.pause();
        
        // Info: read more from buffer, if there are a lot of 414 errors
        var host = buffer.toString('ascii', 0, 1000);
        
        // is the URL to long?
        if (host.indexOf('\n') > 2048) {
            return send(socket, 414, 'Request-URL Too Long.');
        }
        
        // get host
        if (!(host = host.match(/host\: *([a-z0-9\-_\.]+)(:[0-9]{2,5})?/i))) {
            return send(socket, 400, 'No Host found in headers.');
        }
        host = host[1];
        
        // get application from cache
        var app = M.cache.get(host);
        if (app === null) {
            return send(socket, 503, 'Application is starting...');
        }
        
        // proxy request
        if (app) {
            return pipe(app, socket, buffer);
        }
        
        // mark application as "starting"
        M.cache.save(host, null);
        
        // start app
        startApp(host, function (err, application) {
            
            // handle error
            if (err) {
                
                // remove app from cache
                M.cache.rm(host);
                
                // end socket connection
                if (err === 503) {
                    return send(socket, 503, '503 Application ' + application + ' unavailable');
                }
                
                // set appropriate status code
                var statusCode = 500;
                if (err.code === M.error.APP_NOT_FOUND) {
                    statusCode = 404;
                }

                // send error
                return send(socket, statusCode, err.message);
            }
            
            // save app in cache
            M.cache.save(host, application);
            
            // pipe socket to application
            pipe(application, socket, buffer);
        });
    });
    
    // handle socket errors
    socket.on('error', function (err) {
        console.log(err.message);
    });
}

function send (socket, status, msg) {
    
    socket.resume();
    socket.end(
        'HTTP/1.1 ' + status + '\r\n' +
        'Date: ' + new Date().toString() + '\r\n' +
        'Server: Mono Web Server\r\n' +
        'Content-Length: ' + msg.length + '\r\n' +
        'Connection: close\r\n' +
        'Content-Type: text/plain; charset=utf-8\r\n' +
        '\r\n' + msg
    );
}

function killApps (err) {
    
    var apps = M.cache.getAll();
    for (var host in apps) {
        
        if (apps[host] && apps[host].pid) {
            process.kill(apps[host].pid);
        }
    }
    
    if (err) {
        console.error(err.message);
        console.error(err.stack);
        return process.exit(1);
    }
    
    process.exit();
}

// on user term/int or exception, kill all application processes and exit
process.on('SIGTERM', killApps);
process.on('SIGINT', killApps);
process.on('uncaughtException', killApps);

// handle api errors
M.on('error', function (err) {
    throw new Error(err);
});

// start proxy server
M.on('ready', function () {
    net.createServer(server).listen(M.config.port, M.config.host);
});

{'fraction_non_alphanumeric': 0.1383364602876798, 'fraction_numerical': 0.012507817385866166, 'mean_word_length': 1.5505582137161085, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, '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': '2534464', 'n_tokens_mistral': 2429, 'n_tokens_neox': 2323, 'n_words': 602}