// LICENSE_CODE ZON
'use strict'; /*jslint node:true*/
require('./config.js');
const _http_common = require('_http_common');
remove_header_checks();
const http = require('http');
const semver = require('semver');
const node10 = semver.gte(process.version, '10.0.0');
const req_proto = http.IncomingMessage.prototype;
const res_proto = http.OutgoingMessage.prototype;
const crlf_buf = Buffer.from('\r\n');
const debug = _http_common.debug;
const async_id_symbol = process.binding('async_wrap').async_id_symbol;
const onFinish = outmsg=>outmsg.emit('finish');

function writeAfterEndNT(msg, err, cb){
    msg.emit('error', err);
    if (cb) cb(err);
}
let kIsCorked;
function connectionCorkNT(msg, conn){
    if (node10) conn[kIsCorked] = false;
    conn.uncork();
}

function get_symbol(obj, name){
    for (let s of Object.getOwnPropertySymbols(obj))
    {
        if (s.toString()==`Symbol(${name})`)
            return s;
    }
}

function remove_header_checks(){
    if (remove_header_checks.done)
        return;
    const checks = {
        _checkInvalidHeaderChar: ()=>false,
        _checkIsHttpToken: ()=>true
    };
    for (let c in checks)
    {
        const orig = _http_common[c];
        _http_common[c] = checks[c];
        _http_common[c].orig = orig;
    }
    remove_header_checks.done = true;
}

function req_utf8_headers_patch(){
    if (req_utf8_headers_patch.done)
        return;
    req_utf8_headers_patch.done = 1;
    if (+process.env.HTTP_PARSER_JS)
        return;
    let orig = req_proto._addHeaderLine;
    req_proto._addHeaderLine = function(field, _value, dest){
        // node 0.12 accepting data as binary in c http parser
        _value = Buffer.from(_value, 'binary').toString();
        orig.call(this, field, _value, dest);
    };
}
req_utf8_headers_patch();

function write_(msg, chunk, encoding, callback, fromEnd) {
    if (msg.finished)
    {
        let err = new Error('write after end');
        process.nextTick(msg.socket[async_id_symbol],
            writeAfterEndNT, msg, err, callback);
        return true;
    }
    if (!msg._header)
        msg._implicitHeader();
    if (!msg._hasBody)
        return true;
    if (!fromEnd && typeof chunk !== 'string' && !(chunk instanceof Buffer))
        throw new TypeError('First argument must be a string or Buffer');
    // If we get an empty string or buffer, then just do nothing, and
    // signal the user to keep writing.
    if (chunk.length === 0) return true;
    if (node10 && !kIsCorked)
        kIsCorked = get_symbol(msg, 'isCorked');
    if (node10 && !fromEnd && msg.connection && !msg[kIsCorked]){
        msg.connection.cork();
        msg[kIsCorked] = true;
        process.nextTick(connectionCorkNT, msg, msg.connection);
    }
    let len, ret;
    if (msg.chunkedEncoding) {
        if (typeof chunk === 'string')
            len = Buffer.byteLength(chunk, encoding);
        else
            len = chunk.length;
        if (!node10 && msg.connection && !msg.connection.corked)
        {
            msg.connection.cork();
            process.nextTick(connectionCorkNT, msg, msg.connection);
        }
        msg._send(len.toString(16), 'utf8', null);
        msg._send(crlf_buf, null, null);
        msg._send(chunk, encoding, null);
        ret = msg._send(crlf_buf, null, callback);
    } else
        ret = msg._send(chunk, encoding, callback);
    return ret;
}

let redefine_eval = (obj, name, cb, hd)=>{
    let orig = obj[name];
    let f = cb(orig.toString());
    /*jslint evil:true*/
    obj[name] = eval(`(${f})`);
    obj[name].orig = orig;
};

function res_utf8_hdrs_patch(){
    if (res_utf8_hdrs_patch.done)
        return;
    res_utf8_hdrs_patch.done = 1;
    redefine_eval(res_proto, '_send', s=>s.replace(/'latin1'/g, "'utf8'"));
    redefine_eval(res_proto, 'end', s=>s.replace(/'latin1'/g, "'utf8'"));
}
res_utf8_hdrs_patch();

