среда, 25 апреля 2018 г.

Node.js - Proxy Server

Файл index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Proxy Server</title>
</head>
<body>
    <form method="GET" action="proxy">
        <input name="proxyreq" type="text" value="http://127.0.0.2:80/" />
        <input type="submit" value="Показать" />
    </form>
</body>
</html>

Файл proxy.js (загружает также картинку favicon.png)

var http = require('http')
    , url = require('url')
    , querystring = require('querystring')
    , path = require('path')
    , fs = require('fs')
    , replaceHTML = require('./replaceHTML')
    , replaceCSS = require('./replaceCSS')
    , replaceJavaScript = require('./replaceJavaScript');

var proxyHost = '127.0.0.1'
    , proxyPort = 80;

var proxyServer = http.createServer();

proxyServer.on('request', function (proxyRequest, proxyResponse) {
    var reqUrl = url.parse(proxyRequest.url)
        , pathname = reqUrl.pathname;
    if (pathname === '/' || pathname === 'index.html') {
        fs.readFile(path.resolve(path.join(__dirname, 'index.html')), function (error, data) {
            if (error) {throw error;}
            proxyResponse.writeHead(200, 'OK', {'Content-Type': 'text/html'});
            proxyResponse.write(data);
            proxyResponse.end();
        });
    } else if (pathname === '/favicon.ico') {
        fs.readFile('./favicon.png', function (error, data) {
            if (error) {throw error;}
            response.writeHead(200, 'OK', {'Content-Type': 'image/png', 'Content-Length': Buffer.byteLength(data)});
            response.write(data);
            response.end();
        });
    } else if (pathname.indexOf('/proxyform/') === 0) {
        var proxyreq = pathname.slice('/proxyform/'.length);
        proxyreq = querystring.unescape(proxyreq);
        proxyreq = path.posix.normalize(proxyreq);
        proxyreq = proxyreq.replace('http:/', 'http://');
        var proxyUrl = url.parse(proxyreq);
        var requestOptions = {
              method: proxyRequest.method
            , protocol: proxyUrl.protocol
            , hostname: proxyUrl.hostname
            , port: proxyUrl.port
            , path: proxyUrl.pathname + (proxyRequest.method === 'GET' ? url.parse(proxyRequest.url).search : '')
            , headers: proxyRequest.headers
        };
        var requestToOutterServer = http.request(requestOptions);
        requestToOutterServer.on('response', function (outterServerResponse) {
            var outterServerResponseData = [];
            outterServerResponse.on('data', function (chunk) {outterServerResponseData.push(chunk);});
                outterServerResponse.on('end', function () {
                    if (outterServerResponse.headers['content-type'] === 'text/html') {
                        outterServerResponseData = replaceHTML(
                              outterServerResponseData.join('')
                            , 'http://' + proxyHost + ':' + proxyPort + '/proxy/?proxyreq='
                            , proxyUrl.protocol + '//' + proxyUrl.hostname + ':' + proxyUrl.port
                        );
                        outterServerResponse.headers['content-length'] = Buffer.byteLength(outterServerResponseData);
                        proxyResponse.writeHead(outterServerResponse.statusCode, outterServerResponse.statusMessage, outterServerResponse.headers);
                        proxyResponse.end(outterServerResponseData);
                    } else if (outterServerResponse.headers['content-type'] === 'text/css') {
                        outterServerResponseData = replaceCSS(
                              outterServerResponseData.join('')
                            , 'http://' + proxyHost + ':' + proxyPort + '/proxy/?proxyreq='
                            , proxyUrl.protocol + '//' + proxyUrl.hostname + ':' + proxyUrl.port
                            , path.parse(proxyUrl.pathname).dir
                        );
                        outterServerResponse.headers['content-length'] = Buffer.byteLength(outterServerResponseData);
                        proxyResponse.writeHead(outterServerResponse.statusCode, outterServerResponse.statusMessage, outterServerResponse.headers);
                        proxyResponse.end(outterServerResponseData);
                    } else if (outterServerResponse.headers['content-type'] === 'text/javascript') {
                        outterServerResponseData = replaceJavaScript(
                              outterServerResponseData.join('')
                            , 'http://' + proxyHost + ':' + proxyPort + '/proxy/?proxyreq='
                            , proxyUrl.protocol + '//' + proxyUrl.hostname + ':' + proxyUrl.port
                        );
                        outterServerResponse.headers['content-length'] = Buffer.byteLength(outterServerResponseData);
                        proxyResponse.writeHead(outterServerResponse.statusCode, outterServerResponse.statusMessage, outterServerResponse.headers);
                        proxyResponse.end(outterServerResponseData);
                    } else {
                        proxyResponse.writeHead(outterServerResponse.statusCode, outterServerResponse.statusMessage, outterServerResponse.headers);
                        outterServerResponseData.forEach(function (chunk) {
                            proxyResponse.write(chunk);
                        });
                        proxyResponse.end();
                    }
                });
        });
        requestToOutterServer.on('error', function (error) {
            console.log(requestOptions);
            /*
            console.error(error);
            proxyResponse.writeHead(500, 'Internal Server Error', {'Content-Type': 'text/html'});
            proxyResponse.write('500 Internal Server Error');
            proxyResponse.end();
            */
            throw error;
        });
        if (proxyRequest.method === 'GET') {
            requestToOutterServer.end();
        } else if (proxyRequest.method === 'POST') {
            proxyRequest.on('data', function (chunk) {requestToOutterServer.write(chunk);});
            proxyRequest.on('end', function () {requestToOutterServer.end();});
        }
    } else if (pathname.indexOf('/proxy') === 0) {
        if (reqUrl.search) {
            var searchData = reqUrl.search.slice(1);
            searchData = querystring.parse(searchData);
            var proxyreq = searchData.proxyreq;
            proxyreq = querystring.unescape(proxyreq);
            proxyreq = path.posix.normalize(proxyreq);
            proxyreq = proxyreq.replace('http:/', 'http://');
            var proxyUrl = url.parse(proxyreq);
            delete searchData.proxyreq;
            var requestOptions = {
                  method: proxyRequest.method
                , protocol: proxyUrl.protocol
                , hostname: proxyUrl.hostname
                , port: proxyUrl.port
                , path: proxyUrl.pathname + (proxyRequest.method === 'GET' ? querystring.stringify(searchData) : '')
                , headers: proxyRequest.headers
            };
            var requestToOutterServer = http.request(requestOptions);
            requestToOutterServer.on('response', function (outterServerResponse) {
                var outterServerResponseData = [];
                outterServerResponse.on('data', function (chunk) {outterServerResponseData.push(chunk);});
                outterServerResponse.on('end', function () {
                    if (outterServerResponse.headers['content-type'] === 'text/html') {
                        outterServerResponseData = replaceHTML(
                              outterServerResponseData.join('')
                            , 'http://' + proxyHost + ':' + proxyPort + '/proxy/?proxyreq='
                            , proxyUrl.protocol + '//' + proxyUrl.hostname + ':' + proxyUrl.port
                        );
                        outterServerResponse.headers['content-length'] = Buffer.byteLength(outterServerResponseData);
                        proxyResponse.writeHead(outterServerResponse.statusCode, outterServerResponse.statusMessage, outterServerResponse.headers);
                        proxyResponse.end(outterServerResponseData);
                    } else if (outterServerResponse.headers['content-type'] === 'text/css') {
                        outterServerResponseData = replaceCSS(
                              outterServerResponseData.join('')
                            , 'http://' + proxyHost + ':' + proxyPort + '/proxy/?proxyreq='
                            , proxyUrl.protocol + '//' + proxyUrl.hostname + ':' + proxyUrl.port
                            , path.parse(proxyUrl.pathname).dir
                        );
                        outterServerResponse.headers['content-length'] = Buffer.byteLength(outterServerResponseData);
                        proxyResponse.writeHead(outterServerResponse.statusCode, outterServerResponse.statusMessage, outterServerResponse.headers);
                        proxyResponse.end(outterServerResponseData);
                    } else if (outterServerResponse.headers['content-type'] === 'text/javascript') {
                        outterServerResponseData = replaceJavaScript(
                              outterServerResponseData.join('')
                            , 'http://' + proxyHost + ':' + proxyPort + '/proxy/?proxyreq='
                            , proxyUrl.protocol + '//' + proxyUrl.hostname + ':' + proxyUrl.port
                        );
                        outterServerResponse.headers['content-length'] = Buffer.byteLength(outterServerResponseData);
                        proxyResponse.writeHead(outterServerResponse.statusCode, outterServerResponse.statusMessage, outterServerResponse.headers);
                        proxyResponse.end(outterServerResponseData);
                    } else {
                        proxyResponse.writeHead(outterServerResponse.statusCode, outterServerResponse.statusMessage, outterServerResponse.headers);
                        outterServerResponseData.forEach(function (chunk) {
                            proxyResponse.write(chunk);
                        });
                        proxyResponse.end();
                    }
                });
            });
            requestToOutterServer.on('error', function (error) {
                console.log(requestOptions);
                /*
                console.error(error);
                proxyResponse.writeHead(500, 'Internal Server Error', {'Content-Type': 'text/html'});
                proxyResponse.write('500 Internal Server Error');
                proxyResponse.end();
                */
                throw error;
            });
            if (proxyRequest.method === 'GET') {
                requestToOutterServer.end();
            } else if (proxyRequest.method === 'POST') {
                proxyRequest.on('data', function (chunk) {requestToOutterServer.write(chunk);});
                proxyRequest.on('end', function () {requestToOutterServer.end();});
            }
        } else {
            proxyResponse.writeHead(200, 'OK', {'Content-Type': 'text/html'});
            proxyResponse.write('<p>Адрес не задан.</p>');
            proxyResponse.write('<a href="/">Вернуться на страницу ввода адреса.</a>');
            proxyResponse.end();
        }
    } else {
        proxyResponse.writeHead(404, 'Not Found', {'Content-Type': 'text/html'});
        proxyResponse.write('404 Not Found');
        proxyResponse.end();
    }
});

proxyServer.on('error', function (error) {throw error;});

proxyServer.listen(proxyPort, proxyHost, function () {
    console.log('Proxy server started at ' + proxyHost + ':' + proxyPort);
});

Файл replaceHTML.js

var querystring = require('querystring');

function replacePath (htmlString, regexp, proxyServerAddress, outterServerAddress) {
    return htmlString.replace(regexp, function (match, $1, $2, $3) {
        var newPath;
        if ($2.indexOf('http') === 0) {
             newPath = proxyServerAddress + querystring.escape($2);
        } else if ($2.indexOf('/') === 0) {
             newPath = proxyServerAddress + querystring.escape(outterServerAddress + $2);
        } else {
             newPath = proxyServerAddress + querystring.escape(outterServerAddress + '/' + $2);
        }
        return $1 + newPath + $3;
    });
}

function replaceHref (htmlString, proxyServerAddress, outterServerAddress) {
    return replacePath(htmlString, /(href=")([^"]+)(")/gi, proxyServerAddress, outterServerAddress)
}

/*
console.log(replaceHref([
      '<link href="style1.css" rel="stylesheet" type="text/css" />'
    , '<link href="/style2.css" rel="stylesheet" type="text/css" />'
    , '<link href="http://www.google.com/style3.css" rel="stylesheet" type="text/css" />'
].join(' | '), 'http://127.0.0.1:80/?proxyreq=', 'http://127.0.0.2:80'));
*/

function replaceSrc (htmlString, proxyServerAddress, outterServerAddress) {
    return replacePath(htmlString, /(src=")([^"]+)(")/gi, proxyServerAddress, outterServerAddress)
}

/*
console.log(replaceSrc([
      '<img src="image1.png" />'
    , '<img src="/image2.png" />'
    , '<img src="http://www.google.com/image3.png" />'
].join(' | '), 'http://127.0.0.1:80/?proxyreq=', 'http://127.0.0.2:80'));
*/

function replaceAction (htmlString, proxyServerAddress, outterServerAddress) {
    proxyServerAddress = proxyServerAddress.replace('/proxy/?proxyreq=', '/proxyform/');
    return htmlString.replace(/(action=")([^"]+)(")/gi, function (match, $1, $2, $3) {
        var newPath;
        if ($2.indexOf('http') === 0) {
             newPath = proxyServerAddress + querystring.escape($2);
        } else if ($2.indexOf('/') === 0) {
             newPath = proxyServerAddress + querystring.escape(outterServerAddress + $2);
        } else {
             newPath = proxyServerAddress + querystring.escape(outterServerAddress + '/' + $2);
        }
        return $1 + newPath + $3;
    });
    // return replacePath(htmlString, /(action=")([^"]+)(")/gi, proxyServerAddress, outterServerAddress)
}

/*
console.log(replaceAction([
      '<form action="submit" method="GET"><input name= "a" type="texy" value="1" /><input type="submit" value="Submit 1" /></form>'
    , '<form action="/submit" method="GET"><input name= "b" type="texy" value="2" /><input type="submit" value="Submit 2" /></form>'
    , '<form action="http://127.0.0.2:80/submit" method="GET"><input name= "c" type="texy" value="3" /><input type="submit" value="Submit 3" /></form>'
].join(' | '), 'http://127.0.0.1:80/?proxyreq=', 'http://127.0.0.2:80'));
*/

function replaceData (htmlString, proxyServerAddress, outterServerAddress) {
    return replacePath(htmlString, /(data=")([^"]+)(")/gi, proxyServerAddress, outterServerAddress)
}

/*
console.log(replaceData([
      '<object data="video.mp4" />'
    , '<object data="/video.mp4" />'
    , '<object data="http://www.google.com/video.mp4" />'
].join(' | '), 'http://127.0.0.1:80/?proxyreq=', 'http://127.0.0.2:80'));
*/

function replaceXMLHttpRequest (htmlString, proxyServerAddress, outterServerAddress) {
    return htmlString.replace(/<head>/gi, function () {
        return '<head>' + [
                    '<script type="text/javascript">'
                        + ';(function (open) {'
                            + 'window.XMLHttpRequest.prototype.open = function (method, url, async) {'
                                + 'var newUrl;'
                                + 'if (url.indexOf("http") === 0) {'
                                     + 'newUrl = "' + proxyServerAddress + '" + window.encodeURIComponent(url);'
                                + '} else if (url.indexOf("/") === 0) {'
                                     + 'newUrl = "' + proxyServerAddress + '" + window.encodeURIComponent("' + outterServerAddress + '" + url);'
                                + '} else {'
                                     + 'newUrl = "' + proxyServerAddress + '" + window.encodeURIComponent("' + outterServerAddress + '" + "/" + url);'
                                + '}'
                                + 'open.call(this, method, newUrl, async);'
                            + '};'
                        + '})(window.XMLHttpRequest.prototype.open);'
                    + '</script>'
                ].join('');
    });
}

/*
console.log(replaceXMLHttpRequest([
    '<!DOCTYPE html>'
    + '<html>'
    + '<head>'
    + '<link href="favicon.png" rel="icon" type="image/png" />'
    + '<link href="static/css/style1.css" rel="stylesheet" type="text/css" />'
    + '<link href="/static/css/style2.css" rel="stylesheet" type="text/css" />'
    + '<link href="http://127.0.0.2:80/static/css/style3.css" rel="stylesheet" type="text/css" />'
    + '<script src="static/js/script1.js"></script>'
    + '<script src="/static/js/script2.js"></script>'
    + '<script src="http://127.0.0.2:80/static/js/script3.js"></script>'
    + '<meta charset="utf-8" />'
    + '<title>Test page</title>'
    + '</head>'
].join('\n'), 'http://127.0.0.1:80/?proxyreq=', 'http://127.0.0.2:80'));
*/

module.exports = function replaceHTML (htmlString, proxyServerAddress, outterServerAddress) {
    htmlString = replaceHref(htmlString, proxyServerAddress, outterServerAddress);
    htmlString = replaceSrc(htmlString, proxyServerAddress, outterServerAddress);
    htmlString = replaceAction(htmlString, proxyServerAddress, outterServerAddress);
    htmlString = replaceData(htmlString, proxyServerAddress, outterServerAddress);
    htmlString = replaceXMLHttpRequest(htmlString, proxyServerAddress, outterServerAddress);
    return htmlString;
};

Файл replaceCSS.js

function replaceUrl (cssString, proxyServerAddress, outterServerAddress, rootPath) {
    return cssString.replace(/url\([^\)]+\)/gi, function (match) {
        match = match.replace(/url\(/gi, '');
        match = match.replace(/\)/gi, '');
        match = match.replace(/"/gi, '');
        match = match.replace(/'/gi, '');
        match = match.replace(/^\s+/, '');
        match = match.replace(/\s+$/, '');
        if (!(match.indexOf('/') === 0 || match.indexOf('http') === 0)) {
            match = joinPaths(rootPath, match);
        }
        var newPath;
        if (match.indexOf('http') === 0) {
            newPath = proxyServerAddress + match;
        } else if (match.indexOf('/') === 0) {
             newPath = proxyServerAddress + outterServerAddress + match;
        } else {
             newPath = proxyServerAddress + outterServerAddress + '/' + match;
        }
        return 'url("' + newPath + '")';
    });
}

function joinPaths (root, path) {
    if (path.indexOf('../') === 0) {
        if (root.indexOf('/') === root.lastIndexOf('/')) {
            if (root[root.length - 1] === '/') {root = root.slice(0, root.length - 1);}
            if (path[0] === '/') {path = path.slice(1);}
            return root + '/' + path;
        } else {
            path = path.slice(3);
            root = root.slice(0, root.lastIndexOf('/'));
            return joinPaths(root, path);
        }
    } else {
        if (root[root.length - 1] === '/') {root = root.slice(0, root.length - 1);}
        if (path[0] === '/') {path = path.slice(1);}
        return root + '/' + path;
    }
}

/*
console.log(replaceUrl([
      'body {background: url(image.png) repeat-x repeat-y;}'
    , 'div {background: url(/image.png) repeat-x repeat-y;}'
    , 'p {background: url(http://www.google.com/image.png) repeat-x repeat-y;}'

    , 'body {background: url("image.png") repeat-x repeat-y;}'
    , 'div {background: url("/image.png") repeat-x repeat-y;}'
    , 'p {background: url("http://www.google.com/image.png") repeat-x repeat-y;}'

    , '@import url(\'style2.css\');'
    , '@import url("/style2.css");'
    , '@import url("http://www.google.com/style2.css");'
].join(' | '), 'http://127.0.0.1:80/?proxyreq=', 'http://127.0.0.2:80'));
*/

module.exports = function replaceCSS (cssString, proxyServerAddress, outterServerAddress, rootPath) {
    cssString = replaceUrl(cssString, proxyServerAddress, outterServerAddress, rootPath);
    return cssString;
};

Файл replaceJavaScript.js

var querystring = require('querystring');

function replacePath (javascriptString, regexp, proxyServerAddress, outterServerAddress) {
    return javascriptString.replace(regexp, function (match, $1, $2, $3, $4) {
        var newPath;
        if ($3.indexOf('http') === 0) {
             newPath = proxyServerAddress + querystring.escape($3);
        } else if ($3.indexOf('/') === 0) {
             newPath = proxyServerAddress + querystring.escape(outterServerAddress + $3);
        } else {
             newPath = proxyServerAddress + querystring.escape(outterServerAddress + '/' + $3);
        }
        return $1 + $2 + newPath + $4;
    });
}

function replaceVarPath (javascriptString, regexp, proxyServerAddress, outterServerAddress) {
    return javascriptString.replace(regexp, function (match, $1, $2) {
        return $1 + '"' + proxyServerAddress + querystring.escape(outterServerAddress) + '/" + ' + $2;
    });
}

function replaceVarSrc (javascriptString, proxyServerAddress, outterServerAddress) {
    return replaceVarPath(javascriptString, /(\.\s*src\s*=\s*)([a-zA-Z$_]+)/gi, proxyServerAddress, outterServerAddress);
}

/*
console.log(replaceVarSrc([
      'img . src = someVar'
    , 'img.src = someVar + "/path"'
].join(' | '), 'http://127.0.0.1:80/?proxyreq=', 'http://127.0.0.2:80'));
*/

function replaceSrc (javascriptString, proxyServerAddress, outterServerAddress) {
    return replacePath(javascriptString, /(\.\s*src\s*=\s*)('|")([^'"]+)('|")/gi, proxyServerAddress, outterServerAddress);
}

/*
console.log(replaceSrc([
      'img . src = \'path\''
    , 'img.src = "/path"'
    , 'img.src="http://www.google.com/path"'
].join(' | '), 'http://127.0.0.1:80/?proxyreq=', 'http://127.0.0.2:80'));
*/

function replaceHref (javascriptString, proxyServerAddress, outterServerAddress) {
    return replacePath(javascriptString, /(\.\s*href\s*=\s*)('|")([^'"]+)('|")/gi, proxyServerAddress, outterServerAddress);
}

/*
console.log(replaceHref([
      'location . href = \'path\''
    , 'location.href = "/path"'
    , 'location.href="http://www.google.com/path"'
    , 'a . href = \'path\''
    , 'a.href = "/path"'
    , 'a.href="http://www.google.com/path"'
].join(' | '), 'http://127.0.0.1:80/?proxyreq=', 'http://127.0.0.2:80'));
*/

function replaceVarHref (javascriptString, proxyServerAddress, outterServerAddress) {
    return replaceVarPath(javascriptString, /(\.\s*href\s*=\s*)([a-zA-Z$_]+)/gi, proxyServerAddress, outterServerAddress);
}

/*
console.log(replaceVarSrc([
      'img . href = someVar'
    , 'img.href = someVar + "/path"'
].join(' | '), 'http://127.0.0.1:80/?proxyreq=', 'http://127.0.0.2:80'));
*/

module.exports = function replaceJavaScript (javascriptString, proxyServerAddress, outterServerAddress) {
    javascriptString = replaceSrc(javascriptString, proxyServerAddress, outterServerAddress);
    javascriptString = replaceVarSrc(javascriptString, proxyServerAddress, outterServerAddress);
    javascriptString = replaceHref(javascriptString, proxyServerAddress, outterServerAddress);
    javascriptString = replaceVarHref(javascriptString, proxyServerAddress, outterServerAddress);
    return javascriptString;
};

пятница, 6 апреля 2018 г.

Node.js Socket Client HTTP Request example

var net = require('net');

var clientSocket = new net.Socket();

clientSocket.on('connect', function () {
    console.log('Connected to Google.');
});

clientSocket.on('data', function (data) {
    console.log(data.toString());
});

clientSocket.on('end', function () {
    console.log('Google sent all data.');
});

clientSocket.on('close', function () {
    console.log('Connection closed.');
});

clientSocket.on('error', function (error) {
   throw error;
});

clientSocket.connect(80, 'google.com', function (error) {
    if (error) {throw error;}
});

clientSocket.write('GET /index.html HTTP/1.0'); // Передаем тип запроса GET или POST и какую страницу хотим загрузить - index.html
clientSocket.write('Accept-Encoding: gzip, compress');
// clientSocket.write('Host: 127.0.0.1');
// clientSocket.write('User-Agent: My programm');
// clientSocket.write('Accept-Language: en');
clientSocket.write(''); // Обязательно должна в конце отправлять пустая строка.
clientSocket.end();

понедельник, 26 марта 2018 г.

Ошибка System.js traceur.js 404 Not Found - Как исправить - Инструкция

При загрузке файлов с помощью SystemJS часто возникает ошибка:
http://localhost:8000/traceur 404 (Not Found)

Возникает она, как правило, из-за того, что скорее всего в своем коде вы с помощью многострочных комментариев закомментировали импорты в своих файлах/

/*
import SomeThing from 'somewhere';
*/

В коде файла system.js есть регулярное выражение, которое обнаруживает закомментированные импорты и пытается из обработать, что приводит к ошибке.

var esmRegEx = /(^\s*|[}\);\n]\s*)(import\s*(['"]|(\*\s+as\s+)?[^"'\(\)\n;]+\s*from\s*['"]|\{)|export\s+\*\s+from\s+["']|export\s*(\{|default|function|class|var|const|let|async\s+function))/;

Для того. чтобы ошибку устранить вам надо удалить закомментированные импорты из вашего кода.

Или, если вам надо оставить комментарии, то чтобы ошибка не проявлялась, просто добавьте кавычки вокруг слова import.

/*
"import" SomeThing from 'somewhere';
*/

В крайнем случае в конфиге tsconfig.json для TypeScript можно прописать настройку, чтобы он не оставлял в коде комментарии при транспиляции.

{
    "compilerOptions": {
        "removeComments": true
    }
}

Также ошибка может быть вызвана тем, что вы пытаетесь загрузить в своем коде некорректный UMD-файл.

Убедитесь также, что вы загружаете JavaScript, а не TypeScript-файлы.

среда, 21 марта 2018 г.

Вынос внешних файлов из сборки Webpack

Файл webpack.config.js

const path = require('path')
        , CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = [
    {
          entry: path.resolve(__dirname, 'src/index.js')
        , output: {
              path: path.resolve(__dirname, 'dist')
            , filename: 'bundle.js'
          }
        , externals: { // Модули, которые надо из итогового файла сборки исключить. Они будут загружаться отдельно.
              './my-module.js': 'a'
            , './lodash.js': '_' // Заменить импорт модуля на имя этой глобальной перемеменной: require('lodash') --> var _ В итоговый файл попадет: {1: function (...) {module.exports = _;}}
            , './jquery.js': '$' // Заменить импорт модуля на имя этой глобальной перемеменной: require('jquery') --> var jQuery В итоговый файл попадет: {1: function (...) {module.exports = jQuery;}}
          }
        , plugins: [
              new CleanWebpackPlugin(['dist'])
          ]
        , bail: true
    }
];

Файл index.js

import a from './my-module.js';
import * as _ from './lodash.js';
import * as $ from './jquery.js';

a();
console.log(_.tail([1, 2, 3]));
console.log($);

Файл my-module.js

export function a () {
    console.log(1);
}

Скомпилированный файл my-module-compiled.js

function a () {
    console.log(1);
}

Файл index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <script src="src/my-module-compiled.js"></script>
    <script src="src/lodash.js"></script>
    <script src="src/jquery.js"></script>
    <title>Webpack App</title>
  </head>
  <body>
    <script src="dist/bundle.js"></script>
  </body>
</html>

Система преобразования типов в JavaScript.

Система преобразования типов в JavaScript очень простая.

В JavaScript есть всего 3 приведения типов:

1) Строковое - String() - приведение к строке в строковом контексте. Происходит, например, при конкатенации строк ("abc" + 10). Строковое преобразование происходит при необходимости представления чего-либо в виде строки.

2) Числовое - Number() - приведение к примитиву в числовом контексте, включая унарный плюс (+value). Происходит при сравнении разных типов (==, !=, >=, <=). Числовое преобразование происходит в математических выражениях (_, *, /, %), а также при сравнении.

3) Логическое - Boolean() - приведение к логическому типу в логическом контексте (if, &&, ||). Логическое преобразование происходит при приведение к true и false в логическом контексте, например в условиях и при применении логических операторов. Все значения, которые можно трактовать как пустые, становятся false: пустая строка, 0, null, undefined, NaN. Все остальное трактовать как true.

Особым случаем является проверка на равенство таких специальных типов как Null и Undefined. Они равны только друг другу и неравны всему остальному:

null == null
undefined == undefined
undefined == null
null == undefined

В JavaScript логическое преобразование особенно интересно своим сочетанием с числовым.

Приведем пример.

var a = 0;
var b = "0";
console.log(a == b); // true

Когда мы сравниваем число 0 и строку "0", то мы сравниваем их не в логическом контексте, а в числовом.
Поэтому у нас получается такое сравнение с преобразованием типов:

console.log(Number(0) == Number("0")); // true

Разберем другой код:

var bool = new Boolean(false); // bool = object
if (bool) {console.log(bool);} // Boolean(bool) = true, поэтому if (true) {console.log(false);}
if (bool == false) {console.log(bool);} // bool.valueOf() = false, поэтому if (false == false) {console.log(false);}

Мы используем класс Boolean, который создает не примитив, а объект bool - экземпляр класса Boolean.
Если bool  -  это объект, то в логическом контексте он всегда будет приведен к true.
Если мы производим сравнение, то тут сработает числовое сравнение, при котором будет вызван метод bool.valueOf().

пятница, 16 марта 2018 г.

Browser XMLHttpRequest and Node Server

Файл server.js

var http = require('http')
    , fs = require('fs')
    , path = require('path')
    , url = require('url')
    , querystring = require('querystring');

var server = http.createServer();

server.on('request', function (request, response) {
    if (request.method === 'POST' && url.parse(request.url).path === '/ajax') {
        var data = [];
        request.on('data', function (chunk) {data.push(chunk)});
        request.on('end', function () {
            setTimeout(function () {
                response.writeHead(200, {'Content-Type': 'text/plain'});
                var query = querystring.parse(data.join());
                response.end(query.data);
            }, 1000);
        });
    } else {
        fs.readFile(path.resolve(path.join(__dirname, 'index.html')), function (error, indexHTML) {
            if (error) {throw error;}
            response.writeHead(200, {'Content-Type': 'text/html'});
            response.end(indexHTML);
        });
    }
});

server.listen(80, '127.0.0.1', function (error) {
    if (error) {throw error;}
    console.log('Server started at 127.0.0.1:80');
});

Файл index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>XMLHttpRequest</title>
</head>
<body>
    <div id="server-response"></div>
    <script type="text/javascript">
        setTimeout(function () {
            var request = new XMLHttpRequest();
            if (request.readyState === 0) {
                console.log('readyState 0 UNSENT: ' + (request.readyState === request.UNSENT));
            }
            request.open('POST', '/ajax', true, 'username', 'password');
            if (request.readyState === 1) {
                console.log('readyState 1 OPENED: ' + (request.readyState === request.OPENED));
            }
            request.onreadystatechange = function () {
                if (request.readyState === 2) {
                    console.log('readyState 2 HEADERS_RECEIVED: ' + (request.readyState === request.HEADERS_RECEIVED));
                }
                if (request.readyState === 3) {
                    console.log('readyState 3 LOADING: ' + (request.readyState === request.LOADING));
                }
                if (request.readyState === 4) {
                    console.log('readyState 4 DONE: ' + (request.readyState === request.DONE));
                }
                if (request.readyState === 4) {
                    var response = this;
                    if (response.status === 200) {
                        document.getElementById('server-response').innerHTML = (
                               '<p>RESPONSE STATUS:</p>'
                            + '<p>' + response.status + '</p>'
                            + '<p>RESPONSE STATUS TEXT:</p>'
                            + '<p>' + response.statusText + '</p>'
                            + '<p>HEADERS:</p>'
                            + '<p>' + response.getAllResponseHeaders() + '</p>'
                            + '<p>HEADER CONTENT_TYPE:</p>'
                            + '<p>' + response.getResponseHeader('Content-Type') + '</p>'
                            + '<p>RESPONSE URL:</p>'
                            + '<p>' + response.responseURL + '</p>'
                            + '<p>RESPONSE:</p>'
                            + '<p>' +response.response + '</p>'
                            + '<p>RESPONSE TEXT:</p>'
                            + '<p>' + response.responseText + '</p>'
                        );
                    } else {
                        document.getElementById('server-response').innerHTML = 'Error. Status code: ' + response.status + ', status text: ' + response.statusText;
                    }
                }
            };
            request.timeout = 2000;
            request.ontimeout = function () {document.getElementById('server-response').innerHTML = 'Timeout'};
            request.onabort = function () {document.getElementById('server-response').innerHTML = 'Abort'};
            request.onerror = function (event) {document.getElementById('server-response').innerHTML = 'Error. Server is not available. Status code: ' + event.target.status;};
            if (request.overrideMimeType) {request.overrideMimeType('text/plain', 'x-user-defined');}
            request.setRequestHeader('My-Header', 'abcdef');
            request.withCredentials = true;
            request.responseType = 'text';
            request.send('data=123&value=456');
            setTimeout(function () {request.abort();}, 500);
        }, 5000);
    </script>
</body>
</html>

четверг, 15 марта 2018 г.

new XMLHttpRequest()

var xhr = new XMLHttpRequest();

xhr.open('POST', 'http://www.site.com/', true, 'username', 'password');

xhr.withCredentials = true;

xhr.timeout = 120000;

xhr.setRequestHeader('My-Header', '123');

xhr.overrideMimeType('text/plain; charset=x-user-defined');

xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
        if (xhr.status === 200 || xhr.status === 0) {
            console.log('status: ' + xhr.status === 1223 ? 204 : xhr.status);
            console.log('statusText: ' + xhr.status === 1223 ? 'No Content' : xhr.statusText);
            console.log('getAllResponseHeaders: ' + xhr.getAllResponseHeaders());
            console.log('getResponseHeader: ' + xhr.getResponseHeader('Content-Type'));
            console.log('responseText: ' + xhr.responseText);
        } else {
            console.log('Error in request.');
        }
    }
};

xhr.onabort = function () {
    if (!xhr) {return;}
    xhr = null;
    console.log('Aborted.');
};

xhr.ontimeout = function () {
    xhr = null;
    console.log('Timeout.');
};

xhr.onerror = function (error) {
    xhr = null;
    console.log('Error.');
    console.log(error.message);
};

xhr.send(JSON.stringify({a: 1, b: 2}));

setTimeout(function () {
    if (xhr) {xhr.abort();}
}, 1000);