<commit_before>include('binary');
include('io');
include('ringo/buffer');
include('./util');
include('./mime');

export('Response',
       'skinResponse',
       'jsonResponse',
       'xmlResponse',
       'staticResponse',
       'redirectResponse',
       'notFoundResponse',
       'errorResponse');

module.shared = true;

function Response() {

    // missing new security belt
    if (!(this instanceof Response)) {
        var response = new Response();
        response.write.apply(response, arguments);
        return response;
    }

    var config = require('ringo/webapp/env').config;
    var status = 200;
    var charset = config && config.charset || 'utf-8';
    var contentType = config && config.contentType || 'text/html';
    var headers = new Headers();
    var buffer = new Buffer();

    this.write = function write() {
        var length = arguments.length;
        for (var i = 0; i < length; i++) {
            buffer.write(String(arguments[i]));
            if (i < length - 1)
                buffer.write(' ');
        }
        return this;
    };

    this.write.apply(this, arguments);

    this.writeln = function writeln() {
        this.write.apply(this, arguments);
        buffer.write('\r\n');
        return this;
    };


    /**
     * Render a skin to the response's buffer
     * @param skin path to skin resource
     * @param context context object
     */
    this.render = function render(skin, context) {
        var render = require('ringo/skin').render;
        buffer.write(render(skin, context));
    };

    /**
     * Print a debug message to the rendered page.
     */
    this.debug =  function debug() {
        var buffer = this.debugBuffer || new Buffer();
        buffer.write("<div class=\"ringo-debug-line\" style=\"background: yellow;");
        buffer.write("color: black; border-top: 1px solid black;\">");
        var length = arguments.length;
        for (var i = 0; i < length; i++) {
            buffer.write(arguments[i]);
            if (i < length - 1) {
                buffer.write(" ");
            }
        }
        buffer.writeln("</div>");
        this.debugBuffer = buffer;
        return null;
    };

    /**
     * Write the debug buffer to the response's main buffer.
     */
    this.flushDebug = function() {
        if (this.debugBuffer != null) {
            this.write(this.debugBuffer);
            this.debugBuffer.reset();
        }
        return null;
    };

    this.redirect = function(location) {
        status = 303;
        headers.set('Location', String(location));
    };

    Object.defineProperty(this, 'charset', {
        get: function() {
            return charset;
        },
        set: function(c) {
            charset = c;
        }
    });

    Object.defineProperty(this, 'contentType', {
        get: function() {
            return contentType;
        },
        set: function(c) {
            contentType = c;
        }
    });

    Object.defineProperty(this, 'status', {
        get: function() {
            return status;
        },
        set: function(s) {
            status = s;
        }
    });

    this.getHeader = function(key) {
        headers.get(String(key));
    };

    this.setHeader = function(key, value) {
        key = String(key);
        if (key.toLowerCase() == "content-type") {
            contentType = String(value);
            charset = getMimeParameter(contentType, "charset") || charset;
        }
        headers.set(String(key), String(value));
    };

    this.close = function() {
        this.flushDebug();<commit_msg>Add setCookie() and addHeader() methods to response object, and improve jsdoc comments.<commit_after>include('binary');
include('io');
include('ringo/buffer');
include('./util');
include('./mime');

export('Response',
       'skinResponse',
       'jsonResponse',
       'xmlResponse',
       'staticResponse',
       'redirectResponse',
       'notFoundResponse',
       'errorResponse');

module.shared = true;

function Response() {

    // missing new security belt
    if (!(this instanceof Response)) {
        var response = new Response();
        response.write.apply(response, arguments);
        return response;
    }

    var config = require('ringo/webapp/env').config;
    var status = 200;
    var charset = config && config.charset || 'utf-8';
    var contentType = config && config.contentType || 'text/html';
    var headers = new Headers();
    var buffer = new Buffer();

    this.write = function write() {
        var length = arguments.length;
        for (var i = 0; i < length; i++) {
            buffer.write(String(arguments[i]));
            if (i < length - 1)
                buffer.write(' ');
        }
        return this;
    };

    this.write.apply(this, arguments);

    this.writeln = function writeln() {
        this.write.apply(this, arguments);
        buffer.write('\r\n');
        return this;
    };


    /**
     * Render a skin to the response's buffer
     * @param skin path to skin resource
     * @param context context object
     */
    this.render = function render(skin, context) {
        var render = require('ringo/skin').render;
        buffer.write(render(skin, context));
    };

    /**
     * Print a debug message to the rendered page.
     */
    this.debug =  function debug() {
        var buffer = this.debugBuffer || new Buffer();
        buffer.write("<div class=\"ringo-debug-line\" style=\"background: yellow;");
        buffer.write("color: black; border-top: 1px solid black;\">");
        var length = arguments.length;
        for (var i = 0; i < length; i++) {
            buffer.write(arguments[i]);
            if (i < length - 1) {
                buffer.write(" ");
            }
        }
        buffer.writeln("</div>");
        this.debugBuffer = buffer;
        return null;
    };

    /**
     * Write the debug buffer to the response's main buffer.
     */
    this.flushDebug = function() {
        if (this.debugBuffer != null) {
            this.write(this.debugBuffer);
            this.debugBuffer.reset();
        }
        return null;
    };

    this.redirect = function(location) {
        status = 303;
        headers.set('Location', String(location));
    };

    Object.defineProperty(this, 'charset', {
        get: function() {
            return charset;
        },
        set: function(c) {
            charset = c;
        }
    });

    Object.defineProperty(this, 'contentType', {
        get: function() {
            return contentType;
        },
        set: function(c) {
            contentType = c;
        }
    });

    Object.defineProperty(this, 'status', {
        get: function() {
            return status;
        },
        set: function(s) {
            status = s;
        }
    });

    this.getHeader = function(key) {
        headers.get(String(key));
    };

    /**
     * Set a header to be sent to the client. If a header with this name was previously
     * set it will be replaced.
     * @param {String} key the header name
     * @param {String} value the header value
     * @returns {Response} this response for chainability
     */
    this.setHeader = function(key, value) {
        key = String(key);
        if (key.toLowerCase() == "content-type") {
            contentType = String(value);
            charset = getMimeParameter(contentType, "charset") || charset;
        }
        headers.set(key, String(value));
        return this;
    };

    /**
     * Add a header to be sent to the client. If a header with this name was previously
     * set it will not be replaced.
     * @param {String} key the header name
     * @param {String} value the header value
     * @returns {Response} this response for chainability
     */
    this.addHeader = function(key, value) {
        headers.add(String(key), String(value));
        return this;
    };

    /**
     * Sets a cookie to be sent to the client.
     * All arguments except for key and value are optional.
     * days specifies the number of days until the cookie expires.
     * To delete a cookie immediately, set the days argument to 0.
     * If the argument is not specified or set to a negative value, the cookie
     * will be discarded is at the end of the browser session.
     * <br /><br />
     * pathString specifies
     * <br /><br />
     * domainString specifies the domain on which to set the cookie (defaults to unset)
     * <br /><br />
     * Example:
     * @example <pre>res.setCookie("username", "michi");
     * res.setCookie("password", "<PASSWORD>", 10, "/mypath", ".mydomain.org");</pre>
     *
     * @param {String} key the cookie name
     * @param {String} value the cookie value
     * @param {Number} days the number of days to live
     * @param {String} path the path on which to set the cookie (defaults to /)
     * @param {String} domain the domain on which to set the cookie (defaults to current domain)
     * @param {Boolean} httpOnly to make the cookie inaccessible to client side scripts
     * @return {Response} this response object for chainability;
     */
    this.setCookie = function(key, value, days, path, domain, httpOnly) {
        // remove newline chars to prevent response splitting attack as value may be user-provided
        if (value) {
            value = value.replace(/[\r\n]/g, "");
        }
        var buffer = new Buffer(key, "=", value);
        if (typeof days == "number" && days > -1) {
            var expires = days == 0 ?
                new Date(0) : new Date(Date.now() + days * 1000 * 60 * 60 * 24);
            var cookieDateFormat = "EEE, dd-MMM-yyyy HH:mm:ss zzz";
            buffer.write("; expires=");
            buffer.write(expires.format(cookieDateFormat, "en", "GMT"));
        }
        if (path) {
            buffer.write("; path=", path);
        }
        if (domain) {
            buffer.write("; domain=", domain.toLowerCase());
        }
        if (httpOnly) {
            buffer.write("; HttpOnly");
        }
        print(buffer);
        this.addHeader("Set-Cookie", buffer.toString());
        return this;
    };

    /**
     * Close and convert this response into a JSGI response object.
     * @returns a JSGI 0.2 response object
     */
    this.close = function() {
        this.flushDebug();
{'fraction_non_alphanumeric': 0.1337864077669903, 'fraction_numerical': 0.004077669902912621, 'mean_word_length': 1.9823393167342211, 'pattern_counts': {'":': 0, '<': 24, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, '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': '3200652', 'n_tokens_mistral': 2918, 'n_tokens_neox': 2815, 'n_words': 830}