赋予html页面跨域请求能力的chrome扩展

chrome浏览器插件的background.js环境权限很高,可以跨域访问接口。自己封装一个Chrome插件,安装在扩展程序中,解决跨越问题。

使用方法

在自己的项目中id为cross-request-sign的标签。在页面加载之后可以访问window.crossRequest()就能跨越访问

  1. GET
crossRequest({    
    url: 'http://caibaojian.com/ajax-jsonp.html',    
    method: 'GET',    
    success: function(res, header){
    }})
  1. POST
    crossRequest({
        url: 'http://127.0.0.1:3000/api',
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        data: {
            a: 1,
            b: 2,
            c: {
                t: 1
            }
        },
        success: function (res) {
            console.log(arguments)
        }
    })
  1. FILE upload
    crossRequest({
        url: 'http://127.0.0.1:8081/upload',
        method: 'POST',
        data: {
            name: 'hello',
            id: '19'
        },
        files: {
            file: 'fileId' //File Upload-Input dom id
        },
        success: function (res) {
            alert(res)
        }
    })

文件结构

  1. manifest.json
{
    "manifest_version": 2,
    "name": "easy swagger 跨域请求",
    "description": "easy swagger 跨域请求",
    "version": "1.0",
    "browser_action": {
        "default_icon": "icon.png",
        "default_popup": "popup.html"
    },
    "icons": {
        "16": "icon.png",
        "48": "icon.png",
        "128": "icon.png"
    },
    "permissions": [ "webRequest", "webRequestBlocking" ],
    "background": 
    {
      "scripts" : [
          "background.js"
          ]
    },
    "web_accessible_resources":[
        "index.js"        
    ],
     "content_scripts": [{
          "matches": ["http://*/*", "https://*/*"],
        "js": [
            "response.js"
        ],
        "all_frames": true
    }]
}
  1. response.js
/*==============common begin=================*/
var container = 'y-request';
var INITSTATUS = 0;
var RUNSTATUS = 1;
var ENDSTATUS = 2;

var base64 = _base64();
function encode(data) {
    return base64.encode(encodeURIComponent(JSON.stringify(data)));
}

function decode(data) {
    return JSON.parse(decodeURIComponent(base64.decode(data)));
}

function formUrlencode(data) {
    if(!data || typeof data !== 'object') return ''
    return Object.keys(data).map(function (key) {
        return encodeURIComponent(key) + '=' + encodeURIComponent(data[key]);
    }).join('&')
}

function _base64() {

    /*--------------------------------------------------------------------------*/

    var InvalidCharacterError = function (message) {
        this.message = message;
    };
    InvalidCharacterError.prototype = new Error;
    InvalidCharacterError.prototype.name = 'InvalidCharacterError';

    var error = function (message) {
        // Note: the error messages used throughout this file match those used by
        // the native `atob`/`btoa` implementation in Chromium.
        throw new InvalidCharacterError(message);
    };

    var TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    // http://whatwg.org/html/common-microsyntaxes.html#space-character
    var REGEX_SPACE_CHARACTERS = /<%= spaceCharacters %>/g;

    // `decode` is designed to be fully compatible with `atob` as described in the
    // HTML Standard. http://whatwg.org/html/webappapis.html#dom-windowbase64-atob
    // The optimized base64-decoding algorithm used is based on @atk’s excellent
    // implementation. https://gist.github.com/atk/1020396
    var decode = function (input) {
        input = String(input)
            .replace(REGEX_SPACE_CHARACTERS, '');
        var length = input.length;
        if (length % 4 == 0) {
            input = input.replace(/==?$/, '');
            length = input.length;
        }
        if (
            length % 4 == 1 ||
            // http://whatwg.org/C#alphanumeric-ascii-characters
            /[^+a-zA-Z0-9/]/.test(input)
        ) {
            error(
                'Invalid character: the string to be decoded is not correctly encoded.'
            );
        }
        var bitCounter = 0;
        var bitStorage;
        var buffer;
        var output = '';
        var position = -1;
        while (++position < length) {
            buffer = TABLE.indexOf(input.charAt(position));
            bitStorage = bitCounter % 4 ? bitStorage * 64 + buffer : buffer;
            // Unless this is the first of a group of 4 characters…
            if (bitCounter++ % 4) {
                // …convert the first 8 bits to a single ASCII character.
                output += String.fromCharCode(
                    0xFF & bitStorage >> (-2 * bitCounter & 6)
                );
            }
        }
        return output;
    };

    // `encode` is designed to be fully compatible with `btoa` as described in the
    // HTML Standard: http://whatwg.org/html/webappapis.html#dom-windowbase64-btoa
    var encode = function (input) {
        input = String(input);
        if (/[^\0-\xFF]/.test(input)) {
            // Note: no need to special-case astral symbols here, as surrogates are
            // matched, and the input is supposed to only contain ASCII anyway.
            error(
                'The string to be encoded contains characters outside of the ' +
                'Latin1 range.'
            );
        }
        var padding = input.length % 3;
        var output = '';
        var position = -1;
        var a;
        var b;
        var c;
        var d;
        var buffer;
        // Make sure any padding is handled outside of the loop.
        var length = input.length - padding;

        while (++position < length) {
            // Read three bytes, i.e. 24 bits.
            a = input.charCodeAt(position) << 16;
            b = input.charCodeAt(++position) << 8;
            c = input.charCodeAt(++position);
            buffer = a + b + c;
            // Turn the 24 bits into four chunks of 6 bits each, and append the
            // matching character for each of them to the output.
            output += (
                TABLE.charAt(buffer >> 18 & 0x3F) +
                TABLE.charAt(buffer >> 12 & 0x3F) +
                TABLE.charAt(buffer >> 6 & 0x3F) +
                TABLE.charAt(buffer & 0x3F)
            );
        }

        if (padding == 2) {
            a = input.charCodeAt(position) << 8;
            b = input.charCodeAt(++position);
            buffer = a + b;
            output += (
                TABLE.charAt(buffer >> 10) +
                TABLE.charAt((buffer >> 4) & 0x3F) +
                TABLE.charAt((buffer << 2) & 0x3F) +
                '='
            );
        } else if (padding == 1) {
            buffer = input.charCodeAt(position);
            output += (
                TABLE.charAt(buffer >> 2) +
                TABLE.charAt((buffer << 4) & 0x3F) +
                '=='
            );
        }

        return output;
    };

    return {
        'encode': encode,
        'decode': decode,
        'version': '<%= version %>'
    };
};

var unsafeHeader = [ 'Accept-Charset',
'Accept-Encoding',
'Access-Control-Request-Headers',
'Access-Control-Request-Method',
'Connection',
'Content-Length',
'Cookie',
'Cookie2',
'Content-Transfer-Encoding',
'Date',
'Expect',
'Host',
'Keep-Alive',
'Origin',
'Referer',
'TE',
'Trailer',
'Transfer-Encoding',
'Upgrade',
'User-Agent',
'Via' ];
/*==============common end=================*/
var connect = chrome.runtime.connect({ name: "request" });

function injectJs(path) {
    var s = document.createElement('script');
    // TODO: add "script.js" to web_accessible_resources in manifest.json
    s.src = chrome.extension.getURL(path);
    s.onload = function () {
        this.remove();
    };
    (document.head || document.documentElement).appendChild(s);
}

injectJs('index.js');

var yRequestDom, successFns = {}, errorFns = {};

function handleHeader(headers) {
    if (!headers) return;
    if (typeof headers === 'object') {
        return headers;
    }
    var newHeaders = {}, headers = headers.split(/[\r\n]/).forEach(function (header) {
        var index = header.indexOf(":");
        var name = header.substr(0, index);
        var value = header.substr(index + 2);
        if (name) {
            newHeaders[name] = value;
        }

    })
    return newHeaders;
}

function responseCallback(res, dom, data) {
    var id = dom.getAttribute("_id");
    var headers = handleHeader(res.headers);
    data.runTime = new Date().getTime() - data.runTime;
    data.res = {
        id: id,
        status: res.status,
        statusText: res.statusText,
        header: headers,
        body: res.body
    }
    dom.innerText = encode(data);
    dom.setAttribute('status', ENDSTATUS);
}

function sendAjaxByContent(req, successFn, errorFn) {

    var formDatas;
    var xhr = new XMLHttpRequest();

    req.headers = req.headers || {};

    req.headers['Content-Type'] = req.headers['Content-Type'] || req.headers['Content-type'] || req.headers['content-type'];

    if (req.files && Object.keys(req.files).length > 0) {
        req.headers['Content-Type'] = 'multipart/form-data'
    }

    xhr.timeout = req.timeout || 5000;

    req.method = req.method || 'GET';
    req.async = req.async === false ? false : true;
    req.headers = req.headers || {};

    if (req.method.toLowerCase() !== 'get' && req.method.toLowerCase() !== 'head' && req.method.toLowerCase() !== 'options') {
        if (!req.headers['Content-Type'] || req.headers['Content-Type'] == 'application/x-www-form-urlencoded') {
            req.headers['Content-Type'] = 'application/x-www-form-urlencoded';
            req.data = formUrlencode(req.data);
        } else if (req.headers['Content-Type'] === 'multipart/form-data') {
            delete req.headers['Content-Type'];
            formDatas = new FormData();
            if (req.data) {
                for (var name in req.data) {
                    formDatas.append(name, req.data[name]);
                }
            }
            if (req.files) {
                for (var name in req.files) {
                    var files = document.getElementById(req.files[name]).files;
                    if (files.length > 0) {
                        formDatas.append(name, files[0]);
                    }
                }
            }
            req.data = formDatas;
        } else if (typeof req.data === 'object' && req.data) {
            req.data = JSON.stringify(req.data);
        }
        if (req.file) {
            req.data = document.getElementById(req.file).files[0];
        }
    }else{
      delete req.headers['Content-Type'];
    }
    if (req.query && typeof req.query === 'object') {
        var getUrl = formUrlencode(req.query);
        req.url = req.url + '?' + getUrl;
        req.query = '';
    }
    xhr.open(req.method, req.url, req.async);
    var response = {};
    if (req.headers) {
        var unsafeHeaderArr = [];
        for (var name in req.headers) {
            if(unsafeHeader.indexOf(name) > -1){
                unsafeHeaderArr.push({
                    name: name,
                    value: req.headers[name]
                })
            }else{
                xhr.setRequestHeader(name, req.headers[name]);
            }        
        }
        if(unsafeHeaderArr.length > 0){
            xhr.setRequestHeader('cross-request-unsafe-headers-list', encode(unsafeHeaderArr));
        }
    }

    xhr.setRequestHeader('cross-request-open-sign', '1')

    xhr.onload = function (e) {
        var headers = xhr.getAllResponseHeaders();
        headers = handleHeader(headers);
        var newHeaders;
        if(headers['cross-response-unsafe-headers-list']){
            newHeaders = decode(headers['cross-response-unsafe-headers-list'])
            delete headers['cross-response-unsafe-headers-list'];
            if(newHeaders && typeof newHeaders === 'object' && Object.keys(newHeaders).length > 0){
                headers = newHeaders;
            }
        }
        response = {
            headers: headers,
            status: xhr.status,
            statusText: xhr.statusText,
            body: xhr.responseText
        }
        if (xhr.status == 200) {
            successFn(response);
        } else {
            errorFn(response);
        }
    };
    xhr.ontimeout = function (e) {
        errorFn({
            body: 'Error:Request timeout that the time is ' + xhr.timeout
        })
    };
    xhr.onerror = function (e) {
        errorFn({
            body: xhr.statusText
        })
    };
    xhr.upload.onprogress = function (e) { };

    try {
        xhr.send(req.data);
    } catch (error) {
        errorFn({
            body: error.message
        })
    }


}

function sendAjaxByBack(id, req, successFn, errorFn) {
    successFns[id] = successFn;
    errorFns[id] = errorFn;
    connect.postMessage({
        id: id,
        req: req
    });
}

connect.onMessage.addListener(function (msg) {
    var id = msg.id;
    var res = msg.res;
    res.status === 200 ?
        successFns[id](res) :
        errorFns[id](res);
    delete successFns[id];
    delete errorFns[id];
});

function checkFileRequest(req) {
    if (req.files && typeof req.files === 'object' && Object.keys(req.files).length > 0) {
        return true;
    }
    return false;
}

function run() {
    var reqsDom = yRequestDom.childNodes;
    if (!reqsDom || reqsDom.length === 0) return;
    reqsDom.forEach(function (dom) {
        try {
            var status = dom.getAttribute("status"), request;
            if (+status === INITSTATUS) {
                dom.setAttribute("status", RUNSTATUS);
                var data = decode(dom.innerText);
                var req = data.req;
                req.url = req.url || '';
                var id = dom.getAttribute('_id');
                data.runTime = new Date().getTime();

                sendAjaxByBack(id, req, function (res) {                        
                    responseCallback(res, dom, data);
                }, function (err) {
                    responseCallback(err, dom, data);
                })

                // if (location.protocol.indexOf('https') === 0 && req.url.indexOf('https') !== 0) {
                //     sendAjaxByBack(id, req, function (res) {                        
                //         responseCallback(res, dom, data);
                //     }, function (err) {
                //         responseCallback(err, dom, data);
                //     })
                // } else {
                //     sendAjaxByContent(req, function (res) {
                //         responseCallback(res, dom, data);
                //     }, function (err) {
                //         responseCallback(err, dom, data);
                //     })
                // }


            }
        } catch (error) {
            console.error(error)
            dom.parentNode.removeChild(dom)
        }

    })
}

//因注入 index.js ,需要等到 indexScript 初始化完成后执行
var findDom = setInterval(function () {
    try {
        yRequestDom = document.getElementById(container);
        if (yRequestDom) {
            clearInterval(findDom)
            yRequestDom.setAttribute('key', 'yapi');
            setInterval(function () {
                run()
            }, 100)
        }

    } catch (e) {
        clearInterval(findDom)
        console.error(e)
    }
}, 100)
  1. index.js
(function (win) {

    if(!document.getElementById('cross-request-sign')){
        return;
    }

    /*==============common begin=================*/

    var container = 'y-request';
    var INITSTATUS = 0;
    var RUNSTATUS = 1;
    var ENDSTATUS = 2;

    var base64 = _base64();
    function encode(data) {
        return base64.encode(encodeURIComponent(JSON.stringify(data)));
    }

    function decode(data) {
        return JSON.parse(decodeURIComponent(base64.decode(data)));
    }

    function _base64() {

        /*--------------------------------------------------------------------------*/

        var InvalidCharacterError = function (message) {
            this.message = message;
        };
        InvalidCharacterError.prototype = new Error;
        InvalidCharacterError.prototype.name = 'InvalidCharacterError';

        var error = function (message) {
            // Note: the error messages used throughout this file match those used by
            // the native `atob`/`btoa` implementation in Chromium.
            throw new InvalidCharacterError(message);
        };

        var TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
        // http://whatwg.org/html/common-microsyntaxes.html#space-character
        var REGEX_SPACE_CHARACTERS = /<%= spaceCharacters %>/g;

        // `decode` is designed to be fully compatible with `atob` as described in the
        // HTML Standard. http://whatwg.org/html/webappapis.html#dom-windowbase64-atob
        // The optimized base64-decoding algorithm used is based on @atk’s excellent
        // implementation. https://gist.github.com/atk/1020396
        var decode = function (input) {
            input = String(input)
                .replace(REGEX_SPACE_CHARACTERS, '');
            var length = input.length;
            if (length % 4 == 0) {
                input = input.replace(/==?$/, '');
                length = input.length;
            }
            if (
                length % 4 == 1 ||
                // http://whatwg.org/C#alphanumeric-ascii-characters
                /[^+a-zA-Z0-9/]/.test(input)
            ) {
                error(
                    'Invalid character: the string to be decoded is not correctly encoded.'
                );
            }
            var bitCounter = 0;
            var bitStorage;
            var buffer;
            var output = '';
            var position = -1;
            while (++position < length) {
                buffer = TABLE.indexOf(input.charAt(position));
                bitStorage = bitCounter % 4 ? bitStorage * 64 + buffer : buffer;
                // Unless this is the first of a group of 4 characters…
                if (bitCounter++ % 4) {
                    // …convert the first 8 bits to a single ASCII character.
                    output += String.fromCharCode(
                        0xFF & bitStorage >> (-2 * bitCounter & 6)
                    );
                }
            }
            return output;
        };

        // `encode` is designed to be fully compatible with `btoa` as described in the
        // HTML Standard: http://whatwg.org/html/webappapis.html#dom-windowbase64-btoa
        var encode = function (input) {
            input = String(input);
            if (/[^\0-\xFF]/.test(input)) {
                // Note: no need to special-case astral symbols here, as surrogates are
                // matched, and the input is supposed to only contain ASCII anyway.
                error(
                    'The string to be encoded contains characters outside of the ' +
                    'Latin1 range.'
                );
            }
            var padding = input.length % 3;
            var output = '';
            var position = -1;
            var a;
            var b;
            var c;
            var d;
            var buffer;
            // Make sure any padding is handled outside of the loop.
            var length = input.length - padding;

            while (++position < length) {
                // Read three bytes, i.e. 24 bits.
                a = input.charCodeAt(position) << 16;
                b = input.charCodeAt(++position) << 8;
                c = input.charCodeAt(++position);
                buffer = a + b + c;
                // Turn the 24 bits into four chunks of 6 bits each, and append the
                // matching character for each of them to the output.
                output += (
                    TABLE.charAt(buffer >> 18 & 0x3F) +
                    TABLE.charAt(buffer >> 12 & 0x3F) +
                    TABLE.charAt(buffer >> 6 & 0x3F) +
                    TABLE.charAt(buffer & 0x3F)
                );
            }

            if (padding == 2) {
                a = input.charCodeAt(position) << 8;
                b = input.charCodeAt(++position);
                buffer = a + b;
                output += (
                    TABLE.charAt(buffer >> 10) +
                    TABLE.charAt((buffer >> 4) & 0x3F) +
                    TABLE.charAt((buffer << 2) & 0x3F) +
                    '='
                );
            } else if (padding == 1) {
                buffer = input.charCodeAt(position);
                output += (
                    TABLE.charAt(buffer >> 2) +
                    TABLE.charAt((buffer << 4) & 0x3F) +
                    '=='
                );
            }

            return output;
        };

        return {
            'encode': encode,
            'decode': decode,
            'version': '<%= version %>'
        };
    };

    var unsafeHeader = ['Accept-Charset',
        'Accept-Encoding',
        'Access-Control-Request-Headers',
        'Access-Control-Request-Method',
        'Connection',
        'Content-Length',
        'Cookie',
        'Cookie2',
        'Content-Transfer-Encoding',
        'Date',
        'Expect',
        'Host',
        'Keep-Alive',
        'Origin',
        'Referer',
        'TE',
        'Trailer',
        'Transfer-Encoding',
        'Upgrade',
        'User-Agent',
        'Via'];
    /*==============common end=================*/


    function createNode(tagName, attributes, parentNode) {
        options = attributes || {};
        tagName = tagName || 'div';
        var dom = document.createElement(tagName);
        for (var attr in attributes) {
            if (attr === 'id') dom.id = options[attr];
            else dom.setAttribute(attr, options[attr]);
        }
        if (parentNode) parentNode.appendChild(dom);
        return dom;
    }

    function getid() {
        return container + '-' + id++;
    }


    var yRequestDom = createNode('div', { id: container, style: 'display:none' }, document.getElementsByTagName('body')[0]);
    var yRequestMap = {};
    var id = 0;
    var interval;


    function run(req) {
        if (!req) return;
        if (typeof req === 'string') req = { url: req }

        data = {
            res: null,
            req: req
        }
        data = encode(data);
        var newId = getid();
        var div = createNode('div', {
            _id: newId,
            status: INITSTATUS
        }, yRequestDom);
        div.innerText = data;
        yRequestMap[newId] = {
            id: newId,
            status: INITSTATUS,
            success: function (res, header, data) {
                if (typeof req.success === 'function') {
                    req.success(res, header, data);
                }
            },
            error: function (error, header, data) {
                if (typeof req.error === 'function') {
                    req.error(error, header, data)
                }
            }
        }
        monitor();
    }



    function monitor() {
        if (interval) return;
        interval = setInterval(function () {
            var queueDom = yRequestDom.childNodes;
            if (!queueDom || queueDom.length === 0) {
                interval = clearInterval(interval);
            }

            try {
                for (var i = 0; i < queueDom.length; i++) {
                    try {
                        var dom = queueDom[i];
                        if (+dom.getAttribute('status') === ENDSTATUS) {
                            var text = dom.innerText;
                            if (text) {
                                var data = decode(dom.innerText);
                                var id = dom.getAttribute('_id');
                                var res = data.res;
                                if (res.status === 200) {
                                    yRequestMap[id].success(res.body, res.header, data);
                                } else {
                                    yRequestMap[id].error(res.statusText, res.header, data);
                                }
                                dom.parentNode.removeChild(dom);
                            } else {
                                dom.parentNode.removeChild(dom);
                            }

                        }
                    } catch (err) {
                        console.error(err.message);
                        dom.parentNode.removeChild(dom);
                    }
                }
            } catch (err) {
                console.error(err.message);
                interval = clearInterval(interval);
            }


        }, 50)
    }

    win.crossRequest = run;
    if (typeof define == 'function' && define.amd) {
        define('crossRequest', [], function () {
            return run;
        });
    }

})(window)
  1. background.js
'use strict';

var base64 = _base64();

function formUrlencode(data) {
  if(data && typeof data === 'object'){
    return Object.keys(data).map(function (key) {
      return encodeURIComponent(key) + '=' + encodeURIComponent(data[key]);
    }).join('&')
  }
    return '';
}

function encode(data) {
    return base64.encode(encodeURIComponent(JSON.stringify(data)));
}

function decode(data) {
    return JSON.parse(decodeURIComponent(base64.decode(data)));
}


function _base64() {

    /*--------------------------------------------------------------------------*/

    var InvalidCharacterError = function (message) {
        this.message = message;
    };
    InvalidCharacterError.prototype = new Error;
    InvalidCharacterError.prototype.name = 'InvalidCharacterError';

    var error = function (message) {
        // Note: the error messages used throughout this file match those used by
        // the native `atob`/`btoa` implementation in Chromium.
        throw new InvalidCharacterError(message);
    };

    var TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    // http://whatwg.org/html/common-microsyntaxes.html#space-character
    var REGEX_SPACE_CHARACTERS = /<%= spaceCharacters %>/g;

    // `decode` is designed to be fully compatible with `atob` as described in the
    // HTML Standard. http://whatwg.org/html/webappapis.html#dom-windowbase64-atob
    // The optimized base64-decoding algorithm used is based on @atk’s excellent
    // implementation. https://gist.github.com/atk/1020396
    var decode = function (input) {
        input = String(input)
            .replace(REGEX_SPACE_CHARACTERS, '');
        var length = input.length;
        if (length % 4 == 0) {
            input = input.replace(/==?$/, '');
            length = input.length;
        }
        if (
            length % 4 == 1 ||
            // http://whatwg.org/C#alphanumeric-ascii-characters
            /[^+a-zA-Z0-9/]/.test(input)
        ) {
            error(
                'Invalid character: the string to be decoded is not correctly encoded.'
            );
        }
        var bitCounter = 0;
        var bitStorage;
        var buffer;
        var output = '';
        var position = -1;
        while (++position < length) {
            buffer = TABLE.indexOf(input.charAt(position));
            bitStorage = bitCounter % 4 ? bitStorage * 64 + buffer : buffer;
            // Unless this is the first of a group of 4 characters…
            if (bitCounter++ % 4) {
                // …convert the first 8 bits to a single ASCII character.
                output += String.fromCharCode(
                    0xFF & bitStorage >> (-2 * bitCounter & 6)
                );
            }
        }
        return output;
    };

    // `encode` is designed to be fully compatible with `btoa` as described in the
    // HTML Standard: http://whatwg.org/html/webappapis.html#dom-windowbase64-btoa
    var encode = function (input) {
        input = String(input);
        if (/[^\0-\xFF]/.test(input)) {
            // Note: no need to special-case astral symbols here, as surrogates are
            // matched, and the input is supposed to only contain ASCII anyway.
            error(
                'The string to be encoded contains characters outside of the ' +
                'Latin1 range.'
            );
        }
        var padding = input.length % 3;
        var output = '';
        var position = -1;
        var a;
        var b;
        var c;
        var d;
        var buffer;
        // Make sure any padding is handled outside of the loop.
        var length = input.length - padding;

        while (++position < length) {
            // Read three bytes, i.e. 24 bits.
            a = input.charCodeAt(position) << 16;
            b = input.charCodeAt(++position) << 8;
            c = input.charCodeAt(++position);
            buffer = a + b + c;
            // Turn the 24 bits into four chunks of 6 bits each, and append the
            // matching character for each of them to the output.
            output += (
                TABLE.charAt(buffer >> 18 & 0x3F) +
                TABLE.charAt(buffer >> 12 & 0x3F) +
                TABLE.charAt(buffer >> 6 & 0x3F) +
                TABLE.charAt(buffer & 0x3F)
            );
        }

        if (padding == 2) {
            a = input.charCodeAt(position) << 8;
            b = input.charCodeAt(++position);
            buffer = a + b;
            output += (
                TABLE.charAt(buffer >> 10) +
                TABLE.charAt((buffer >> 4) & 0x3F) +
                TABLE.charAt((buffer << 2) & 0x3F) +
                '='
            );
        } else if (padding == 1) {
            buffer = input.charCodeAt(position);
            output += (
                TABLE.charAt(buffer >> 2) +
                TABLE.charAt((buffer << 4) & 0x3F) +
                '=='
            );
        }

        return output;
    };

    return {
        'encode': encode,
        'decode': decode,
        'version': '<%= version %>'
    };
};






var unsafeHeader = ['Accept-Charset',
    'Accept-Encoding',
    'Access-Control-Request-Headers',
    'Access-Control-Request-Method',
    'Connection',
    'Content-Length',
    'Cookie',
    'Cookie2',
    'Content-Transfer-Encoding',
    'Date',
    'Expect',
    'Host',
    'Keep-Alive',
    'Origin',
    'Referer',
    'TE',
    'Trailer',
    'Transfer-Encoding',
    'Upgrade',
    'User-Agent',
    'Via'];

var requestBatch = {};

function handleHeader(headers) {
    if (!headers) return;
    var newHeaders = {}, headers = headers.split(/[\r\n]/).forEach(function (header) {
        var index = header.indexOf(":");
        var name = header.substr(0, index);
        var value = header.substr(index + 2);
        if (name) {
            newHeaders[name] = value;
        }

    })
    return newHeaders;
}

chrome.runtime.onMessage.addListener(function (request, _, cb) {
    var data;

    if (request.action === 'get') {
        data = localStorage.getItem(request.name);
        if (typeof cb === 'function') {
            cb(data)
        }
    } else if (request.action === 'set') {
        localStorage.setItem(request.name, request.value);
        var newdata = data = localStorage.getItem(request.name);
    }
})

function sendAjax(req, successFn, errorFn) {
    var formDatas;
    var xhr = new XMLHttpRequest();

    req.headers = req.headers || {};
    req.headers['Content-Type'] = req.headers['Content-Type'] || req.headers['Content-type'] || req.headers['content-type'];// 兼容多种写法

    xhr.timeout = req.timeout || 1000000;

    req.method = req.method || 'GET';
    req.async = req.async === false ? false : true;
    req.headers = req.headers || {};

    if (req.method.toLowerCase() !== 'get' && req.method.toLowerCase() !== 'head' && req.method.toLowerCase() !== 'options') {
        if (!req.headers['Content-Type'] || req.headers['Content-Type'].startsWith('application/x-www-form-urlencoded')) {
            req.headers['Content-Type'] = req.headers['Content-Type'] || 'application/x-www-form-urlencoded';
            req.data = formUrlencode(req.data);
        } else if (typeof req.data === 'object' && req.data) {
            req.data = JSON.stringify(req.data);
        }
    }else{
    delete req.headers['Content-Type'];
  }
    if (req.query && typeof req.query === 'object') {
        var getUrl = formUrlencode(req.query);
        req.url = req.url + '?' + getUrl;
        req.query = '';
    }
    xhr.open(req.method, req.url, req.async);
    var response = {};
    if (req.headers) {
        var unsafeHeaderArr = [];
        for (var name in req.headers) {
            if (unsafeHeader.indexOf(name) > -1) {
                unsafeHeaderArr.push({
                    name: name,
                    value: req.headers[name]
                })
            } else {
                xhr.setRequestHeader(name, req.headers[name]);
            }
        }
        if (unsafeHeaderArr.length > 0) {
            xhr.setRequestHeader('cross-request-unsafe-headers-list', encode(unsafeHeaderArr));
        }
    }

    xhr.setRequestHeader('cross-request-open-sign', '1')

    xhr.onload = function (e) {
        var headers = xhr.getAllResponseHeaders();
        headers = handleHeader(headers);
        var newHeaders;
        if(headers['cross-response-unsafe-headers-list']){
            newHeaders = decode(headers['cross-response-unsafe-headers-list'])
            delete headers['cross-response-unsafe-headers-list'];
            if(newHeaders && typeof newHeaders === 'object' && Object.keys(newHeaders).length > 0){
                    headers = newHeaders;
            }
        }
        response = {
            headers: headers,
            status: xhr.status,
            statusText: xhr.statusText,
            body: xhr.responseText
        }
        if (xhr.status == 200) {
            successFn(response);
        } else {
            errorFn(response);
        }
    };
    xhr.ontimeout = function (e) {
        errorFn({
            body: 'Error:Request timeout that the time is ' + xhr.timeout
        })
    };
    xhr.onerror = function (e) {
        errorFn({
            body: xhr.statusText
        })
    };
    xhr.upload.onprogress = function (e) { };

    try {
        xhr.send(req.data);
    } catch (error) {
        errorFn({
            body: error.message
        })
    }
}

chrome.runtime.onConnect.addListener(function (connect) {
    if (connect.name === 'request') {
        connect.onMessage.addListener(function (msg) {
            sendAjax(msg.req, function (res) {
                connect.postMessage({
                    id: msg.id,
                    res: res
                })
            }, function (err) {
                connect.postMessage({
                    id: msg.id,
                    res: err
                })
            })
        })
    }
});

function ensureItem(arr, name, value) {
    if (!arr || !Array.isArray(arr)) {
        return arr;
    }
    var find = false;
    arr = arr.map(function (item) {
        if (item.name == name) {
            item.value = value;
            find = true;
        }
        return item;
    })
    if (find === false) {
        arr.push({ name: name, value: value })
    }
    return arr;
}

 function responseListener(details) {
    if (requestBatch[details.requestId] === true) {
        delete requestBatch[details.requestId];
        var unsafeHeaderArr = { cookie: [] };
        var cookie = unsafeHeaderArr.cookie;
        details.responseHeaders.forEach(function (item) {
            if (item.name === 'Set-Cookie') {
                cookie.push(item.value)
            }
            else {
                unsafeHeaderArr[item.name] = item.value;
            }
        })
        details.responseHeaders.push({
            name: 'cross-response-unsafe-headers-list',
            value: encode(unsafeHeaderArr)
        })
    }
    return { responseHeaders: details.responseHeaders }

}

function requestListener (details) {
    var find = false;
    details.requestHeaders.forEach(function (item, index) {
        if (item.name === 'cross-request-open-sign' && item.value == '1') {
            requestBatch[details.requestId] = true;
        }
        if (item.name === 'cross-request-unsafe-headers-list') {
            var val = decode(item.value);
            val.forEach(function (v) {
                details.requestHeaders = ensureItem(details.requestHeaders, v.name, v.value)
            })
        }
    })

    return { requestHeaders: details.requestHeaders };
}

chrome.webRequest.onHeadersReceived.removeListener(responseListener);
chrome.webRequest.onBeforeSendHeaders.removeListener(requestListener);

chrome.webRequest.onHeadersReceived.addListener(responseListener, {
        urls: ["<all_urls>"]
    }, ['blocking', 'responseHeaders', 'extraHeaders']);

chrome.webRequest.onBeforeSendHeaders.addListener(requestListener, {
        urls: ["<all_urls>"]
    }, ['blocking', 'requestHeaders', 'extraHeaders']);

You May Also Like

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注